Merge remote-tracking branch 'origin/master' into llvm8
This commit is contained in:
commit
f32f7a937f
@ -12,28 +12,28 @@ tasks:
|
||||
ninja install
|
||||
- test: |
|
||||
cd zig/build
|
||||
bin/zig test ../test/behavior.zig
|
||||
bin/zig test ../test/stage1/behavior.zig
|
||||
bin/zig test ../std/special/compiler_rt/index.zig
|
||||
|
||||
bin/zig test ../test/behavior.zig --library c
|
||||
bin/zig test ../test/stage1/behavior.zig --library c
|
||||
bin/zig test ../std/special/compiler_rt/index.zig --library c
|
||||
|
||||
bin/zig test ../test/behavior.zig --release-fast
|
||||
bin/zig test ../test/stage1/behavior.zig --release-fast
|
||||
bin/zig test ../std/special/compiler_rt/index.zig --release-fast
|
||||
|
||||
bin/zig test ../test/behavior.zig --release-fast --library c
|
||||
bin/zig test ../test/stage1/behavior.zig --release-fast --library c
|
||||
bin/zig test ../std/special/compiler_rt/index.zig --release-fast --library c
|
||||
|
||||
bin/zig test ../test/behavior.zig --release-small --library c
|
||||
bin/zig test ../test/stage1/behavior.zig --release-small --library c
|
||||
bin/zig test ../std/special/compiler_rt/index.zig --release-small --library c
|
||||
|
||||
bin/zig test ../test/behavior.zig --release-small
|
||||
bin/zig test ../test/stage1/behavior.zig --release-small
|
||||
bin/zig test ../std/special/compiler_rt/index.zig --release-small
|
||||
|
||||
bin/zig test ../test/behavior.zig --release-safe
|
||||
bin/zig test ../test/stage1/behavior.zig --release-safe
|
||||
bin/zig test ../std/special/compiler_rt/index.zig --release-safe
|
||||
|
||||
bin/zig test ../test/behavior.zig --release-safe --library c
|
||||
bin/zig test ../test/stage1/behavior.zig --release-safe --library c
|
||||
bin/zig test ../std/special/compiler_rt/index.zig --release-safe --library c
|
||||
# TODO enable all tests
|
||||
#bin/zig build --build-file ../build.zig test
|
||||
|
@ -454,10 +454,10 @@ set(ZIG_STD_FILES
|
||||
"crypto/hmac.zig"
|
||||
"crypto/index.zig"
|
||||
"crypto/md5.zig"
|
||||
"crypto/poly1305.zig"
|
||||
"crypto/sha1.zig"
|
||||
"crypto/sha2.zig"
|
||||
"crypto/sha3.zig"
|
||||
"crypto/poly1305.zig"
|
||||
"crypto/x25519.zig"
|
||||
"cstr.zig"
|
||||
"debug/failing_allocator.zig"
|
||||
@ -566,9 +566,9 @@ set(ZIG_STD_FILES
|
||||
"math/tan.zig"
|
||||
"math/tanh.zig"
|
||||
"math/trunc.zig"
|
||||
"mem.zig"
|
||||
"meta/index.zig"
|
||||
"meta/trait.zig"
|
||||
"mem.zig"
|
||||
"mutex.zig"
|
||||
"net.zig"
|
||||
"os/child_process.zig"
|
||||
@ -576,16 +576,16 @@ set(ZIG_STD_FILES
|
||||
"os/darwin/errno.zig"
|
||||
"os/epoch.zig"
|
||||
"os/file.zig"
|
||||
"os/freebsd/errno.zig"
|
||||
"os/freebsd/index.zig"
|
||||
"os/get_app_data_dir.zig"
|
||||
"os/get_user_id.zig"
|
||||
"os/index.zig"
|
||||
"os/linux/arm64.zig"
|
||||
"os/linux/errno.zig"
|
||||
"os/linux/index.zig"
|
||||
"os/linux/vdso.zig"
|
||||
"os/linux/x86_64.zig"
|
||||
"os/linux/arm64.zig"
|
||||
"os/freebsd/errno.zig"
|
||||
"os/freebsd/index.zig"
|
||||
"os/path.zig"
|
||||
"os/time.zig"
|
||||
"os/uefi.zig"
|
||||
@ -612,6 +612,16 @@ set(ZIG_STD_FILES
|
||||
"special/compiler_rt/comparetf2.zig"
|
||||
"special/compiler_rt/divti3.zig"
|
||||
"special/compiler_rt/extendXfYf2.zig"
|
||||
"special/compiler_rt/fixdfdi.zig"
|
||||
"special/compiler_rt/fixdfsi.zig"
|
||||
"special/compiler_rt/fixdfti.zig"
|
||||
"special/compiler_rt/fixint.zig"
|
||||
"special/compiler_rt/fixsfdi.zig"
|
||||
"special/compiler_rt/fixsfsi.zig"
|
||||
"special/compiler_rt/fixsfti.zig"
|
||||
"special/compiler_rt/fixtfdi.zig"
|
||||
"special/compiler_rt/fixtfsi.zig"
|
||||
"special/compiler_rt/fixtfti.zig"
|
||||
"special/compiler_rt/fixuint.zig"
|
||||
"special/compiler_rt/fixunsdfdi.zig"
|
||||
"special/compiler_rt/fixunsdfsi.zig"
|
||||
@ -622,16 +632,6 @@ set(ZIG_STD_FILES
|
||||
"special/compiler_rt/fixunstfdi.zig"
|
||||
"special/compiler_rt/fixunstfsi.zig"
|
||||
"special/compiler_rt/fixunstfti.zig"
|
||||
"special/compiler_rt/fixint.zig"
|
||||
"special/compiler_rt/fixdfdi.zig"
|
||||
"special/compiler_rt/fixdfsi.zig"
|
||||
"special/compiler_rt/fixdfti.zig"
|
||||
"special/compiler_rt/fixsfdi.zig"
|
||||
"special/compiler_rt/fixsfsi.zig"
|
||||
"special/compiler_rt/fixsfti.zig"
|
||||
"special/compiler_rt/fixtfdi.zig"
|
||||
"special/compiler_rt/fixtfsi.zig"
|
||||
"special/compiler_rt/fixtfti.zig"
|
||||
"special/compiler_rt/floattidf.zig"
|
||||
"special/compiler_rt/floattisf.zig"
|
||||
"special/compiler_rt/floattitf.zig"
|
||||
@ -656,6 +656,7 @@ set(ZIG_STD_FILES
|
||||
"special/panic.zig"
|
||||
"special/test_runner.zig"
|
||||
"spinlock.zig"
|
||||
"statically_initialized_mutex.zig"
|
||||
"unicode.zig"
|
||||
"zig/ast.zig"
|
||||
"zig/index.zig"
|
||||
|
21
build.zig
21
build.zig
@ -104,7 +104,7 @@ pub fn build(b: *Builder) !void {
|
||||
}
|
||||
const modes = chosen_modes[0..chosen_mode_index];
|
||||
|
||||
test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", modes));
|
||||
test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes));
|
||||
|
||||
test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes));
|
||||
|
||||
@ -189,14 +189,14 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
|
||||
const prefix_output = try b.exec([][]const u8{ llvm_config_exe, "--prefix" });
|
||||
|
||||
var result = LibraryDep{
|
||||
.prefix = mem.split(prefix_output, " \r\n").next().?,
|
||||
.prefix = mem.tokenize(prefix_output, " \r\n").next().?,
|
||||
.libs = ArrayList([]const u8).init(b.allocator),
|
||||
.system_libs = ArrayList([]const u8).init(b.allocator),
|
||||
.includes = ArrayList([]const u8).init(b.allocator),
|
||||
.libdirs = ArrayList([]const u8).init(b.allocator),
|
||||
};
|
||||
{
|
||||
var it = mem.split(libs_output, " \r\n");
|
||||
var it = mem.tokenize(libs_output, " \r\n");
|
||||
while (it.next()) |lib_arg| {
|
||||
if (mem.startsWith(u8, lib_arg, "-l")) {
|
||||
try result.system_libs.append(lib_arg[2..]);
|
||||
@ -210,7 +210,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
|
||||
}
|
||||
}
|
||||
{
|
||||
var it = mem.split(includes_output, " \r\n");
|
||||
var it = mem.tokenize(includes_output, " \r\n");
|
||||
while (it.next()) |include_arg| {
|
||||
if (mem.startsWith(u8, include_arg, "-I")) {
|
||||
try result.includes.append(include_arg[2..]);
|
||||
@ -220,7 +220,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
|
||||
}
|
||||
}
|
||||
{
|
||||
var it = mem.split(libdir_output, " \r\n");
|
||||
var it = mem.tokenize(libdir_output, " \r\n");
|
||||
while (it.next()) |libdir| {
|
||||
if (mem.startsWith(u8, libdir, "-L")) {
|
||||
try result.libdirs.append(libdir[2..]);
|
||||
@ -233,7 +233,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
|
||||
}
|
||||
|
||||
pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
|
||||
var it = mem.split(stdlib_files, ";");
|
||||
var it = mem.tokenize(stdlib_files, ";");
|
||||
while (it.next()) |stdlib_file| {
|
||||
const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable;
|
||||
const dest_path = os.path.join(b.allocator, "lib", "zig", "std", stdlib_file) catch unreachable;
|
||||
@ -242,7 +242,7 @@ pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
|
||||
}
|
||||
|
||||
pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void {
|
||||
var it = mem.split(c_header_files, ";");
|
||||
var it = mem.tokenize(c_header_files, ";");
|
||||
while (it.next()) |c_header_file| {
|
||||
const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable;
|
||||
const dest_path = os.path.join(b.allocator, "lib", "zig", "include", c_header_file) catch unreachable;
|
||||
@ -277,7 +277,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
|
||||
addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
|
||||
if (ctx.lld_include_dir.len != 0) {
|
||||
exe.addIncludeDir(ctx.lld_include_dir);
|
||||
var it = mem.split(ctx.lld_libraries, ";");
|
||||
var it = mem.tokenize(ctx.lld_libraries, ";");
|
||||
while (it.next()) |lib| {
|
||||
exe.addObjectFile(lib);
|
||||
}
|
||||
@ -299,8 +299,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
|
||||
} else if (exe.target.isFreeBSD()) {
|
||||
try addCxxKnownPath(b, ctx, exe, "libc++.a", null);
|
||||
exe.linkSystemLibrary("pthread");
|
||||
}
|
||||
else if (exe.target.isDarwin()) {
|
||||
} else if (exe.target.isDarwin()) {
|
||||
if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) {
|
||||
// Compiler is GCC.
|
||||
try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null);
|
||||
@ -335,7 +334,7 @@ fn addCxxKnownPath(
|
||||
ctx.cxx_compiler,
|
||||
b.fmt("-print-file-name={}", objname),
|
||||
});
|
||||
const path_unpadded = mem.split(path_padded, "\r\n").next().?;
|
||||
const path_unpadded = mem.tokenize(path_padded, "\r\n").next().?;
|
||||
if (mem.eql(u8, path_unpadded, objname)) {
|
||||
if (errtxt) |msg| {
|
||||
warn("{}", msg);
|
||||
|
@ -1531,6 +1531,29 @@ test "array initialization with function calls" {
|
||||
{#code_end#}
|
||||
{#see_also|for|Slices#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|Vectors#}
|
||||
<p>
|
||||
A vector is a group of {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on
|
||||
in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin
|
||||
function {#link|@Vector#}.
|
||||
</p>
|
||||
<p>
|
||||
TODO talk about C ABI interop
|
||||
</p>
|
||||
{#header_open|SIMD#}
|
||||
<p>
|
||||
TODO Zig's SIMD abilities are just beginning to be fleshed out. Here are some talking points to update the
|
||||
docs with:
|
||||
* What kind of operations can you do? All the operations on integers and floats? What about mixing scalar and vector?
|
||||
* How to convert to/from vectors/arrays
|
||||
* How to access individual elements from vectors, how to loop over the elements
|
||||
* "shuffle"
|
||||
* Advice on writing high perf software, how to abstract the best way
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|Pointers#}
|
||||
<p>
|
||||
Zig has two kinds of pointers:
|
||||
@ -4327,7 +4350,7 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {
|
||||
<p>
|
||||
For example, if we were to introduce another function to the above snippet:
|
||||
</p>
|
||||
{#code_begin|test_err|unable to evaluate constant expression#}
|
||||
{#code_begin|test_err|values of type 'type' must be comptime known#}
|
||||
fn max(comptime T: type, a: T, b: T) T {
|
||||
return if (a > b) a else b;
|
||||
}
|
||||
@ -5905,13 +5928,13 @@ fn add(a: i32, b: i32) i32 { return a + b; }
|
||||
This function is a low level intrinsic with no safety mechanisms. Most code
|
||||
should not use this function, instead using something like this:
|
||||
</p>
|
||||
<pre>{#syntax#}for (source[0...byte_count]) |b, i| dest[i] = b;{#endsyntax#}</pre>
|
||||
<pre>{#syntax#}for (source[0..byte_count]) |b, i| dest[i] = b;{#endsyntax#}</pre>
|
||||
<p>
|
||||
The optimizer is intelligent enough to turn the above snippet into a memcpy.
|
||||
</p>
|
||||
<p>There is also a standard library function for this:</p>
|
||||
<pre>{#syntax#}const mem = @import("std").mem;
|
||||
mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#}</pre>
|
||||
mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre>
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@memset#}
|
||||
@ -5923,7 +5946,7 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#}</pre>
|
||||
This function is a low level intrinsic with no safety mechanisms. Most
|
||||
code should not use this function, instead using something like this:
|
||||
</p>
|
||||
<pre>{#syntax#}for (dest[0...byte_count]) |*b| b.* = c;{#endsyntax#}</pre>
|
||||
<pre>{#syntax#}for (dest[0..byte_count]) |*b| b.* = c;{#endsyntax#}</pre>
|
||||
<p>
|
||||
The optimizer is intelligent enough to turn the above snippet into a memset.
|
||||
</p>
|
||||
@ -6592,9 +6615,10 @@ pub const TypeInfo = union(TypeId) {
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@typeName#}
|
||||
<pre>{#syntax#}@typeName(T: type) []u8{#endsyntax#}</pre>
|
||||
<pre>{#syntax#}@typeName(T: type) [N]u8{#endsyntax#}</pre>
|
||||
<p>
|
||||
This function returns the string representation of a type.
|
||||
This function returns the string representation of a type, as
|
||||
an array. It is equivalent to a string literal of the type name.
|
||||
</p>
|
||||
|
||||
{#header_close#}
|
||||
@ -6606,6 +6630,17 @@ pub const TypeInfo = union(TypeId) {
|
||||
expression passed as an argument. The expression is evaluated.
|
||||
</p>
|
||||
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@Vector#}
|
||||
<pre>{#syntax#}@Vector(comptime len: u32, comptime ElemType: type) type{#endsyntax#}</pre>
|
||||
<p>
|
||||
This function returns a vector type for {#link|SIMD#}.
|
||||
</p>
|
||||
<p>
|
||||
{#syntax#}ElemType{#endsyntax#} must be an {#link|integer|Integers#}, a {#link|float|Floats#}, or a
|
||||
{#link|pointer|Pointers#}.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
|
||||
@ -6679,6 +6714,25 @@ pub fn build(b: *Builder) void {
|
||||
{#header_close#}
|
||||
{#see_also|Compile Variables|Zig Build System|Undefined Behavior#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|Single Threaded Builds#}
|
||||
<p>Zig has a compile option <code>--single-threaded</code> which has the following effects:
|
||||
<ul>
|
||||
<li>{#link|@atomicLoad#} is emitted as a normal load.</li>
|
||||
<li>{#link|@atomicRmw#} is emitted as a normal memory load, modify, store.</li>
|
||||
<li>{#link|@fence#} becomes a no-op.</li>
|
||||
<li>Variables which have Thread Local Storage instead become globals. TODO thread local variables
|
||||
are not implemented yet.</li>
|
||||
<li>The overhead of {#link|Coroutines#} becomes equivalent to function call overhead.
|
||||
TODO: please note this will not be implemented until the upcoming Coroutine Rewrite</li>
|
||||
<li>The {#syntax#}@import("builtin").single_threaded{#endsyntax#} becomes {#syntax#}true{#endsyntax#}
|
||||
and therefore various userland APIs which read this variable become more efficient.
|
||||
For example {#syntax#}std.Mutex{#endsyntax#} becomes
|
||||
an empty data structure and all of its functions become no-ops.</li>
|
||||
</ul>
|
||||
</p>
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|Undefined Behavior#}
|
||||
<p>
|
||||
Zig has many instances of undefined behavior. If undefined behavior is
|
||||
|
@ -83,10 +83,11 @@ pub const ZigCompiler = struct {
|
||||
const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
|
||||
errdefer c.LLVMContextDispose(context_ref);
|
||||
|
||||
const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node{
|
||||
const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node);
|
||||
node.* = std.atomic.Stack(llvm.ContextRef).Node{
|
||||
.next = undefined,
|
||||
.data = context_ref,
|
||||
});
|
||||
};
|
||||
errdefer self.loop.allocator.destroy(node);
|
||||
|
||||
return LlvmHandle{ .node = node };
|
||||
@ -596,7 +597,8 @@ pub const Compilation = struct {
|
||||
}
|
||||
|
||||
fn initTypes(comp: *Compilation) !void {
|
||||
comp.meta_type = try comp.arena().create(Type.MetaType{
|
||||
comp.meta_type = try comp.arena().create(Type.MetaType);
|
||||
comp.meta_type.* = Type.MetaType{
|
||||
.base = Type{
|
||||
.name = "type",
|
||||
.base = Value{
|
||||
@ -608,12 +610,13 @@ pub const Compilation = struct {
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
.value = undefined,
|
||||
});
|
||||
};
|
||||
comp.meta_type.value = &comp.meta_type.base;
|
||||
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.arena().create(Type.Void{
|
||||
comp.void_type = try comp.arena().create(Type.Void);
|
||||
comp.void_type.* = Type.Void{
|
||||
.base = Type{
|
||||
.name = "void",
|
||||
.base = Value{
|
||||
@ -624,10 +627,11 @@ pub const Compilation = struct {
|
||||
.id = builtin.TypeId.Void,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
};
|
||||
assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
|
||||
|
||||
comp.noreturn_type = try comp.arena().create(Type.NoReturn{
|
||||
comp.noreturn_type = try comp.arena().create(Type.NoReturn);
|
||||
comp.noreturn_type.* = Type.NoReturn{
|
||||
.base = Type{
|
||||
.name = "noreturn",
|
||||
.base = Value{
|
||||
@ -638,10 +642,11 @@ pub const Compilation = struct {
|
||||
.id = builtin.TypeId.NoReturn,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
};
|
||||
assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
|
||||
|
||||
comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{
|
||||
comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt);
|
||||
comp.comptime_int_type.* = Type.ComptimeInt{
|
||||
.base = Type{
|
||||
.name = "comptime_int",
|
||||
.base = Value{
|
||||
@ -652,10 +657,11 @@ pub const Compilation = struct {
|
||||
.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{
|
||||
comp.bool_type = try comp.arena().create(Type.Bool);
|
||||
comp.bool_type.* = Type.Bool{
|
||||
.base = Type{
|
||||
.name = "bool",
|
||||
.base = Value{
|
||||
@ -666,45 +672,50 @@ pub const Compilation = struct {
|
||||
.id = builtin.TypeId.Bool,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
};
|
||||
assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
|
||||
|
||||
comp.void_value = try comp.arena().create(Value.Void{
|
||||
comp.void_value = try comp.arena().create(Value.Void);
|
||||
comp.void_value.* = Value.Void{
|
||||
.base = Value{
|
||||
.id = Value.Id.Void,
|
||||
.typ = &Type.Void.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
comp.true_value = try comp.arena().create(Value.Bool{
|
||||
comp.true_value = try comp.arena().create(Value.Bool);
|
||||
comp.true_value.* = Value.Bool{
|
||||
.base = Value{
|
||||
.id = Value.Id.Bool,
|
||||
.typ = &Type.Bool.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.x = true,
|
||||
});
|
||||
};
|
||||
|
||||
comp.false_value = try comp.arena().create(Value.Bool{
|
||||
comp.false_value = try comp.arena().create(Value.Bool);
|
||||
comp.false_value.* = Value.Bool{
|
||||
.base = Value{
|
||||
.id = Value.Id.Bool,
|
||||
.typ = &Type.Bool.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.x = false,
|
||||
});
|
||||
};
|
||||
|
||||
comp.noreturn_value = try comp.arena().create(Value.NoReturn{
|
||||
comp.noreturn_value = try comp.arena().create(Value.NoReturn);
|
||||
comp.noreturn_value.* = Value.NoReturn{
|
||||
.base = Value{
|
||||
.id = Value.Id.NoReturn,
|
||||
.typ = &Type.NoReturn.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
for (CInt.list) |cint, i| {
|
||||
const c_int_type = try comp.arena().create(Type.Int{
|
||||
const c_int_type = try comp.arena().create(Type.Int);
|
||||
c_int_type.* = Type.Int{
|
||||
.base = Type{
|
||||
.name = cint.zig_name,
|
||||
.base = Value{
|
||||
@ -720,11 +731,12 @@ pub const Compilation = struct {
|
||||
.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{
|
||||
comp.u8_type = try comp.arena().create(Type.Int);
|
||||
comp.u8_type.* = Type.Int{
|
||||
.base = Type{
|
||||
.name = "u8",
|
||||
.base = Value{
|
||||
@ -740,7 +752,7 @@ pub const Compilation = struct {
|
||||
.bit_count = 8,
|
||||
},
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
};
|
||||
assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
|
||||
}
|
||||
|
||||
@ -829,7 +841,7 @@ pub const Compilation = struct {
|
||||
};
|
||||
errdefer self.gpa().free(source_code);
|
||||
|
||||
const tree = try self.gpa().createOne(ast.Tree);
|
||||
const tree = try self.gpa().create(ast.Tree);
|
||||
tree.* = try std.zig.parse(self.gpa(), source_code);
|
||||
errdefer {
|
||||
tree.deinit();
|
||||
@ -925,7 +937,8 @@ pub const Compilation = struct {
|
||||
}
|
||||
} else {
|
||||
// add new decl
|
||||
const fn_decl = try self.gpa().create(Decl.Fn{
|
||||
const fn_decl = try self.gpa().create(Decl.Fn);
|
||||
fn_decl.* = Decl.Fn{
|
||||
.base = Decl{
|
||||
.id = Decl.Id.Fn,
|
||||
.name = name,
|
||||
@ -936,7 +949,7 @@ pub const Compilation = struct {
|
||||
},
|
||||
.value = Decl.Fn.Val{ .Unresolved = {} },
|
||||
.fn_proto = fn_proto,
|
||||
});
|
||||
};
|
||||
tree_scope.base.ref();
|
||||
errdefer self.gpa().destroy(fn_decl);
|
||||
|
||||
@ -1140,12 +1153,13 @@ pub const Compilation = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const link_lib = try self.gpa().create(LinkLib{
|
||||
const link_lib = try self.gpa().create(LinkLib);
|
||||
link_lib.* = LinkLib{
|
||||
.name = name,
|
||||
.path = null,
|
||||
.provided_explicitly = provided_explicitly,
|
||||
.symbols = ArrayList([]u8).init(self.gpa()),
|
||||
});
|
||||
};
|
||||
try self.link_libs_list.append(link_lib);
|
||||
if (is_libc) {
|
||||
self.libc_link_lib = link_lib;
|
||||
|
@ -118,7 +118,8 @@ pub const Msg = struct {
|
||||
const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
|
||||
errdefer comp.gpa().free(realpath);
|
||||
|
||||
const msg = try comp.gpa().create(Msg{
|
||||
const msg = try comp.gpa().create(Msg);
|
||||
msg.* = Msg{
|
||||
.text = text,
|
||||
.realpath = realpath,
|
||||
.data = Data{
|
||||
@ -128,7 +129,7 @@ pub const Msg = struct {
|
||||
.span = span,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
tree_scope.base.ref();
|
||||
return msg;
|
||||
}
|
||||
@ -139,13 +140,14 @@ pub const Msg = struct {
|
||||
const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath);
|
||||
errdefer comp.gpa().free(realpath_copy);
|
||||
|
||||
const msg = try comp.gpa().create(Msg{
|
||||
const msg = try comp.gpa().create(Msg);
|
||||
msg.* = Msg{
|
||||
.text = text,
|
||||
.realpath = realpath_copy,
|
||||
.data = Data{
|
||||
.Cli = Cli{ .allocator = comp.gpa() },
|
||||
},
|
||||
});
|
||||
};
|
||||
return msg;
|
||||
}
|
||||
|
||||
@ -164,7 +166,8 @@ pub const Msg = struct {
|
||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||
try parse_error.render(&tree_scope.tree.tokens, out_stream);
|
||||
|
||||
const msg = try comp.gpa().create(Msg{
|
||||
const msg = try comp.gpa().create(Msg);
|
||||
msg.* = Msg{
|
||||
.text = undefined,
|
||||
.realpath = realpath_copy,
|
||||
.data = Data{
|
||||
@ -177,7 +180,7 @@ pub const Msg = struct {
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
tree_scope.base.ref();
|
||||
msg.text = text_buf.toOwnedSlice();
|
||||
return msg;
|
||||
@ -203,7 +206,8 @@ pub const Msg = struct {
|
||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||
try parse_error.render(&tree.tokens, out_stream);
|
||||
|
||||
const msg = try allocator.create(Msg{
|
||||
const msg = try allocator.create(Msg);
|
||||
msg.* = Msg{
|
||||
.text = undefined,
|
||||
.realpath = realpath_copy,
|
||||
.data = Data{
|
||||
@ -216,7 +220,7 @@ pub const Msg = struct {
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
msg.text = text_buf.toOwnedSlice();
|
||||
errdefer allocator.destroy(msg);
|
||||
|
||||
|
@ -1021,12 +1021,13 @@ pub const Builder = struct {
|
||||
pub const Error = Analyze.Error;
|
||||
|
||||
pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, begin_scope: ?*Scope) !Builder {
|
||||
const code = try comp.gpa().create(Code{
|
||||
const code = try comp.gpa().create(Code);
|
||||
code.* = Code{
|
||||
.basic_block_list = undefined,
|
||||
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
|
||||
.return_type = null,
|
||||
.tree_scope = tree_scope,
|
||||
});
|
||||
};
|
||||
code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
|
||||
errdefer code.destroy(comp.gpa());
|
||||
|
||||
@ -1052,7 +1053,8 @@ pub const Builder = struct {
|
||||
|
||||
/// No need to clean up resources thanks to the arena allocator.
|
||||
pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock {
|
||||
const basic_block = try self.arena().create(BasicBlock{
|
||||
const basic_block = try self.arena().create(BasicBlock);
|
||||
basic_block.* = BasicBlock{
|
||||
.ref_count = 0,
|
||||
.name_hint = name_hint,
|
||||
.debug_id = self.next_debug_id,
|
||||
@ -1063,7 +1065,7 @@ pub const Builder = struct {
|
||||
.ref_instruction = null,
|
||||
.llvm_block = undefined,
|
||||
.llvm_exit_block = undefined,
|
||||
});
|
||||
};
|
||||
self.next_debug_id += 1;
|
||||
return basic_block;
|
||||
}
|
||||
@ -1774,7 +1776,8 @@ pub const Builder = struct {
|
||||
params: I.Params,
|
||||
is_generated: bool,
|
||||
) !*Inst {
|
||||
const inst = try self.arena().create(I{
|
||||
const inst = try self.arena().create(I);
|
||||
inst.* = I{
|
||||
.base = Inst{
|
||||
.id = Inst.typeToId(I),
|
||||
.is_generated = is_generated,
|
||||
@ -1793,7 +1796,7 @@ pub const Builder = struct {
|
||||
.owner_bb = self.current_basic_block,
|
||||
},
|
||||
.params = params,
|
||||
});
|
||||
};
|
||||
|
||||
// Look at the params and ref() other instructions
|
||||
comptime var i = 0;
|
||||
|
@ -57,10 +57,10 @@ pub const LibCInstallation = struct {
|
||||
const contents = try std.io.readFileAlloc(allocator, libc_file);
|
||||
defer allocator.free(contents);
|
||||
|
||||
var it = std.mem.split(contents, "\n");
|
||||
var it = std.mem.tokenize(contents, "\n");
|
||||
while (it.next()) |line| {
|
||||
if (line.len == 0 or line[0] == '#') continue;
|
||||
var line_it = std.mem.split(line, "=");
|
||||
var line_it = std.mem.separate(line, "=");
|
||||
const name = line_it.next() orelse {
|
||||
try stderr.print("missing equal sign after field name\n");
|
||||
return error.ParseError;
|
||||
@ -213,7 +213,7 @@ pub const LibCInstallation = struct {
|
||||
},
|
||||
}
|
||||
|
||||
var it = std.mem.split(exec_result.stderr, "\n\r");
|
||||
var it = std.mem.tokenize(exec_result.stderr, "\n\r");
|
||||
var search_paths = std.ArrayList([]const u8).init(loop.allocator);
|
||||
defer search_paths.deinit();
|
||||
while (it.next()) |line| {
|
||||
@ -410,7 +410,7 @@ async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bo
|
||||
return error.CCompilerCrashed;
|
||||
},
|
||||
}
|
||||
var it = std.mem.split(exec_result.stdout, "\n\r");
|
||||
var it = std.mem.tokenize(exec_result.stdout, "\n\r");
|
||||
const line = it.next() orelse return error.LibCRuntimeNotFound;
|
||||
const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound;
|
||||
|
||||
|
@ -351,7 +351,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
const root_name = if (provided_name) |n| n else blk: {
|
||||
if (root_source_file) |file| {
|
||||
const basename = os.path.basename(file);
|
||||
var it = mem.split(basename, ".");
|
||||
var it = mem.separate(basename, ".");
|
||||
break :blk it.next() orelse basename;
|
||||
} else {
|
||||
try stderr.write("--name [name] not provided and unable to infer\n");
|
||||
@ -944,12 +944,13 @@ const CliPkg = struct {
|
||||
parent: ?*CliPkg,
|
||||
|
||||
pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg {
|
||||
var pkg = try allocator.create(CliPkg{
|
||||
var pkg = try allocator.create(CliPkg);
|
||||
pkg.* = CliPkg{
|
||||
.name = name,
|
||||
.path = path,
|
||||
.children = ArrayList(*CliPkg).init(allocator),
|
||||
.parent = parent,
|
||||
});
|
||||
};
|
||||
return pkg;
|
||||
}
|
||||
|
||||
|
@ -15,11 +15,13 @@ pub const Package = struct {
|
||||
/// makes internal copies of root_src_dir and root_src_path
|
||||
/// allocator should be an arena allocator because Package never frees anything
|
||||
pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package {
|
||||
return allocator.create(Package{
|
||||
const ptr = try allocator.create(Package);
|
||||
ptr.* = Package{
|
||||
.root_src_dir = try Buffer.init(allocator, root_src_dir),
|
||||
.root_src_path = try Buffer.init(allocator, root_src_path),
|
||||
.table = Table.init(allocator),
|
||||
});
|
||||
};
|
||||
return ptr;
|
||||
}
|
||||
|
||||
pub fn add(self: *Package, name: []const u8, package: *Package) !void {
|
||||
|
@ -120,7 +120,7 @@ pub const Scope = struct {
|
||||
/// Creates a Root scope with 1 reference
|
||||
/// Takes ownership of realpath
|
||||
pub fn create(comp: *Compilation, realpath: []u8) !*Root {
|
||||
const self = try comp.gpa().createOne(Root);
|
||||
const self = try comp.gpa().create(Root);
|
||||
self.* = Root{
|
||||
.base = Scope{
|
||||
.id = Id.Root,
|
||||
@ -150,7 +150,7 @@ pub const Scope = struct {
|
||||
/// Creates a scope with 1 reference
|
||||
/// Takes ownership of tree, will deinit and destroy when done.
|
||||
pub fn create(comp: *Compilation, tree: *ast.Tree, root_scope: *Root) !*AstTree {
|
||||
const self = try comp.gpa().createOne(AstTree);
|
||||
const self = try comp.gpa().create(AstTree);
|
||||
self.* = AstTree{
|
||||
.base = undefined,
|
||||
.tree = tree,
|
||||
@ -182,7 +182,7 @@ pub const Scope = struct {
|
||||
|
||||
/// Creates a Decls scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
|
||||
const self = try comp.gpa().createOne(Decls);
|
||||
const self = try comp.gpa().create(Decls);
|
||||
self.* = Decls{
|
||||
.base = undefined,
|
||||
.table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
|
||||
@ -235,7 +235,7 @@ pub const Scope = struct {
|
||||
|
||||
/// Creates a Block scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: *Scope) !*Block {
|
||||
const self = try comp.gpa().createOne(Block);
|
||||
const self = try comp.gpa().create(Block);
|
||||
self.* = Block{
|
||||
.base = undefined,
|
||||
.incoming_values = undefined,
|
||||
@ -262,7 +262,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 {
|
||||
const self = try comp.gpa().createOne(FnDef);
|
||||
const self = try comp.gpa().create(FnDef);
|
||||
self.* = FnDef{
|
||||
.base = undefined,
|
||||
.fn_val = null,
|
||||
@ -281,7 +281,7 @@ pub const Scope = struct {
|
||||
|
||||
/// Creates a CompTime scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
|
||||
const self = try comp.gpa().createOne(CompTime);
|
||||
const self = try comp.gpa().create(CompTime);
|
||||
self.* = CompTime{ .base = undefined };
|
||||
self.base.init(Id.CompTime, parent);
|
||||
return self;
|
||||
@ -309,7 +309,7 @@ pub const Scope = struct {
|
||||
kind: Kind,
|
||||
defer_expr_scope: *DeferExpr,
|
||||
) !*Defer {
|
||||
const self = try comp.gpa().createOne(Defer);
|
||||
const self = try comp.gpa().create(Defer);
|
||||
self.* = Defer{
|
||||
.base = undefined,
|
||||
.defer_expr_scope = defer_expr_scope,
|
||||
@ -333,7 +333,7 @@ pub const Scope = struct {
|
||||
|
||||
/// Creates a DeferExpr scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
|
||||
const self = try comp.gpa().createOne(DeferExpr);
|
||||
const self = try comp.gpa().create(DeferExpr);
|
||||
self.* = DeferExpr{
|
||||
.base = undefined,
|
||||
.expr_node = expr_node,
|
||||
@ -398,7 +398,7 @@ pub const Scope = struct {
|
||||
}
|
||||
|
||||
fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var {
|
||||
const self = try comp.gpa().createOne(Var);
|
||||
const self = try comp.gpa().create(Var);
|
||||
self.* = Var{
|
||||
.base = undefined,
|
||||
.name = name,
|
||||
|
@ -44,6 +44,7 @@ pub const Type = struct {
|
||||
Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(comp),
|
||||
Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp),
|
||||
Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(comp),
|
||||
Id.Vector => @fieldParentPtr(Vector, "base", base).destroy(comp),
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,6 +78,7 @@ pub const Type = struct {
|
||||
Id.ArgTuple => unreachable,
|
||||
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Vector => return @fieldParentPtr(Vector, "base", base).getLlvmType(allocator, llvm_context),
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +105,7 @@ pub const Type = struct {
|
||||
Id.Enum,
|
||||
Id.Fn,
|
||||
Id.Promise,
|
||||
Id.Vector,
|
||||
=> return false,
|
||||
|
||||
Id.Struct => @panic("TODO"),
|
||||
@ -135,6 +138,7 @@ pub const Type = struct {
|
||||
Id.Float,
|
||||
Id.Fn,
|
||||
Id.Promise,
|
||||
Id.Vector,
|
||||
=> return true,
|
||||
|
||||
Id.Pointer => {
|
||||
@ -409,7 +413,7 @@ pub const Type = struct {
|
||||
key.ref();
|
||||
errdefer key.deref(comp);
|
||||
|
||||
const self = try comp.gpa().createOne(Fn);
|
||||
const self = try comp.gpa().create(Fn);
|
||||
self.* = Fn{
|
||||
.base = undefined,
|
||||
.key = key,
|
||||
@ -611,11 +615,12 @@ pub const Type = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const self = try comp.gpa().create(Int{
|
||||
const self = try comp.gpa().create(Int);
|
||||
self.* = Int{
|
||||
.base = undefined,
|
||||
.key = key,
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
};
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
const u_or_i = "ui"[@boolToInt(key.is_signed)];
|
||||
@ -777,11 +782,12 @@ pub const Type = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const self = try comp.gpa().create(Pointer{
|
||||
const self = try comp.gpa().create(Pointer);
|
||||
self.* = Pointer{
|
||||
.base = undefined,
|
||||
.key = normal_key,
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
};
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
const size_str = switch (self.key.size) {
|
||||
@ -875,11 +881,12 @@ pub const Type = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const self = try comp.gpa().create(Array{
|
||||
const self = try comp.gpa().create(Array);
|
||||
self.* = 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);
|
||||
@ -902,6 +909,18 @@ pub const Type = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Vector = struct {
|
||||
base: Type,
|
||||
|
||||
pub fn destroy(self: *Vector, comp: *Compilation) void {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
||||
pub const ComptimeFloat = struct {
|
||||
base: Type,
|
||||
|
||||
|
@ -135,14 +135,15 @@ pub const Value = struct {
|
||||
symbol_name: Buffer,
|
||||
|
||||
pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto {
|
||||
const self = try comp.gpa().create(FnProto{
|
||||
const self = try comp.gpa().create(FnProto);
|
||||
self.* = 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;
|
||||
}
|
||||
@ -190,14 +191,16 @@ pub const Value = struct {
|
||||
/// Creates a Fn value with 1 ref
|
||||
/// Takes ownership of symbol_name
|
||||
pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn {
|
||||
const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{
|
||||
const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node);
|
||||
link_set_node.* = Compilation.FnLinkSet.Node{
|
||||
.data = null,
|
||||
.next = undefined,
|
||||
.prev = undefined,
|
||||
});
|
||||
};
|
||||
errdefer comp.gpa().destroy(link_set_node);
|
||||
|
||||
const self = try comp.gpa().create(Fn{
|
||||
const self = try comp.gpa().create(Fn);
|
||||
self.* = Fn{
|
||||
.base = Value{
|
||||
.id = Value.Id.Fn,
|
||||
.typ = &fn_type.base,
|
||||
@ -209,7 +212,7 @@ pub const Value = struct {
|
||||
.symbol_name = symbol_name,
|
||||
.containing_object = Buffer.initNull(comp.gpa()),
|
||||
.link_set_node = link_set_node,
|
||||
});
|
||||
};
|
||||
fn_type.base.base.ref();
|
||||
fndef_scope.fn_val = self;
|
||||
fndef_scope.base.ref();
|
||||
@ -353,7 +356,8 @@ pub const Value = struct {
|
||||
var ptr_type_consumed = false;
|
||||
errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
|
||||
|
||||
const self = try comp.gpa().create(Value.Ptr{
|
||||
const self = try comp.gpa().create(Value.Ptr);
|
||||
self.* = Value.Ptr{
|
||||
.base = Value{
|
||||
.id = Value.Id.Ptr,
|
||||
.typ = &ptr_type.base,
|
||||
@ -366,7 +370,7 @@ pub const Value = struct {
|
||||
},
|
||||
},
|
||||
.mut = Mut.CompTimeConst,
|
||||
});
|
||||
};
|
||||
ptr_type_consumed = true;
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
@ -430,14 +434,15 @@ pub const Value = struct {
|
||||
}) catch unreachable);
|
||||
errdefer array_type.base.base.deref(comp);
|
||||
|
||||
const self = try comp.gpa().create(Value.Array{
|
||||
const self = try comp.gpa().create(Value.Array);
|
||||
self.* = 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;
|
||||
@ -509,14 +514,15 @@ pub const Value = struct {
|
||||
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{
|
||||
const self = try comp.gpa().create(Value.Int);
|
||||
self.* = 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);
|
||||
|
||||
@ -557,14 +563,15 @@ pub const Value = struct {
|
||||
old.base.typ.base.ref();
|
||||
errdefer old.base.typ.base.deref(comp);
|
||||
|
||||
const new = try comp.gpa().create(Value.Int{
|
||||
const new = try comp.gpa().create(Value.Int);
|
||||
new.* = 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();
|
||||
|
@ -56,9 +56,6 @@ struct IrExecutable {
|
||||
size_t next_debug_id;
|
||||
size_t *backward_branch_count;
|
||||
size_t backward_branch_quota;
|
||||
bool invalid;
|
||||
bool is_inline;
|
||||
bool is_generic_instantiation;
|
||||
ZigFn *fn_entry;
|
||||
Buf *c_import_buf;
|
||||
AstNode *source_node;
|
||||
@ -78,6 +75,10 @@ struct IrExecutable {
|
||||
IrBasicBlock *coro_suspend_block;
|
||||
IrBasicBlock *coro_final_cleanup_block;
|
||||
ZigVar *coro_allocator_var;
|
||||
|
||||
bool invalid;
|
||||
bool is_inline;
|
||||
bool is_generic_instantiation;
|
||||
};
|
||||
|
||||
enum OutType {
|
||||
@ -90,6 +91,9 @@ enum OutType {
|
||||
enum ConstParentId {
|
||||
ConstParentIdNone,
|
||||
ConstParentIdStruct,
|
||||
ConstParentIdErrUnionCode,
|
||||
ConstParentIdErrUnionPayload,
|
||||
ConstParentIdOptionalPayload,
|
||||
ConstParentIdArray,
|
||||
ConstParentIdUnion,
|
||||
ConstParentIdScalar,
|
||||
@ -107,6 +111,15 @@ struct ConstParent {
|
||||
ConstExprValue *struct_val;
|
||||
size_t field_index;
|
||||
} p_struct;
|
||||
struct {
|
||||
ConstExprValue *err_union_val;
|
||||
} p_err_union_code;
|
||||
struct {
|
||||
ConstExprValue *err_union_val;
|
||||
} p_err_union_payload;
|
||||
struct {
|
||||
ConstExprValue *optional_val;
|
||||
} p_optional_payload;
|
||||
struct {
|
||||
ConstExprValue *union_val;
|
||||
} p_union;
|
||||
@ -118,13 +131,11 @@ struct ConstParent {
|
||||
|
||||
struct ConstStructValue {
|
||||
ConstExprValue *fields;
|
||||
ConstParent parent;
|
||||
};
|
||||
|
||||
struct ConstUnionValue {
|
||||
BigInt tag;
|
||||
ConstExprValue *payload;
|
||||
ConstParent parent;
|
||||
};
|
||||
|
||||
enum ConstArraySpecial {
|
||||
@ -138,7 +149,6 @@ struct ConstArrayValue {
|
||||
union {
|
||||
struct {
|
||||
ConstExprValue *elements;
|
||||
ConstParent parent;
|
||||
} s_none;
|
||||
Buf *s_buf;
|
||||
} data;
|
||||
@ -153,19 +163,29 @@ enum ConstPtrSpecial {
|
||||
ConstPtrSpecialBaseArray,
|
||||
// The pointer points to a field in an underlying struct.
|
||||
ConstPtrSpecialBaseStruct,
|
||||
// The pointer points to the error set field of an error union
|
||||
ConstPtrSpecialBaseErrorUnionCode,
|
||||
// The pointer points to the payload field of an error union
|
||||
ConstPtrSpecialBaseErrorUnionPayload,
|
||||
// The pointer points to the payload field of an optional
|
||||
ConstPtrSpecialBaseOptionalPayload,
|
||||
// This means that we did a compile-time pointer reinterpret and we cannot
|
||||
// understand the value of pointee at compile time. However, we will still
|
||||
// emit a binary with a compile time known address.
|
||||
// In this case index is the numeric address value.
|
||||
// We also use this for null pointer. We need the data layout for ConstCastOnly == true
|
||||
// types to be the same, so all optionals of pointer types use x_ptr
|
||||
// instead of x_optional
|
||||
ConstPtrSpecialHardCodedAddr,
|
||||
// This means that the pointer represents memory of assigning to _.
|
||||
// That is, storing discards the data, and loading is invalid.
|
||||
ConstPtrSpecialDiscard,
|
||||
// This is actually a function.
|
||||
ConstPtrSpecialFunction,
|
||||
// This means the pointer is null. This is only allowed when the type is ?*T.
|
||||
// We use this instead of ConstPtrSpecialHardCodedAddr because often we check
|
||||
// for that value to avoid doing comptime work.
|
||||
// We need the data layout for ConstCastOnly == true
|
||||
// types to be the same, so all optionals of pointer types use x_ptr
|
||||
// instead of x_optional.
|
||||
ConstPtrSpecialNull,
|
||||
};
|
||||
|
||||
enum ConstPtrMut {
|
||||
@ -199,6 +219,15 @@ struct ConstPtrValue {
|
||||
ConstExprValue *struct_val;
|
||||
size_t field_index;
|
||||
} base_struct;
|
||||
struct {
|
||||
ConstExprValue *err_union_val;
|
||||
} base_err_union_code;
|
||||
struct {
|
||||
ConstExprValue *err_union_val;
|
||||
} base_err_union_payload;
|
||||
struct {
|
||||
ConstExprValue *optional_val;
|
||||
} base_optional_payload;
|
||||
struct {
|
||||
uint64_t addr;
|
||||
} hard_coded_addr;
|
||||
@ -209,7 +238,7 @@ struct ConstPtrValue {
|
||||
};
|
||||
|
||||
struct ConstErrValue {
|
||||
ErrorTableEntry *err;
|
||||
ConstExprValue *error_set;
|
||||
ConstExprValue *payload;
|
||||
};
|
||||
|
||||
@ -265,6 +294,7 @@ struct ConstGlobalRefs {
|
||||
struct ConstExprValue {
|
||||
ZigType *type;
|
||||
ConstValSpecial special;
|
||||
ConstParent parent;
|
||||
ConstGlobalRefs *global_refs;
|
||||
|
||||
union {
|
||||
@ -433,7 +463,7 @@ enum NodeType {
|
||||
NodeTypeArrayType,
|
||||
NodeTypeErrorType,
|
||||
NodeTypeIfErrorExpr,
|
||||
NodeTypeTestExpr,
|
||||
NodeTypeIfOptional,
|
||||
NodeTypeErrorSetDecl,
|
||||
NodeTypeCancel,
|
||||
NodeTypeResume,
|
||||
@ -677,7 +707,7 @@ struct AstNodeUse {
|
||||
AstNode *expr;
|
||||
|
||||
TldResolution resolution;
|
||||
IrInstruction *value;
|
||||
ConstExprValue *value;
|
||||
};
|
||||
|
||||
struct AstNodeIfBoolExpr {
|
||||
@ -1180,6 +1210,12 @@ struct ZigTypePromise {
|
||||
ZigType *result_type;
|
||||
};
|
||||
|
||||
struct ZigTypeVector {
|
||||
// The type must be a pointer, integer, or float
|
||||
ZigType *elem_type;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
enum ZigTypeId {
|
||||
ZigTypeIdInvalid,
|
||||
ZigTypeIdMetaType,
|
||||
@ -1206,6 +1242,7 @@ enum ZigTypeId {
|
||||
ZigTypeIdArgTuple,
|
||||
ZigTypeIdOpaque,
|
||||
ZigTypeIdPromise,
|
||||
ZigTypeIdVector,
|
||||
};
|
||||
|
||||
struct ZigType {
|
||||
@ -1232,6 +1269,7 @@ struct ZigType {
|
||||
ZigTypeFn fn;
|
||||
ZigTypeBoundFn bound_fn;
|
||||
ZigTypePromise promise;
|
||||
ZigTypeVector vector;
|
||||
} data;
|
||||
|
||||
// use these fields to make sure we don't duplicate type table entries for the same type
|
||||
@ -1385,6 +1423,7 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdEnumToInt,
|
||||
BuiltinFnIdIntToEnum,
|
||||
BuiltinFnIdIntType,
|
||||
BuiltinFnIdVectorType,
|
||||
BuiltinFnIdSetCold,
|
||||
BuiltinFnIdSetRuntimeSafety,
|
||||
BuiltinFnIdSetFloatMode,
|
||||
@ -1475,6 +1514,10 @@ struct TypeId {
|
||||
ZigType *err_set_type;
|
||||
ZigType *payload_type;
|
||||
} error_union;
|
||||
struct {
|
||||
ZigType *elem_type;
|
||||
uint32_t len;
|
||||
} vector;
|
||||
} data;
|
||||
};
|
||||
|
||||
@ -1610,7 +1653,7 @@ struct CodeGen {
|
||||
HashMap<FnTypeId *, ZigType *, fn_type_id_hash, fn_type_id_eql> fn_type_table;
|
||||
HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table;
|
||||
HashMap<GenericFnTypeId *, ZigFn *, generic_fn_type_id_hash, generic_fn_type_id_eql> generic_table;
|
||||
HashMap<Scope *, IrInstruction *, fn_eval_hash, fn_eval_eql> memoized_fn_eval_table;
|
||||
HashMap<Scope *, ConstExprValue *, fn_eval_hash, fn_eval_eql> memoized_fn_eval_table;
|
||||
HashMap<ZigLLVMFnKey, LLVMValueRef, zig_llvm_fn_key_hash, zig_llvm_fn_key_eql> llvm_fn_table;
|
||||
HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> exported_symbol_names;
|
||||
HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> external_prototypes;
|
||||
@ -1760,6 +1803,7 @@ struct CodeGen {
|
||||
bool is_static;
|
||||
bool strip_debug_symbols;
|
||||
bool is_test_build;
|
||||
bool is_single_threaded;
|
||||
bool is_native_target;
|
||||
bool linker_rdynamic;
|
||||
bool no_rosegment_workaround;
|
||||
@ -1802,10 +1846,9 @@ enum VarLinkage {
|
||||
|
||||
struct ZigVar {
|
||||
Buf name;
|
||||
ConstExprValue *value;
|
||||
ConstExprValue *const_value;
|
||||
ZigType *var_type;
|
||||
LLVMValueRef value_ref;
|
||||
bool src_is_const;
|
||||
bool gen_is_const;
|
||||
IrInstruction *is_comptime;
|
||||
// which node is the declaration of the variable
|
||||
AstNode *decl_node;
|
||||
@ -1815,17 +1858,21 @@ struct ZigVar {
|
||||
Scope *parent_scope;
|
||||
Scope *child_scope;
|
||||
LLVMValueRef param_value_ref;
|
||||
bool shadowable;
|
||||
size_t mem_slot_index;
|
||||
IrExecutable *owner_exec;
|
||||
size_t ref_count;
|
||||
VarLinkage linkage;
|
||||
uint32_t align_bytes;
|
||||
|
||||
// In an inline loop, multiple variables may be created,
|
||||
// In this case, a reference to a variable should follow
|
||||
// this pointer to the redefined variable.
|
||||
ZigVar *next_var;
|
||||
|
||||
uint32_t align_bytes;
|
||||
VarLinkage linkage;
|
||||
|
||||
bool shadowable;
|
||||
bool src_is_const;
|
||||
bool gen_is_const;
|
||||
};
|
||||
|
||||
struct ErrorTableEntry {
|
||||
@ -1891,10 +1938,11 @@ struct ScopeBlock {
|
||||
ZigList<IrInstruction *> *incoming_values;
|
||||
ZigList<IrBasicBlock *> *incoming_blocks;
|
||||
|
||||
bool safety_off;
|
||||
AstNode *safety_set_node;
|
||||
bool fast_math_on;
|
||||
AstNode *fast_math_set_node;
|
||||
|
||||
bool safety_off;
|
||||
bool fast_math_on;
|
||||
};
|
||||
|
||||
// This scope is created from every defer expression.
|
||||
@ -2030,8 +2078,19 @@ struct IrBasicBlock {
|
||||
IrInstruction *must_be_comptime_source_instr;
|
||||
};
|
||||
|
||||
// These instructions are in transition to having "pass 1" instructions
|
||||
// and "pass 2" instructions. The pass 1 instructions are suffixed with Src
|
||||
// and pass 2 are suffixed with Gen.
|
||||
// Once all instructions are separated in this way, they'll have different
|
||||
// base types for better type safety.
|
||||
// Src instructions are generated by ir_gen_* functions in ir.cpp from AST.
|
||||
// ir_analyze_* functions consume Src instructions and produce Gen instructions.
|
||||
// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.
|
||||
// Src instructions do not have type information; Gen instructions do.
|
||||
enum IrInstructionId {
|
||||
IrInstructionIdInvalid,
|
||||
IrInstructionIdDeclVarSrc,
|
||||
IrInstructionIdDeclVarGen,
|
||||
IrInstructionIdBr,
|
||||
IrInstructionIdCondBr,
|
||||
IrInstructionIdSwitchBr,
|
||||
@ -2040,7 +2099,6 @@ enum IrInstructionId {
|
||||
IrInstructionIdPhi,
|
||||
IrInstructionIdUnOp,
|
||||
IrInstructionIdBinOp,
|
||||
IrInstructionIdDeclVar,
|
||||
IrInstructionIdLoadPtr,
|
||||
IrInstructionIdStorePtr,
|
||||
IrInstructionIdFieldPtr,
|
||||
@ -2069,7 +2127,7 @@ enum IrInstructionId {
|
||||
IrInstructionIdAsm,
|
||||
IrInstructionIdSizeOf,
|
||||
IrInstructionIdTestNonNull,
|
||||
IrInstructionIdUnwrapOptional,
|
||||
IrInstructionIdOptionalUnwrapPtr,
|
||||
IrInstructionIdOptionalWrap,
|
||||
IrInstructionIdUnionTag,
|
||||
IrInstructionIdClz,
|
||||
@ -2085,7 +2143,8 @@ enum IrInstructionId {
|
||||
IrInstructionIdCompileLog,
|
||||
IrInstructionIdErrName,
|
||||
IrInstructionIdEmbedFile,
|
||||
IrInstructionIdCmpxchg,
|
||||
IrInstructionIdCmpxchgSrc,
|
||||
IrInstructionIdCmpxchgGen,
|
||||
IrInstructionIdFence,
|
||||
IrInstructionIdTruncate,
|
||||
IrInstructionIdIntCast,
|
||||
@ -2094,6 +2153,7 @@ enum IrInstructionId {
|
||||
IrInstructionIdFloatToInt,
|
||||
IrInstructionIdBoolToInt,
|
||||
IrInstructionIdIntType,
|
||||
IrInstructionIdVectorType,
|
||||
IrInstructionIdBoolNot,
|
||||
IrInstructionIdMemset,
|
||||
IrInstructionIdMemcpy,
|
||||
@ -2114,7 +2174,8 @@ enum IrInstructionId {
|
||||
IrInstructionIdErrWrapPayload,
|
||||
IrInstructionIdFnProto,
|
||||
IrInstructionIdTestComptime,
|
||||
IrInstructionIdPtrCast,
|
||||
IrInstructionIdPtrCastSrc,
|
||||
IrInstructionIdPtrCastGen,
|
||||
IrInstructionIdBitCast,
|
||||
IrInstructionIdWidenOrShorten,
|
||||
IrInstructionIdIntToPtr,
|
||||
@ -2173,6 +2234,8 @@ enum IrInstructionId {
|
||||
IrInstructionIdToBytes,
|
||||
IrInstructionIdFromBytes,
|
||||
IrInstructionIdCheckRuntimeScope,
|
||||
IrInstructionIdVectorToArray,
|
||||
IrInstructionIdArrayToVector,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
@ -2194,6 +2257,22 @@ struct IrInstruction {
|
||||
bool is_gen;
|
||||
};
|
||||
|
||||
struct IrInstructionDeclVarSrc {
|
||||
IrInstruction base;
|
||||
|
||||
ZigVar *var;
|
||||
IrInstruction *var_type;
|
||||
IrInstruction *align_value;
|
||||
IrInstruction *init_value;
|
||||
};
|
||||
|
||||
struct IrInstructionDeclVarGen {
|
||||
IrInstruction base;
|
||||
|
||||
ZigVar *var;
|
||||
IrInstruction *init_value;
|
||||
};
|
||||
|
||||
struct IrInstructionCondBr {
|
||||
IrInstruction base;
|
||||
|
||||
@ -2302,20 +2381,11 @@ struct IrInstructionBinOp {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *op1;
|
||||
IrBinOp op_id;
|
||||
IrInstruction *op2;
|
||||
IrBinOp op_id;
|
||||
bool safety_check_on;
|
||||
};
|
||||
|
||||
struct IrInstructionDeclVar {
|
||||
IrInstruction base;
|
||||
|
||||
ZigVar *var;
|
||||
IrInstruction *var_type;
|
||||
IrInstruction *align_value;
|
||||
IrInstruction *init_value;
|
||||
};
|
||||
|
||||
struct IrInstructionLoadPtr {
|
||||
IrInstruction base;
|
||||
|
||||
@ -2335,7 +2405,6 @@ struct IrInstructionFieldPtr {
|
||||
IrInstruction *container_ptr;
|
||||
Buf *field_name_buffer;
|
||||
IrInstruction *field_name_expr;
|
||||
bool is_const;
|
||||
};
|
||||
|
||||
struct IrInstructionStructFieldPtr {
|
||||
@ -2378,13 +2447,13 @@ struct IrInstructionCall {
|
||||
ZigFn *fn_entry;
|
||||
size_t arg_count;
|
||||
IrInstruction **args;
|
||||
bool is_comptime;
|
||||
LLVMValueRef tmp_ptr;
|
||||
FnInline fn_inline;
|
||||
bool is_async;
|
||||
|
||||
IrInstruction *async_allocator;
|
||||
IrInstruction *new_stack;
|
||||
FnInline fn_inline;
|
||||
bool is_async;
|
||||
bool is_comptime;
|
||||
};
|
||||
|
||||
struct IrInstructionConst {
|
||||
@ -2527,9 +2596,9 @@ struct IrInstructionSliceType {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *align_value;
|
||||
IrInstruction *child_type;
|
||||
bool is_const;
|
||||
bool is_volatile;
|
||||
IrInstruction *child_type;
|
||||
};
|
||||
|
||||
struct IrInstructionAsm {
|
||||
@ -2557,10 +2626,12 @@ struct IrInstructionTestNonNull {
|
||||
IrInstruction *value;
|
||||
};
|
||||
|
||||
struct IrInstructionUnwrapOptional {
|
||||
// Takes a pointer to an optional value, returns a pointer
|
||||
// to the payload.
|
||||
struct IrInstructionOptionalUnwrapPtr {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *value;
|
||||
IrInstruction *base_ptr;
|
||||
bool safety_check_on;
|
||||
};
|
||||
|
||||
@ -2651,7 +2722,7 @@ struct IrInstructionEmbedFile {
|
||||
IrInstruction *name;
|
||||
};
|
||||
|
||||
struct IrInstructionCmpxchg {
|
||||
struct IrInstructionCmpxchgSrc {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *type_value;
|
||||
@ -2661,14 +2732,19 @@ struct IrInstructionCmpxchg {
|
||||
IrInstruction *success_order_value;
|
||||
IrInstruction *failure_order_value;
|
||||
|
||||
// if this instruction gets to runtime then we know these values:
|
||||
ZigType *type;
|
||||
bool is_weak;
|
||||
};
|
||||
|
||||
struct IrInstructionCmpxchgGen {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *ptr;
|
||||
IrInstruction *cmp_value;
|
||||
IrInstruction *new_value;
|
||||
LLVMValueRef tmp_ptr;
|
||||
AtomicOrder success_order;
|
||||
AtomicOrder failure_order;
|
||||
|
||||
bool is_weak;
|
||||
|
||||
LLVMValueRef tmp_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionFence {
|
||||
@ -2748,6 +2824,13 @@ struct IrInstructionIntType {
|
||||
IrInstruction *bit_count;
|
||||
};
|
||||
|
||||
struct IrInstructionVectorType {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *len;
|
||||
IrInstruction *elem_type;
|
||||
};
|
||||
|
||||
struct IrInstructionBoolNot {
|
||||
IrInstruction base;
|
||||
|
||||
@ -2851,7 +2934,7 @@ struct IrInstructionTestErr {
|
||||
struct IrInstructionUnwrapErrCode {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *value;
|
||||
IrInstruction *err_union;
|
||||
};
|
||||
|
||||
struct IrInstructionUnwrapErrPayload {
|
||||
@ -2899,13 +2982,19 @@ struct IrInstructionTestComptime {
|
||||
IrInstruction *value;
|
||||
};
|
||||
|
||||
struct IrInstructionPtrCast {
|
||||
struct IrInstructionPtrCastSrc {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *dest_type;
|
||||
IrInstruction *ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionPtrCastGen {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionBitCast {
|
||||
IrInstruction base;
|
||||
|
||||
@ -3276,6 +3365,19 @@ struct IrInstructionBitReverse {
|
||||
IrInstruction *op;
|
||||
};
|
||||
|
||||
struct IrInstructionArrayToVector {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *array;
|
||||
};
|
||||
|
||||
struct IrInstructionVectorToArray {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *vector;
|
||||
LLVMValueRef tmp_ptr;
|
||||
};
|
||||
|
||||
static const size_t slice_ptr_index = 0;
|
||||
static const size_t slice_len_index = 1;
|
||||
|
||||
|
512
src/analyze.cpp
512
src/analyze.cpp
@ -250,6 +250,7 @@ AstNode *type_decl_node(ZigType *type_entry) {
|
||||
case ZigTypeIdBoundFn:
|
||||
case ZigTypeIdArgTuple:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdVector:
|
||||
return nullptr;
|
||||
}
|
||||
zig_unreachable();
|
||||
@ -311,6 +312,7 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
|
||||
case ZigTypeIdBoundFn:
|
||||
case ZigTypeIdArgTuple:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdVector:
|
||||
return true;
|
||||
}
|
||||
zig_unreachable();
|
||||
@ -570,7 +572,7 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
|
||||
if (child_type->zero_bits) {
|
||||
entry->type_ref = LLVMInt1Type();
|
||||
entry->di_type = g->builtin_types.entry_bool->di_type;
|
||||
} else if (type_is_codegen_pointer(child_type)) {
|
||||
} else if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) {
|
||||
assert(child_type->di_type);
|
||||
// this is an optimization but also is necessary for calling C
|
||||
// functions where all pointers are maybe pointers
|
||||
@ -1055,11 +1057,7 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
|
||||
}
|
||||
if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
|
||||
X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
|
||||
if (abi_class == X64CABIClass_MEMORY) {
|
||||
return true;
|
||||
}
|
||||
zig_panic("TODO implement C ABI for x86_64 return types. type '%s'\nSee https://github.com/ziglang/zig/issues/1481",
|
||||
buf_ptr(&fn_type_id->return_type->name));
|
||||
return abi_class == X64CABIClass_MEMORY;
|
||||
} else if (target_is_arm(&g->zig_target)) {
|
||||
return type_size(g, fn_type_id->return_type) > 16;
|
||||
}
|
||||
@ -1278,7 +1276,9 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind
|
||||
return entry;
|
||||
}
|
||||
|
||||
static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name) {
|
||||
static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry,
|
||||
Buf *type_name)
|
||||
{
|
||||
size_t backward_branch_count = 0;
|
||||
return ir_eval_const_value(g, scope, node, type_entry,
|
||||
&backward_branch_count, default_backward_branch_quota,
|
||||
@ -1286,12 +1286,12 @@ static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *nod
|
||||
}
|
||||
|
||||
ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
|
||||
IrInstruction *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr);
|
||||
if (result->value.type->id == ZigTypeIdInvalid)
|
||||
ConstExprValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr);
|
||||
if (type_is_invalid(result->type))
|
||||
return g->builtin_types.entry_invalid;
|
||||
|
||||
assert(result->value.special != ConstValSpecialRuntime);
|
||||
return result->value.data.x_type;
|
||||
assert(result->special != ConstValSpecialRuntime);
|
||||
return result->data.x_type;
|
||||
}
|
||||
|
||||
ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
|
||||
@ -1342,11 +1342,11 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
|
||||
}
|
||||
|
||||
static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) {
|
||||
IrInstruction *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr);
|
||||
if (type_is_invalid(align_result->value.type))
|
||||
ConstExprValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr);
|
||||
if (type_is_invalid(align_result->type))
|
||||
return false;
|
||||
|
||||
uint32_t align_bytes = bigint_as_unsigned(&align_result->value.data.x_bigint);
|
||||
uint32_t align_bytes = bigint_as_unsigned(&align_result->data.x_bigint);
|
||||
if (align_bytes == 0) {
|
||||
add_node_error(g, node, buf_sprintf("alignment must be >= 1"));
|
||||
return false;
|
||||
@ -1364,12 +1364,12 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **
|
||||
ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
|
||||
PtrLenUnknown, 0, 0, 0);
|
||||
ZigType *str_type = get_slice_type(g, ptr_type);
|
||||
IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr);
|
||||
if (type_is_invalid(instr->value.type))
|
||||
ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr);
|
||||
if (type_is_invalid(result_val->type))
|
||||
return false;
|
||||
|
||||
ConstExprValue *ptr_field = &instr->value.data.x_struct.fields[slice_ptr_index];
|
||||
ConstExprValue *len_field = &instr->value.data.x_struct.fields[slice_len_index];
|
||||
ConstExprValue *ptr_field = &result_val->data.x_struct.fields[slice_ptr_index];
|
||||
ConstExprValue *len_field = &result_val->data.x_struct.fields[slice_len_index];
|
||||
|
||||
assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
|
||||
ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
|
||||
@ -1422,6 +1422,7 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) {
|
||||
case ZigTypeIdPointer:
|
||||
case ZigTypeIdArray:
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdVector:
|
||||
return true;
|
||||
case ZigTypeIdStruct:
|
||||
return type_entry->data.structure.layout == ContainerLayoutPacked;
|
||||
@ -1470,6 +1471,8 @@ static bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case ZigTypeIdVector:
|
||||
return type_allowed_in_extern(g, type_entry->data.vector.elem_type);
|
||||
case ZigTypeIdFloat:
|
||||
return true;
|
||||
case ZigTypeIdArray:
|
||||
@ -1623,6 +1626,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
|
||||
case ZigTypeIdUnion:
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdVector:
|
||||
switch (type_requires_comptime(g, type_entry)) {
|
||||
case ReqCompTimeNo:
|
||||
break;
|
||||
@ -1718,6 +1722,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
|
||||
case ZigTypeIdUnion:
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdVector:
|
||||
switch (type_requires_comptime(g, fn_type_id.return_type)) {
|
||||
case ReqCompTimeInvalid:
|
||||
return g->builtin_types.entry_invalid;
|
||||
@ -2504,20 +2509,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
|
||||
// In this first pass we resolve explicit tag values.
|
||||
// In a second pass we will fill in the unspecified ones.
|
||||
if (tag_value != nullptr) {
|
||||
IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr);
|
||||
if (result_inst->value.type->id == ZigTypeIdInvalid) {
|
||||
ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr);
|
||||
if (type_is_invalid(result->type)) {
|
||||
enum_type->data.enumeration.is_invalid = true;
|
||||
continue;
|
||||
}
|
||||
assert(result_inst->value.special != ConstValSpecialRuntime);
|
||||
assert(result_inst->value.type->id == ZigTypeIdInt ||
|
||||
result_inst->value.type->id == ZigTypeIdComptimeInt);
|
||||
auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value);
|
||||
assert(result->special != ConstValSpecialRuntime);
|
||||
assert(result->type->id == ZigTypeIdInt ||
|
||||
result->type->id == ZigTypeIdComptimeInt);
|
||||
auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value);
|
||||
if (entry == nullptr) {
|
||||
bigint_init_bigint(&type_enum_field->value, &result_inst->value.data.x_bigint);
|
||||
bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
|
||||
} else {
|
||||
Buf *val_buf = buf_alloc();
|
||||
bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10);
|
||||
bigint_append_buf(val_buf, &result->data.x_bigint, 10);
|
||||
|
||||
ErrorMsg *msg = add_node_error(g, tag_value,
|
||||
buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
|
||||
@ -2944,19 +2949,19 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
|
||||
// In a second pass we will fill in the unspecified ones.
|
||||
if (tag_value != nullptr) {
|
||||
ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type;
|
||||
IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr);
|
||||
if (result_inst->value.type->id == ZigTypeIdInvalid) {
|
||||
ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr);
|
||||
if (type_is_invalid(result->type)) {
|
||||
union_type->data.unionation.is_invalid = true;
|
||||
continue;
|
||||
}
|
||||
assert(result_inst->value.special != ConstValSpecialRuntime);
|
||||
assert(result_inst->value.type->id == ZigTypeIdInt);
|
||||
auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value);
|
||||
assert(result->special != ConstValSpecialRuntime);
|
||||
assert(result->type->id == ZigTypeIdInt);
|
||||
auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value);
|
||||
if (entry == nullptr) {
|
||||
bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint);
|
||||
bigint_init_bigint(&union_field->enum_field->value, &result->data.x_bigint);
|
||||
} else {
|
||||
Buf *val_buf = buf_alloc();
|
||||
bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10);
|
||||
bigint_append_buf(val_buf, &result->data.x_bigint, 10);
|
||||
|
||||
ErrorMsg *msg = add_node_error(g, tag_value,
|
||||
buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
|
||||
@ -3419,7 +3424,8 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) {
|
||||
resolve_top_level_decl(g, tld, false, tld->source_node);
|
||||
assert(tld->id == TldIdVar);
|
||||
TldVar *tld_var = (TldVar *)tld;
|
||||
tld_var->var->value = value;
|
||||
tld_var->var->const_value = value;
|
||||
tld_var->var->var_type = value->type;
|
||||
tld_var->var->align_bytes = get_abi_alignment(g, value->type);
|
||||
}
|
||||
|
||||
@ -3513,7 +3519,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
|
||||
case NodeTypeArrayType:
|
||||
case NodeTypeErrorType:
|
||||
case NodeTypeIfErrorExpr:
|
||||
case NodeTypeTestExpr:
|
||||
case NodeTypeIfOptional:
|
||||
case NodeTypeErrorSetDecl:
|
||||
case NodeTypeCancel:
|
||||
case NodeTypeResume:
|
||||
@ -3574,6 +3580,7 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdBoundFn:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdVector:
|
||||
return type_entry;
|
||||
}
|
||||
zig_unreachable();
|
||||
@ -3582,13 +3589,15 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry
|
||||
// Set name to nullptr to make the variable anonymous (not visible to programmer).
|
||||
// TODO merge with definition of add_local_var in ir.cpp
|
||||
ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
|
||||
bool is_const, ConstExprValue *value, Tld *src_tld)
|
||||
bool is_const, ConstExprValue *const_value, Tld *src_tld, ZigType *var_type)
|
||||
{
|
||||
Error err;
|
||||
assert(value);
|
||||
assert(const_value != nullptr);
|
||||
assert(var_type != nullptr);
|
||||
|
||||
ZigVar *variable_entry = allocate<ZigVar>(1);
|
||||
variable_entry->value = value;
|
||||
variable_entry->const_value = const_value;
|
||||
variable_entry->var_type = var_type;
|
||||
variable_entry->parent_scope = parent_scope;
|
||||
variable_entry->shadowable = false;
|
||||
variable_entry->mem_slot_index = SIZE_MAX;
|
||||
@ -3597,23 +3606,23 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf
|
||||
assert(name);
|
||||
buf_init_from_buf(&variable_entry->name, name);
|
||||
|
||||
if ((err = type_resolve(g, value->type, ResolveStatusAlignmentKnown))) {
|
||||
variable_entry->value->type = g->builtin_types.entry_invalid;
|
||||
if ((err = type_resolve(g, var_type, ResolveStatusAlignmentKnown))) {
|
||||
variable_entry->var_type = g->builtin_types.entry_invalid;
|
||||
} else {
|
||||
variable_entry->align_bytes = get_abi_alignment(g, value->type);
|
||||
variable_entry->align_bytes = get_abi_alignment(g, var_type);
|
||||
|
||||
ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr);
|
||||
if (existing_var && !existing_var->shadowable) {
|
||||
ErrorMsg *msg = add_node_error(g, source_node,
|
||||
buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
|
||||
add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
|
||||
variable_entry->value->type = g->builtin_types.entry_invalid;
|
||||
variable_entry->var_type = g->builtin_types.entry_invalid;
|
||||
} else {
|
||||
ZigType *type;
|
||||
if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) {
|
||||
add_node_error(g, source_node,
|
||||
buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
|
||||
variable_entry->value->type = g->builtin_types.entry_invalid;
|
||||
variable_entry->var_type = g->builtin_types.entry_invalid;
|
||||
} else {
|
||||
Scope *search_scope = nullptr;
|
||||
if (src_tld == nullptr) {
|
||||
@ -3627,7 +3636,7 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf
|
||||
ErrorMsg *msg = add_node_error(g, source_node,
|
||||
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
|
||||
add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here"));
|
||||
variable_entry->value->type = g->builtin_types.entry_invalid;
|
||||
variable_entry->var_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3677,7 +3686,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
|
||||
linkage = VarLinkageInternal;
|
||||
}
|
||||
|
||||
IrInstruction *init_value = nullptr;
|
||||
ConstExprValue *init_value = nullptr;
|
||||
|
||||
// TODO more validation for types that can't be used for export/extern variables
|
||||
ZigType *implicit_type = nullptr;
|
||||
@ -3686,7 +3695,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
|
||||
} else if (var_decl->expr) {
|
||||
init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, var_decl->symbol);
|
||||
assert(init_value);
|
||||
implicit_type = init_value->value.type;
|
||||
implicit_type = init_value->type;
|
||||
|
||||
if (implicit_type->id == ZigTypeIdUnreachable) {
|
||||
add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable"));
|
||||
@ -3704,7 +3713,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
|
||||
add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
|
||||
implicit_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
assert(implicit_type->id == ZigTypeIdInvalid || init_value->value.special != ConstValSpecialRuntime);
|
||||
assert(implicit_type->id == ZigTypeIdInvalid || init_value->special != ConstValSpecialRuntime);
|
||||
} else if (linkage != VarLinkageExternal) {
|
||||
add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
|
||||
implicit_type = g->builtin_types.entry_invalid;
|
||||
@ -3713,19 +3722,19 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
|
||||
ZigType *type = explicit_type ? explicit_type : implicit_type;
|
||||
assert(type != nullptr); // should have been caught by the parser
|
||||
|
||||
ConstExprValue *init_val = init_value ? &init_value->value : create_const_runtime(type);
|
||||
ConstExprValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(type);
|
||||
|
||||
tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
|
||||
is_const, init_val, &tld_var->base);
|
||||
is_const, init_val, &tld_var->base, type);
|
||||
tld_var->var->linkage = linkage;
|
||||
|
||||
if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
|
||||
tld_var->var->value->type = g->builtin_types.entry_invalid;
|
||||
tld_var->var->var_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
if (var_decl->align_expr != nullptr) {
|
||||
if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) {
|
||||
tld_var->var->value->type = g->builtin_types.entry_invalid;
|
||||
tld_var->var->var_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3938,6 +3947,7 @@ static bool is_container(ZigType *type_entry) {
|
||||
case ZigTypeIdArgTuple:
|
||||
case ZigTypeIdOpaque:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdVector:
|
||||
return false;
|
||||
}
|
||||
zig_unreachable();
|
||||
@ -3997,6 +4007,7 @@ void resolve_container_type(CodeGen *g, ZigType *type_entry) {
|
||||
case ZigTypeIdArgTuple:
|
||||
case ZigTypeIdOpaque:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdVector:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
@ -4090,7 +4101,7 @@ static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) {
|
||||
}
|
||||
|
||||
ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
|
||||
param_name, true, create_const_runtime(param_type), nullptr);
|
||||
param_name, true, create_const_runtime(param_type), nullptr, param_type);
|
||||
var->src_arg_index = i;
|
||||
fn_table_entry->child_scope = var->child_scope;
|
||||
var->shadowable = var->shadowable || is_var_args;
|
||||
@ -4228,18 +4239,17 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *
|
||||
preview_use_decl(g, src_use_node);
|
||||
}
|
||||
|
||||
IrInstruction *use_target_value = src_use_node->data.use.value;
|
||||
if (use_target_value->value.type->id == ZigTypeIdInvalid) {
|
||||
ConstExprValue *use_target_value = src_use_node->data.use.value;
|
||||
if (type_is_invalid(use_target_value->type)) {
|
||||
dst_use_node->owner->any_imports_failed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
dst_use_node->data.use.resolution = TldResolutionOk;
|
||||
|
||||
ConstExprValue *const_val = &use_target_value->value;
|
||||
assert(const_val->special != ConstValSpecialRuntime);
|
||||
assert(use_target_value->special != ConstValSpecialRuntime);
|
||||
|
||||
ImportTableEntry *target_import = const_val->data.x_import;
|
||||
ImportTableEntry *target_import = use_target_value->data.x_import;
|
||||
assert(target_import);
|
||||
|
||||
if (target_import->any_imports_failed) {
|
||||
@ -4302,10 +4312,10 @@ void preview_use_decl(CodeGen *g, AstNode *node) {
|
||||
}
|
||||
|
||||
node->data.use.resolution = TldResolutionResolving;
|
||||
IrInstruction *result = analyze_const_value(g, &node->owner->decls_scope->base,
|
||||
ConstExprValue *result = analyze_const_value(g, &node->owner->decls_scope->base,
|
||||
node->data.use.expr, g->builtin_types.entry_namespace, nullptr);
|
||||
|
||||
if (result->value.type->id == ZigTypeIdInvalid)
|
||||
if (type_is_invalid(result->type))
|
||||
node->owner->any_imports_failed = true;
|
||||
|
||||
node->data.use.value = result;
|
||||
@ -4447,6 +4457,42 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
|
||||
return new_entry;
|
||||
}
|
||||
|
||||
bool is_valid_vector_elem_type(ZigType *elem_type) {
|
||||
return elem_type->id == ZigTypeIdInt ||
|
||||
elem_type->id == ZigTypeIdFloat ||
|
||||
get_codegen_ptr_type(elem_type) != nullptr;
|
||||
}
|
||||
|
||||
ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
|
||||
assert(is_valid_vector_elem_type(elem_type));
|
||||
|
||||
TypeId type_id = {};
|
||||
type_id.id = ZigTypeIdVector;
|
||||
type_id.data.vector.len = len;
|
||||
type_id.data.vector.elem_type = elem_type;
|
||||
|
||||
{
|
||||
auto entry = g->type_table.maybe_get(type_id);
|
||||
if (entry)
|
||||
return entry->value;
|
||||
}
|
||||
|
||||
ZigType *entry = new_type_table_entry(ZigTypeIdVector);
|
||||
entry->zero_bits = (len == 0) || !type_has_bits(elem_type);
|
||||
entry->type_ref = entry->zero_bits ? LLVMVoidType() : LLVMVectorType(elem_type->type_ref, len);
|
||||
entry->data.vector.len = len;
|
||||
entry->data.vector.elem_type = elem_type;
|
||||
|
||||
buf_resize(&entry->name, 0);
|
||||
buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name));
|
||||
|
||||
entry->di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, len,
|
||||
LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref), elem_type->di_type);
|
||||
|
||||
g->type_table.put(type_id, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) {
|
||||
return &g->builtin_types.entry_c_int[c_int_type];
|
||||
}
|
||||
@ -4478,6 +4524,7 @@ bool handle_is_ptr(ZigType *type_entry) {
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdEnum:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdVector:
|
||||
return false;
|
||||
case ZigTypeIdArray:
|
||||
case ZigTypeIdStruct:
|
||||
@ -4486,7 +4533,8 @@ bool handle_is_ptr(ZigType *type_entry) {
|
||||
return type_has_bits(type_entry->data.error_union.payload_type);
|
||||
case ZigTypeIdOptional:
|
||||
return type_has_bits(type_entry->data.maybe.child_type) &&
|
||||
!type_is_codegen_pointer(type_entry->data.maybe.child_type);
|
||||
!type_is_codegen_pointer(type_entry->data.maybe.child_type) &&
|
||||
type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet;
|
||||
case ZigTypeIdUnion:
|
||||
assert(type_entry->data.unionation.zero_bits_known);
|
||||
if (type_entry->data.unionation.gen_field_count == 0)
|
||||
@ -4732,6 +4780,11 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t hash_const_val_error_set(ConstExprValue *const_val) {
|
||||
assert(const_val->data.x_err_set != nullptr);
|
||||
return const_val->data.x_err_set->value ^ 2630160122;
|
||||
}
|
||||
|
||||
static uint32_t hash_const_val_ptr(ConstExprValue *const_val) {
|
||||
uint32_t hash_val = 0;
|
||||
switch (const_val->data.x_ptr.mut) {
|
||||
@ -4763,6 +4816,18 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) {
|
||||
hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
|
||||
hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
|
||||
return hash_val;
|
||||
case ConstPtrSpecialBaseErrorUnionCode:
|
||||
hash_val += (uint32_t)2994743799;
|
||||
hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val);
|
||||
return hash_val;
|
||||
case ConstPtrSpecialBaseErrorUnionPayload:
|
||||
hash_val += (uint32_t)3456080131;
|
||||
hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val);
|
||||
return hash_val;
|
||||
case ConstPtrSpecialBaseOptionalPayload:
|
||||
hash_val += (uint32_t)3163140517;
|
||||
hash_val += hash_ptr(const_val->data.x_ptr.data.base_optional_payload.optional_val);
|
||||
return hash_val;
|
||||
case ConstPtrSpecialHardCodedAddr:
|
||||
hash_val += (uint32_t)4048518294;
|
||||
hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
|
||||
@ -4774,6 +4839,9 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) {
|
||||
hash_val += (uint32_t)2590901619;
|
||||
hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
|
||||
return hash_val;
|
||||
case ConstPtrSpecialNull:
|
||||
hash_val += (uint32_t)1486246455;
|
||||
return hash_val;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -4872,7 +4940,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
|
||||
return 2709806591;
|
||||
case ZigTypeIdOptional:
|
||||
if (get_codegen_ptr_type(const_val->type) != nullptr) {
|
||||
return hash_const_val(const_val) * 1992916303;
|
||||
return hash_const_val_ptr(const_val) * 1992916303;
|
||||
} else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) {
|
||||
return hash_const_val_error_set(const_val) * 3147031929;
|
||||
} else {
|
||||
if (const_val->data.x_optional) {
|
||||
return hash_const_val(const_val->data.x_optional) * 1992916303;
|
||||
@ -4884,10 +4954,12 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
|
||||
// TODO better hashing algorithm
|
||||
return 3415065496;
|
||||
case ZigTypeIdErrorSet:
|
||||
assert(const_val->data.x_err_set != nullptr);
|
||||
return const_val->data.x_err_set->value ^ 2630160122;
|
||||
return hash_const_val_error_set(const_val);
|
||||
case ZigTypeIdNamespace:
|
||||
return hash_ptr(const_val->data.x_import);
|
||||
case ZigTypeIdVector:
|
||||
// TODO better hashing algorithm
|
||||
return 3647867726;
|
||||
case ZigTypeIdBoundFn:
|
||||
case ZigTypeIdInvalid:
|
||||
case ZigTypeIdUnreachable:
|
||||
@ -4940,6 +5012,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
|
||||
case ZigTypeIdBool:
|
||||
case ZigTypeIdUnreachable:
|
||||
case ZigTypeIdInt:
|
||||
case ZigTypeIdVector:
|
||||
case ZigTypeIdFloat:
|
||||
case ZigTypeIdComptimeFloat:
|
||||
case ZigTypeIdComptimeInt:
|
||||
@ -4987,7 +5060,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
|
||||
return can_mutate_comptime_var_state(value->data.x_optional);
|
||||
|
||||
case ZigTypeIdErrorUnion:
|
||||
if (value->data.x_err_union.err != nullptr)
|
||||
if (value->data.x_err_union.error_set->data.x_err_set != nullptr)
|
||||
return false;
|
||||
assert(value->data.x_err_union.payload != nullptr);
|
||||
return can_mutate_comptime_var_state(value->data.x_err_union.payload);
|
||||
@ -5023,6 +5096,7 @@ static bool return_type_is_cacheable(ZigType *return_type) {
|
||||
case ZigTypeIdErrorSet:
|
||||
case ZigTypeIdEnum:
|
||||
case ZigTypeIdPointer:
|
||||
case ZigTypeIdVector:
|
||||
return true;
|
||||
|
||||
case ZigTypeIdArray:
|
||||
@ -5048,9 +5122,9 @@ bool fn_eval_cacheable(Scope *scope, ZigType *return_type) {
|
||||
while (scope) {
|
||||
if (scope->id == ScopeIdVarDecl) {
|
||||
ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
|
||||
if (type_is_invalid(var_scope->var->value->type))
|
||||
if (type_is_invalid(var_scope->var->var_type))
|
||||
return false;
|
||||
if (can_mutate_comptime_var_state(var_scope->var->value))
|
||||
if (can_mutate_comptime_var_state(var_scope->var->const_value))
|
||||
return false;
|
||||
} else if (scope->id == ScopeIdFnDef) {
|
||||
return true;
|
||||
@ -5068,7 +5142,7 @@ uint32_t fn_eval_hash(Scope* scope) {
|
||||
while (scope) {
|
||||
if (scope->id == ScopeIdVarDecl) {
|
||||
ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
|
||||
result += hash_const_val(var_scope->var->value);
|
||||
result += hash_const_val(var_scope->var->const_value);
|
||||
} else if (scope->id == ScopeIdFnDef) {
|
||||
ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
|
||||
result += hash_ptr(fn_scope->fn_entry);
|
||||
@ -5092,10 +5166,16 @@ bool fn_eval_eql(Scope *a, Scope *b) {
|
||||
if (a->id == ScopeIdVarDecl) {
|
||||
ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a;
|
||||
ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b;
|
||||
if (a_var_scope->var->value->type != b_var_scope->var->value->type)
|
||||
return false;
|
||||
if (!const_values_equal(a->codegen, a_var_scope->var->value, b_var_scope->var->value))
|
||||
if (a_var_scope->var->var_type != b_var_scope->var->var_type)
|
||||
return false;
|
||||
if (a_var_scope->var->var_type == a_var_scope->var->const_value->type &&
|
||||
b_var_scope->var->var_type == b_var_scope->var->const_value->type)
|
||||
{
|
||||
if (!const_values_equal(a->codegen, a_var_scope->var->const_value, b_var_scope->var->const_value))
|
||||
return false;
|
||||
} else {
|
||||
zig_panic("TODO comptime ptr reinterpret for fn_eval_eql");
|
||||
}
|
||||
} else if (a->id == ScopeIdFnDef) {
|
||||
ScopeFnDef *a_fn_scope = (ScopeFnDef *)a;
|
||||
ScopeFnDef *b_fn_scope = (ScopeFnDef *)b;
|
||||
@ -5113,6 +5193,7 @@ bool fn_eval_eql(Scope *a, Scope *b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Whether the type has bits at runtime.
|
||||
bool type_has_bits(ZigType *type_entry) {
|
||||
assert(type_entry);
|
||||
assert(!type_is_invalid(type_entry));
|
||||
@ -5120,6 +5201,66 @@ bool type_has_bits(ZigType *type_entry) {
|
||||
return !type_entry->zero_bits;
|
||||
}
|
||||
|
||||
// Whether you can infer the value based solely on the type.
|
||||
OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
|
||||
assert(type_entry != nullptr);
|
||||
Error err;
|
||||
if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
|
||||
return OnePossibleValueInvalid;
|
||||
switch (type_entry->id) {
|
||||
case ZigTypeIdInvalid:
|
||||
zig_unreachable();
|
||||
case ZigTypeIdOpaque:
|
||||
case ZigTypeIdComptimeFloat:
|
||||
case ZigTypeIdComptimeInt:
|
||||
case ZigTypeIdMetaType:
|
||||
case ZigTypeIdNamespace:
|
||||
case ZigTypeIdBoundFn:
|
||||
case ZigTypeIdArgTuple:
|
||||
case ZigTypeIdOptional:
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdBool:
|
||||
case ZigTypeIdFloat:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdErrorUnion:
|
||||
return OnePossibleValueNo;
|
||||
case ZigTypeIdUndefined:
|
||||
case ZigTypeIdNull:
|
||||
case ZigTypeIdVoid:
|
||||
case ZigTypeIdUnreachable:
|
||||
return OnePossibleValueYes;
|
||||
case ZigTypeIdArray:
|
||||
if (type_entry->data.array.len == 0)
|
||||
return OnePossibleValueYes;
|
||||
return type_has_one_possible_value(g, type_entry->data.array.child_type);
|
||||
case ZigTypeIdStruct:
|
||||
for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
|
||||
TypeStructField *field = &type_entry->data.structure.fields[i];
|
||||
switch (type_has_one_possible_value(g, field->type_entry)) {
|
||||
case OnePossibleValueInvalid:
|
||||
return OnePossibleValueInvalid;
|
||||
case OnePossibleValueNo:
|
||||
return OnePossibleValueNo;
|
||||
case OnePossibleValueYes:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return OnePossibleValueYes;
|
||||
case ZigTypeIdErrorSet:
|
||||
case ZigTypeIdEnum:
|
||||
case ZigTypeIdInt:
|
||||
case ZigTypeIdVector:
|
||||
return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes;
|
||||
case ZigTypeIdPointer:
|
||||
return type_has_one_possible_value(g, type_entry->data.pointer.child_type);
|
||||
case ZigTypeIdUnion:
|
||||
if (type_entry->data.unionation.src_field_count > 1)
|
||||
return OnePossibleValueNo;
|
||||
return type_has_one_possible_value(g, type_entry->data.unionation.fields[0].type_entry);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) {
|
||||
Error err;
|
||||
if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
|
||||
@ -5159,6 +5300,7 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) {
|
||||
case ZigTypeIdErrorSet:
|
||||
case ZigTypeIdBool:
|
||||
case ZigTypeIdInt:
|
||||
case ZigTypeIdVector:
|
||||
case ZigTypeIdFloat:
|
||||
case ZigTypeIdVoid:
|
||||
case ZigTypeIdUnreachable:
|
||||
@ -5574,6 +5716,33 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
|
||||
if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
|
||||
return false;
|
||||
return true;
|
||||
case ConstPtrSpecialBaseErrorUnionCode:
|
||||
if (a->data.x_ptr.data.base_err_union_code.err_union_val !=
|
||||
b->data.x_ptr.data.base_err_union_code.err_union_val &&
|
||||
a->data.x_ptr.data.base_err_union_code.err_union_val->global_refs !=
|
||||
b->data.x_ptr.data.base_err_union_code.err_union_val->global_refs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case ConstPtrSpecialBaseErrorUnionPayload:
|
||||
if (a->data.x_ptr.data.base_err_union_payload.err_union_val !=
|
||||
b->data.x_ptr.data.base_err_union_payload.err_union_val &&
|
||||
a->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs !=
|
||||
b->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case ConstPtrSpecialBaseOptionalPayload:
|
||||
if (a->data.x_ptr.data.base_optional_payload.optional_val !=
|
||||
b->data.x_ptr.data.base_optional_payload.optional_val &&
|
||||
a->data.x_ptr.data.base_optional_payload.optional_val->global_refs !=
|
||||
b->data.x_ptr.data.base_optional_payload.optional_val->global_refs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case ConstPtrSpecialHardCodedAddr:
|
||||
if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
|
||||
return false;
|
||||
@ -5582,10 +5751,34 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
|
||||
return true;
|
||||
case ConstPtrSpecialFunction:
|
||||
return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
|
||||
case ConstPtrSpecialNull:
|
||||
return true;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprValue *b, size_t len) {
|
||||
assert(a->data.x_array.special != ConstArraySpecialUndef);
|
||||
assert(b->data.x_array.special != ConstArraySpecialUndef);
|
||||
if (a->data.x_array.special == ConstArraySpecialBuf &&
|
||||
b->data.x_array.special == ConstArraySpecialBuf)
|
||||
{
|
||||
return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
|
||||
}
|
||||
expand_undef_array(g, a);
|
||||
expand_undef_array(g, b);
|
||||
|
||||
ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
|
||||
ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
|
||||
|
||||
for (size_t i = 0; i < len; i += 1) {
|
||||
if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
|
||||
assert(a->type->id == b->type->id);
|
||||
assert(a->special == ConstValSpecialStatic);
|
||||
@ -5640,28 +5833,12 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
|
||||
case ZigTypeIdPointer:
|
||||
case ZigTypeIdFn:
|
||||
return const_values_equal_ptr(a, b);
|
||||
case ZigTypeIdVector:
|
||||
assert(a->type->data.vector.len == b->type->data.vector.len);
|
||||
return const_values_equal_array(g, a, b, a->type->data.vector.len);
|
||||
case ZigTypeIdArray: {
|
||||
assert(a->type->data.array.len == b->type->data.array.len);
|
||||
assert(a->data.x_array.special != ConstArraySpecialUndef);
|
||||
assert(b->data.x_array.special != ConstArraySpecialUndef);
|
||||
if (a->data.x_array.special == ConstArraySpecialBuf &&
|
||||
b->data.x_array.special == ConstArraySpecialBuf)
|
||||
{
|
||||
return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
|
||||
}
|
||||
expand_undef_array(g, a);
|
||||
expand_undef_array(g, b);
|
||||
|
||||
size_t len = a->type->data.array.len;
|
||||
ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
|
||||
ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return const_values_equal_array(g, a, b, a->type->data.array.len);
|
||||
}
|
||||
case ZigTypeIdStruct:
|
||||
for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
|
||||
@ -5750,7 +5927,7 @@ void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_v
|
||||
}
|
||||
}
|
||||
|
||||
void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) {
|
||||
static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) {
|
||||
assert(type_entry->id == ZigTypeIdPointer);
|
||||
|
||||
if (type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) {
|
||||
@ -5763,6 +5940,9 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy
|
||||
zig_unreachable();
|
||||
case ConstPtrSpecialRef:
|
||||
case ConstPtrSpecialBaseStruct:
|
||||
case ConstPtrSpecialBaseErrorUnionCode:
|
||||
case ConstPtrSpecialBaseErrorUnionPayload:
|
||||
case ConstPtrSpecialBaseOptionalPayload:
|
||||
buf_appendf(buf, "*");
|
||||
// TODO we need a source node for const_ptr_pointee because it can generate compile errors
|
||||
render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
|
||||
@ -5790,6 +5970,51 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy
|
||||
buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
|
||||
return;
|
||||
}
|
||||
case ConstPtrSpecialNull:
|
||||
buf_append_str(buf, "null");
|
||||
return;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) {
|
||||
if (const_val->data.x_err_set == nullptr) {
|
||||
buf_append_str(buf, "null");
|
||||
} else {
|
||||
buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name));
|
||||
}
|
||||
}
|
||||
|
||||
static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_val, size_t len) {
|
||||
switch (const_val->data.x_array.special) {
|
||||
case ConstArraySpecialUndef:
|
||||
buf_append_str(buf, "undefined");
|
||||
return;
|
||||
case ConstArraySpecialBuf: {
|
||||
Buf *array_buf = const_val->data.x_array.data.s_buf;
|
||||
buf_append_char(buf, '"');
|
||||
for (size_t i = 0; i < buf_len(array_buf); i += 1) {
|
||||
uint8_t c = buf_ptr(array_buf)[i];
|
||||
if (c == '"') {
|
||||
buf_append_str(buf, "\\\"");
|
||||
} else {
|
||||
buf_append_char(buf, c);
|
||||
}
|
||||
}
|
||||
buf_append_char(buf, '"');
|
||||
return;
|
||||
}
|
||||
case ConstArraySpecialNone: {
|
||||
buf_appendf(buf, "%s{", buf_ptr(&const_val->type->name));
|
||||
for (uint64_t i = 0; i < len; i += 1) {
|
||||
if (i != 0)
|
||||
buf_appendf(buf, ",");
|
||||
ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
|
||||
render_const_value(g, buf, child_value);
|
||||
}
|
||||
buf_appendf(buf, "}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -5874,39 +6099,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
|
||||
}
|
||||
case ZigTypeIdPointer:
|
||||
return render_const_val_ptr(g, buf, const_val, type_entry);
|
||||
case ZigTypeIdVector:
|
||||
return render_const_val_array(g, buf, const_val, type_entry->data.vector.len);
|
||||
case ZigTypeIdArray:
|
||||
switch (const_val->data.x_array.special) {
|
||||
case ConstArraySpecialUndef:
|
||||
buf_append_str(buf, "undefined");
|
||||
return;
|
||||
case ConstArraySpecialBuf: {
|
||||
Buf *array_buf = const_val->data.x_array.data.s_buf;
|
||||
buf_append_char(buf, '"');
|
||||
for (size_t i = 0; i < buf_len(array_buf); i += 1) {
|
||||
uint8_t c = buf_ptr(array_buf)[i];
|
||||
if (c == '"') {
|
||||
buf_append_str(buf, "\\\"");
|
||||
} else {
|
||||
buf_append_char(buf, c);
|
||||
}
|
||||
}
|
||||
buf_append_char(buf, '"');
|
||||
return;
|
||||
}
|
||||
case ConstArraySpecialNone: {
|
||||
buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
|
||||
uint64_t len = type_entry->data.array.len;
|
||||
for (uint64_t i = 0; i < len; i += 1) {
|
||||
if (i != 0)
|
||||
buf_appendf(buf, ",");
|
||||
ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
|
||||
render_const_value(g, buf, child_value);
|
||||
}
|
||||
buf_appendf(buf, "}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
return render_const_val_array(g, buf, const_val, type_entry->data.array.len);
|
||||
case ZigTypeIdNull:
|
||||
{
|
||||
buf_appendf(buf, "null");
|
||||
@ -5921,6 +6117,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
|
||||
{
|
||||
if (get_codegen_ptr_type(const_val->type) != nullptr)
|
||||
return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type);
|
||||
if (type_entry->data.maybe.child_type->id == ZigTypeIdErrorSet)
|
||||
return render_const_val_err_set(g, buf, const_val, type_entry->data.maybe.child_type);
|
||||
if (const_val->data.x_optional) {
|
||||
render_const_value(g, buf, const_val->data.x_optional);
|
||||
} else {
|
||||
@ -5958,11 +6156,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
|
||||
case ZigTypeIdErrorUnion:
|
||||
{
|
||||
buf_appendf(buf, "%s(", buf_ptr(&type_entry->name));
|
||||
if (const_val->data.x_err_union.err == nullptr) {
|
||||
ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set;
|
||||
if (err_set == nullptr) {
|
||||
render_const_value(g, buf, const_val->data.x_err_union.payload);
|
||||
} else {
|
||||
buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name),
|
||||
buf_ptr(&const_val->data.x_err_union.err->name));
|
||||
buf_ptr(&err_set->name));
|
||||
}
|
||||
buf_appendf(buf, ")");
|
||||
return;
|
||||
@ -5977,10 +6176,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
|
||||
return;
|
||||
}
|
||||
case ZigTypeIdErrorSet:
|
||||
{
|
||||
buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name));
|
||||
return;
|
||||
}
|
||||
return render_const_val_err_set(g, buf, const_val, type_entry);
|
||||
case ZigTypeIdArgTuple:
|
||||
{
|
||||
buf_appendf(buf, "(args value)");
|
||||
@ -6065,6 +6261,8 @@ uint32_t type_id_hash(TypeId x) {
|
||||
case ZigTypeIdInt:
|
||||
return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) +
|
||||
(((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557);
|
||||
case ZigTypeIdVector:
|
||||
return hash_ptr(x.data.vector.elem_type) * (x.data.vector.len * 526582681);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -6113,6 +6311,9 @@ bool type_id_eql(TypeId a, TypeId b) {
|
||||
case ZigTypeIdInt:
|
||||
return a.data.integer.is_signed == b.data.integer.is_signed &&
|
||||
a.data.integer.bit_count == b.data.integer.bit_count;
|
||||
case ZigTypeIdVector:
|
||||
return a.data.vector.elem_type == b.data.vector.elem_type &&
|
||||
a.data.vector.len == b.data.vector.len;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -6171,24 +6372,34 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
|
||||
|
||||
// Canonicalize the array value as ConstArraySpecialNone
|
||||
void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
|
||||
assert(const_val->type->id == ZigTypeIdArray);
|
||||
size_t elem_count;
|
||||
ZigType *elem_type;
|
||||
if (const_val->type->id == ZigTypeIdArray) {
|
||||
elem_count = const_val->type->data.array.len;
|
||||
elem_type = const_val->type->data.array.child_type;
|
||||
} else if (const_val->type->id == ZigTypeIdVector) {
|
||||
elem_count = const_val->type->data.vector.len;
|
||||
elem_type = const_val->type->data.vector.elem_type;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
if (const_val->special == ConstValSpecialUndef) {
|
||||
const_val->special = ConstValSpecialStatic;
|
||||
const_val->data.x_array.special = ConstArraySpecialUndef;
|
||||
}
|
||||
switch (const_val->data.x_array.special) {
|
||||
case ConstArraySpecialNone:
|
||||
return;
|
||||
case ConstArraySpecialUndef: {
|
||||
const_val->data.x_array.special = ConstArraySpecialNone;
|
||||
size_t elem_count = const_val->type->data.array.len;
|
||||
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
|
||||
for (size_t i = 0; i < elem_count; i += 1) {
|
||||
ConstExprValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
|
||||
element_val->type = const_val->type->data.array.child_type;
|
||||
element_val->type = elem_type;
|
||||
init_const_undefined(g, element_val);
|
||||
ConstParent *parent = get_const_val_parent(g, element_val);
|
||||
if (parent != nullptr) {
|
||||
parent->id = ConstParentIdArray;
|
||||
parent->data.p_array.array_val = const_val;
|
||||
parent->data.p_array.elem_index = i;
|
||||
}
|
||||
element_val->parent.id = ConstParentIdArray;
|
||||
element_val->parent.data.p_array.array_val = const_val;
|
||||
element_val->parent.data.p_array.elem_index = i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -6199,7 +6410,6 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
|
||||
g->string_literals_table.maybe_remove(buf);
|
||||
|
||||
const_val->data.x_array.special = ConstArraySpecialNone;
|
||||
size_t elem_count = const_val->type->data.array.len;
|
||||
assert(elem_count == buf_len(buf));
|
||||
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
|
||||
for (size_t i = 0; i < elem_count; i += 1) {
|
||||
@ -6207,6 +6417,9 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
|
||||
this_char->special = ConstValSpecialStatic;
|
||||
this_char->type = g->builtin_types.entry_u8;
|
||||
bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]);
|
||||
this_char->parent.id = ConstParentIdArray;
|
||||
this_char->parent.data.p_array.array_val = const_val;
|
||||
this_char->parent.data.p_array.elem_index = i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -6214,18 +6427,9 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
// Deprecated. Reference the parent field directly.
|
||||
ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
|
||||
assert(value->type);
|
||||
ZigType *type_entry = value->type;
|
||||
if (type_entry->id == ZigTypeIdArray) {
|
||||
expand_undef_array(g, value);
|
||||
return &value->data.x_array.data.s_none.parent;
|
||||
} else if (type_entry->id == ZigTypeIdStruct) {
|
||||
return &value->data.x_struct.parent;
|
||||
} else if (type_entry->id == ZigTypeIdUnion) {
|
||||
return &value->data.x_union.parent;
|
||||
}
|
||||
return nullptr;
|
||||
return &value->parent;
|
||||
}
|
||||
|
||||
static const ZigTypeId all_type_ids[] = {
|
||||
@ -6253,6 +6457,7 @@ static const ZigTypeId all_type_ids[] = {
|
||||
ZigTypeIdArgTuple,
|
||||
ZigTypeIdOpaque,
|
||||
ZigTypeIdPromise,
|
||||
ZigTypeIdVector,
|
||||
};
|
||||
|
||||
ZigTypeId type_id_at_index(size_t index) {
|
||||
@ -6318,6 +6523,8 @@ size_t type_id_index(ZigType *entry) {
|
||||
return 22;
|
||||
case ZigTypeIdPromise:
|
||||
return 23;
|
||||
case ZigTypeIdVector:
|
||||
return 24;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -6374,6 +6581,8 @@ const char *type_id_name(ZigTypeId id) {
|
||||
return "Opaque";
|
||||
case ZigTypeIdPromise:
|
||||
return "Promise";
|
||||
case ZigTypeIdVector:
|
||||
return "Vector";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -6453,7 +6662,7 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
|
||||
resolve_top_level_decl(codegen, tld, false, nullptr);
|
||||
assert(tld->id == TldIdVar);
|
||||
TldVar *tld_var = (TldVar *)tld;
|
||||
ConstExprValue *var_value = tld_var->var->value;
|
||||
ConstExprValue *var_value = tld_var->var->const_value;
|
||||
assert(var_value != nullptr);
|
||||
return var_value;
|
||||
}
|
||||
@ -6529,6 +6738,7 @@ X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
|
||||
case ZigTypeIdBool:
|
||||
return X64CABIClass_INTEGER;
|
||||
case ZigTypeIdFloat:
|
||||
case ZigTypeIdVector:
|
||||
return X64CABIClass_SSE;
|
||||
case ZigTypeIdStruct: {
|
||||
// "If the size of an object is larger than four eightbytes, or it contains unaligned
|
||||
|
@ -20,6 +20,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
|
||||
uint64_t type_size(CodeGen *g, ZigType *type_entry);
|
||||
uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
|
||||
ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
|
||||
ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type);
|
||||
ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
|
||||
ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type);
|
||||
ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id);
|
||||
@ -73,6 +74,7 @@ TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag);
|
||||
bool is_ref(ZigType *type_entry);
|
||||
bool is_array_ref(ZigType *type_entry);
|
||||
bool is_container_ref(ZigType *type_entry);
|
||||
bool is_valid_vector_elem_type(ZigType *elem_type);
|
||||
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
|
||||
void scan_import(CodeGen *g, ImportTableEntry *import);
|
||||
void preview_use_decl(CodeGen *g, AstNode *node);
|
||||
@ -81,7 +83,7 @@ ZigFn *scope_fn_entry(Scope *scope);
|
||||
ImportTableEntry *get_scope_import(Scope *scope);
|
||||
void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope);
|
||||
ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
|
||||
bool is_const, ConstExprValue *init_value, Tld *src_tld);
|
||||
bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type);
|
||||
ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node);
|
||||
ZigFn *create_fn(CodeGen *g, AstNode *proto_node);
|
||||
ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value);
|
||||
@ -222,6 +224,13 @@ enum ReqCompTime {
|
||||
};
|
||||
ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry);
|
||||
|
||||
enum OnePossibleValue {
|
||||
OnePossibleValueInvalid,
|
||||
OnePossibleValueNo,
|
||||
OnePossibleValueYes,
|
||||
};
|
||||
OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry);
|
||||
|
||||
Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
|
||||
ConstExprValue *const_val, ZigType *wanted_type);
|
||||
|
||||
|
@ -233,8 +233,8 @@ static const char *node_type_str(NodeType node_type) {
|
||||
return "ErrorType";
|
||||
case NodeTypeIfErrorExpr:
|
||||
return "IfErrorExpr";
|
||||
case NodeTypeTestExpr:
|
||||
return "TestExpr";
|
||||
case NodeTypeIfOptional:
|
||||
return "IfOptional";
|
||||
case NodeTypeErrorSetDecl:
|
||||
return "ErrorSetDecl";
|
||||
case NodeTypeCancel:
|
||||
@ -387,7 +387,7 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
|
||||
if (node->data.if_err_expr.else_node)
|
||||
return statement_terminates_without_semicolon(node->data.if_err_expr.else_node);
|
||||
return node->data.if_err_expr.then_node->type == NodeTypeBlock;
|
||||
case NodeTypeTestExpr:
|
||||
case NodeTypeIfOptional:
|
||||
if (node->data.test_expr.else_node)
|
||||
return statement_terminates_without_semicolon(node->data.test_expr.else_node);
|
||||
return node->data.test_expr.then_node->type == NodeTypeBlock;
|
||||
@ -974,7 +974,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeTestExpr:
|
||||
case NodeTypeIfOptional:
|
||||
{
|
||||
fprintf(ar->f, "if (");
|
||||
render_node_grouped(ar, node->data.test_expr.target_node);
|
||||
|
@ -222,14 +222,9 @@ static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf, Buf *contents
|
||||
assert(chf->path != nullptr);
|
||||
|
||||
OsFile this_file;
|
||||
if ((err = os_file_open_r(chf->path, &this_file)))
|
||||
if ((err = os_file_open_r(chf->path, &this_file, &chf->mtime)))
|
||||
return err;
|
||||
|
||||
if ((err = os_file_mtime(this_file, &chf->mtime))) {
|
||||
os_file_close(this_file);
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = hash_file(chf->bin_digest, this_file, contents))) {
|
||||
os_file_close(this_file);
|
||||
return err;
|
||||
@ -351,17 +346,12 @@ Error cache_hit(CacheHash *ch, Buf *out_digest) {
|
||||
|
||||
// if the mtime matches we can trust the digest
|
||||
OsFile this_file;
|
||||
if ((err = os_file_open_r(chf->path, &this_file))) {
|
||||
OsTimeStamp actual_mtime;
|
||||
if ((err = os_file_open_r(chf->path, &this_file, &actual_mtime))) {
|
||||
fprintf(stderr, "Unable to open %s\n: %s", buf_ptr(chf->path), err_str(err));
|
||||
os_file_close(ch->manifest_file);
|
||||
return ErrorCacheUnavailable;
|
||||
}
|
||||
OsTimeStamp actual_mtime;
|
||||
if ((err = os_file_mtime(this_file, &actual_mtime))) {
|
||||
os_file_close(this_file);
|
||||
os_file_close(ch->manifest_file);
|
||||
return err;
|
||||
}
|
||||
if (chf->mtime.sec == actual_mtime.sec && chf->mtime.nsec == actual_mtime.nsec) {
|
||||
os_file_close(this_file);
|
||||
} else {
|
||||
|
500
src/codegen.cpp
500
src/codegen.cpp
@ -118,6 +118,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
|
||||
g->string_literals_table.init(16);
|
||||
g->type_info_cache.init(32);
|
||||
g->is_test_build = false;
|
||||
g->is_single_threaded = false;
|
||||
buf_resize(&g->global_asm, 0);
|
||||
|
||||
for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) {
|
||||
@ -313,6 +314,8 @@ static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *
|
||||
static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name);
|
||||
static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name);
|
||||
static void generate_error_name_table(CodeGen *g);
|
||||
static bool value_is_all_undef(ConstExprValue *const_val);
|
||||
static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr);
|
||||
|
||||
static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) {
|
||||
unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name));
|
||||
@ -461,6 +464,21 @@ static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkag
|
||||
}
|
||||
}
|
||||
|
||||
static bool cc_want_sret_attr(CallingConvention cc) {
|
||||
switch (cc) {
|
||||
case CallingConventionNaked:
|
||||
zig_unreachable();
|
||||
case CallingConventionC:
|
||||
case CallingConventionCold:
|
||||
case CallingConventionStdcall:
|
||||
return true;
|
||||
case CallingConventionAsync:
|
||||
case CallingConventionUnspecified:
|
||||
return false;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
|
||||
if (fn_table_entry->llvm_value)
|
||||
return fn_table_entry->llvm_value;
|
||||
@ -598,9 +616,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
|
||||
} else if (type_is_codegen_pointer(return_type)) {
|
||||
addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
|
||||
} else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
|
||||
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
|
||||
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");
|
||||
if (cc == CallingConventionC) {
|
||||
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
|
||||
if (cc_want_sret_attr(cc)) {
|
||||
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias");
|
||||
}
|
||||
init_gen_i = 1;
|
||||
@ -1903,9 +1921,8 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) {
|
||||
}
|
||||
|
||||
static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) {
|
||||
assert(alignment > 0);
|
||||
LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name);
|
||||
LLVMSetAlignment(result, alignment);
|
||||
LLVMSetAlignment(result, (alignment == 0) ? get_abi_alignment(g, type_entry) : alignment);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1963,7 +1980,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
|
||||
break;
|
||||
}
|
||||
|
||||
if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat ||
|
||||
if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || ty->id == ZigTypeIdVector ||
|
||||
ty->id == ZigTypeIdInt // TODO investigate if we need to change this
|
||||
) {
|
||||
switch (fn_walk->id) {
|
||||
@ -2200,10 +2217,10 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) {
|
||||
assert(variable);
|
||||
assert(variable->value_ref);
|
||||
|
||||
if (!handle_is_ptr(variable->value->type)) {
|
||||
if (!handle_is_ptr(variable->var_type)) {
|
||||
clear_debug_source_node(g);
|
||||
gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), variable->value_ref,
|
||||
variable->align_bytes, false);
|
||||
gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index),
|
||||
variable->value_ref, variable->align_bytes, false);
|
||||
}
|
||||
|
||||
if (variable->decl_node) {
|
||||
@ -2643,6 +2660,27 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||
} else {
|
||||
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else if (type_entry->id == ZigTypeIdVector) {
|
||||
ZigType *elem_type = type_entry->data.vector.elem_type;
|
||||
if (elem_type->id == ZigTypeIdFloat) {
|
||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
||||
return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
|
||||
} else if (elem_type->id == ZigTypeIdPointer) {
|
||||
zig_panic("TODO codegen for pointers in vectors");
|
||||
} else if (elem_type->id == ZigTypeIdInt) {
|
||||
bool is_wrapping = (op_id == IrBinOpAddWrap);
|
||||
if (is_wrapping) {
|
||||
return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
|
||||
} else if (want_runtime_safety) {
|
||||
zig_panic("TODO runtime safety for vector integer addition");
|
||||
} else if (elem_type->data.integral.is_signed) {
|
||||
return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -2961,7 +2999,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionPtrCast *instruction)
|
||||
IrInstructionPtrCastGen *instruction)
|
||||
{
|
||||
ZigType *wanted_type = instruction->base.value.type;
|
||||
if (!type_has_bits(wanted_type)) {
|
||||
@ -3149,11 +3187,11 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionDeclVar *decl_var_instruction)
|
||||
IrInstructionDeclVarGen *decl_var_instruction)
|
||||
{
|
||||
ZigVar *var = decl_var_instruction->var;
|
||||
|
||||
if (!type_has_bits(var->value->type))
|
||||
if (!type_has_bits(var->var_type))
|
||||
return nullptr;
|
||||
|
||||
if (var->ref_count == 0 && g->build_mode != BuildModeDebug)
|
||||
@ -3161,34 +3199,16 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
|
||||
|
||||
IrInstruction *init_value = decl_var_instruction->init_value;
|
||||
|
||||
bool have_init_expr = false;
|
||||
|
||||
ConstExprValue *const_val = &init_value->value;
|
||||
if (const_val->special == ConstValSpecialRuntime || const_val->special == ConstValSpecialStatic)
|
||||
have_init_expr = true;
|
||||
bool have_init_expr = !value_is_all_undef(&init_value->value);
|
||||
|
||||
if (have_init_expr) {
|
||||
assert(var->value->type == init_value->value.type);
|
||||
ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false,
|
||||
ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->var_type, false, false,
|
||||
PtrLenSingle, var->align_bytes, 0, 0);
|
||||
LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value);
|
||||
gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val);
|
||||
} else {
|
||||
bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base);
|
||||
if (want_safe) {
|
||||
ZigType *usize = g->builtin_types.entry_usize;
|
||||
uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value->type->type_ref);
|
||||
assert(size_bytes > 0);
|
||||
|
||||
assert(var->align_bytes > 0);
|
||||
|
||||
// memset uninitialized memory to 0xa
|
||||
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
|
||||
LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, var->value_ref, ptr_u8, "");
|
||||
LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
|
||||
ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, var->align_bytes, false);
|
||||
}
|
||||
} else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) {
|
||||
uint32_t align_bytes = (var->align_bytes == 0) ? get_abi_alignment(g, var->var_type) : var->align_bytes;
|
||||
gen_undef_init(g, align_bytes, var->var_type, var->value_ref);
|
||||
}
|
||||
|
||||
gen_var_debug_decl(g, var);
|
||||
@ -3225,21 +3245,81 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
|
||||
return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
|
||||
}
|
||||
|
||||
static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) {
|
||||
switch (const_val->data.x_array.special) {
|
||||
case ConstArraySpecialUndef:
|
||||
return true;
|
||||
case ConstArraySpecialBuf:
|
||||
return false;
|
||||
case ConstArraySpecialNone:
|
||||
for (size_t i = 0; i < len; i += 1) {
|
||||
if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static bool value_is_all_undef(ConstExprValue *const_val) {
|
||||
switch (const_val->special) {
|
||||
case ConstValSpecialRuntime:
|
||||
return false;
|
||||
case ConstValSpecialUndef:
|
||||
return true;
|
||||
case ConstValSpecialStatic:
|
||||
if (const_val->type->id == ZigTypeIdStruct) {
|
||||
for (size_t i = 0; i < const_val->type->data.structure.src_field_count; i += 1) {
|
||||
if (!value_is_all_undef(&const_val->data.x_struct.fields[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (const_val->type->id == ZigTypeIdArray) {
|
||||
return value_is_all_undef_array(const_val, const_val->type->data.array.len);
|
||||
} else if (const_val->type->id == ZigTypeIdVector) {
|
||||
return value_is_all_undef_array(const_val, const_val->type->data.vector.len);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) {
|
||||
assert(type_has_bits(value_type));
|
||||
uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref);
|
||||
assert(size_bytes > 0);
|
||||
assert(ptr_align_bytes > 0);
|
||||
// memset uninitialized memory to 0xaa
|
||||
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
|
||||
LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, "");
|
||||
ZigType *usize = g->builtin_types.entry_usize;
|
||||
LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
|
||||
ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false);
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) {
|
||||
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
|
||||
LLVMValueRef value = ir_llvm_value(g, instruction->value);
|
||||
|
||||
assert(instruction->ptr->value.type->id == ZigTypeIdPointer);
|
||||
ZigType *ptr_type = instruction->ptr->value.type;
|
||||
assert(ptr_type->id == ZigTypeIdPointer);
|
||||
if (!type_has_bits(ptr_type))
|
||||
return nullptr;
|
||||
|
||||
gen_assign_raw(g, ptr, ptr_type, value);
|
||||
|
||||
bool have_init_expr = !value_is_all_undef(&instruction->value->value);
|
||||
if (have_init_expr) {
|
||||
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
|
||||
LLVMValueRef value = ir_llvm_value(g, instruction->value);
|
||||
gen_assign_raw(g, ptr, ptr_type, value);
|
||||
} else if (ir_want_runtime_safety(g, &instruction->base)) {
|
||||
gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type,
|
||||
ir_llvm_value(g, instruction->ptr));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) {
|
||||
ZigVar *var = instruction->var;
|
||||
if (type_has_bits(var->value->type)) {
|
||||
if (type_has_bits(var->var_type)) {
|
||||
assert(var->value_ref);
|
||||
return var->value_ref;
|
||||
} else {
|
||||
@ -3553,7 +3633,8 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
||||
LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, "");
|
||||
LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr,
|
||||
union_type->data.unionation.gen_union_index, "");
|
||||
LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
|
||||
return bitcasted_union_field_ptr;
|
||||
}
|
||||
@ -3715,8 +3796,8 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueR
|
||||
if (child_type->zero_bits) {
|
||||
return maybe_handle;
|
||||
} else {
|
||||
bool maybe_is_ptr = type_is_codegen_pointer(child_type);
|
||||
if (maybe_is_ptr) {
|
||||
bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet;
|
||||
if (is_scalar) {
|
||||
return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), "");
|
||||
} else {
|
||||
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, "");
|
||||
@ -3731,17 +3812,17 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable
|
||||
return gen_non_null_bit(g, instruction->value->value.type, ir_llvm_value(g, instruction->value));
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionUnwrapOptional *instruction)
|
||||
static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionOptionalUnwrapPtr *instruction)
|
||||
{
|
||||
ZigType *ptr_type = instruction->value->value.type;
|
||||
ZigType *ptr_type = instruction->base_ptr->value.type;
|
||||
assert(ptr_type->id == ZigTypeIdPointer);
|
||||
ZigType *maybe_type = ptr_type->data.pointer.child_type;
|
||||
assert(maybe_type->id == ZigTypeIdOptional);
|
||||
ZigType *child_type = maybe_type->data.maybe.child_type;
|
||||
LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value);
|
||||
LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
|
||||
LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->base_ptr);
|
||||
if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) {
|
||||
LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
|
||||
LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle);
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk");
|
||||
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail");
|
||||
@ -3755,8 +3836,8 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
|
||||
if (child_type->zero_bits) {
|
||||
return nullptr;
|
||||
} else {
|
||||
bool maybe_is_ptr = type_is_codegen_pointer(child_type);
|
||||
if (maybe_is_ptr) {
|
||||
bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet;
|
||||
if (is_scalar) {
|
||||
return maybe_ptr;
|
||||
} else {
|
||||
LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
|
||||
@ -4174,7 +4255,7 @@ static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed)
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchg *instruction) {
|
||||
static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchgGen *instruction) {
|
||||
LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr);
|
||||
LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value);
|
||||
LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value);
|
||||
@ -4189,18 +4270,18 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
|
||||
assert(maybe_type->id == ZigTypeIdOptional);
|
||||
ZigType *child_type = maybe_type->data.maybe.child_type;
|
||||
|
||||
if (type_is_codegen_pointer(child_type)) {
|
||||
if (!handle_is_ptr(maybe_type)) {
|
||||
LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
|
||||
LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
|
||||
return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, "");
|
||||
}
|
||||
|
||||
assert(instruction->tmp_ptr != nullptr);
|
||||
assert(type_has_bits(instruction->type));
|
||||
assert(type_has_bits(child_type));
|
||||
|
||||
LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
|
||||
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, "");
|
||||
gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val);
|
||||
gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val);
|
||||
|
||||
LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
|
||||
LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, "");
|
||||
@ -4351,6 +4432,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
|
||||
assert(array_type->data.structure.is_slice);
|
||||
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
||||
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
|
||||
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind);
|
||||
|
||||
size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index;
|
||||
assert(ptr_index != SIZE_MAX);
|
||||
@ -4540,12 +4622,14 @@ static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrI
|
||||
return LLVMBuildICmp(g->builder, LLVMIntNE, err_val, zero, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrCode *instruction) {
|
||||
ZigType *ptr_type = instruction->value->value.type;
|
||||
static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionUnwrapErrCode *instruction)
|
||||
{
|
||||
ZigType *ptr_type = instruction->err_union->value.type;
|
||||
assert(ptr_type->id == ZigTypeIdPointer);
|
||||
ZigType *err_union_type = ptr_type->data.pointer.child_type;
|
||||
ZigType *payload_type = err_union_type->data.error_union.payload_type;
|
||||
LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
|
||||
LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->err_union);
|
||||
LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type);
|
||||
|
||||
if (type_has_bits(payload_type)) {
|
||||
@ -4556,7 +4640,13 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) {
|
||||
static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionUnwrapErrPayload *instruction)
|
||||
{
|
||||
bool want_safety = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on &&
|
||||
g->errors_by_index.length > 1;
|
||||
if (!want_safety && !type_has_bits(instruction->base.value.type))
|
||||
return nullptr;
|
||||
ZigType *ptr_type = instruction->value->value.type;
|
||||
assert(ptr_type->id == ZigTypeIdPointer);
|
||||
ZigType *err_union_type = ptr_type->data.pointer.child_type;
|
||||
@ -4568,7 +4658,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
|
||||
return err_union_handle;
|
||||
}
|
||||
|
||||
if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) {
|
||||
if (want_safety) {
|
||||
LLVMValueRef err_val;
|
||||
if (type_has_bits(payload_type)) {
|
||||
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
|
||||
@ -4607,7 +4697,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I
|
||||
}
|
||||
|
||||
LLVMValueRef payload_val = ir_llvm_value(g, instruction->value);
|
||||
if (type_is_codegen_pointer(child_type)) {
|
||||
if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) {
|
||||
return payload_val;
|
||||
}
|
||||
|
||||
@ -5109,6 +5199,32 @@ static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable,
|
||||
return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionVectorToArray *instruction)
|
||||
{
|
||||
ZigType *array_type = instruction->base.value.type;
|
||||
assert(array_type->id == ZigTypeIdArray);
|
||||
assert(handle_is_ptr(array_type));
|
||||
assert(instruction->tmp_ptr);
|
||||
LLVMValueRef vector = ir_llvm_value(g, instruction->vector);
|
||||
LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr,
|
||||
LLVMPointerType(instruction->vector->value.type->type_ref, 0), "");
|
||||
gen_store_untyped(g, vector, casted_ptr, 0, false);
|
||||
return instruction->tmp_ptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionArrayToVector *instruction)
|
||||
{
|
||||
ZigType *vector_type = instruction->base.value.type;
|
||||
assert(vector_type->id == ZigTypeIdVector);
|
||||
assert(!handle_is_ptr(vector_type));
|
||||
LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array);
|
||||
LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr,
|
||||
LLVMPointerType(vector_type->type_ref, 0), "");
|
||||
return gen_load_untyped(g, casted_ptr, 0, false, "");
|
||||
}
|
||||
|
||||
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
|
||||
AstNode *source_node = instruction->source_node;
|
||||
Scope *scope = instruction->scope;
|
||||
@ -5148,6 +5264,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
case IrInstructionIdCUndef:
|
||||
case IrInstructionIdEmbedFile:
|
||||
case IrInstructionIdIntType:
|
||||
case IrInstructionIdVectorType:
|
||||
case IrInstructionIdMemberCount:
|
||||
case IrInstructionIdMemberType:
|
||||
case IrInstructionIdMemberName:
|
||||
@ -5184,12 +5301,15 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
case IrInstructionIdToBytes:
|
||||
case IrInstructionIdEnumToInt:
|
||||
case IrInstructionIdCheckRuntimeScope:
|
||||
case IrInstructionIdDeclVarSrc:
|
||||
case IrInstructionIdPtrCastSrc:
|
||||
case IrInstructionIdCmpxchgSrc:
|
||||
zig_unreachable();
|
||||
|
||||
case IrInstructionIdDeclVarGen:
|
||||
return ir_render_decl_var(g, executable, (IrInstructionDeclVarGen *)instruction);
|
||||
case IrInstructionIdReturn:
|
||||
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
|
||||
case IrInstructionIdDeclVar:
|
||||
return ir_render_decl_var(g, executable, (IrInstructionDeclVar *)instruction);
|
||||
case IrInstructionIdBinOp:
|
||||
return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction);
|
||||
case IrInstructionIdCast:
|
||||
@ -5220,8 +5340,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
|
||||
case IrInstructionIdTestNonNull:
|
||||
return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction);
|
||||
case IrInstructionIdUnwrapOptional:
|
||||
return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction);
|
||||
case IrInstructionIdOptionalUnwrapPtr:
|
||||
return ir_render_optional_unwrap_ptr(g, executable, (IrInstructionOptionalUnwrapPtr *)instruction);
|
||||
case IrInstructionIdClz:
|
||||
return ir_render_clz(g, executable, (IrInstructionClz *)instruction);
|
||||
case IrInstructionIdCtz:
|
||||
@ -5236,8 +5356,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_ref(g, executable, (IrInstructionRef *)instruction);
|
||||
case IrInstructionIdErrName:
|
||||
return ir_render_err_name(g, executable, (IrInstructionErrName *)instruction);
|
||||
case IrInstructionIdCmpxchg:
|
||||
return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction);
|
||||
case IrInstructionIdCmpxchgGen:
|
||||
return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchgGen *)instruction);
|
||||
case IrInstructionIdFence:
|
||||
return ir_render_fence(g, executable, (IrInstructionFence *)instruction);
|
||||
case IrInstructionIdTruncate:
|
||||
@ -5278,8 +5398,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
|
||||
case IrInstructionIdUnionInit:
|
||||
return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction);
|
||||
case IrInstructionIdPtrCast:
|
||||
return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction);
|
||||
case IrInstructionIdPtrCastGen:
|
||||
return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction);
|
||||
case IrInstructionIdBitCast:
|
||||
return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction);
|
||||
case IrInstructionIdWidenOrShorten:
|
||||
@ -5350,6 +5470,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction);
|
||||
case IrInstructionIdBitReverse:
|
||||
return ir_render_bit_reverse(g, executable, (IrInstructionBitReverse *)instruction);
|
||||
case IrInstructionIdArrayToVector:
|
||||
return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction);
|
||||
case IrInstructionIdVectorToArray:
|
||||
return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -5377,6 +5501,9 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) {
|
||||
static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index);
|
||||
static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index);
|
||||
static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val);
|
||||
static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val);
|
||||
static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val);
|
||||
static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val);
|
||||
|
||||
static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {
|
||||
switch (parent->id) {
|
||||
@ -5387,6 +5514,12 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent
|
||||
case ConstParentIdStruct:
|
||||
return gen_const_ptr_struct_recursive(g, parent->data.p_struct.struct_val,
|
||||
parent->data.p_struct.field_index);
|
||||
case ConstParentIdErrUnionCode:
|
||||
return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val);
|
||||
case ConstParentIdErrUnionPayload:
|
||||
return gen_const_ptr_err_union_payload_recursive(g, parent->data.p_err_union_payload.err_union_val);
|
||||
case ConstParentIdOptionalPayload:
|
||||
return gen_const_ptr_optional_payload_recursive(g, parent->data.p_optional_payload.optional_val);
|
||||
case ConstParentIdArray:
|
||||
return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val,
|
||||
parent->data.p_array.elem_index);
|
||||
@ -5402,7 +5535,7 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent
|
||||
|
||||
static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index) {
|
||||
expand_undef_array(g, array_const_val);
|
||||
ConstParent *parent = &array_const_val->data.x_array.data.s_none.parent;
|
||||
ConstParent *parent = &array_const_val->parent;
|
||||
LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent);
|
||||
|
||||
LLVMTypeKind el_type = LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(base_ptr)));
|
||||
@ -5427,7 +5560,7 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index) {
|
||||
ConstParent *parent = &struct_const_val->data.x_struct.parent;
|
||||
ConstParent *parent = &struct_const_val->parent;
|
||||
LLVMValueRef base_ptr = gen_parent_ptr(g, struct_const_val, parent);
|
||||
|
||||
ZigType *u32 = g->builtin_types.entry_u32;
|
||||
@ -5438,8 +5571,44 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s
|
||||
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val) {
|
||||
ConstParent *parent = &err_union_const_val->parent;
|
||||
LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent);
|
||||
|
||||
ZigType *u32 = g->builtin_types.entry_u32;
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(u32->type_ref),
|
||||
LLVMConstInt(u32->type_ref, err_union_err_index, false),
|
||||
};
|
||||
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val) {
|
||||
ConstParent *parent = &err_union_const_val->parent;
|
||||
LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent);
|
||||
|
||||
ZigType *u32 = g->builtin_types.entry_u32;
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(u32->type_ref),
|
||||
LLVMConstInt(u32->type_ref, err_union_payload_index, false),
|
||||
};
|
||||
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val) {
|
||||
ConstParent *parent = &optional_const_val->parent;
|
||||
LLVMValueRef base_ptr = gen_parent_ptr(g, optional_const_val, parent);
|
||||
|
||||
ZigType *u32 = g->builtin_types.entry_u32;
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(u32->type_ref),
|
||||
LLVMConstInt(u32->type_ref, maybe_child_index, false),
|
||||
};
|
||||
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) {
|
||||
ConstParent *parent = &union_const_val->data.x_union.parent;
|
||||
ConstParent *parent = &union_const_val->parent;
|
||||
LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent);
|
||||
|
||||
ZigType *u32 = g->builtin_types.entry_u32;
|
||||
@ -5509,6 +5678,8 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
|
||||
}
|
||||
case ZigTypeIdArray:
|
||||
zig_panic("TODO bit pack an array");
|
||||
case ZigTypeIdVector:
|
||||
zig_panic("TODO bit pack a vector");
|
||||
case ZigTypeIdUnion:
|
||||
zig_panic("TODO bit pack a union");
|
||||
case ZigTypeIdStruct:
|
||||
@ -5609,6 +5780,63 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
|
||||
render_const_val_global(g, const_val, "");
|
||||
return ptr_val;
|
||||
}
|
||||
case ConstPtrSpecialBaseErrorUnionCode:
|
||||
{
|
||||
render_const_val_global(g, const_val, name);
|
||||
ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val;
|
||||
assert(err_union_const_val->type->id == ZigTypeIdErrorUnion);
|
||||
if (err_union_const_val->type->zero_bits) {
|
||||
// make this a null pointer
|
||||
ZigType *usize = g->builtin_types.entry_usize;
|
||||
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
|
||||
const_val->type->type_ref);
|
||||
render_const_val_global(g, const_val, "");
|
||||
return const_val->global_refs->llvm_value;
|
||||
}
|
||||
LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val);
|
||||
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
|
||||
const_val->global_refs->llvm_value = ptr_val;
|
||||
render_const_val_global(g, const_val, "");
|
||||
return ptr_val;
|
||||
}
|
||||
case ConstPtrSpecialBaseErrorUnionPayload:
|
||||
{
|
||||
render_const_val_global(g, const_val, name);
|
||||
ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val;
|
||||
assert(err_union_const_val->type->id == ZigTypeIdErrorUnion);
|
||||
if (err_union_const_val->type->zero_bits) {
|
||||
// make this a null pointer
|
||||
ZigType *usize = g->builtin_types.entry_usize;
|
||||
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
|
||||
const_val->type->type_ref);
|
||||
render_const_val_global(g, const_val, "");
|
||||
return const_val->global_refs->llvm_value;
|
||||
}
|
||||
LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val);
|
||||
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
|
||||
const_val->global_refs->llvm_value = ptr_val;
|
||||
render_const_val_global(g, const_val, "");
|
||||
return ptr_val;
|
||||
}
|
||||
case ConstPtrSpecialBaseOptionalPayload:
|
||||
{
|
||||
render_const_val_global(g, const_val, name);
|
||||
ConstExprValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val;
|
||||
assert(optional_const_val->type->id == ZigTypeIdOptional);
|
||||
if (optional_const_val->type->zero_bits) {
|
||||
// make this a null pointer
|
||||
ZigType *usize = g->builtin_types.entry_usize;
|
||||
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
|
||||
const_val->type->type_ref);
|
||||
render_const_val_global(g, const_val, "");
|
||||
return const_val->global_refs->llvm_value;
|
||||
}
|
||||
LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val);
|
||||
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
|
||||
const_val->global_refs->llvm_value = ptr_val;
|
||||
render_const_val_global(g, const_val, "");
|
||||
return ptr_val;
|
||||
}
|
||||
case ConstPtrSpecialHardCodedAddr:
|
||||
{
|
||||
render_const_val_global(g, const_val, name);
|
||||
@ -5621,10 +5849,17 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
|
||||
}
|
||||
case ConstPtrSpecialFunction:
|
||||
return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
|
||||
case ConstPtrSpecialNull:
|
||||
return LLVMConstNull(const_val->type->type_ref);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_const_val_err_set(CodeGen *g, ConstExprValue *const_val, const char *name) {
|
||||
uint64_t value = (const_val->data.x_err_set == nullptr) ? 0 : const_val->data.x_err_set->value;
|
||||
return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, value, false);
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
|
||||
Error err;
|
||||
|
||||
@ -5644,9 +5879,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
|
||||
case ZigTypeIdInt:
|
||||
return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint);
|
||||
case ZigTypeIdErrorSet:
|
||||
assert(const_val->data.x_err_set != nullptr);
|
||||
return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref,
|
||||
const_val->data.x_err_set->value, false);
|
||||
return gen_const_val_err_set(g, const_val, name);
|
||||
case ZigTypeIdFloat:
|
||||
switch (type_entry->data.floating.bit_count) {
|
||||
case 16:
|
||||
@ -5680,6 +5913,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
|
||||
return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false);
|
||||
} else if (type_is_codegen_pointer(child_type)) {
|
||||
return gen_const_val_ptr(g, const_val, name);
|
||||
} else if (child_type->id == ZigTypeIdErrorSet) {
|
||||
return gen_const_val_err_set(g, const_val, name);
|
||||
} else {
|
||||
LLVMValueRef child_val;
|
||||
LLVMValueRef maybe_val;
|
||||
@ -5816,7 +6051,33 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
|
||||
return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), true);
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
case ZigTypeIdVector: {
|
||||
uint32_t len = type_entry->data.vector.len;
|
||||
switch (const_val->data.x_array.special) {
|
||||
case ConstArraySpecialUndef:
|
||||
return LLVMGetUndef(type_entry->type_ref);
|
||||
case ConstArraySpecialNone: {
|
||||
LLVMValueRef *values = allocate<LLVMValueRef>(len);
|
||||
for (uint64_t i = 0; i < len; i += 1) {
|
||||
ConstExprValue *elem_value = &const_val->data.x_array.data.s_none.elements[i];
|
||||
values[i] = gen_const_val(g, elem_value, "");
|
||||
}
|
||||
return LLVMConstVector(values, len);
|
||||
}
|
||||
case ConstArraySpecialBuf: {
|
||||
Buf *buf = const_val->data.x_array.data.s_buf;
|
||||
assert(buf_len(buf) == len);
|
||||
LLVMValueRef *values = allocate<LLVMValueRef>(len);
|
||||
for (uint64_t i = 0; i < len; i += 1) {
|
||||
values[i] = LLVMConstInt(g->builtin_types.entry_u8->type_ref, buf_ptr(buf)[i], false);
|
||||
}
|
||||
return LLVMConstVector(values, len);
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
case ZigTypeIdUnion:
|
||||
{
|
||||
LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
|
||||
@ -5914,7 +6175,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
|
||||
ZigType *err_set_type = type_entry->data.error_union.err_set_type;
|
||||
if (!type_has_bits(payload_type)) {
|
||||
assert(type_has_bits(err_set_type));
|
||||
uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0;
|
||||
ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set;
|
||||
uint64_t value = (err_set == nullptr) ? 0 : err_set->value;
|
||||
return LLVMConstInt(g->err_tag_type->type_ref, value, false);
|
||||
} else if (!type_has_bits(err_set_type)) {
|
||||
assert(type_has_bits(payload_type));
|
||||
@ -5923,8 +6185,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
|
||||
LLVMValueRef err_tag_value;
|
||||
LLVMValueRef err_payload_value;
|
||||
bool make_unnamed_struct;
|
||||
if (const_val->data.x_err_union.err) {
|
||||
err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false);
|
||||
ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set;
|
||||
if (err_set != nullptr) {
|
||||
err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, err_set->value, false);
|
||||
err_payload_value = LLVMConstNull(payload_type->type_ref);
|
||||
make_unnamed_struct = false;
|
||||
} else {
|
||||
@ -6130,10 +6393,13 @@ static void do_code_gen(CodeGen *g) {
|
||||
TldVar *tld_var = g->global_vars.at(i);
|
||||
ZigVar *var = tld_var->var;
|
||||
|
||||
if (var->value->type->id == ZigTypeIdComptimeFloat) {
|
||||
if (var->var_type->id == ZigTypeIdComptimeFloat) {
|
||||
// Generate debug info for it but that's it.
|
||||
ConstExprValue *const_val = var->value;
|
||||
ConstExprValue *const_val = var->const_value;
|
||||
assert(const_val->special != ConstValSpecialRuntime);
|
||||
if (const_val->type != var->var_type) {
|
||||
zig_panic("TODO debug info for var with ptr casted value");
|
||||
}
|
||||
ZigType *var_type = g->builtin_types.entry_f128;
|
||||
ConstExprValue coerced_value;
|
||||
coerced_value.special = ConstValSpecialStatic;
|
||||
@ -6144,10 +6410,13 @@ static void do_code_gen(CodeGen *g) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (var->value->type->id == ZigTypeIdComptimeInt) {
|
||||
if (var->var_type->id == ZigTypeIdComptimeInt) {
|
||||
// Generate debug info for it but that's it.
|
||||
ConstExprValue *const_val = var->value;
|
||||
ConstExprValue *const_val = var->const_value;
|
||||
assert(const_val->special != ConstValSpecialRuntime);
|
||||
if (const_val->type != var->var_type) {
|
||||
zig_panic("TODO debug info for var with ptr casted value");
|
||||
}
|
||||
size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint);
|
||||
if (bits_needed < 8) {
|
||||
bits_needed = 8;
|
||||
@ -6158,7 +6427,7 @@ static void do_code_gen(CodeGen *g) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!type_has_bits(var->value->type))
|
||||
if (!type_has_bits(var->var_type))
|
||||
continue;
|
||||
|
||||
assert(var->decl_node);
|
||||
@ -6167,9 +6436,9 @@ static void do_code_gen(CodeGen *g) {
|
||||
if (var->linkage == VarLinkageExternal) {
|
||||
LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, buf_ptr(&var->name));
|
||||
if (existing_llvm_var) {
|
||||
global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->value->type->type_ref, 0));
|
||||
global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->var_type->type_ref, 0));
|
||||
} else {
|
||||
global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name));
|
||||
global_value = LLVMAddGlobal(g->module, var->var_type->type_ref, buf_ptr(&var->name));
|
||||
// TODO debug info for the extern variable
|
||||
|
||||
LLVMSetLinkage(global_value, LLVMExternalLinkage);
|
||||
@ -6180,9 +6449,9 @@ static void do_code_gen(CodeGen *g) {
|
||||
} else {
|
||||
bool exported = (var->linkage == VarLinkageExport);
|
||||
const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported));
|
||||
render_const_val(g, var->value, mangled_name);
|
||||
render_const_val_global(g, var->value, mangled_name);
|
||||
global_value = var->value->global_refs->llvm_global;
|
||||
render_const_val(g, var->const_value, mangled_name);
|
||||
render_const_val_global(g, var->const_value, mangled_name);
|
||||
global_value = var->const_value->global_refs->llvm_global;
|
||||
|
||||
if (exported) {
|
||||
LLVMSetLinkage(global_value, LLVMExternalLinkage);
|
||||
@ -6194,8 +6463,10 @@ static void do_code_gen(CodeGen *g) {
|
||||
LLVMSetAlignment(global_value, var->align_bytes);
|
||||
|
||||
// TODO debug info for function pointers
|
||||
if (var->gen_is_const && var->value->type->id != ZigTypeIdFn) {
|
||||
gen_global_var(g, var, var->value->global_refs->llvm_value, var->value->type);
|
||||
// Here we use const_value->type because that's the type of the llvm global,
|
||||
// which we const ptr cast upon use to whatever it needs to be.
|
||||
if (var->gen_is_const && var->const_value->type->id != ZigTypeIdFn) {
|
||||
gen_global_var(g, var, var->const_value->global_refs->llvm_value, var->const_value->type);
|
||||
}
|
||||
|
||||
LLVMSetGlobalConstant(global_value, var->gen_is_const);
|
||||
@ -6249,6 +6520,7 @@ static void do_code_gen(CodeGen *g) {
|
||||
IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
|
||||
LLVMValueRef *slot;
|
||||
ZigType *slot_type = instruction->value.type;
|
||||
uint32_t alignment_bytes = 0;
|
||||
if (instruction->id == IrInstructionIdCast) {
|
||||
IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction;
|
||||
slot = &cast_instruction->tmp_ptr;
|
||||
@ -6281,13 +6553,17 @@ static void do_code_gen(CodeGen *g) {
|
||||
} else if (instruction->id == IrInstructionIdErrWrapCode) {
|
||||
IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;
|
||||
slot = &err_wrap_code_instruction->tmp_ptr;
|
||||
} else if (instruction->id == IrInstructionIdCmpxchg) {
|
||||
IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction;
|
||||
} else if (instruction->id == IrInstructionIdCmpxchgGen) {
|
||||
IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction;
|
||||
slot = &cmpxchg_instruction->tmp_ptr;
|
||||
} else if (instruction->id == IrInstructionIdVectorToArray) {
|
||||
IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction;
|
||||
alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type);
|
||||
slot = &vector_to_array_instruction->tmp_ptr;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
*slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type));
|
||||
*slot = build_alloca(g, slot_type, "", alignment_bytes);
|
||||
}
|
||||
|
||||
ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);
|
||||
@ -6304,12 +6580,12 @@ static void do_code_gen(CodeGen *g) {
|
||||
for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) {
|
||||
ZigVar *var = fn_table_entry->variable_list.at(var_i);
|
||||
|
||||
if (!type_has_bits(var->value->type)) {
|
||||
if (!type_has_bits(var->var_type)) {
|
||||
continue;
|
||||
}
|
||||
if (ir_get_var_is_comptime(var))
|
||||
continue;
|
||||
switch (type_requires_comptime(g, var->value->type)) {
|
||||
switch (type_requires_comptime(g, var->var_type)) {
|
||||
case ReqCompTimeInvalid:
|
||||
zig_unreachable();
|
||||
case ReqCompTimeYes:
|
||||
@ -6319,11 +6595,11 @@ static void do_code_gen(CodeGen *g) {
|
||||
}
|
||||
|
||||
if (var->src_arg_index == SIZE_MAX) {
|
||||
var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes);
|
||||
var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes);
|
||||
|
||||
var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
|
||||
buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1),
|
||||
var->value->type->di_type, !g->strip_debug_symbols, 0);
|
||||
var->var_type->di_type, !g->strip_debug_symbols, 0);
|
||||
|
||||
} else if (is_c_abi) {
|
||||
fn_walk_var.data.vars.var = var;
|
||||
@ -6333,16 +6609,16 @@ static void do_code_gen(CodeGen *g) {
|
||||
ZigType *gen_type;
|
||||
FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index];
|
||||
|
||||
if (handle_is_ptr(var->value->type)) {
|
||||
if (handle_is_ptr(var->var_type)) {
|
||||
if (gen_info->is_byval) {
|
||||
gen_type = var->value->type;
|
||||
gen_type = var->var_type;
|
||||
} else {
|
||||
gen_type = gen_info->type;
|
||||
}
|
||||
var->value_ref = LLVMGetParam(fn, (unsigned)var->gen_arg_index);
|
||||
} else {
|
||||
gen_type = var->value->type;
|
||||
var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes);
|
||||
gen_type = var->var_type;
|
||||
var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes);
|
||||
}
|
||||
if (var->decl_node) {
|
||||
var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
|
||||
@ -6742,6 +7018,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int
|
||||
create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 1);
|
||||
@ -6967,6 +7244,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
|
||||
" ArgTuple: void,\n"
|
||||
" Opaque: void,\n"
|
||||
" Promise: Promise,\n"
|
||||
" Vector: Vector,\n"
|
||||
"\n\n"
|
||||
" pub const Int = struct {\n"
|
||||
" is_signed: bool,\n"
|
||||
@ -7085,6 +7363,11 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
|
||||
" child: ?type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Vector = struct {\n"
|
||||
" len: u32,\n"
|
||||
" child: type,\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" pub const Definition = struct {\n"
|
||||
" name: []const u8,\n"
|
||||
" is_pub: bool,\n"
|
||||
@ -7153,6 +7436,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
|
||||
buf_appendf(contents, "pub const endian = %s;\n", endian_str);
|
||||
}
|
||||
buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
|
||||
buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
|
||||
buf_appendf(contents, "pub const os = Os.%s;\n", cur_os);
|
||||
buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch);
|
||||
buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ);
|
||||
@ -7187,6 +7471,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
|
||||
cache_buf(&cache_hash, compiler_id);
|
||||
cache_int(&cache_hash, g->build_mode);
|
||||
cache_bool(&cache_hash, g->is_test_build);
|
||||
cache_bool(&cache_hash, g->is_single_threaded);
|
||||
cache_int(&cache_hash, g->zig_target.arch.arch);
|
||||
cache_int(&cache_hash, g->zig_target.arch.sub_arch);
|
||||
cache_int(&cache_hash, g->zig_target.vendor);
|
||||
@ -7458,9 +7743,9 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
|
||||
ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i];
|
||||
this_val->special = ConstValSpecialStatic;
|
||||
this_val->type = struct_type;
|
||||
this_val->data.x_struct.parent.id = ConstParentIdArray;
|
||||
this_val->data.x_struct.parent.data.p_array.array_val = test_fn_array;
|
||||
this_val->data.x_struct.parent.data.p_array.elem_index = i;
|
||||
this_val->parent.id = ConstParentIdArray;
|
||||
this_val->parent.data.p_array.array_val = test_fn_array;
|
||||
this_val->parent.data.p_array.elem_index = i;
|
||||
this_val->data.x_struct.fields = create_const_vals(2);
|
||||
|
||||
ConstExprValue *name_field = &this_val->data.x_struct.fields[0];
|
||||
@ -7656,6 +7941,9 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e
|
||||
case ZigTypeIdArray:
|
||||
prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type);
|
||||
return;
|
||||
case ZigTypeIdVector:
|
||||
prepend_c_type_to_decl_list(g, gen_h, type_entry->data.vector.elem_type);
|
||||
return;
|
||||
case ZigTypeIdOptional:
|
||||
prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type);
|
||||
return;
|
||||
@ -7787,6 +8075,8 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu
|
||||
buf_appendf(out_buf, "%s", buf_ptr(child_buf));
|
||||
return;
|
||||
}
|
||||
case ZigTypeIdVector:
|
||||
zig_panic("TODO implement get_c_type for vector types");
|
||||
case ZigTypeIdErrorUnion:
|
||||
case ZigTypeIdErrorSet:
|
||||
case ZigTypeIdFn:
|
||||
@ -7952,6 +8242,7 @@ static void gen_h_file(CodeGen *g) {
|
||||
case ZigTypeIdOptional:
|
||||
case ZigTypeIdFn:
|
||||
case ZigTypeIdPromise:
|
||||
case ZigTypeIdVector:
|
||||
zig_unreachable();
|
||||
case ZigTypeIdEnum:
|
||||
if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
|
||||
@ -8099,6 +8390,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
|
||||
cache_bool(ch, g->is_static);
|
||||
cache_bool(ch, g->strip_debug_symbols);
|
||||
cache_bool(ch, g->is_test_build);
|
||||
cache_bool(ch, g->is_single_threaded);
|
||||
cache_bool(ch, g->is_native_target);
|
||||
cache_bool(ch, g->linker_rdynamic);
|
||||
cache_bool(ch, g->no_rosegment_workaround);
|
||||
|
2024
src/ir.cpp
2024
src/ir.cpp
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@
|
||||
bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutable *ir_executable);
|
||||
bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry);
|
||||
|
||||
IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
|
||||
ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
|
||||
ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
|
||||
ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
|
||||
IrExecutable *parent_exec);
|
||||
|
112
src/ir_print.cpp
112
src/ir_print.cpp
@ -172,7 +172,7 @@ static void ir_print_bin_op(IrPrint *irp, IrInstructionBinOp *bin_op_instruction
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instruction) {
|
||||
static void ir_print_decl_var_src(IrPrint *irp, IrInstructionDeclVarSrc *decl_var_instruction) {
|
||||
const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var";
|
||||
const char *name = buf_ptr(&decl_var_instruction->var->name);
|
||||
if (decl_var_instruction->var_type) {
|
||||
@ -332,8 +332,8 @@ static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) {
|
||||
}
|
||||
|
||||
static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) {
|
||||
fprintf(irp->f, "*");
|
||||
ir_print_other_instruction(irp, instruction->ptr);
|
||||
fprintf(irp->f, ".*");
|
||||
}
|
||||
|
||||
static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) {
|
||||
@ -479,15 +479,15 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) {
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instruction) {
|
||||
fprintf(irp->f, "*");
|
||||
static void ir_print_test_non_null(IrPrint *irp, IrInstructionTestNonNull *instruction) {
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
fprintf(irp->f, " != null");
|
||||
}
|
||||
|
||||
static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) {
|
||||
fprintf(irp->f, "&??*");
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
static void ir_print_optional_unwrap_ptr(IrPrint *irp, IrInstructionOptionalUnwrapPtr *instruction) {
|
||||
fprintf(irp->f, "&");
|
||||
ir_print_other_instruction(irp, instruction->base_ptr);
|
||||
fprintf(irp->f, ".*.?");
|
||||
if (!instruction->safety_check_on) {
|
||||
fprintf(irp->f, " // no safety");
|
||||
}
|
||||
@ -613,7 +613,7 @@ static void ir_print_embed_file(IrPrint *irp, IrInstructionEmbedFile *instructio
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) {
|
||||
static void ir_print_cmpxchg_src(IrPrint *irp, IrInstructionCmpxchgSrc *instruction) {
|
||||
fprintf(irp->f, "@cmpxchg(");
|
||||
ir_print_other_instruction(irp, instruction->ptr);
|
||||
fprintf(irp->f, ", ");
|
||||
@ -627,6 +627,16 @@ static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) {
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_cmpxchg_gen(IrPrint *irp, IrInstructionCmpxchgGen *instruction) {
|
||||
fprintf(irp->f, "@cmpxchg(");
|
||||
ir_print_other_instruction(irp, instruction->ptr);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->cmp_value);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->new_value);
|
||||
fprintf(irp->f, ", TODO print atomic orders)");
|
||||
}
|
||||
|
||||
static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) {
|
||||
fprintf(irp->f, "@fence(");
|
||||
ir_print_other_instruction(irp, instruction->order_value);
|
||||
@ -709,6 +719,14 @@ static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) {
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_vector_type(IrPrint *irp, IrInstructionVectorType *instruction) {
|
||||
fprintf(irp->f, "@Vector(");
|
||||
ir_print_other_instruction(irp, instruction->len);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->elem_type);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_bool_not(IrPrint *irp, IrInstructionBoolNot *instruction) {
|
||||
fprintf(irp->f, "! ");
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
@ -820,13 +838,13 @@ static void ir_print_test_err(IrPrint *irp, IrInstructionTestErr *instruction) {
|
||||
}
|
||||
|
||||
static void ir_print_unwrap_err_code(IrPrint *irp, IrInstructionUnwrapErrCode *instruction) {
|
||||
fprintf(irp->f, "@unwrapErrorCode(");
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
fprintf(irp->f, "UnwrapErrorCode(");
|
||||
ir_print_other_instruction(irp, instruction->err_union);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayload *instruction) {
|
||||
fprintf(irp->f, "@unwrapErrorPayload(");
|
||||
fprintf(irp->f, "ErrorUnionFieldPayload(");
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
fprintf(irp->f, ")");
|
||||
if (!instruction->safety_check_on) {
|
||||
@ -879,7 +897,7 @@ static void ir_print_test_comptime(IrPrint *irp, IrInstructionTestComptime *inst
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) {
|
||||
static void ir_print_ptr_cast_src(IrPrint *irp, IrInstructionPtrCastSrc *instruction) {
|
||||
fprintf(irp->f, "@ptrCast(");
|
||||
if (instruction->dest_type) {
|
||||
ir_print_other_instruction(irp, instruction->dest_type);
|
||||
@ -889,6 +907,12 @@ static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) {
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_ptr_cast_gen(IrPrint *irp, IrInstructionPtrCastGen *instruction) {
|
||||
fprintf(irp->f, "@ptrCast(");
|
||||
ir_print_other_instruction(irp, instruction->ptr);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) {
|
||||
fprintf(irp->f, "@bitCast(");
|
||||
if (instruction->dest_type) {
|
||||
@ -900,7 +924,7 @@ static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) {
|
||||
}
|
||||
|
||||
static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) {
|
||||
fprintf(irp->f, "@widenOrShorten(");
|
||||
fprintf(irp->f, "WidenOrShorten(");
|
||||
ir_print_other_instruction(irp, instruction->target);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
@ -948,6 +972,18 @@ static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntime
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_array_to_vector(IrPrint *irp, IrInstructionArrayToVector *instruction) {
|
||||
fprintf(irp->f, "ArrayToVector(");
|
||||
ir_print_other_instruction(irp, instruction->array);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *instruction) {
|
||||
fprintf(irp->f, "VectorToArray(");
|
||||
ir_print_other_instruction(irp, instruction->vector);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) {
|
||||
fprintf(irp->f, "inttoerr ");
|
||||
ir_print_other_instruction(irp, instruction->target);
|
||||
@ -1323,6 +1359,20 @@ static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) {
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *decl_var_instruction) {
|
||||
ZigVar *var = decl_var_instruction->var;
|
||||
const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var";
|
||||
const char *name = buf_ptr(&decl_var_instruction->var->name);
|
||||
fprintf(irp->f, "%s %s: %s align(%u) = ", var_or_const, name, buf_ptr(&var->var_type->name),
|
||||
var->align_bytes);
|
||||
|
||||
ir_print_other_instruction(irp, decl_var_instruction->init_value);
|
||||
if (decl_var_instruction->var->is_comptime != nullptr) {
|
||||
fprintf(irp->f, " // comptime = ");
|
||||
ir_print_other_instruction(irp, decl_var_instruction->var->is_comptime);
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_print_bswap(IrPrint *irp, IrInstructionBswap *instruction) {
|
||||
fprintf(irp->f, "@bswap(");
|
||||
if (instruction->type != nullptr) {
|
||||
@ -1361,8 +1411,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdBinOp:
|
||||
ir_print_bin_op(irp, (IrInstructionBinOp *)instruction);
|
||||
break;
|
||||
case IrInstructionIdDeclVar:
|
||||
ir_print_decl_var(irp, (IrInstructionDeclVar *)instruction);
|
||||
case IrInstructionIdDeclVarSrc:
|
||||
ir_print_decl_var_src(irp, (IrInstructionDeclVarSrc *)instruction);
|
||||
break;
|
||||
case IrInstructionIdCast:
|
||||
ir_print_cast(irp, (IrInstructionCast *)instruction);
|
||||
@ -1452,10 +1502,10 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
ir_print_size_of(irp, (IrInstructionSizeOf *)instruction);
|
||||
break;
|
||||
case IrInstructionIdTestNonNull:
|
||||
ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction);
|
||||
ir_print_test_non_null(irp, (IrInstructionTestNonNull *)instruction);
|
||||
break;
|
||||
case IrInstructionIdUnwrapOptional:
|
||||
ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction);
|
||||
case IrInstructionIdOptionalUnwrapPtr:
|
||||
ir_print_optional_unwrap_ptr(irp, (IrInstructionOptionalUnwrapPtr *)instruction);
|
||||
break;
|
||||
case IrInstructionIdCtz:
|
||||
ir_print_ctz(irp, (IrInstructionCtz *)instruction);
|
||||
@ -1508,8 +1558,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdEmbedFile:
|
||||
ir_print_embed_file(irp, (IrInstructionEmbedFile *)instruction);
|
||||
break;
|
||||
case IrInstructionIdCmpxchg:
|
||||
ir_print_cmpxchg(irp, (IrInstructionCmpxchg *)instruction);
|
||||
case IrInstructionIdCmpxchgSrc:
|
||||
ir_print_cmpxchg_src(irp, (IrInstructionCmpxchgSrc *)instruction);
|
||||
break;
|
||||
case IrInstructionIdCmpxchgGen:
|
||||
ir_print_cmpxchg_gen(irp, (IrInstructionCmpxchgGen *)instruction);
|
||||
break;
|
||||
case IrInstructionIdFence:
|
||||
ir_print_fence(irp, (IrInstructionFence *)instruction);
|
||||
@ -1544,6 +1597,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdIntType:
|
||||
ir_print_int_type(irp, (IrInstructionIntType *)instruction);
|
||||
break;
|
||||
case IrInstructionIdVectorType:
|
||||
ir_print_vector_type(irp, (IrInstructionVectorType *)instruction);
|
||||
break;
|
||||
case IrInstructionIdBoolNot:
|
||||
ir_print_bool_not(irp, (IrInstructionBoolNot *)instruction);
|
||||
break;
|
||||
@ -1607,8 +1663,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdTestComptime:
|
||||
ir_print_test_comptime(irp, (IrInstructionTestComptime *)instruction);
|
||||
break;
|
||||
case IrInstructionIdPtrCast:
|
||||
ir_print_ptr_cast(irp, (IrInstructionPtrCast *)instruction);
|
||||
case IrInstructionIdPtrCastSrc:
|
||||
ir_print_ptr_cast_src(irp, (IrInstructionPtrCastSrc *)instruction);
|
||||
break;
|
||||
case IrInstructionIdPtrCastGen:
|
||||
ir_print_ptr_cast_gen(irp, (IrInstructionPtrCastGen *)instruction);
|
||||
break;
|
||||
case IrInstructionIdBitCast:
|
||||
ir_print_bit_cast(irp, (IrInstructionBitCast *)instruction);
|
||||
@ -1775,6 +1834,15 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdCheckRuntimeScope:
|
||||
ir_print_check_runtime_scope(irp, (IrInstructionCheckRuntimeScope *)instruction);
|
||||
break;
|
||||
case IrInstructionIdDeclVarGen:
|
||||
ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction);
|
||||
break;
|
||||
case IrInstructionIdArrayToVector:
|
||||
ir_print_array_to_vector(irp, (IrInstructionArrayToVector *)instruction);
|
||||
break;
|
||||
case IrInstructionIdVectorToArray:
|
||||
ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction);
|
||||
break;
|
||||
}
|
||||
fprintf(irp->f, "\n");
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ static int print_full_usage(const char *arg0) {
|
||||
" --release-fast build with optimizations on and safety off\n"
|
||||
" --release-safe build with optimizations on and safety on\n"
|
||||
" --release-small build with size optimizations on and safety off\n"
|
||||
" --single-threaded source may assume it is only used single-threaded\n"
|
||||
" --static output will be statically linked\n"
|
||||
" --strip exclude debug symbols\n"
|
||||
" --target-arch [name] specify target architecture\n"
|
||||
@ -393,6 +394,7 @@ int main(int argc, char **argv) {
|
||||
bool no_rosegment_workaround = false;
|
||||
bool system_linker_hack = false;
|
||||
TargetSubsystem subsystem = TargetSubsystemAuto;
|
||||
bool is_single_threaded = false;
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
|
||||
Buf zig_exe_path_buf = BUF_INIT;
|
||||
@ -550,6 +552,8 @@ int main(int argc, char **argv) {
|
||||
disable_pic = true;
|
||||
} else if (strcmp(arg, "--system-linker-hack") == 0) {
|
||||
system_linker_hack = true;
|
||||
} else if (strcmp(arg, "--single-threaded") == 0) {
|
||||
is_single_threaded = true;
|
||||
} else if (strcmp(arg, "--test-cmd-bin") == 0) {
|
||||
test_exec_args.append(nullptr);
|
||||
} else if (arg[1] == 'L' && arg[2] != 0) {
|
||||
@ -816,6 +820,7 @@ int main(int argc, char **argv) {
|
||||
switch (cmd) {
|
||||
case CmdBuiltin: {
|
||||
CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());
|
||||
g->is_single_threaded = is_single_threaded;
|
||||
Buf *builtin_source = codegen_generate_builtin_source(g);
|
||||
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
|
||||
fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
|
||||
@ -889,6 +894,7 @@ int main(int argc, char **argv) {
|
||||
codegen_set_out_name(g, buf_out_name);
|
||||
codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
|
||||
codegen_set_is_test(g, cmd == CmdTest);
|
||||
g->is_single_threaded = is_single_threaded;
|
||||
codegen_set_linker_script(g, linker_script);
|
||||
if (each_lib_rpath)
|
||||
codegen_set_each_lib_rpath(g, each_lib_rpath);
|
||||
|
62
src/os.cpp
62
src/os.cpp
@ -1808,7 +1808,7 @@ Error os_self_exe_shared_libs(ZigList<Buf *> &paths) {
|
||||
#endif
|
||||
}
|
||||
|
||||
Error os_file_open_r(Buf *full_path, OsFile *out_file) {
|
||||
Error os_file_open_r(Buf *full_path, OsFile *out_file, OsTimeStamp *mtime) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
// TODO use CreateFileW
|
||||
HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
@ -1834,8 +1834,18 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file) {
|
||||
return ErrorUnexpected;
|
||||
}
|
||||
}
|
||||
|
||||
*out_file = result;
|
||||
|
||||
if (mtime != nullptr) {
|
||||
FILETIME last_write_time;
|
||||
if (!GetFileTime(result, nullptr, nullptr, &last_write_time)) {
|
||||
CloseHandle(result);
|
||||
return ErrorUnexpected;
|
||||
}
|
||||
mtime->sec = (((ULONGLONG) last_write_time.dwHighDateTime) << 32) + last_write_time.dwLowDateTime;
|
||||
mtime->nsec = 0;
|
||||
}
|
||||
|
||||
return ErrorNone;
|
||||
#else
|
||||
for (;;) {
|
||||
@ -1858,7 +1868,26 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file) {
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
}
|
||||
struct stat statbuf;
|
||||
if (fstat(fd, &statbuf) == -1) {
|
||||
close(fd);
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
close(fd);
|
||||
return ErrorIsDir;
|
||||
}
|
||||
*out_file = fd;
|
||||
|
||||
if (mtime != nullptr) {
|
||||
#if defined(ZIG_OS_DARWIN)
|
||||
mtime->sec = statbuf.st_mtimespec.tv_sec;
|
||||
mtime->nsec = statbuf.st_mtimespec.tv_nsec;
|
||||
#else
|
||||
mtime->sec = statbuf.st_mtim.tv_sec;
|
||||
mtime->nsec = statbuf.st_mtim.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
return ErrorNone;
|
||||
}
|
||||
#endif
|
||||
@ -1948,35 +1977,6 @@ Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) {
|
||||
#endif
|
||||
}
|
||||
|
||||
Error os_file_mtime(OsFile file, OsTimeStamp *mtime) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
FILETIME last_write_time;
|
||||
if (!GetFileTime(file, nullptr, nullptr, &last_write_time))
|
||||
return ErrorUnexpected;
|
||||
mtime->sec = (((ULONGLONG) last_write_time.dwHighDateTime) << 32) + last_write_time.dwLowDateTime;
|
||||
mtime->nsec = 0;
|
||||
return ErrorNone;
|
||||
#elif defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD)
|
||||
struct stat statbuf;
|
||||
if (fstat(file, &statbuf) == -1)
|
||||
return ErrorFileSystem;
|
||||
|
||||
mtime->sec = statbuf.st_mtim.tv_sec;
|
||||
mtime->nsec = statbuf.st_mtim.tv_nsec;
|
||||
return ErrorNone;
|
||||
#elif defined(ZIG_OS_DARWIN)
|
||||
struct stat statbuf;
|
||||
if (fstat(file, &statbuf) == -1)
|
||||
return ErrorFileSystem;
|
||||
|
||||
mtime->sec = statbuf.st_mtimespec.tv_sec;
|
||||
mtime->nsec = statbuf.st_mtimespec.tv_nsec;
|
||||
return ErrorNone;
|
||||
#else
|
||||
#error unimplemented
|
||||
#endif
|
||||
}
|
||||
|
||||
Error os_file_read(OsFile file, void *ptr, size_t *len) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
DWORD amt_read;
|
||||
|
@ -101,9 +101,8 @@ bool os_path_is_absolute(Buf *path);
|
||||
Error ATTRIBUTE_MUST_USE os_make_path(Buf *path);
|
||||
Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path);
|
||||
|
||||
Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file);
|
||||
Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file, OsTimeStamp *mtime);
|
||||
Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file);
|
||||
Error ATTRIBUTE_MUST_USE os_file_mtime(OsFile file, OsTimeStamp *mtime);
|
||||
Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len);
|
||||
Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents);
|
||||
Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents);
|
||||
|
@ -381,7 +381,7 @@ static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parse
|
||||
else_body = ast_expect(pc, body_parser);
|
||||
}
|
||||
|
||||
assert(res->type == NodeTypeTestExpr);
|
||||
assert(res->type == NodeTypeIfOptional);
|
||||
if (err_payload != nullptr) {
|
||||
AstNodeTestExpr old = res->data.test_expr;
|
||||
res->type = NodeTypeIfErrorExpr;
|
||||
@ -990,7 +990,7 @@ static AstNode *ast_parse_if_statement(ParseContext *pc) {
|
||||
if (requires_semi && else_body == nullptr)
|
||||
expect_token(pc, TokenIdSemicolon);
|
||||
|
||||
assert(res->type == NodeTypeTestExpr);
|
||||
assert(res->type == NodeTypeIfOptional);
|
||||
if (err_payload != nullptr) {
|
||||
AstNodeTestExpr old = res->data.test_expr;
|
||||
res->type = NodeTypeIfErrorExpr;
|
||||
@ -2204,7 +2204,7 @@ static AstNode *ast_parse_if_prefix(ParseContext *pc) {
|
||||
Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc);
|
||||
|
||||
PtrPayload payload;
|
||||
AstNode *res = ast_create_node(pc, NodeTypeTestExpr, first);
|
||||
AstNode *res = ast_create_node(pc, NodeTypeIfOptional, first);
|
||||
res->data.test_expr.target_node = condition;
|
||||
if (opt_payload.unwrap(&payload)) {
|
||||
res->data.test_expr.var_symbol = token_buf(payload.payload);
|
||||
@ -2999,7 +2999,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
||||
visit_field(&node->data.if_err_expr.then_node, visit, context);
|
||||
visit_field(&node->data.if_err_expr.else_node, visit, context);
|
||||
break;
|
||||
case NodeTypeTestExpr:
|
||||
case NodeTypeIfOptional:
|
||||
visit_field(&node->data.test_expr.target_node, visit, context);
|
||||
visit_field(&node->data.test_expr.then_node, visit, context);
|
||||
visit_field(&node->data.test_expr.else_node, visit, context);
|
||||
|
@ -248,13 +248,8 @@ ATTRIBUTE_PRINTF(2, 3)
|
||||
static void tokenize_error(Tokenize *t, const char *format, ...) {
|
||||
t->state = TokenizeStateError;
|
||||
|
||||
if (t->cur_tok) {
|
||||
t->out->err_line = t->cur_tok->start_line;
|
||||
t->out->err_column = t->cur_tok->start_column;
|
||||
} else {
|
||||
t->out->err_line = t->line;
|
||||
t->out->err_column = t->column;
|
||||
}
|
||||
t->out->err_line = t->line;
|
||||
t->out->err_column = t->column;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
@ -886,6 +881,9 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
break;
|
||||
case TokenizeStateSawAmpersand:
|
||||
switch (c) {
|
||||
case '&':
|
||||
tokenize_error(&t, "`&&` is invalid. Note that `and` is boolean AND.");
|
||||
break;
|
||||
case '=':
|
||||
set_token_id(&t, t.cur_tok, TokenIdBitAndEq);
|
||||
end_token(&t);
|
||||
|
@ -263,6 +263,19 @@ ZigLLVMDIType *ZigLLVMCreateDebugBasicType(ZigLLVMDIBuilder *dibuilder, const ch
|
||||
return reinterpret_cast<ZigLLVMDIType*>(di_type);
|
||||
}
|
||||
|
||||
struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder,
|
||||
uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty)
|
||||
{
|
||||
SmallVector<Metadata *, 1> subrange;
|
||||
subrange.push_back(reinterpret_cast<DIBuilder*>(dibuilder)->getOrCreateSubrange(0, Size));
|
||||
DIType *di_type = reinterpret_cast<DIBuilder*>(dibuilder)->createVectorType(
|
||||
Size,
|
||||
AlignInBits,
|
||||
reinterpret_cast<DIType*>(Ty),
|
||||
reinterpret_cast<DIBuilder*>(dibuilder)->getOrCreateArray(subrange));
|
||||
return reinterpret_cast<ZigLLVMDIType*>(di_type);
|
||||
}
|
||||
|
||||
ZigLLVMDIType *ZigLLVMCreateDebugArrayType(ZigLLVMDIBuilder *dibuilder, uint64_t size_in_bits,
|
||||
uint64_t align_in_bits, ZigLLVMDIType *elem_type, int elem_count)
|
||||
{
|
||||
|
@ -191,6 +191,8 @@ ZIG_EXTERN_C struct ZigLLVMDISubprogram *ZigLLVMCreateFunction(struct ZigLLVMDIB
|
||||
unsigned lineno, struct ZigLLVMDIType *fn_di_type, bool is_local_to_unit, bool is_definition,
|
||||
unsigned scope_line, unsigned flags, bool is_optimized, struct ZigLLVMDISubprogram *decl_subprogram);
|
||||
|
||||
ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder,
|
||||
uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty);
|
||||
|
||||
ZIG_EXTERN_C void ZigLLVMFnSetSubprogram(LLVMValueRef fn, struct ZigLLVMDISubprogram *subprogram);
|
||||
|
||||
|
@ -170,20 +170,36 @@ test "std.atomic.Queue" {
|
||||
.get_count = 0,
|
||||
};
|
||||
|
||||
var putters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
if (builtin.single_threaded) {
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < put_thread_count) : (i += 1) {
|
||||
std.debug.assertOrPanic(startPuts(&context) == 0);
|
||||
}
|
||||
}
|
||||
context.puts_done = 1;
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < put_thread_count) : (i += 1) {
|
||||
std.debug.assertOrPanic(startGets(&context) == 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var putters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
|
||||
for (putters) |t|
|
||||
t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t|
|
||||
t.wait();
|
||||
for (putters) |t|
|
||||
t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t|
|
||||
t.wait();
|
||||
}
|
||||
|
||||
if (context.put_sum != context.get_sum) {
|
||||
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
|
||||
@ -205,11 +221,12 @@ fn startPuts(ctx: *Context) u8 {
|
||||
while (put_count != 0) : (put_count -= 1) {
|
||||
std.os.time.sleep(1); // let the os scheduler be our fuzz
|
||||
const x = @bitCast(i32, r.random.scalar(u32));
|
||||
const node = ctx.allocator.create(Queue(i32).Node{
|
||||
const node = ctx.allocator.create(Queue(i32).Node) catch unreachable;
|
||||
node.* = Queue(i32).Node{
|
||||
.prev = undefined,
|
||||
.next = undefined,
|
||||
.data = x,
|
||||
}) catch unreachable;
|
||||
};
|
||||
ctx.queue.put(node);
|
||||
_ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
|
||||
}
|
||||
|
@ -4,10 +4,13 @@ const AtomicOrder = builtin.AtomicOrder;
|
||||
|
||||
/// Many reader, many writer, non-allocating, thread-safe
|
||||
/// Uses a spinlock to protect push() and pop()
|
||||
/// When building in single threaded mode, this is a simple linked list.
|
||||
pub fn Stack(comptime T: type) type {
|
||||
return struct {
|
||||
root: ?*Node,
|
||||
lock: u8,
|
||||
lock: @typeOf(lock_init),
|
||||
|
||||
const lock_init = if (builtin.single_threaded) {} else u8(0);
|
||||
|
||||
pub const Self = @This();
|
||||
|
||||
@ -19,7 +22,7 @@ pub fn Stack(comptime T: type) type {
|
||||
pub fn init() Self {
|
||||
return Self{
|
||||
.root = null,
|
||||
.lock = 0,
|
||||
.lock = lock_init,
|
||||
};
|
||||
}
|
||||
|
||||
@ -31,20 +34,31 @@ pub fn Stack(comptime T: type) type {
|
||||
}
|
||||
|
||||
pub fn push(self: *Self, node: *Node) void {
|
||||
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
|
||||
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
|
||||
if (builtin.single_threaded) {
|
||||
node.next = self.root;
|
||||
self.root = node;
|
||||
} else {
|
||||
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
|
||||
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
|
||||
|
||||
node.next = self.root;
|
||||
self.root = node;
|
||||
node.next = self.root;
|
||||
self.root = node;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(self: *Self) ?*Node {
|
||||
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
|
||||
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
|
||||
if (builtin.single_threaded) {
|
||||
const root = self.root orelse return null;
|
||||
self.root = root.next;
|
||||
return root;
|
||||
} else {
|
||||
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
|
||||
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
|
||||
|
||||
const root = self.root orelse return null;
|
||||
self.root = root.next;
|
||||
return root;
|
||||
const root = self.root orelse return null;
|
||||
self.root = root.next;
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
@ -90,20 +104,36 @@ test "std.atomic.stack" {
|
||||
.get_count = 0,
|
||||
};
|
||||
|
||||
var putters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
if (builtin.single_threaded) {
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < put_thread_count) : (i += 1) {
|
||||
std.debug.assertOrPanic(startPuts(&context) == 0);
|
||||
}
|
||||
}
|
||||
context.puts_done = 1;
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < put_thread_count) : (i += 1) {
|
||||
std.debug.assertOrPanic(startGets(&context) == 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var putters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (putters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startPuts);
|
||||
}
|
||||
var getters: [put_thread_count]*std.os.Thread = undefined;
|
||||
for (getters) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, startGets);
|
||||
}
|
||||
|
||||
for (putters) |t|
|
||||
t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t|
|
||||
t.wait();
|
||||
for (putters) |t|
|
||||
t.wait();
|
||||
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||
for (getters) |t|
|
||||
t.wait();
|
||||
}
|
||||
|
||||
if (context.put_sum != context.get_sum) {
|
||||
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
|
||||
@ -125,10 +155,11 @@ fn startPuts(ctx: *Context) u8 {
|
||||
while (put_count != 0) : (put_count -= 1) {
|
||||
std.os.time.sleep(1); // let the os scheduler be our fuzz
|
||||
const x = @bitCast(i32, r.random.scalar(u32));
|
||||
const node = ctx.allocator.create(Stack(i32).Node{
|
||||
const node = ctx.allocator.create(Stack(i32).Node) catch unreachable;
|
||||
node.* = Stack(i32).Node{
|
||||
.next = undefined,
|
||||
.data = x,
|
||||
}) catch unreachable;
|
||||
};
|
||||
ctx.stack.push(node);
|
||||
_ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ pub const Builder = struct {
|
||||
};
|
||||
|
||||
pub fn init(allocator: *Allocator, zig_exe: []const u8, build_root: []const u8, cache_root: []const u8) Builder {
|
||||
const env_map = allocator.createOne(BufMap) catch unreachable;
|
||||
const env_map = allocator.create(BufMap) catch unreachable;
|
||||
env_map.* = os.getEnvMap(allocator) catch unreachable;
|
||||
var self = Builder{
|
||||
.zig_exe = zig_exe,
|
||||
@ -170,7 +170,8 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
pub fn addTest(self: *Builder, root_src: []const u8) *TestStep {
|
||||
const test_step = self.allocator.create(TestStep.init(self, root_src)) catch unreachable;
|
||||
const test_step = self.allocator.create(TestStep) catch unreachable;
|
||||
test_step.* = TestStep.init(self, root_src);
|
||||
return test_step;
|
||||
}
|
||||
|
||||
@ -202,18 +203,21 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {
|
||||
const write_file_step = self.allocator.create(WriteFileStep.init(self, file_path, data)) catch unreachable;
|
||||
const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
|
||||
write_file_step.* = WriteFileStep.init(self, file_path, data);
|
||||
return write_file_step;
|
||||
}
|
||||
|
||||
pub fn addLog(self: *Builder, comptime format: []const u8, args: ...) *LogStep {
|
||||
const data = self.fmt(format, args);
|
||||
const log_step = self.allocator.create(LogStep.init(self, data)) catch unreachable;
|
||||
const log_step = self.allocator.create(LogStep) catch unreachable;
|
||||
log_step.* = LogStep.init(self, data);
|
||||
return log_step;
|
||||
}
|
||||
|
||||
pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep {
|
||||
const remove_dir_step = self.allocator.create(RemoveDirStep.init(self, dir_path)) catch unreachable;
|
||||
const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
|
||||
remove_dir_step.* = RemoveDirStep.init(self, dir_path);
|
||||
return remove_dir_step;
|
||||
}
|
||||
|
||||
@ -320,7 +324,7 @@ pub const Builder = struct {
|
||||
|
||||
fn processNixOSEnvVars(self: *Builder) void {
|
||||
if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
|
||||
var it = mem.split(nix_cflags_compile, " ");
|
||||
var it = mem.tokenize(nix_cflags_compile, " ");
|
||||
while (true) {
|
||||
const word = it.next() orelse break;
|
||||
if (mem.eql(u8, word, "-isystem")) {
|
||||
@ -338,7 +342,7 @@ pub const Builder = struct {
|
||||
assert(err == error.EnvironmentVariableNotFound);
|
||||
}
|
||||
if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| {
|
||||
var it = mem.split(nix_ldflags, " ");
|
||||
var it = mem.tokenize(nix_ldflags, " ");
|
||||
while (true) {
|
||||
const word = it.next() orelse break;
|
||||
if (mem.eql(u8, word, "-rpath")) {
|
||||
@ -414,10 +418,11 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step {
|
||||
const step_info = self.allocator.create(TopLevelStep{
|
||||
const step_info = self.allocator.create(TopLevelStep) catch unreachable;
|
||||
step_info.* = TopLevelStep{
|
||||
.step = Step.initNoOp(name, self.allocator),
|
||||
.description = description,
|
||||
}) catch unreachable;
|
||||
};
|
||||
self.top_level_steps.append(step_info) catch unreachable;
|
||||
return &step_info.step;
|
||||
}
|
||||
@ -616,7 +621,8 @@ pub const Builder = struct {
|
||||
const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable;
|
||||
self.pushInstalledFile(full_dest_path);
|
||||
|
||||
const install_step = self.allocator.create(InstallFileStep.init(self, src_path, full_dest_path)) catch unreachable;
|
||||
const install_step = self.allocator.create(InstallFileStep) catch unreachable;
|
||||
install_step.* = InstallFileStep.init(self, src_path, full_dest_path);
|
||||
return install_step;
|
||||
}
|
||||
|
||||
@ -683,7 +689,7 @@ pub const Builder = struct {
|
||||
if (os.path.isAbsolute(name)) {
|
||||
return name;
|
||||
}
|
||||
var it = mem.split(PATH, []u8{os.path.delimiter});
|
||||
var it = mem.tokenize(PATH, []u8{os.path.delimiter});
|
||||
while (it.next()) |path| {
|
||||
const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
|
||||
if (os.path.real(self.allocator, full_path)) |real_path| {
|
||||
@ -865,43 +871,51 @@ pub const LibExeObjStep = struct {
|
||||
};
|
||||
|
||||
pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep {
|
||||
const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Lib, false, ver)) catch unreachable;
|
||||
const self = builder.allocator.create(LibExeObjStep) catch unreachable;
|
||||
self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, ver);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn createCSharedLibrary(builder: *Builder, name: []const u8, version: Version) *LibExeObjStep {
|
||||
const self = builder.allocator.create(initC(builder, name, Kind.Lib, version, false)) catch unreachable;
|
||||
const self = builder.allocator.create(LibExeObjStep) catch unreachable;
|
||||
self.* = initC(builder, name, Kind.Lib, version, false);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
|
||||
const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0))) catch unreachable;
|
||||
const self = builder.allocator.create(LibExeObjStep) catch unreachable;
|
||||
self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0));
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn createCStaticLibrary(builder: *Builder, name: []const u8) *LibExeObjStep {
|
||||
const self = builder.allocator.create(initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true)) catch unreachable;
|
||||
const self = builder.allocator.create(LibExeObjStep) catch unreachable;
|
||||
self.* = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn createObject(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep {
|
||||
const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0))) catch unreachable;
|
||||
const self = builder.allocator.create(LibExeObjStep) catch unreachable;
|
||||
self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0));
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn createCObject(builder: *Builder, name: []const u8, src: []const u8) *LibExeObjStep {
|
||||
const self = builder.allocator.create(initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false)) catch unreachable;
|
||||
const self = builder.allocator.create(LibExeObjStep) catch unreachable;
|
||||
self.* = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false);
|
||||
self.object_src = src;
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, static: bool) *LibExeObjStep {
|
||||
const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0))) catch unreachable;
|
||||
const self = builder.allocator.create(LibExeObjStep) catch unreachable;
|
||||
self.* = initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0));
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn createCExecutable(builder: *Builder, name: []const u8) *LibExeObjStep {
|
||||
const self = builder.allocator.create(initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false)) catch unreachable;
|
||||
const self = builder.allocator.create(LibExeObjStep) catch unreachable;
|
||||
self.* = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false);
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -1914,13 +1928,14 @@ pub const CommandStep = struct {
|
||||
|
||||
/// ::argv is copied.
|
||||
pub fn create(builder: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep {
|
||||
const self = builder.allocator.create(CommandStep{
|
||||
const self = builder.allocator.create(CommandStep) catch unreachable;
|
||||
self.* = CommandStep{
|
||||
.builder = builder,
|
||||
.step = Step.init(argv[0], builder.allocator, make),
|
||||
.argv = builder.allocator.alloc([]u8, argv.len) catch unreachable,
|
||||
.cwd = cwd,
|
||||
.env_map = env_map,
|
||||
}) catch unreachable;
|
||||
};
|
||||
|
||||
mem.copy([]const u8, self.argv, argv);
|
||||
self.step.name = self.argv[0];
|
||||
@ -1949,12 +1964,13 @@ const InstallArtifactStep = struct {
|
||||
LibExeObjStep.Kind.Exe => builder.exe_dir,
|
||||
LibExeObjStep.Kind.Lib => builder.lib_dir,
|
||||
};
|
||||
const self = builder.allocator.create(Self{
|
||||
const self = builder.allocator.create(Self) catch unreachable;
|
||||
self.* = Self{
|
||||
.builder = builder,
|
||||
.step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make),
|
||||
.artifact = artifact,
|
||||
.dest_file = os.path.join(builder.allocator, dest_dir, artifact.out_filename) catch unreachable,
|
||||
}) catch unreachable;
|
||||
};
|
||||
self.step.dependOn(&artifact.step);
|
||||
builder.pushInstalledFile(self.dest_file);
|
||||
if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {
|
||||
|
@ -73,8 +73,8 @@ pub const sockaddr_in6 = extern struct {
|
||||
};
|
||||
|
||||
pub const timeval = extern struct {
|
||||
tv_sec: isize,
|
||||
tv_usec: isize,
|
||||
tv_sec: c_long,
|
||||
tv_usec: i32,
|
||||
};
|
||||
|
||||
pub const timezone = extern struct {
|
||||
@ -176,6 +176,24 @@ pub const kevent64_s = extern struct {
|
||||
ext: [2]u64,
|
||||
};
|
||||
|
||||
pub const mach_port_t = c_uint;
|
||||
pub const clock_serv_t = mach_port_t;
|
||||
pub const clock_res_t = c_int;
|
||||
pub const mach_port_name_t = natural_t;
|
||||
pub const natural_t = c_uint;
|
||||
pub const mach_timespec_t = extern struct {
|
||||
tv_sec: c_uint,
|
||||
tv_nsec: clock_res_t,
|
||||
};
|
||||
pub const kern_return_t = c_int;
|
||||
pub const host_t = mach_port_t;
|
||||
pub const CALENDAR_CLOCK = 1;
|
||||
|
||||
pub extern fn mach_host_self() mach_port_t;
|
||||
pub extern fn clock_get_time(clock_serv: clock_serv_t, cur_time: *mach_timespec_t) kern_return_t;
|
||||
pub extern fn host_get_clock_service(host: host_t, clock_id: clock_id_t, clock_serv: ?[*]clock_serv_t) kern_return_t;
|
||||
pub extern fn mach_port_deallocate(task: ipc_space_t, name: mach_port_name_t) kern_return_t;
|
||||
|
||||
// sys/types.h on macos uses #pragma pack() so these checks are
|
||||
// to make sure the struct is laid out the same. These values were
|
||||
// produced from C code using the offsetof macro.
|
||||
|
@ -44,7 +44,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int;
|
||||
pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize;
|
||||
pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8;
|
||||
pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int;
|
||||
pub extern "c" fn gettimeofday(tv: ?*timeval, tz: ?*timezone) c_int;
|
||||
pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
|
||||
pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int;
|
||||
pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int;
|
||||
pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
|
||||
|
@ -751,7 +751,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
|
||||
const self_file = try os.openSelfExe();
|
||||
defer self_file.close();
|
||||
|
||||
const coff_obj = try allocator.createOne(coff.Coff);
|
||||
const coff_obj = try allocator.create(coff.Coff);
|
||||
coff_obj.* = coff.Coff{
|
||||
.in_file = self_file,
|
||||
.allocator = allocator,
|
||||
@ -1036,7 +1036,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
const sentinel = try allocator.createOne(macho.nlist_64);
|
||||
const sentinel = try allocator.create(macho.nlist_64);
|
||||
sentinel.* = macho.nlist_64{
|
||||
.n_strx = 0,
|
||||
.n_type = 36,
|
||||
@ -1949,7 +1949,8 @@ fn scanAllCompileUnits(di: *DwarfInfo) !void {
|
||||
|
||||
try di.dwarf_seekable_stream.seekTo(compile_unit_pos);
|
||||
|
||||
const compile_unit_die = try di.allocator().create(try parseDie(di, abbrev_table, is_64));
|
||||
const compile_unit_die = try di.allocator().create(Die);
|
||||
compile_unit_die.* = try parseDie(di, abbrev_table, is_64);
|
||||
|
||||
if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;
|
||||
|
||||
|
@ -54,7 +54,8 @@ pub fn Channel(comptime T: type) type {
|
||||
const buffer_nodes = try loop.allocator.alloc(T, capacity);
|
||||
errdefer loop.allocator.free(buffer_nodes);
|
||||
|
||||
const self = try loop.allocator.create(SelfChannel{
|
||||
const self = try loop.allocator.create(SelfChannel);
|
||||
self.* = SelfChannel{
|
||||
.loop = loop,
|
||||
.buffer_len = 0,
|
||||
.buffer_nodes = buffer_nodes,
|
||||
@ -66,7 +67,7 @@ pub fn Channel(comptime T: type) type {
|
||||
.or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(),
|
||||
.get_count = 0,
|
||||
.put_count = 0,
|
||||
});
|
||||
};
|
||||
errdefer loop.allocator.destroy(self);
|
||||
|
||||
return self;
|
||||
@ -319,6 +320,9 @@ pub fn Channel(comptime T: type) type {
|
||||
}
|
||||
|
||||
test "std.event.Channel" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -495,7 +495,7 @@ pub const CloseOperation = struct {
|
||||
};
|
||||
|
||||
pub fn start(loop: *Loop) (error{OutOfMemory}!*CloseOperation) {
|
||||
const self = try loop.allocator.createOne(CloseOperation);
|
||||
const self = try loop.allocator.create(CloseOperation);
|
||||
self.* = CloseOperation{
|
||||
.loop = loop,
|
||||
.os_data = switch (builtin.os) {
|
||||
@ -787,7 +787,7 @@ pub fn Watch(comptime V: type) type {
|
||||
},
|
||||
|
||||
builtin.Os.windows => {
|
||||
const self = try loop.allocator.createOne(Self);
|
||||
const self = try loop.allocator.create(Self);
|
||||
errdefer loop.allocator.destroy(self);
|
||||
self.* = Self{
|
||||
.channel = channel,
|
||||
@ -802,7 +802,7 @@ pub fn Watch(comptime V: type) type {
|
||||
},
|
||||
|
||||
builtin.Os.macosx, builtin.Os.freebsd => {
|
||||
const self = try loop.allocator.createOne(Self);
|
||||
const self = try loop.allocator.create(Self);
|
||||
errdefer loop.allocator.destroy(self);
|
||||
|
||||
self.* = Self{
|
||||
@ -1068,7 +1068,7 @@ pub fn Watch(comptime V: type) type {
|
||||
}
|
||||
} else {
|
||||
errdefer _ = self.os_data.dir_table.remove(dirname);
|
||||
const dir = try self.channel.loop.allocator.createOne(OsData.Dir);
|
||||
const dir = try self.channel.loop.allocator.create(OsData.Dir);
|
||||
errdefer self.channel.loop.allocator.destroy(dir);
|
||||
|
||||
dir.* = OsData.Dir{
|
||||
@ -1307,32 +1307,29 @@ pub fn Watch(comptime V: type) type {
|
||||
|
||||
const test_tmp_dir = "std_event_fs_test";
|
||||
|
||||
test "write a file, watch it, write it again" {
|
||||
if (builtin.os == builtin.Os.windows) {
|
||||
// TODO this test is disabled on windows until the coroutine rewrite is finished.
|
||||
// https://github.com/ziglang/zig/issues/1363
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
const allocator = &da.allocator;
|
||||
|
||||
// TODO move this into event loop too
|
||||
try os.makePath(allocator, test_tmp_dir);
|
||||
defer os.deleteTree(allocator, test_tmp_dir) catch {};
|
||||
|
||||
var loop: Loop = undefined;
|
||||
try loop.initMultiThreaded(allocator);
|
||||
defer loop.deinit();
|
||||
|
||||
var result: anyerror!void = error.ResultNeverWritten;
|
||||
const handle = try async<allocator> testFsWatchCantFail(&loop, &result);
|
||||
defer cancel handle;
|
||||
|
||||
loop.run();
|
||||
return result;
|
||||
}
|
||||
// TODO this test is disabled until the coroutine rewrite is finished.
|
||||
//test "write a file, watch it, write it again" {
|
||||
// return error.SkipZigTest;
|
||||
// var da = std.heap.DirectAllocator.init();
|
||||
// defer da.deinit();
|
||||
//
|
||||
// const allocator = &da.allocator;
|
||||
//
|
||||
// // TODO move this into event loop too
|
||||
// try os.makePath(allocator, test_tmp_dir);
|
||||
// defer os.deleteTree(allocator, test_tmp_dir) catch {};
|
||||
//
|
||||
// var loop: Loop = undefined;
|
||||
// try loop.initMultiThreaded(allocator);
|
||||
// defer loop.deinit();
|
||||
//
|
||||
// var result: anyerror!void = error.ResultNeverWritten;
|
||||
// const handle = try async<allocator> testFsWatchCantFail(&loop, &result);
|
||||
// defer cancel handle;
|
||||
//
|
||||
// loop.run();
|
||||
// return result;
|
||||
//}
|
||||
|
||||
async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
|
||||
result.* = await (async testFsWatch(loop) catch unreachable);
|
||||
|
@ -84,6 +84,9 @@ pub fn Future(comptime T: type) type {
|
||||
}
|
||||
|
||||
test "std.event.Future" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -42,10 +42,11 @@ pub fn Group(comptime ReturnType: type) type {
|
||||
|
||||
/// Add a promise to the group. Thread-safe.
|
||||
pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {
|
||||
const node = try self.lock.loop.allocator.create(Stack.Node{
|
||||
const node = try self.lock.loop.allocator.create(Stack.Node);
|
||||
node.* = Stack.Node{
|
||||
.next = undefined,
|
||||
.data = handle,
|
||||
});
|
||||
};
|
||||
self.alloc_stack.push(node);
|
||||
}
|
||||
|
||||
@ -121,6 +122,9 @@ pub fn Group(comptime ReturnType: type) type {
|
||||
}
|
||||
|
||||
test "std.event.Group" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -122,6 +122,9 @@ pub const Lock = struct {
|
||||
};
|
||||
|
||||
test "std.event.Lock" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -97,6 +97,7 @@ pub const Loop = struct {
|
||||
/// TODO copy elision / named return values so that the threads referencing *Loop
|
||||
/// have the correct pointer value.
|
||||
pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
|
||||
if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode");
|
||||
const core_count = try os.cpuCount(allocator);
|
||||
return self.initInternal(allocator, core_count);
|
||||
}
|
||||
@ -201,6 +202,11 @@ pub const Loop = struct {
|
||||
self.os_data.fs_thread.wait();
|
||||
}
|
||||
|
||||
if (builtin.single_threaded) {
|
||||
assert(extra_thread_count == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
var extra_thread_index: usize = 0;
|
||||
errdefer {
|
||||
// writing 8 bytes to an eventfd cannot fail
|
||||
@ -301,6 +307,11 @@ pub const Loop = struct {
|
||||
self.os_data.fs_thread.wait();
|
||||
}
|
||||
|
||||
if (builtin.single_threaded) {
|
||||
assert(extra_thread_count == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
var extra_thread_index: usize = 0;
|
||||
errdefer {
|
||||
_ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable;
|
||||
@ -338,6 +349,11 @@ pub const Loop = struct {
|
||||
self.available_eventfd_resume_nodes.push(eventfd_node);
|
||||
}
|
||||
|
||||
if (builtin.single_threaded) {
|
||||
assert(extra_thread_count == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
var extra_thread_index: usize = 0;
|
||||
errdefer {
|
||||
var i: usize = 0;
|
||||
@ -845,6 +861,9 @@ pub const Loop = struct {
|
||||
};
|
||||
|
||||
test "std.event.Loop - basic" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
@ -858,6 +877,9 @@ test "std.event.Loop - basic" {
|
||||
}
|
||||
|
||||
test "std.event.Loop - call" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -269,6 +269,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !os.File {
|
||||
}
|
||||
|
||||
test "listen on a port, send bytes, receive bytes" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
if (builtin.os != builtin.Os.linux) {
|
||||
// TODO build abstractions for other operating systems
|
||||
return error.SkipZigTest;
|
||||
|
@ -211,6 +211,9 @@ pub const RwLock = struct {
|
||||
};
|
||||
|
||||
test "std.event.RwLock" {
|
||||
// https://github.com/ziglang/zig/issues/1908
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var da = std.heap.DirectAllocator.init();
|
||||
defer da.deinit();
|
||||
|
||||
|
@ -982,13 +982,11 @@ test "fmt.format" {
|
||||
context = BufPrintContext{ .remaining = buf1[0..] };
|
||||
try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite);
|
||||
res = buf1[0 .. buf1.len - context.remaining.len];
|
||||
debug.warn("{}\n", res);
|
||||
assert(mem.eql(u8, res, "a"));
|
||||
|
||||
context = BufPrintContext{ .remaining = buf1[0..] };
|
||||
try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite);
|
||||
res = buf1[0 .. buf1.len - context.remaining.len];
|
||||
debug.warn("{}\n", res);
|
||||
assert(mem.eql(u8, res, "1100"));
|
||||
}
|
||||
{
|
||||
|
@ -508,6 +508,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
|
||||
|
||||
builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"),
|
||||
builtin.TypeId.Array => @compileError("TODO auto hash for arrays"),
|
||||
builtin.TypeId.Vector => @compileError("TODO auto hash for vectors"),
|
||||
builtin.TypeId.Struct => @compileError("TODO auto hash for structs"),
|
||||
builtin.TypeId.Union => @compileError("TODO auto hash for unions"),
|
||||
builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"),
|
||||
@ -555,5 +556,6 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool {
|
||||
builtin.TypeId.Struct => @compileError("TODO auto eql for structs"),
|
||||
builtin.TypeId.Union => @compileError("TODO auto eql for unions"),
|
||||
builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"),
|
||||
builtin.TypeId.Vector => @compileError("TODO auto eql for vectors"),
|
||||
}
|
||||
}
|
||||
|
@ -518,7 +518,8 @@ fn testAllocator(allocator: *mem.Allocator) !void {
|
||||
var slice = try allocator.alloc(*i32, 100);
|
||||
assert(slice.len == 100);
|
||||
for (slice) |*item, i| {
|
||||
item.* = try allocator.create(@intCast(i32, i));
|
||||
item.* = try allocator.create(i32);
|
||||
item.*.* = @intCast(i32, i);
|
||||
}
|
||||
|
||||
slice = try allocator.realloc(*i32, slice, 20000);
|
||||
|
@ -9,6 +9,7 @@ pub const DynLib = @import("dynamic_library.zig").DynLib;
|
||||
pub const HashMap = @import("hash_map.zig").HashMap;
|
||||
pub const LinkedList = @import("linked_list.zig").LinkedList;
|
||||
pub const Mutex = @import("mutex.zig").Mutex;
|
||||
pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;
|
||||
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
|
||||
pub const SpinLock = @import("spinlock.zig").SpinLock;
|
||||
|
||||
@ -55,6 +56,7 @@ test "std" {
|
||||
_ = @import("hash_map.zig");
|
||||
_ = @import("linked_list.zig");
|
||||
_ = @import("mutex.zig");
|
||||
_ = @import("statically_initialized_mutex.zig");
|
||||
_ = @import("segmented_list.zig");
|
||||
_ = @import("spinlock.zig");
|
||||
|
||||
|
667
std/io.zig
667
std/io.zig
@ -8,6 +8,8 @@ const debug = std.debug;
|
||||
const assert = debug.assert;
|
||||
const os = std.os;
|
||||
const mem = std.mem;
|
||||
const meta = std.meta;
|
||||
const trait = meta.trait;
|
||||
const Buffer = std.Buffer;
|
||||
const fmt = std.fmt;
|
||||
const File = std.os.File;
|
||||
@ -463,6 +465,153 @@ pub const SliceInStream = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Creates a stream which allows for reading bit fields from another stream
|
||||
pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
in_stream: *Stream,
|
||||
bit_buffer: u7,
|
||||
bit_count: u3,
|
||||
stream: Stream,
|
||||
|
||||
pub const Stream = InStream(Error);
|
||||
const u8_bit_count = comptime meta.bitCount(u8);
|
||||
const u7_bit_count = comptime meta.bitCount(u7);
|
||||
const u4_bit_count = comptime meta.bitCount(u4);
|
||||
|
||||
pub fn init(in_stream: *Stream) Self {
|
||||
return Self{
|
||||
.in_stream = in_stream,
|
||||
.bit_buffer = 0,
|
||||
.bit_count = 0,
|
||||
.stream = Stream{ .readFn = read },
|
||||
};
|
||||
}
|
||||
|
||||
/// Reads `bits` bits from the stream and returns a specified unsigned int type
|
||||
/// containing them in the least significant end, returning an error if the
|
||||
/// specified number of bits could not be read.
|
||||
pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U {
|
||||
var n: usize = undefined;
|
||||
const result = try self.readBits(U, bits, &n);
|
||||
if (n < bits) return error.EndOfStream;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Reads `bits` bits from the stream and returns a specified unsigned int type
|
||||
/// containing them in the least significant end. The number of bits successfully
|
||||
/// read is placed in `out_bits`, as reaching the end of the stream is not an error.
|
||||
pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U {
|
||||
comptime assert(trait.isUnsignedInt(U));
|
||||
|
||||
//by extending the buffer to a minimum of u8 we can cover a number of edge cases
|
||||
// related to shifting and casting.
|
||||
const u_bit_count = comptime meta.bitCount(U);
|
||||
const buf_bit_count = bc: {
|
||||
assert(u_bit_count >= bits);
|
||||
break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
|
||||
};
|
||||
const Buf = @IntType(false, buf_bit_count);
|
||||
const BufShift = math.Log2Int(Buf);
|
||||
|
||||
out_bits.* = usize(0);
|
||||
if (U == u0 or bits == 0) return 0;
|
||||
var out_buffer = Buf(0);
|
||||
|
||||
if (self.bit_count > 0) {
|
||||
const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count;
|
||||
const shift = u7_bit_count - n;
|
||||
switch (endian) {
|
||||
builtin.Endian.Big => {
|
||||
out_buffer = Buf(self.bit_buffer >> shift);
|
||||
self.bit_buffer <<= n;
|
||||
},
|
||||
builtin.Endian.Little => {
|
||||
const value = (self.bit_buffer << shift) >> shift;
|
||||
out_buffer = Buf(value);
|
||||
self.bit_buffer >>= n;
|
||||
},
|
||||
}
|
||||
self.bit_count -= n;
|
||||
out_bits.* = n;
|
||||
}
|
||||
//at this point we know bit_buffer is empty
|
||||
|
||||
//copy bytes until we have enough bits, then leave the rest in bit_buffer
|
||||
while (out_bits.* < bits) {
|
||||
const n = bits - out_bits.*;
|
||||
const next_byte = self.in_stream.readByte() catch |err| {
|
||||
if (err == error.EndOfStream) {
|
||||
return @intCast(U, out_buffer);
|
||||
}
|
||||
//@BUG: See #1810. Not sure if the bug is that I have to do this for some
|
||||
// streams, or that I don't for streams with emtpy errorsets.
|
||||
return @errSetCast(Error, err);
|
||||
};
|
||||
|
||||
switch (endian) {
|
||||
builtin.Endian.Big => {
|
||||
if (n >= u8_bit_count) {
|
||||
out_buffer <<= @intCast(u3, u8_bit_count - 1);
|
||||
out_buffer <<= 1;
|
||||
out_buffer |= Buf(next_byte);
|
||||
out_bits.* += u8_bit_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
const shift = @intCast(u3, u8_bit_count - n);
|
||||
out_buffer <<= @intCast(BufShift, n);
|
||||
out_buffer |= Buf(next_byte >> shift);
|
||||
out_bits.* += n;
|
||||
self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1));
|
||||
self.bit_count = shift;
|
||||
},
|
||||
builtin.Endian.Little => {
|
||||
if (n >= u8_bit_count) {
|
||||
out_buffer |= Buf(next_byte) << @intCast(BufShift, out_bits.*);
|
||||
out_bits.* += u8_bit_count;
|
||||
continue;
|
||||
}
|
||||
|
||||
const shift = @intCast(u3, u8_bit_count - n);
|
||||
const value = (next_byte << shift) >> shift;
|
||||
out_buffer |= Buf(value) << @intCast(BufShift, out_bits.*);
|
||||
out_bits.* += n;
|
||||
self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n));
|
||||
self.bit_count = shift;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return @intCast(U, out_buffer);
|
||||
}
|
||||
|
||||
pub fn alignToByte(self: *Self) void {
|
||||
self.bit_buffer = 0;
|
||||
self.bit_count = 0;
|
||||
}
|
||||
|
||||
pub fn read(self_stream: *Stream, buffer: []u8) Error!usize {
|
||||
var self = @fieldParentPtr(Self, "stream", self_stream);
|
||||
|
||||
var out_bits: usize = undefined;
|
||||
var out_bits_total = usize(0);
|
||||
//@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced
|
||||
if (self.bit_count > 0) {
|
||||
for (buffer) |*b, i| {
|
||||
b.* = try self.readBits(u8, u8_bit_count, &out_bits);
|
||||
out_bits_total += out_bits;
|
||||
}
|
||||
const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0);
|
||||
return (out_bits_total / u8_bit_count) + incomplete_byte;
|
||||
}
|
||||
|
||||
return self.in_stream.read(buffer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This is a simple OutStream that writes to a slice, and returns an error
|
||||
/// when it runs out of space.
|
||||
pub const SliceOutStream = struct {
|
||||
@ -515,7 +664,7 @@ test "io.SliceOutStream" {
|
||||
const stream = &slice_stream.stream;
|
||||
|
||||
try stream.print("{}{}!", "Hello", "World");
|
||||
debug.assert(mem.eql(u8, "HelloWorld!", slice_stream.getWritten()));
|
||||
debug.assertOrPanic(mem.eql(u8, "HelloWorld!", slice_stream.getWritten()));
|
||||
}
|
||||
|
||||
var null_out_stream_state = NullOutStream.init();
|
||||
@ -577,7 +726,7 @@ test "io.CountingOutStream" {
|
||||
|
||||
const bytes = "yay" ** 10000;
|
||||
stream.write(bytes) catch unreachable;
|
||||
debug.assert(counting_stream.bytes_written == bytes.len);
|
||||
debug.assertOrPanic(counting_stream.bytes_written == bytes.len);
|
||||
}
|
||||
|
||||
pub fn BufferedOutStream(comptime Error: type) type {
|
||||
@ -656,6 +805,137 @@ pub const BufferOutStream = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Creates a stream which allows for writing bit fields to another stream
|
||||
pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
out_stream: *Stream,
|
||||
bit_buffer: u8,
|
||||
bit_count: u4,
|
||||
stream: Stream,
|
||||
|
||||
pub const Stream = OutStream(Error);
|
||||
const u8_bit_count = comptime meta.bitCount(u8);
|
||||
const u4_bit_count = comptime meta.bitCount(u4);
|
||||
|
||||
pub fn init(out_stream: *Stream) Self {
|
||||
return Self{
|
||||
.out_stream = out_stream,
|
||||
.bit_buffer = 0,
|
||||
.bit_count = 0,
|
||||
.stream = Stream{ .writeFn = write },
|
||||
};
|
||||
}
|
||||
|
||||
/// Write the specified number of bits to the stream from the least significant bits of
|
||||
/// the specified unsigned int value. Bits will only be written to the stream when there
|
||||
/// are enough to fill a byte.
|
||||
pub fn writeBits(self: *Self, value: var, bits: usize) Error!void {
|
||||
if (bits == 0) return;
|
||||
|
||||
const U = @typeOf(value);
|
||||
comptime assert(trait.isUnsignedInt(U));
|
||||
|
||||
//by extending the buffer to a minimum of u8 we can cover a number of edge cases
|
||||
// related to shifting and casting.
|
||||
const u_bit_count = comptime meta.bitCount(U);
|
||||
const buf_bit_count = bc: {
|
||||
assert(u_bit_count >= bits);
|
||||
break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count;
|
||||
};
|
||||
const Buf = @IntType(false, buf_bit_count);
|
||||
const BufShift = math.Log2Int(Buf);
|
||||
|
||||
const buf_value = @intCast(Buf, value);
|
||||
|
||||
const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count);
|
||||
var in_buffer = switch (endian) {
|
||||
builtin.Endian.Big => buf_value << @intCast(BufShift, buf_bit_count - bits),
|
||||
builtin.Endian.Little => buf_value,
|
||||
};
|
||||
var in_bits = bits;
|
||||
|
||||
if (self.bit_count > 0) {
|
||||
const bits_remaining = u8_bit_count - self.bit_count;
|
||||
const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining);
|
||||
switch (endian) {
|
||||
builtin.Endian.Big => {
|
||||
const shift = @intCast(BufShift, high_byte_shift + self.bit_count);
|
||||
const v = @intCast(u8, in_buffer >> shift);
|
||||
self.bit_buffer |= v;
|
||||
in_buffer <<= n;
|
||||
},
|
||||
builtin.Endian.Little => {
|
||||
const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count);
|
||||
self.bit_buffer |= v;
|
||||
in_buffer >>= n;
|
||||
},
|
||||
}
|
||||
self.bit_count += n;
|
||||
in_bits -= n;
|
||||
|
||||
//if we didn't fill the buffer, it's because bits < bits_remaining;
|
||||
if (self.bit_count != u8_bit_count) return;
|
||||
try self.out_stream.writeByte(self.bit_buffer);
|
||||
self.bit_buffer = 0;
|
||||
self.bit_count = 0;
|
||||
}
|
||||
//at this point we know bit_buffer is empty
|
||||
|
||||
//copy bytes until we can't fill one anymore, then leave the rest in bit_buffer
|
||||
while (in_bits >= u8_bit_count) {
|
||||
switch (endian) {
|
||||
builtin.Endian.Big => {
|
||||
const v = @intCast(u8, in_buffer >> high_byte_shift);
|
||||
try self.out_stream.writeByte(v);
|
||||
in_buffer <<= @intCast(u3, u8_bit_count - 1);
|
||||
in_buffer <<= 1;
|
||||
},
|
||||
builtin.Endian.Little => {
|
||||
const v = @truncate(u8, in_buffer);
|
||||
try self.out_stream.writeByte(v);
|
||||
in_buffer >>= @intCast(u3, u8_bit_count - 1);
|
||||
in_buffer >>= 1;
|
||||
},
|
||||
}
|
||||
in_bits -= u8_bit_count;
|
||||
}
|
||||
|
||||
if (in_bits > 0) {
|
||||
self.bit_count = @intCast(u4, in_bits);
|
||||
self.bit_buffer = switch (endian) {
|
||||
builtin.Endian.Big => @truncate(u8, in_buffer >> high_byte_shift),
|
||||
builtin.Endian.Little => @truncate(u8, in_buffer),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Flush any remaining bits to the stream.
|
||||
pub fn flushBits(self: *Self) !void {
|
||||
if (self.bit_count == 0) return;
|
||||
try self.out_stream.writeByte(self.bit_buffer);
|
||||
self.bit_buffer = 0;
|
||||
self.bit_count = 0;
|
||||
}
|
||||
|
||||
pub fn write(self_stream: *Stream, buffer: []const u8) Error!void {
|
||||
var self = @fieldParentPtr(Self, "stream", self_stream);
|
||||
|
||||
//@NOTE: I'm not sure this is a good idea, maybe flushBits should be forced
|
||||
if (self.bit_count > 0) {
|
||||
for (buffer) |b, i|
|
||||
try self.writeBits(b, u8_bit_count);
|
||||
return;
|
||||
}
|
||||
|
||||
return self.out_stream.write(buffer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub const BufferedAtomicFile = struct {
|
||||
atomic_file: os.AtomicFile,
|
||||
file_stream: os.File.OutStream,
|
||||
@ -664,12 +944,13 @@ pub const BufferedAtomicFile = struct {
|
||||
|
||||
pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile {
|
||||
// TODO with well defined copy elision we don't need this allocation
|
||||
var self = try allocator.create(BufferedAtomicFile{
|
||||
var self = try allocator.create(BufferedAtomicFile);
|
||||
self.* = BufferedAtomicFile{
|
||||
.atomic_file = undefined,
|
||||
.file_stream = undefined,
|
||||
.buffered_stream = undefined,
|
||||
.allocator = allocator,
|
||||
});
|
||||
};
|
||||
errdefer allocator.destroy(self);
|
||||
|
||||
self.atomic_file = try os.AtomicFile.init(dest_path, os.File.default_mode);
|
||||
@ -696,11 +977,6 @@ pub const BufferedAtomicFile = struct {
|
||||
}
|
||||
};
|
||||
|
||||
test "import io tests" {
|
||||
comptime {
|
||||
_ = @import("io_test.zig");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readLine(buf: *std.Buffer) ![]u8 {
|
||||
var stdin = try getStdIn();
|
||||
@ -738,10 +1014,10 @@ test "io.readLineFrom" {
|
||||
);
|
||||
const stream = &mem_stream.stream;
|
||||
|
||||
debug.assert(mem.eql(u8, "Line 1", try readLineFrom(stream, &buf)));
|
||||
debug.assert(mem.eql(u8, "Line 22", try readLineFrom(stream, &buf)));
|
||||
debug.assertOrPanic(mem.eql(u8, "Line 1", try readLineFrom(stream, &buf)));
|
||||
debug.assertOrPanic(mem.eql(u8, "Line 22", try readLineFrom(stream, &buf)));
|
||||
debug.assertError(readLineFrom(stream, &buf), error.EndOfStream);
|
||||
debug.assert(mem.eql(u8, buf.toSlice(), "Line 1Line 22Line 333"));
|
||||
debug.assertOrPanic(mem.eql(u8, buf.toSlice(), "Line 1Line 22Line 333"));
|
||||
}
|
||||
|
||||
pub fn readLineSlice(slice: []u8) ![]u8 {
|
||||
@ -769,6 +1045,371 @@ test "io.readLineSliceFrom" {
|
||||
);
|
||||
const stream = &mem_stream.stream;
|
||||
|
||||
debug.assert(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..])));
|
||||
debug.assertOrPanic(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..])));
|
||||
debug.assertError(readLineSliceFrom(stream, buf[0..]), error.OutOfMemory);
|
||||
}
|
||||
|
||||
/// Creates a deserializer that deserializes types from any stream.
|
||||
/// If `is_packed` is true, the data stream is treated as bit-packed,
|
||||
/// otherwise data is expected to be packed to the smallest byte.
|
||||
/// Types may implement a custom deserialization routine with a
|
||||
/// function named `deserialize` in the form of:
|
||||
/// pub fn deserialize(self: *Self, deserializer: var) !void
|
||||
/// which will be called when the deserializer is used to deserialize
|
||||
/// that type. It will pass a pointer to the type instance to deserialize
|
||||
/// into and a pointer to the deserializer struct.
|
||||
pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime Error: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
in_stream: if (is_packed) BitInStream(endian, Stream.Error) else *Stream,
|
||||
|
||||
pub const Stream = InStream(Error);
|
||||
|
||||
pub fn init(in_stream: *Stream) Self {
|
||||
return Self{ .in_stream = switch (is_packed) {
|
||||
true => BitInStream(endian, Stream.Error).init(in_stream),
|
||||
else => in_stream,
|
||||
} };
|
||||
}
|
||||
|
||||
pub fn alignToByte(self: *Self) void {
|
||||
if(!is_packed) return;
|
||||
self.in_stream.alignToByte();
|
||||
}
|
||||
|
||||
//@BUG: inferred error issue. See: #1386
|
||||
fn deserializeInt(self: *Self, comptime T: type) (Stream.Error || error{EndOfStream})!T {
|
||||
comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
|
||||
|
||||
const u8_bit_count = 8;
|
||||
const t_bit_count = comptime meta.bitCount(T);
|
||||
|
||||
const U = @IntType(false, t_bit_count);
|
||||
const Log2U = math.Log2Int(U);
|
||||
const int_size = @sizeOf(U);
|
||||
|
||||
if (is_packed) {
|
||||
const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
|
||||
return @bitCast(T, result);
|
||||
}
|
||||
|
||||
var buffer: [int_size]u8 = undefined;
|
||||
const read_size = try self.in_stream.read(buffer[0..]);
|
||||
if (read_size < int_size) return error.EndOfStream;
|
||||
|
||||
if (int_size == 1) {
|
||||
if (t_bit_count == 8) return @bitCast(T, buffer[0]);
|
||||
const PossiblySignedByte = @IntType(T.is_signed, 8);
|
||||
return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
|
||||
}
|
||||
|
||||
var result = U(0);
|
||||
for (buffer) |byte, i| {
|
||||
switch (endian) {
|
||||
builtin.Endian.Big => {
|
||||
result = (result << u8_bit_count) | byte;
|
||||
},
|
||||
builtin.Endian.Little => {
|
||||
result |= U(byte) << @intCast(Log2U, u8_bit_count * i);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return @bitCast(T, result);
|
||||
}
|
||||
|
||||
//@TODO: Replace this with @unionInit or whatever when it is added
|
||||
// see: #1315
|
||||
fn setTag(ptr: var, tag: var) void {
|
||||
const T = @typeOf(ptr);
|
||||
comptime assert(trait.isPtrTo(builtin.TypeId.Union)(T));
|
||||
const U = meta.Child(T);
|
||||
|
||||
const info = @typeInfo(U).Union;
|
||||
if (info.tag_type) |TagType| {
|
||||
comptime assert(TagType == @typeOf(tag));
|
||||
|
||||
var ptr_tag = ptr: {
|
||||
if (@alignOf(TagType) >= @alignOf(U)) break :ptr @ptrCast(*TagType, ptr);
|
||||
const offset = comptime max: {
|
||||
var max_field_size: comptime_int = 0;
|
||||
for (info.fields) |field_info| {
|
||||
const field_size = @sizeOf(field_info.field_type);
|
||||
max_field_size = math.max(max_field_size, field_size);
|
||||
}
|
||||
break :max math.max(max_field_size, @alignOf(U));
|
||||
};
|
||||
break :ptr @intToPtr(*TagType, @ptrToInt(ptr) + offset);
|
||||
};
|
||||
ptr_tag.* = tag;
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializes and returns data of the specified type from the stream
|
||||
pub fn deserialize(self: *Self, comptime T: type) !T {
|
||||
var value: T = undefined;
|
||||
try self.deserializeInto(&value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Deserializes data into the type pointed to by `ptr`
|
||||
pub fn deserializeInto(self: *Self, ptr: var) !void {
|
||||
const T = @typeOf(ptr);
|
||||
comptime assert(trait.is(builtin.TypeId.Pointer)(T));
|
||||
|
||||
if (comptime trait.isSlice(T) or comptime trait.isPtrTo(builtin.TypeId.Array)(T)) {
|
||||
for (ptr) |*v|
|
||||
try self.deserializeInto(v);
|
||||
return;
|
||||
}
|
||||
|
||||
comptime assert(trait.isSingleItemPtr(T));
|
||||
|
||||
const C = comptime meta.Child(T);
|
||||
const child_type_id = @typeId(C);
|
||||
|
||||
//custom deserializer: fn(self: *Self, deserializer: var) !void
|
||||
if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
|
||||
|
||||
if (comptime trait.isPacked(C) and !is_packed) {
|
||||
var packed_deserializer = Deserializer(endian, true, Error).init(self.in_stream);
|
||||
return packed_deserializer.deserializeInto(ptr);
|
||||
}
|
||||
|
||||
switch (child_type_id) {
|
||||
builtin.TypeId.Void => return,
|
||||
builtin.TypeId.Bool => ptr.* = (try self.deserializeInt(u1)) > 0,
|
||||
builtin.TypeId.Float, builtin.TypeId.Int => ptr.* = try self.deserializeInt(C),
|
||||
builtin.TypeId.Struct => {
|
||||
const info = @typeInfo(C).Struct;
|
||||
|
||||
inline for (info.fields) |*field_info| {
|
||||
const name = field_info.name;
|
||||
const FieldType = field_info.field_type;
|
||||
|
||||
if (FieldType == void or FieldType == u0) continue;
|
||||
|
||||
//it doesn't make any sense to read pointers
|
||||
if (comptime trait.is(builtin.TypeId.Pointer)(FieldType)) {
|
||||
@compileError("Will not " ++ "read field " ++ name ++ " of struct " ++
|
||||
@typeName(C) ++ " because it " ++ "is of pointer-type " ++
|
||||
@typeName(FieldType) ++ ".");
|
||||
}
|
||||
|
||||
try self.deserializeInto(&@field(ptr, name));
|
||||
}
|
||||
},
|
||||
builtin.TypeId.Union => {
|
||||
const info = @typeInfo(C).Union;
|
||||
if (info.tag_type) |TagType| {
|
||||
//we avoid duplicate iteration over the enum tags
|
||||
// by getting the int directly and casting it without
|
||||
// safety. If it is bad, it will be caught anyway.
|
||||
const TagInt = @TagType(TagType);
|
||||
const tag = try self.deserializeInt(TagInt);
|
||||
|
||||
{
|
||||
@setRuntimeSafety(false);
|
||||
//See: #1315
|
||||
setTag(ptr, @intToEnum(TagType, tag));
|
||||
}
|
||||
|
||||
inline for (info.fields) |field_info| {
|
||||
if (field_info.enum_field.?.value == tag) {
|
||||
const name = field_info.name;
|
||||
const FieldType = field_info.field_type;
|
||||
@field(ptr, name) = FieldType(undefined);
|
||||
try self.deserializeInto(&@field(ptr, name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
//This is reachable if the enum data is bad
|
||||
return error.InvalidEnumTag;
|
||||
}
|
||||
@compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
|
||||
" because it is an untagged union Use a custom deserialize().");
|
||||
},
|
||||
builtin.TypeId.Optional => {
|
||||
const OC = comptime meta.Child(C);
|
||||
const exists = (try self.deserializeInt(u1)) > 0;
|
||||
if (!exists) {
|
||||
ptr.* = null;
|
||||
return;
|
||||
}
|
||||
|
||||
//The way non-pointer optionals are implemented ensures a pointer to them
|
||||
// will point to the value. The flag is stored at the end of that data.
|
||||
var val_ptr = @ptrCast(*OC, ptr);
|
||||
try self.deserializeInto(val_ptr);
|
||||
//This bit ensures the null flag isn't set. Any actual copying should be
|
||||
// optimized out... I hope.
|
||||
ptr.* = val_ptr.*;
|
||||
},
|
||||
builtin.TypeId.Enum => {
|
||||
var value = try self.deserializeInt(@TagType(C));
|
||||
ptr.* = try meta.intToEnum(C, value);
|
||||
},
|
||||
else => {
|
||||
@compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented).");
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a serializer that serializes types to any stream.
|
||||
/// If `is_packed` is true, the data will be bit-packed into the stream.
|
||||
/// Note that the you must call `serializer.flush()` when you are done
|
||||
/// writing bit-packed data in order ensure any unwritten bits are committed.
|
||||
/// If `is_packed` is false, data is packed to the smallest byte. In the case
|
||||
/// of packed structs, the struct will written bit-packed and with the specified
|
||||
/// endianess, after which data will resume being written at the next byte boundary.
|
||||
/// Types may implement a custom serialization routine with a
|
||||
/// function named `serialize` in the form of:
|
||||
/// pub fn serialize(self: Self, serializer: var) !void
|
||||
/// which will be called when the serializer is used to serialize that type. It will
|
||||
/// pass a const pointer to the type instance to be serialized and a pointer
|
||||
/// to the serializer struct.
|
||||
pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, comptime Error: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
out_stream: if (is_packed) BitOutStream(endian, Stream.Error) else *Stream,
|
||||
|
||||
pub const Stream = OutStream(Error);
|
||||
|
||||
pub fn init(out_stream: *Stream) Self {
|
||||
return Self{ .out_stream = switch (is_packed) {
|
||||
true => BitOutStream(endian, Stream.Error).init(out_stream),
|
||||
else => out_stream,
|
||||
} };
|
||||
}
|
||||
|
||||
/// Flushes any unwritten bits to the stream
|
||||
pub fn flush(self: *Self) Stream.Error!void {
|
||||
if (is_packed) return self.out_stream.flushBits();
|
||||
}
|
||||
|
||||
fn serializeInt(self: *Self, value: var) !void {
|
||||
const T = @typeOf(value);
|
||||
comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
|
||||
|
||||
const t_bit_count = comptime meta.bitCount(T);
|
||||
const u8_bit_count = comptime meta.bitCount(u8);
|
||||
|
||||
const U = @IntType(false, t_bit_count);
|
||||
const Log2U = math.Log2Int(U);
|
||||
const int_size = @sizeOf(U);
|
||||
|
||||
const u_value = @bitCast(U, value);
|
||||
|
||||
if (is_packed) return self.out_stream.writeBits(u_value, t_bit_count);
|
||||
|
||||
var buffer: [int_size]u8 = undefined;
|
||||
if (int_size == 1) buffer[0] = u_value;
|
||||
|
||||
for (buffer) |*byte, i| {
|
||||
const idx = switch (endian) {
|
||||
builtin.Endian.Big => int_size - i - 1,
|
||||
builtin.Endian.Little => i,
|
||||
};
|
||||
const shift = @intCast(Log2U, idx * u8_bit_count);
|
||||
const v = u_value >> shift;
|
||||
byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
|
||||
}
|
||||
|
||||
try self.out_stream.write(buffer);
|
||||
}
|
||||
|
||||
/// Serializes the passed value into the stream
|
||||
pub fn serialize(self: *Self, value: var) !void {
|
||||
const T = comptime @typeOf(value);
|
||||
|
||||
if (comptime trait.isIndexable(T)) {
|
||||
for (value) |v|
|
||||
try self.serialize(v);
|
||||
return;
|
||||
}
|
||||
|
||||
//custom serializer: fn(self: Self, serializer: var) !void
|
||||
if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
|
||||
|
||||
if (comptime trait.isPacked(T) and !is_packed) {
|
||||
var packed_serializer = Serializer(endian, true, Error).init(self.out_stream);
|
||||
try packed_serializer.serialize(value);
|
||||
try packed_serializer.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (@typeId(T)) {
|
||||
builtin.TypeId.Void => return,
|
||||
builtin.TypeId.Bool => try self.serializeInt(u1(@boolToInt(value))),
|
||||
builtin.TypeId.Float, builtin.TypeId.Int => try self.serializeInt(value),
|
||||
builtin.TypeId.Struct => {
|
||||
const info = @typeInfo(T);
|
||||
|
||||
inline for (info.Struct.fields) |*field_info| {
|
||||
const name = field_info.name;
|
||||
const FieldType = field_info.field_type;
|
||||
|
||||
if (FieldType == void or FieldType == u0) continue;
|
||||
|
||||
//It doesn't make sense to write pointers
|
||||
if (comptime trait.is(builtin.TypeId.Pointer)(FieldType)) {
|
||||
@compileError("Will not " ++ "serialize field " ++ name ++
|
||||
" of struct " ++ @typeName(T) ++ " because it " ++
|
||||
"is of pointer-type " ++ @typeName(FieldType) ++ ".");
|
||||
}
|
||||
try self.serialize(@field(value, name));
|
||||
}
|
||||
},
|
||||
builtin.TypeId.Union => {
|
||||
const info = @typeInfo(T).Union;
|
||||
if (info.tag_type) |TagType| {
|
||||
const active_tag = meta.activeTag(value);
|
||||
try self.serialize(active_tag);
|
||||
//This inline loop is necessary because active_tag is a runtime
|
||||
// value, but @field requires a comptime value. Our alternative
|
||||
// is to check each field for a match
|
||||
inline for (info.fields) |field_info| {
|
||||
if (field_info.enum_field.?.value == @enumToInt(active_tag)) {
|
||||
const name = field_info.name;
|
||||
const FieldType = field_info.field_type;
|
||||
try self.serialize(@field(value, name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
@compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
|
||||
" because it is an untagged union Use a custom serialize().");
|
||||
},
|
||||
builtin.TypeId.Optional => {
|
||||
if (value == null) {
|
||||
try self.serializeInt(u1(@boolToInt(false)));
|
||||
return;
|
||||
}
|
||||
try self.serializeInt(u1(@boolToInt(true)));
|
||||
|
||||
const OC = comptime meta.Child(T);
|
||||
|
||||
//The way non-pointer optionals are implemented ensures a pointer to them
|
||||
// will point to the value. The flag is stored at the end of that data.
|
||||
var val_ptr = @ptrCast(*const OC, &value);
|
||||
try self.serialize(val_ptr.*);
|
||||
},
|
||||
builtin.TypeId.Enum => {
|
||||
try self.serializeInt(@enumToInt(value));
|
||||
},
|
||||
else => @compileError("Cannot serialize " ++ @tagName(@typeId(T)) ++ " types (unimplemented)."),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "import io tests" {
|
||||
comptime {
|
||||
_ = @import("io_test.zig");
|
||||
}
|
||||
}
|
||||
|
437
std/io_test.zig
437
std/io_test.zig
@ -1,5 +1,7 @@
|
||||
const std = @import("index.zig");
|
||||
const io = std.io;
|
||||
const meta = std.meta;
|
||||
const trait = std.trait;
|
||||
const DefaultPrng = std.rand.DefaultPrng;
|
||||
const assert = std.debug.assert;
|
||||
const assertError = std.debug.assertError;
|
||||
@ -132,3 +134,438 @@ test "SliceOutStream" {
|
||||
assertError(ss.stream.write("Hello world!"), error.OutOfSpace);
|
||||
assert(mem.eql(u8, ss.getWritten(), "Hello worl"));
|
||||
}
|
||||
|
||||
test "BitInStream" {
|
||||
const mem_be = []u8{ 0b11001101, 0b00001011 };
|
||||
const mem_le = []u8{ 0b00011101, 0b10010101 };
|
||||
|
||||
var mem_in_be = io.SliceInStream.init(mem_be[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream);
|
||||
|
||||
var out_bits: usize = undefined;
|
||||
|
||||
assert(1 == try bit_stream_be.readBits(u2, 1, &out_bits));
|
||||
assert(out_bits == 1);
|
||||
assert(2 == try bit_stream_be.readBits(u5, 2, &out_bits));
|
||||
assert(out_bits == 2);
|
||||
assert(3 == try bit_stream_be.readBits(u128, 3, &out_bits));
|
||||
assert(out_bits == 3);
|
||||
assert(4 == try bit_stream_be.readBits(u8, 4, &out_bits));
|
||||
assert(out_bits == 4);
|
||||
assert(5 == try bit_stream_be.readBits(u9, 5, &out_bits));
|
||||
assert(out_bits == 5);
|
||||
assert(1 == try bit_stream_be.readBits(u1, 1, &out_bits));
|
||||
assert(out_bits == 1);
|
||||
|
||||
mem_in_be.pos = 0;
|
||||
bit_stream_be.bit_count = 0;
|
||||
assert(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits));
|
||||
assert(out_bits == 15);
|
||||
|
||||
mem_in_be.pos = 0;
|
||||
bit_stream_be.bit_count = 0;
|
||||
assert(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits));
|
||||
assert(out_bits == 16);
|
||||
|
||||
_ = try bit_stream_be.readBits(u0, 0, &out_bits);
|
||||
|
||||
assert(0 == try bit_stream_be.readBits(u1, 1, &out_bits));
|
||||
assert(out_bits == 0);
|
||||
assertError(bit_stream_be.readBitsNoEof(u1, 1), error.EndOfStream);
|
||||
|
||||
var mem_in_le = io.SliceInStream.init(mem_le[0..]);
|
||||
var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream);
|
||||
|
||||
assert(1 == try bit_stream_le.readBits(u2, 1, &out_bits));
|
||||
assert(out_bits == 1);
|
||||
assert(2 == try bit_stream_le.readBits(u5, 2, &out_bits));
|
||||
assert(out_bits == 2);
|
||||
assert(3 == try bit_stream_le.readBits(u128, 3, &out_bits));
|
||||
assert(out_bits == 3);
|
||||
assert(4 == try bit_stream_le.readBits(u8, 4, &out_bits));
|
||||
assert(out_bits == 4);
|
||||
assert(5 == try bit_stream_le.readBits(u9, 5, &out_bits));
|
||||
assert(out_bits == 5);
|
||||
assert(1 == try bit_stream_le.readBits(u1, 1, &out_bits));
|
||||
assert(out_bits == 1);
|
||||
|
||||
mem_in_le.pos = 0;
|
||||
bit_stream_le.bit_count = 0;
|
||||
assert(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits));
|
||||
assert(out_bits == 15);
|
||||
|
||||
mem_in_le.pos = 0;
|
||||
bit_stream_le.bit_count = 0;
|
||||
assert(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits));
|
||||
assert(out_bits == 16);
|
||||
|
||||
_ = try bit_stream_le.readBits(u0, 0, &out_bits);
|
||||
|
||||
assert(0 == try bit_stream_le.readBits(u1, 1, &out_bits));
|
||||
assert(out_bits == 0);
|
||||
assertError(bit_stream_le.readBitsNoEof(u1, 1), error.EndOfStream);
|
||||
}
|
||||
|
||||
test "BitOutStream" {
|
||||
var mem_be = []u8{0} ** 2;
|
||||
var mem_le = []u8{0} ** 2;
|
||||
|
||||
var mem_out_be = io.SliceOutStream.init(mem_be[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream);
|
||||
|
||||
try bit_stream_be.writeBits(u2(1), 1);
|
||||
try bit_stream_be.writeBits(u5(2), 2);
|
||||
try bit_stream_be.writeBits(u128(3), 3);
|
||||
try bit_stream_be.writeBits(u8(4), 4);
|
||||
try bit_stream_be.writeBits(u9(5), 5);
|
||||
try bit_stream_be.writeBits(u1(1), 1);
|
||||
|
||||
assert(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011);
|
||||
|
||||
mem_out_be.pos = 0;
|
||||
|
||||
try bit_stream_be.writeBits(u15(0b110011010000101), 15);
|
||||
try bit_stream_be.flushBits();
|
||||
assert(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010);
|
||||
|
||||
mem_out_be.pos = 0;
|
||||
try bit_stream_be.writeBits(u32(0b110011010000101), 16);
|
||||
assert(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101);
|
||||
|
||||
try bit_stream_be.writeBits(u0(0), 0);
|
||||
|
||||
var mem_out_le = io.SliceOutStream.init(mem_le[0..]);
|
||||
var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream);
|
||||
|
||||
try bit_stream_le.writeBits(u2(1), 1);
|
||||
try bit_stream_le.writeBits(u5(2), 2);
|
||||
try bit_stream_le.writeBits(u128(3), 3);
|
||||
try bit_stream_le.writeBits(u8(4), 4);
|
||||
try bit_stream_le.writeBits(u9(5), 5);
|
||||
try bit_stream_le.writeBits(u1(1), 1);
|
||||
|
||||
assert(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101);
|
||||
|
||||
mem_out_le.pos = 0;
|
||||
try bit_stream_le.writeBits(u15(0b110011010000101), 15);
|
||||
try bit_stream_le.flushBits();
|
||||
assert(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110);
|
||||
|
||||
mem_out_le.pos = 0;
|
||||
try bit_stream_le.writeBits(u32(0b1100110100001011), 16);
|
||||
assert(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101);
|
||||
|
||||
try bit_stream_le.writeBits(u0(0), 0);
|
||||
}
|
||||
|
||||
test "BitStreams with File Stream" {
|
||||
const tmp_file_name = "temp_test_file.txt";
|
||||
{
|
||||
var file = try os.File.openWrite(tmp_file_name);
|
||||
defer file.close();
|
||||
|
||||
var file_out = file.outStream();
|
||||
var file_out_stream = &file_out.stream;
|
||||
const OutError = os.File.WriteError;
|
||||
var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream);
|
||||
|
||||
try bit_stream.writeBits(u2(1), 1);
|
||||
try bit_stream.writeBits(u5(2), 2);
|
||||
try bit_stream.writeBits(u128(3), 3);
|
||||
try bit_stream.writeBits(u8(4), 4);
|
||||
try bit_stream.writeBits(u9(5), 5);
|
||||
try bit_stream.writeBits(u1(1), 1);
|
||||
try bit_stream.flushBits();
|
||||
}
|
||||
{
|
||||
var file = try os.File.openRead(tmp_file_name);
|
||||
defer file.close();
|
||||
|
||||
var file_in = file.inStream();
|
||||
var file_in_stream = &file_in.stream;
|
||||
const InError = os.File.ReadError;
|
||||
var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream);
|
||||
|
||||
var out_bits: usize = undefined;
|
||||
|
||||
assert(1 == try bit_stream.readBits(u2, 1, &out_bits));
|
||||
assert(out_bits == 1);
|
||||
assert(2 == try bit_stream.readBits(u5, 2, &out_bits));
|
||||
assert(out_bits == 2);
|
||||
assert(3 == try bit_stream.readBits(u128, 3, &out_bits));
|
||||
assert(out_bits == 3);
|
||||
assert(4 == try bit_stream.readBits(u8, 4, &out_bits));
|
||||
assert(out_bits == 4);
|
||||
assert(5 == try bit_stream.readBits(u9, 5, &out_bits));
|
||||
assert(out_bits == 5);
|
||||
assert(1 == try bit_stream.readBits(u1, 1, &out_bits));
|
||||
assert(out_bits == 1);
|
||||
|
||||
assertError(bit_stream.readBitsNoEof(u1, 1), error.EndOfStream);
|
||||
}
|
||||
try os.deleteFile(tmp_file_name);
|
||||
}
|
||||
|
||||
fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
|
||||
//@NOTE: if this test is taking too long, reduce the maximum tested bitsize
|
||||
const max_test_bitsize = 128;
|
||||
|
||||
const total_bytes = comptime blk: {
|
||||
var bytes = 0;
|
||||
comptime var i = 0;
|
||||
while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
|
||||
break :blk bytes * 2;
|
||||
};
|
||||
|
||||
var data_mem: [total_bytes]u8 = undefined;
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
|
||||
|
||||
comptime var i = 0;
|
||||
inline while (i <= max_test_bitsize) : (i += 1) {
|
||||
const U = @IntType(false, i);
|
||||
const S = @IntType(true, i);
|
||||
try serializer.serializeInt(U(i));
|
||||
if (i != 0) try serializer.serializeInt(S(-1)) else try serializer.serialize(S(0));
|
||||
}
|
||||
try serializer.flush();
|
||||
|
||||
i = 0;
|
||||
inline while (i <= max_test_bitsize) : (i += 1) {
|
||||
const U = @IntType(false, i);
|
||||
const S = @IntType(true, i);
|
||||
const x = try deserializer.deserializeInt(U);
|
||||
const y = try deserializer.deserializeInt(S);
|
||||
assert(x == U(i));
|
||||
if (i != 0) assert(y == S(-1)) else assert(y == 0);
|
||||
}
|
||||
|
||||
const u8_bit_count = comptime meta.bitCount(u8);
|
||||
//0 + 1 + 2 + ... n = (n * (n + 1)) / 2
|
||||
//and we have each for unsigned and signed, so * 2
|
||||
const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
|
||||
const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
|
||||
const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
|
||||
|
||||
assert(in.pos == if (is_packed) total_packed_bytes else total_bytes);
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer Int" {
|
||||
try testIntSerializerDeserializer(builtin.Endian.Big, false);
|
||||
try testIntSerializerDeserializer(builtin.Endian.Little, false);
|
||||
try testIntSerializerDeserializer(builtin.Endian.Big, true);
|
||||
try testIntSerializerDeserializer(builtin.Endian.Little, true);
|
||||
}
|
||||
|
||||
fn testIntSerializerDeserializerInfNaN(comptime endian: builtin.Endian,
|
||||
comptime is_packed: bool) !void
|
||||
{
|
||||
const mem_size = (16*2 + 32*2 + 64*2 + 128*2) / comptime meta.bitCount(u8);
|
||||
var data_mem: [mem_size]u8 = undefined;
|
||||
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
|
||||
|
||||
//@TODO: isInf/isNan not currently implemented for f128.
|
||||
try serializer.serialize(std.math.nan(f16));
|
||||
try serializer.serialize(std.math.inf(f16));
|
||||
try serializer.serialize(std.math.nan(f32));
|
||||
try serializer.serialize(std.math.inf(f32));
|
||||
try serializer.serialize(std.math.nan(f64));
|
||||
try serializer.serialize(std.math.inf(f64));
|
||||
//try serializer.serialize(std.math.nan(f128));
|
||||
//try serializer.serialize(std.math.inf(f128));
|
||||
const nan_check_f16 = try deserializer.deserialize(f16);
|
||||
const inf_check_f16 = try deserializer.deserialize(f16);
|
||||
const nan_check_f32 = try deserializer.deserialize(f32);
|
||||
const inf_check_f32 = try deserializer.deserialize(f32);
|
||||
const nan_check_f64 = try deserializer.deserialize(f64);
|
||||
const inf_check_f64 = try deserializer.deserialize(f64);
|
||||
//const nan_check_f128 = try deserializer.deserialize(f128);
|
||||
//const inf_check_f128 = try deserializer.deserialize(f128);
|
||||
assert(std.math.isNan(nan_check_f16));
|
||||
assert(std.math.isInf(inf_check_f16));
|
||||
assert(std.math.isNan(nan_check_f32));
|
||||
assert(std.math.isInf(inf_check_f32));
|
||||
assert(std.math.isNan(nan_check_f64));
|
||||
assert(std.math.isInf(inf_check_f64));
|
||||
//assert(std.math.isNan(nan_check_f128));
|
||||
//assert(std.math.isInf(inf_check_f128));
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer Int: Inf/NaN" {
|
||||
try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, false);
|
||||
try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, false);
|
||||
try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, true);
|
||||
try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, true);
|
||||
}
|
||||
|
||||
fn testAlternateSerializer(self: var, serializer: var) !void {
|
||||
try serializer.serialize(self.f_f16);
|
||||
}
|
||||
|
||||
fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
|
||||
const ColorType = enum(u4) {
|
||||
RGB8 = 1,
|
||||
RA16 = 2,
|
||||
R32 = 3,
|
||||
};
|
||||
|
||||
const TagAlign = union(enum(u32)) {
|
||||
A: u8,
|
||||
B: u8,
|
||||
C: u8,
|
||||
};
|
||||
|
||||
const Color = union(ColorType) {
|
||||
RGB8: struct {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
a: u8,
|
||||
},
|
||||
RA16: struct {
|
||||
r: u16,
|
||||
a: u16,
|
||||
},
|
||||
R32: u32,
|
||||
};
|
||||
|
||||
const PackedStruct = packed struct {
|
||||
f_i3: i3,
|
||||
f_u2: u2,
|
||||
};
|
||||
|
||||
|
||||
|
||||
//to test custom serialization
|
||||
const Custom = struct {
|
||||
f_f16: f16,
|
||||
f_unused_u32: u32,
|
||||
|
||||
pub fn deserialize(self: *@This(), deserializer: var) !void {
|
||||
try deserializer.deserializeInto(&self.f_f16);
|
||||
self.f_unused_u32 = 47;
|
||||
}
|
||||
|
||||
pub const serialize = testAlternateSerializer;
|
||||
};
|
||||
|
||||
const MyStruct = struct {
|
||||
f_i3: i3,
|
||||
f_u8: u8,
|
||||
f_tag_align: TagAlign,
|
||||
f_u24: u24,
|
||||
f_i19: i19,
|
||||
f_void: void,
|
||||
f_f32: f32,
|
||||
f_f128: f128,
|
||||
f_packed_0: PackedStruct,
|
||||
f_i7arr: [10]i7,
|
||||
f_of64n: ?f64,
|
||||
f_of64v: ?f64,
|
||||
f_color_type: ColorType,
|
||||
f_packed_1: PackedStruct,
|
||||
f_custom: Custom,
|
||||
f_color: Color,
|
||||
};
|
||||
|
||||
const my_inst = MyStruct{
|
||||
.f_i3 = -1,
|
||||
.f_u8 = 8,
|
||||
.f_tag_align = TagAlign{ .B = 148 },
|
||||
.f_u24 = 24,
|
||||
.f_i19 = 19,
|
||||
.f_void = {},
|
||||
.f_f32 = 32.32,
|
||||
.f_f128 = 128.128,
|
||||
.f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
|
||||
.f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
|
||||
.f_of64n = null,
|
||||
.f_of64v = 64.64,
|
||||
.f_color_type = ColorType.R32,
|
||||
.f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
|
||||
.f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
|
||||
.f_color = Color{ .R32 = 123822 },
|
||||
};
|
||||
|
||||
var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
|
||||
|
||||
try serializer.serialize(my_inst);
|
||||
|
||||
const my_copy = try deserializer.deserialize(MyStruct);
|
||||
assert(meta.eql(my_copy, my_inst));
|
||||
}
|
||||
|
||||
test "Serializer/Deserializer generic" {
|
||||
try testSerializerDeserializer(builtin.Endian.Big, false);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, false);
|
||||
try testSerializerDeserializer(builtin.Endian.Big, true);
|
||||
try testSerializerDeserializer(builtin.Endian.Little, true);
|
||||
}
|
||||
|
||||
fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void {
|
||||
const E = enum(u14) {
|
||||
One = 1,
|
||||
Two = 2,
|
||||
};
|
||||
|
||||
const A = struct {
|
||||
e: E,
|
||||
};
|
||||
|
||||
const C = union(E) {
|
||||
One: u14,
|
||||
Two: f16,
|
||||
};
|
||||
|
||||
var data_mem: [4]u8 = undefined;
|
||||
var out = io.SliceOutStream.init(data_mem[0..]);
|
||||
const OutError = io.SliceOutStream.Error;
|
||||
var out_stream = &out.stream;
|
||||
var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream);
|
||||
|
||||
var in = io.SliceInStream.init(data_mem[0..]);
|
||||
const InError = io.SliceInStream.Error;
|
||||
var in_stream = &in.stream;
|
||||
var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream);
|
||||
|
||||
try serializer.serialize(u14(3));
|
||||
assertError(deserializer.deserialize(A), error.InvalidEnumTag);
|
||||
out.pos = 0;
|
||||
try serializer.serialize(u14(3));
|
||||
try serializer.serialize(u14(88));
|
||||
assertError(deserializer.deserialize(C), error.InvalidEnumTag);
|
||||
}
|
||||
|
||||
test "Deserializer bad data" {
|
||||
try testBadData(builtin.Endian.Big, false);
|
||||
try testBadData(builtin.Endian.Little, false);
|
||||
try testBadData(builtin.Endian.Big, true);
|
||||
try testBadData(builtin.Endian.Little, true);
|
||||
}
|
@ -190,7 +190,7 @@ pub fn LinkedList(comptime T: type) type {
|
||||
/// Returns:
|
||||
/// A pointer to the new node.
|
||||
pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
|
||||
return allocator.create(Node(undefined));
|
||||
return allocator.create(Node);
|
||||
}
|
||||
|
||||
/// Deallocate a node.
|
||||
|
159
std/mem.zig
159
std/mem.zig
@ -36,20 +36,9 @@ pub const Allocator = struct {
|
||||
/// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
|
||||
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{});
|
||||
const slice = try self.alloc(T, 1);
|
||||
const ptr = &slice[0];
|
||||
ptr.* = init;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/// Call `destroy` with the result.
|
||||
/// Returns undefined memory.
|
||||
pub fn createOne(self: *Allocator, comptime T: type) Error!*T {
|
||||
pub fn create(self: *Allocator, comptime T: type) Error!*T {
|
||||
if (@sizeOf(T) == 0) return &(T{});
|
||||
const slice = try self.alloc(T, 1);
|
||||
return &slice[0];
|
||||
@ -700,23 +689,114 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool {
|
||||
}
|
||||
|
||||
/// Returns an iterator that iterates over the slices of `buffer` that are not
|
||||
/// any of the bytes in `split_bytes`.
|
||||
/// split(" abc def ghi ", " ")
|
||||
/// any of the bytes in `delimiter_bytes`.
|
||||
/// tokenize(" abc def ghi ", " ")
|
||||
/// Will return slices for "abc", "def", "ghi", null, in that order.
|
||||
pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator {
|
||||
return SplitIterator{
|
||||
/// If `buffer` is empty, the iterator will return null.
|
||||
/// If `delimiter_bytes` does not exist in buffer,
|
||||
/// the iterator will return `buffer`, null, in that order.
|
||||
/// See also the related function `separate`.
|
||||
pub fn tokenize(buffer: []const u8, delimiter_bytes: []const u8) TokenIterator {
|
||||
return TokenIterator{
|
||||
.index = 0,
|
||||
.buffer = buffer,
|
||||
.split_bytes = split_bytes,
|
||||
.delimiter_bytes = delimiter_bytes,
|
||||
};
|
||||
}
|
||||
|
||||
test "mem.split" {
|
||||
var it = split(" abc def ghi ", " ");
|
||||
test "mem.tokenize" {
|
||||
var it = tokenize(" abc def ghi ", " ");
|
||||
assert(eql(u8, it.next().?, "abc"));
|
||||
assert(eql(u8, it.next().?, "def"));
|
||||
assert(eql(u8, it.next().?, "ghi"));
|
||||
assert(it.next() == null);
|
||||
|
||||
it = tokenize("..\\bob", "\\");
|
||||
assert(eql(u8, it.next().?, ".."));
|
||||
assert(eql(u8, "..", "..\\bob"[0..it.index]));
|
||||
assert(eql(u8, it.next().?, "bob"));
|
||||
assert(it.next() == null);
|
||||
|
||||
it = tokenize("//a/b", "/");
|
||||
assert(eql(u8, it.next().?, "a"));
|
||||
assert(eql(u8, it.next().?, "b"));
|
||||
assert(eql(u8, "//a/b", "//a/b"[0..it.index]));
|
||||
assert(it.next() == null);
|
||||
|
||||
it = tokenize("|", "|");
|
||||
assert(it.next() == null);
|
||||
|
||||
it = tokenize("", "|");
|
||||
assert(it.next() == null);
|
||||
|
||||
it = tokenize("hello", "");
|
||||
assert(eql(u8, it.next().?, "hello"));
|
||||
assert(it.next() == null);
|
||||
|
||||
it = tokenize("hello", " ");
|
||||
assert(eql(u8, it.next().?, "hello"));
|
||||
assert(it.next() == null);
|
||||
}
|
||||
|
||||
test "mem.tokenize (multibyte)" {
|
||||
var it = tokenize("a|b,c/d e", " /,|");
|
||||
assert(eql(u8, it.next().?, "a"));
|
||||
assert(eql(u8, it.next().?, "b"));
|
||||
assert(eql(u8, it.next().?, "c"));
|
||||
assert(eql(u8, it.next().?, "d"));
|
||||
assert(eql(u8, it.next().?, "e"));
|
||||
assert(it.next() == null);
|
||||
}
|
||||
|
||||
/// Returns an iterator that iterates over the slices of `buffer` that
|
||||
/// are separated by bytes in `delimiter`.
|
||||
/// separate("abc|def||ghi", "|")
|
||||
/// will return slices for "abc", "def", "", "ghi", null, in that order.
|
||||
/// If `delimiter` does not exist in buffer,
|
||||
/// the iterator will return `buffer`, null, in that order.
|
||||
/// The delimiter length must not be zero.
|
||||
/// See also the related function `tokenize`.
|
||||
/// It is planned to rename this function to `split` before 1.0.0, like this:
|
||||
/// pub fn split(buffer: []const u8, delimiter: []const u8) SplitIterator {
|
||||
pub fn separate(buffer: []const u8, delimiter: []const u8) SplitIterator {
|
||||
assert(delimiter.len != 0);
|
||||
return SplitIterator{
|
||||
.index = 0,
|
||||
.buffer = buffer,
|
||||
.delimiter = delimiter,
|
||||
};
|
||||
}
|
||||
|
||||
test "mem.separate" {
|
||||
var it = separate("abc|def||ghi", "|");
|
||||
assert(eql(u8, it.next().?, "abc"));
|
||||
assert(eql(u8, it.next().?, "def"));
|
||||
assert(eql(u8, it.next().?, ""));
|
||||
assert(eql(u8, it.next().?, "ghi"));
|
||||
assert(it.next() == null);
|
||||
|
||||
it = separate("", "|");
|
||||
assert(eql(u8, it.next().?, ""));
|
||||
assert(it.next() == null);
|
||||
|
||||
it = separate("|", "|");
|
||||
assert(eql(u8, it.next().?, ""));
|
||||
assert(eql(u8, it.next().?, ""));
|
||||
assert(it.next() == null);
|
||||
|
||||
it = separate("hello", " ");
|
||||
assert(eql(u8, it.next().?, "hello"));
|
||||
assert(it.next() == null);
|
||||
}
|
||||
|
||||
test "mem.separate (multibyte)" {
|
||||
var it = separate("a, b ,, c, d, e", ", ");
|
||||
assert(eql(u8, it.next().?, "a"));
|
||||
assert(eql(u8, it.next().?, "b ,"));
|
||||
assert(eql(u8, it.next().?, "c"));
|
||||
assert(eql(u8, it.next().?, "d"));
|
||||
assert(eql(u8, it.next().?, "e"));
|
||||
assert(it.next() == null);
|
||||
}
|
||||
|
||||
pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool {
|
||||
@ -737,12 +817,13 @@ test "mem.endsWith" {
|
||||
assert(!endsWith(u8, "Bob", "Bo"));
|
||||
}
|
||||
|
||||
pub const SplitIterator = struct {
|
||||
pub const TokenIterator = struct {
|
||||
buffer: []const u8,
|
||||
split_bytes: []const u8,
|
||||
delimiter_bytes: []const u8,
|
||||
index: usize,
|
||||
|
||||
pub fn next(self: *SplitIterator) ?[]const u8 {
|
||||
/// Returns a slice of the next token, or null if tokenization is complete.
|
||||
pub fn next(self: *TokenIterator) ?[]const u8 {
|
||||
// move to beginning of token
|
||||
while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {}
|
||||
const start = self.index;
|
||||
@ -758,16 +839,16 @@ pub const SplitIterator = struct {
|
||||
}
|
||||
|
||||
/// Returns a slice of the remaining bytes. Does not affect iterator state.
|
||||
pub fn rest(self: *const SplitIterator) []const u8 {
|
||||
pub fn rest(self: TokenIterator) []const u8 {
|
||||
// move to beginning of token
|
||||
var index: usize = self.index;
|
||||
while (index < self.buffer.len and self.isSplitByte(self.buffer[index])) : (index += 1) {}
|
||||
return self.buffer[index..];
|
||||
}
|
||||
|
||||
fn isSplitByte(self: *const SplitIterator, byte: u8) bool {
|
||||
for (self.split_bytes) |split_byte| {
|
||||
if (byte == split_byte) {
|
||||
fn isSplitByte(self: TokenIterator, byte: u8) bool {
|
||||
for (self.delimiter_bytes) |delimiter_byte| {
|
||||
if (byte == delimiter_byte) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -775,6 +856,32 @@ pub const SplitIterator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const SplitIterator = struct {
|
||||
buffer: []const u8,
|
||||
index: ?usize,
|
||||
delimiter: []const u8,
|
||||
|
||||
/// Returns a slice of the next field, or null if splitting is complete.
|
||||
pub fn next(self: *SplitIterator) ?[]const u8 {
|
||||
const start = self.index orelse return null;
|
||||
const end = if (indexOfPos(u8, self.buffer, start, self.delimiter)) |delim_start| blk: {
|
||||
self.index = delim_start + self.delimiter.len;
|
||||
break :blk delim_start;
|
||||
} else blk: {
|
||||
self.index = null;
|
||||
break :blk self.buffer.len;
|
||||
};
|
||||
return self.buffer[start..end];
|
||||
}
|
||||
|
||||
/// Returns a slice of the remaining bytes. Does not affect iterator state.
|
||||
pub fn rest(self: SplitIterator) []const u8 {
|
||||
const end = self.buffer.len;
|
||||
const start = self.index orelse end;
|
||||
return self.buffer[start..end];
|
||||
}
|
||||
};
|
||||
|
||||
/// Naively combines a series of strings with a separator.
|
||||
/// Allocates memory for the result, which must be freed by the caller.
|
||||
pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {
|
||||
|
@ -95,7 +95,7 @@ test "std.meta.stringToEnum" {
|
||||
debug.assert(null == stringToEnum(E1, "C"));
|
||||
}
|
||||
|
||||
pub fn bitCount(comptime T: type) u32 {
|
||||
pub fn bitCount(comptime T: type) comptime_int {
|
||||
return switch (@typeInfo(T)) {
|
||||
TypeId.Int => |info| info.bits,
|
||||
TypeId.Float => |info| info.bits,
|
||||
@ -108,7 +108,7 @@ test "std.meta.bitCount" {
|
||||
debug.assert(bitCount(f32) == 32);
|
||||
}
|
||||
|
||||
pub fn alignment(comptime T: type) u29 {
|
||||
pub fn alignment(comptime T: type) comptime_int {
|
||||
//@alignOf works on non-pointer types
|
||||
const P = if (comptime trait.is(TypeId.Pointer)(T)) T else *T;
|
||||
return @typeInfo(P).Pointer.alignment;
|
||||
@ -386,6 +386,33 @@ test "std.meta.activeTag" {
|
||||
debug.assert(activeTag(u) == UE.Float);
|
||||
}
|
||||
|
||||
///Given a tagged union type, and an enum, return the type of the union
|
||||
/// field corresponding to the enum tag.
|
||||
pub fn TagPayloadType(comptime U: type, tag: var) type {
|
||||
const Tag = @typeOf(tag);
|
||||
debug.assert(trait.is(builtin.TypeId.Union)(U));
|
||||
debug.assert(trait.is(builtin.TypeId.Enum)(Tag));
|
||||
|
||||
const info = @typeInfo(U).Union;
|
||||
|
||||
inline for (info.fields) |field_info| {
|
||||
if (field_info.enum_field.?.value == @enumToInt(tag)) return field_info.field_type;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
test "std.meta.TagPayloadType" {
|
||||
const Event = union(enum) {
|
||||
Moved: struct {
|
||||
from: i32,
|
||||
to: i32,
|
||||
},
|
||||
};
|
||||
const MovedEvent = TagPayloadType(Event, Event.Moved);
|
||||
var e: Event = undefined;
|
||||
debug.assert(MovedEvent == @typeOf(e.Moved));
|
||||
}
|
||||
|
||||
///Compares two of any type for equality. Containers are compared on a field-by-field basis,
|
||||
/// where possible. Pointers are not followed.
|
||||
pub fn eql(a: var, b: @typeOf(a)) bool {
|
||||
@ -439,6 +466,11 @@ pub fn eql(a: var, b: @typeOf(a)) bool {
|
||||
builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len,
|
||||
}
|
||||
},
|
||||
builtin.TypeId.Optional => {
|
||||
if(a == null and b == null) return true;
|
||||
if(a == null or b == null) return false;
|
||||
return eql(a.?, b.?);
|
||||
},
|
||||
else => return a == b,
|
||||
}
|
||||
}
|
||||
@ -452,7 +484,7 @@ test "std.meta.eql" {
|
||||
|
||||
const U = union(enum) {
|
||||
s: S,
|
||||
f: f32,
|
||||
f: ?f32,
|
||||
};
|
||||
|
||||
const s_1 = S{
|
||||
|
@ -231,6 +231,37 @@ test "std.meta.trait.isPacked" {
|
||||
debug.assert(!isPacked(u8));
|
||||
}
|
||||
|
||||
///
|
||||
pub fn isUnsignedInt(comptime T: type) bool {
|
||||
return switch (@typeId(T)) {
|
||||
builtin.TypeId.Int => !@typeInfo(T).Int.is_signed,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
test "isUnsignedInt" {
|
||||
debug.assert(isUnsignedInt(u32) == true);
|
||||
debug.assert(isUnsignedInt(comptime_int) == false);
|
||||
debug.assert(isUnsignedInt(i64) == false);
|
||||
debug.assert(isUnsignedInt(f64) == false);
|
||||
}
|
||||
|
||||
///
|
||||
pub fn isSignedInt(comptime T: type) bool {
|
||||
return switch (@typeId(T)) {
|
||||
builtin.TypeId.ComptimeInt => true,
|
||||
builtin.TypeId.Int => @typeInfo(T).Int.is_signed,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
test "isSignedInt" {
|
||||
debug.assert(isSignedInt(u32) == false);
|
||||
debug.assert(isSignedInt(comptime_int) == true);
|
||||
debug.assert(isSignedInt(i64) == true);
|
||||
debug.assert(isSignedInt(f64) == false);
|
||||
}
|
||||
|
||||
///
|
||||
pub fn isSingleItemPtr(comptime T: type) bool {
|
||||
if (comptime is(builtin.TypeId.Pointer)(T)) {
|
||||
|
153
std/mutex.zig
153
std/mutex.zig
@ -5,74 +5,124 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||
const assert = std.debug.assert;
|
||||
const SpinLock = std.SpinLock;
|
||||
const linux = std.os.linux;
|
||||
const windows = std.os.windows;
|
||||
|
||||
/// Lock may be held only once. If the same thread
|
||||
/// tries to acquire the same mutex twice, it deadlocks.
|
||||
/// This type must be initialized at runtime, and then deinitialized when no
|
||||
/// longer needed, to free resources.
|
||||
/// If you need static initialization, use std.StaticallyInitializedMutex.
|
||||
/// The Linux implementation is based on mutex3 from
|
||||
/// https://www.akkadia.org/drepper/futex.pdf
|
||||
pub const Mutex = struct {
|
||||
/// 0: unlocked
|
||||
/// 1: locked, no waiters
|
||||
/// 2: locked, one or more waiters
|
||||
linux_lock: @typeOf(linux_lock_init),
|
||||
/// When an application is built in single threaded release mode, all the functions are
|
||||
/// no-ops. In single threaded debug mode, there is deadlock detection.
|
||||
pub const Mutex = if (builtin.single_threaded)
|
||||
struct {
|
||||
lock: @typeOf(lock_init),
|
||||
|
||||
/// TODO better implementation than spin lock
|
||||
spin_lock: @typeOf(spin_lock_init),
|
||||
const lock_init = if (std.debug.runtime_safety) false else {};
|
||||
|
||||
const linux_lock_init = if (builtin.os == builtin.Os.linux) i32(0) else {};
|
||||
const spin_lock_init = if (builtin.os != builtin.Os.linux) SpinLock.init() else {};
|
||||
pub const Held = struct {
|
||||
mutex: *Mutex,
|
||||
|
||||
pub const Held = struct {
|
||||
mutex: *Mutex,
|
||||
pub fn release(self: Held) void {
|
||||
if (std.debug.runtime_safety) {
|
||||
self.mutex.lock = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
pub fn init() Mutex {
|
||||
return Mutex{ .lock = lock_init };
|
||||
}
|
||||
pub fn deinit(self: *Mutex) void {}
|
||||
|
||||
pub fn release(self: Held) void {
|
||||
if (builtin.os == builtin.Os.linux) {
|
||||
const c = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release);
|
||||
pub fn acquire(self: *Mutex) Held {
|
||||
if (std.debug.runtime_safety and self.lock) {
|
||||
@panic("deadlock detected");
|
||||
}
|
||||
return Held{ .mutex = self };
|
||||
}
|
||||
}
|
||||
else switch (builtin.os) {
|
||||
builtin.Os.linux => struct {
|
||||
/// 0: unlocked
|
||||
/// 1: locked, no waiters
|
||||
/// 2: locked, one or more waiters
|
||||
lock: i32,
|
||||
|
||||
pub const Held = struct {
|
||||
mutex: *Mutex,
|
||||
|
||||
pub fn release(self: Held) void {
|
||||
const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release);
|
||||
if (c != 1) {
|
||||
_ = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release);
|
||||
const rc = linux.futex_wake(&self.mutex.linux_lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
|
||||
_ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release);
|
||||
const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
|
||||
switch (linux.getErrno(rc)) {
|
||||
0 => {},
|
||||
linux.EINVAL => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.spin_lock });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init() Mutex {
|
||||
return Mutex{
|
||||
.linux_lock = linux_lock_init,
|
||||
.spin_lock = spin_lock_init,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn acquire(self: *Mutex) Held {
|
||||
if (builtin.os == builtin.Os.linux) {
|
||||
var c = @cmpxchgWeak(i32, &self.linux_lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse
|
||||
pub fn init() Mutex {
|
||||
return Mutex{ .lock = 0 };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Mutex) void {}
|
||||
|
||||
pub fn acquire(self: *Mutex) Held {
|
||||
var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse
|
||||
return Held{ .mutex = self };
|
||||
if (c != 2)
|
||||
c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
|
||||
c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
|
||||
while (c != 0) {
|
||||
const rc = linux.futex_wait(&self.linux_lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null);
|
||||
const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null);
|
||||
switch (linux.getErrno(rc)) {
|
||||
0, linux.EINTR, linux.EAGAIN => {},
|
||||
linux.EINVAL => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
|
||||
c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
|
||||
}
|
||||
} else {
|
||||
_ = self.spin_lock.acquire();
|
||||
return Held{ .mutex = self };
|
||||
}
|
||||
return Held{ .mutex = self };
|
||||
}
|
||||
},
|
||||
// TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a
|
||||
// better implementation of this. The problem is we need the init() function to have access to
|
||||
// the address of the CRITICAL_SECTION, and then have it not move.
|
||||
builtin.Os.windows => std.StaticallyInitializedMutex,
|
||||
else => struct {
|
||||
/// TODO better implementation than spin lock.
|
||||
/// When changing this, one must also change the corresponding
|
||||
/// std.StaticallyInitializedMutex code, since it aliases this type,
|
||||
/// under the assumption that it works both statically and at runtime.
|
||||
lock: SpinLock,
|
||||
|
||||
pub const Held = struct {
|
||||
mutex: *Mutex,
|
||||
|
||||
pub fn release(self: Held) void {
|
||||
SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.lock });
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init() Mutex {
|
||||
return Mutex{ .lock = SpinLock.init() };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Mutex) void {}
|
||||
|
||||
pub fn acquire(self: *Mutex) Held {
|
||||
_ = self.lock.acquire();
|
||||
return Held{ .mutex = self };
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const Context = struct {
|
||||
const TestContext = struct {
|
||||
mutex: *Mutex,
|
||||
data: i128,
|
||||
|
||||
@ -90,25 +140,32 @@ test "std.Mutex" {
|
||||
var a = &fixed_buffer_allocator.allocator;
|
||||
|
||||
var mutex = Mutex.init();
|
||||
var context = Context{
|
||||
defer mutex.deinit();
|
||||
|
||||
var context = TestContext{
|
||||
.mutex = &mutex,
|
||||
.data = 0,
|
||||
};
|
||||
|
||||
const thread_count = 10;
|
||||
var threads: [thread_count]*std.os.Thread = undefined;
|
||||
for (threads) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, worker);
|
||||
}
|
||||
for (threads) |t|
|
||||
t.wait();
|
||||
if (builtin.single_threaded) {
|
||||
worker(&context);
|
||||
std.debug.assertOrPanic(context.data == TestContext.incr_count);
|
||||
} else {
|
||||
const thread_count = 10;
|
||||
var threads: [thread_count]*std.os.Thread = undefined;
|
||||
for (threads) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, worker);
|
||||
}
|
||||
for (threads) |t|
|
||||
t.wait();
|
||||
|
||||
std.debug.assertOrPanic(context.data == thread_count * Context.incr_count);
|
||||
std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
|
||||
}
|
||||
}
|
||||
|
||||
fn worker(ctx: *Context) void {
|
||||
fn worker(ctx: *TestContext) void {
|
||||
var i: usize = 0;
|
||||
while (i != Context.incr_count) : (i += 1) {
|
||||
while (i != TestContext.incr_count) : (i += 1) {
|
||||
const held = ctx.mutex.acquire();
|
||||
defer held.release();
|
||||
|
||||
|
@ -88,7 +88,8 @@ pub const ChildProcess = struct {
|
||||
/// First argument in argv is the executable.
|
||||
/// On success must call deinit.
|
||||
pub fn init(argv: []const []const u8, allocator: *mem.Allocator) !*ChildProcess {
|
||||
const child = try allocator.create(ChildProcess{
|
||||
const child = try allocator.create(ChildProcess);
|
||||
child.* = ChildProcess{
|
||||
.allocator = allocator,
|
||||
.argv = argv,
|
||||
.pid = undefined,
|
||||
@ -109,7 +110,7 @@ pub const ChildProcess = struct {
|
||||
.stdin_behavior = StdIo.Inherit,
|
||||
.stdout_behavior = StdIo.Inherit,
|
||||
.stderr_behavior = StdIo.Inherit,
|
||||
});
|
||||
};
|
||||
errdefer allocator.destroy(child);
|
||||
return child;
|
||||
}
|
||||
@ -594,7 +595,7 @@ pub const ChildProcess = struct {
|
||||
const PATH = try os.getEnvVarOwned(self.allocator, "PATH");
|
||||
defer self.allocator.free(PATH);
|
||||
|
||||
var it = mem.split(PATH, ";");
|
||||
var it = mem.tokenize(PATH, ";");
|
||||
while (it.next()) |search_path| {
|
||||
const joined_path = try os.path.join(self.allocator, search_path, app_name);
|
||||
defer self.allocator.free(joined_path);
|
||||
|
@ -608,7 +608,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator:
|
||||
// +1 for the null terminating byte
|
||||
const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2);
|
||||
defer allocator.free(path_buf);
|
||||
var it = mem.split(PATH, ":");
|
||||
var it = mem.tokenize(PATH, ":");
|
||||
var seen_eacces = false;
|
||||
var err: usize = undefined;
|
||||
while (it.next()) |search_path| {
|
||||
@ -3013,6 +3013,7 @@ pub const SpawnThreadError = error{
|
||||
/// where T is u8, noreturn, void, or !void
|
||||
/// caller must call wait on the returned thread
|
||||
pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread {
|
||||
if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
|
||||
// TODO compile-time call graph analysis to determine stack upper bound
|
||||
// https://github.com/ziglang/zig/issues/157
|
||||
const default_stack_size = 8 * 1024 * 1024;
|
||||
@ -3046,7 +3047,8 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
|
||||
const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory;
|
||||
errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
|
||||
const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count];
|
||||
const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext{
|
||||
const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
|
||||
outer_context.* = WinThread.OuterContext{
|
||||
.thread = Thread{
|
||||
.data = Thread.Data{
|
||||
.heap_handle = heap_handle,
|
||||
@ -3055,7 +3057,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
|
||||
},
|
||||
},
|
||||
.inner = context,
|
||||
}) catch unreachable;
|
||||
};
|
||||
|
||||
const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);
|
||||
outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {
|
||||
|
@ -184,7 +184,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {
|
||||
return relative_path;
|
||||
}
|
||||
|
||||
var it = mem.split(path, []u8{this_sep});
|
||||
var it = mem.tokenize(path, []u8{this_sep});
|
||||
_ = (it.next() orelse return relative_path);
|
||||
_ = (it.next() orelse return relative_path);
|
||||
return WindowsPath{
|
||||
@ -202,7 +202,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {
|
||||
return relative_path;
|
||||
}
|
||||
|
||||
var it = mem.split(path, []u8{this_sep});
|
||||
var it = mem.tokenize(path, []u8{this_sep});
|
||||
_ = (it.next() orelse return relative_path);
|
||||
_ = (it.next() orelse return relative_path);
|
||||
return WindowsPath{
|
||||
@ -264,8 +264,8 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool {
|
||||
const sep1 = ns1[0];
|
||||
const sep2 = ns2[0];
|
||||
|
||||
var it1 = mem.split(ns1, []u8{sep1});
|
||||
var it2 = mem.split(ns2, []u8{sep2});
|
||||
var it1 = mem.tokenize(ns1, []u8{sep1});
|
||||
var it2 = mem.tokenize(ns2, []u8{sep2});
|
||||
|
||||
// TODO ASCII is wrong, we actually need full unicode support to compare paths.
|
||||
return asciiEqlIgnoreCase(it1.next().?, it2.next().?);
|
||||
@ -285,8 +285,8 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8
|
||||
const sep1 = p1[0];
|
||||
const sep2 = p2[0];
|
||||
|
||||
var it1 = mem.split(p1, []u8{sep1});
|
||||
var it2 = mem.split(p2, []u8{sep2});
|
||||
var it1 = mem.tokenize(p1, []u8{sep1});
|
||||
var it2 = mem.tokenize(p2, []u8{sep2});
|
||||
|
||||
// TODO ASCII is wrong, we actually need full unicode support to compare paths.
|
||||
return asciiEqlIgnoreCase(it1.next().?, it2.next().?) and asciiEqlIgnoreCase(it1.next().?, it2.next().?);
|
||||
@ -337,6 +337,8 @@ pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 {
|
||||
/// If all paths are relative it uses the current working directory as a starting point.
|
||||
/// Each drive has its own current working directory.
|
||||
/// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters.
|
||||
/// Note: all usage of this function should be audited due to the existence of symlinks.
|
||||
/// Without performing actual syscalls, resolving `..` could be incorrect.
|
||||
pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
|
||||
if (paths.len == 0) {
|
||||
assert(is_windows); // resolveWindows called on non windows can't use getCwd
|
||||
@ -416,7 +418,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
|
||||
},
|
||||
WindowsPath.Kind.NetworkShare => {
|
||||
result = try allocator.alloc(u8, max_size);
|
||||
var it = mem.split(paths[first_index], "/\\");
|
||||
var it = mem.tokenize(paths[first_index], "/\\");
|
||||
const server_name = it.next().?;
|
||||
const other_name = it.next().?;
|
||||
|
||||
@ -483,7 +485,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
|
||||
if (!correct_disk_designator) {
|
||||
continue;
|
||||
}
|
||||
var it = mem.split(p[parsed.disk_designator.len..], "/\\");
|
||||
var it = mem.tokenize(p[parsed.disk_designator.len..], "/\\");
|
||||
while (it.next()) |component| {
|
||||
if (mem.eql(u8, component, ".")) {
|
||||
continue;
|
||||
@ -516,6 +518,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
|
||||
/// It resolves "." and "..".
|
||||
/// The result does not have a trailing path separator.
|
||||
/// If all paths are relative it uses the current working directory as a starting point.
|
||||
/// Note: all usage of this function should be audited due to the existence of symlinks.
|
||||
/// Without performing actual syscalls, resolving `..` could be incorrect.
|
||||
pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
|
||||
if (paths.len == 0) {
|
||||
assert(!is_windows); // resolvePosix called on windows can't use getCwd
|
||||
@ -550,7 +554,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
|
||||
errdefer allocator.free(result);
|
||||
|
||||
for (paths[first_index..]) |p, i| {
|
||||
var it = mem.split(p, "/");
|
||||
var it = mem.tokenize(p, "/");
|
||||
while (it.next()) |component| {
|
||||
if (mem.eql(u8, component, ".")) {
|
||||
continue;
|
||||
@ -937,8 +941,8 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8)
|
||||
return resolved_to;
|
||||
}
|
||||
|
||||
var from_it = mem.split(resolved_from, "/\\");
|
||||
var to_it = mem.split(resolved_to, "/\\");
|
||||
var from_it = mem.tokenize(resolved_from, "/\\");
|
||||
var to_it = mem.tokenize(resolved_to, "/\\");
|
||||
while (true) {
|
||||
const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
|
||||
const to_rest = to_it.rest();
|
||||
@ -967,7 +971,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8)
|
||||
// shave off the trailing slash
|
||||
result_index -= 1;
|
||||
|
||||
var rest_it = mem.split(to_rest, "/\\");
|
||||
var rest_it = mem.tokenize(to_rest, "/\\");
|
||||
while (rest_it.next()) |to_component| {
|
||||
result[result_index] = '\\';
|
||||
result_index += 1;
|
||||
@ -988,8 +992,8 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![
|
||||
const resolved_to = try resolvePosix(allocator, [][]const u8{to});
|
||||
defer allocator.free(resolved_to);
|
||||
|
||||
var from_it = mem.split(resolved_from, "/");
|
||||
var to_it = mem.split(resolved_to, "/");
|
||||
var from_it = mem.tokenize(resolved_from, "/");
|
||||
var to_it = mem.tokenize(resolved_to, "/");
|
||||
while (true) {
|
||||
const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
|
||||
const to_rest = to_it.rest();
|
||||
|
@ -40,6 +40,8 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void {
|
||||
}
|
||||
|
||||
test "std.os.Thread.getCurrentId" {
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var thread_current_id: os.Thread.Id = undefined;
|
||||
const thread = try os.spawnThread(&thread_current_id, testThreadIdFn);
|
||||
const thread_id = thread.handle();
|
||||
@ -53,6 +55,8 @@ test "std.os.Thread.getCurrentId" {
|
||||
}
|
||||
|
||||
test "spawn threads" {
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var shared_ctx: i32 = 1;
|
||||
|
||||
const thread1 = try std.os.spawnThread({}, start1);
|
||||
|
@ -79,14 +79,12 @@ fn milliTimestampWindows() u64 {
|
||||
}
|
||||
|
||||
fn milliTimestampDarwin() u64 {
|
||||
//Sources suggest MacOS 10.12 has support for
|
||||
// posix clock_gettime.
|
||||
var tv: darwin.timeval = undefined;
|
||||
var err = darwin.gettimeofday(&tv, null);
|
||||
debug.assert(err == 0);
|
||||
const sec_ms = @intCast(u64, tv.tv_sec) * ms_per_s;
|
||||
const usec_ms = @divFloor(@intCast(u64, tv.tv_usec), us_per_s / ms_per_s);
|
||||
return u64(sec_ms) + u64(usec_ms);
|
||||
const sec_ms = tv.tv_sec * ms_per_s;
|
||||
const usec_ms = @divFloor(tv.tv_usec, us_per_s / ms_per_s);
|
||||
return @intCast(u64, sec_ms + usec_ms);
|
||||
}
|
||||
|
||||
fn milliTimestampPosix() u64 {
|
||||
|
@ -49,6 +49,7 @@ pub const UNICODE = false;
|
||||
pub const WCHAR = u16;
|
||||
pub const WORD = u16;
|
||||
pub const LARGE_INTEGER = i64;
|
||||
pub const LONG = c_long;
|
||||
|
||||
pub const TRUE = 1;
|
||||
pub const FALSE = 0;
|
||||
|
@ -220,3 +220,50 @@ pub const FOREGROUND_BLUE = 1;
|
||||
pub const FOREGROUND_GREEN = 2;
|
||||
pub const FOREGROUND_RED = 4;
|
||||
pub const FOREGROUND_INTENSITY = 8;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
|
||||
pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
|
||||
pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
|
||||
pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
|
||||
|
||||
pub const LIST_ENTRY = extern struct {
|
||||
Flink: *LIST_ENTRY,
|
||||
Blink: *LIST_ENTRY,
|
||||
};
|
||||
|
||||
pub const RTL_CRITICAL_SECTION_DEBUG = extern struct {
|
||||
Type: WORD,
|
||||
CreatorBackTraceIndex: WORD,
|
||||
CriticalSection: *RTL_CRITICAL_SECTION,
|
||||
ProcessLocksList: LIST_ENTRY,
|
||||
EntryCount: DWORD,
|
||||
ContentionCount: DWORD,
|
||||
Flags: DWORD,
|
||||
CreatorBackTraceIndexHigh: WORD,
|
||||
SpareWORD: WORD,
|
||||
};
|
||||
|
||||
pub const RTL_CRITICAL_SECTION = extern struct {
|
||||
DebugInfo: *RTL_CRITICAL_SECTION_DEBUG,
|
||||
LockCount: LONG,
|
||||
RecursionCount: LONG,
|
||||
OwningThread: HANDLE,
|
||||
LockSemaphore: HANDLE,
|
||||
SpinCount: ULONG_PTR,
|
||||
};
|
||||
|
||||
pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION;
|
||||
pub const INIT_ONCE = RTL_RUN_ONCE;
|
||||
pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL;
|
||||
|
||||
pub const INIT_ONCE_FN = extern fn(InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL;
|
||||
|
||||
pub const RTL_RUN_ONCE = extern struct {
|
||||
Ptr: ?*c_void,
|
||||
};
|
||||
|
||||
pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE {
|
||||
.Ptr = null,
|
||||
};
|
||||
|
110
std/statically_initialized_mutex.zig
Normal file
110
std/statically_initialized_mutex.zig
Normal file
@ -0,0 +1,110 @@
|
||||
const std = @import("index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||
const assert = std.debug.assert;
|
||||
const windows = std.os.windows;
|
||||
|
||||
/// Lock may be held only once. If the same thread
|
||||
/// tries to acquire the same mutex twice, it deadlocks.
|
||||
/// This type is intended to be initialized statically. If you don't
|
||||
/// require static initialization, use std.Mutex.
|
||||
/// On Windows, this mutex allocates resources when it is
|
||||
/// first used, and the resources cannot be freed.
|
||||
/// On Linux, this is an alias of std.Mutex.
|
||||
pub const StaticallyInitializedMutex = switch(builtin.os) {
|
||||
builtin.Os.linux => std.Mutex,
|
||||
builtin.Os.windows => struct {
|
||||
lock: windows.CRITICAL_SECTION,
|
||||
init_once: windows.RTL_RUN_ONCE,
|
||||
|
||||
pub const Held = struct {
|
||||
mutex: *StaticallyInitializedMutex,
|
||||
|
||||
pub fn release(self: Held) void {
|
||||
windows.LeaveCriticalSection(&self.mutex.lock);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init() StaticallyInitializedMutex {
|
||||
return StaticallyInitializedMutex {
|
||||
.lock = undefined,
|
||||
.init_once = windows.INIT_ONCE_STATIC_INIT,
|
||||
};
|
||||
}
|
||||
|
||||
extern fn initCriticalSection(
|
||||
InitOnce: *windows.RTL_RUN_ONCE,
|
||||
Parameter: ?*c_void,
|
||||
Context: ?*c_void,
|
||||
) windows.BOOL {
|
||||
const lock = @ptrCast(*windows.CRITICAL_SECTION, @alignCast(@alignOf(windows.CRITICAL_SECTION), Parameter));
|
||||
windows.InitializeCriticalSection(lock);
|
||||
return windows.TRUE;
|
||||
}
|
||||
|
||||
/// TODO: once https://github.com/ziglang/zig/issues/287 is solved and std.Mutex has a better
|
||||
/// implementation of a runtime initialized mutex, remove this function.
|
||||
pub fn deinit(self: *StaticallyInitializedMutex) void {
|
||||
assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0);
|
||||
windows.DeleteCriticalSection(&self.lock);
|
||||
}
|
||||
|
||||
pub fn acquire(self: *StaticallyInitializedMutex) Held {
|
||||
assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0);
|
||||
windows.EnterCriticalSection(&self.lock);
|
||||
return Held { .mutex = self };
|
||||
}
|
||||
},
|
||||
else => std.Mutex,
|
||||
};
|
||||
|
||||
test "std.StaticallyInitializedMutex" {
|
||||
const TestContext = struct {
|
||||
data: i128,
|
||||
|
||||
const TestContext = @This();
|
||||
const incr_count = 10000;
|
||||
|
||||
var mutex = StaticallyInitializedMutex.init();
|
||||
|
||||
fn worker(ctx: *TestContext) void {
|
||||
var i: usize = 0;
|
||||
while (i != TestContext.incr_count) : (i += 1) {
|
||||
const held = mutex.acquire();
|
||||
defer held.release();
|
||||
|
||||
ctx.data += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var direct_allocator = std.heap.DirectAllocator.init();
|
||||
defer direct_allocator.deinit();
|
||||
|
||||
var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
|
||||
defer direct_allocator.allocator.free(plenty_of_memory);
|
||||
|
||||
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
|
||||
var a = &fixed_buffer_allocator.allocator;
|
||||
|
||||
|
||||
var context = TestContext{
|
||||
.data = 0,
|
||||
};
|
||||
|
||||
if (builtin.single_threaded) {
|
||||
TestContext.worker(&context);
|
||||
std.debug.assertOrPanic(context.data == TestContext.incr_count);
|
||||
} else {
|
||||
const thread_count = 10;
|
||||
var threads: [thread_count]*std.os.Thread = undefined;
|
||||
for (threads) |*t| {
|
||||
t.* = try std.os.spawnThread(&context, TestContext.worker);
|
||||
}
|
||||
for (threads) |t|
|
||||
t.wait();
|
||||
|
||||
std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,82 +0,0 @@
|
||||
const builtin = @import("builtin");
|
||||
|
||||
comptime {
|
||||
_ = @import("cases/align.zig");
|
||||
_ = @import("cases/alignof.zig");
|
||||
_ = @import("cases/array.zig");
|
||||
_ = @import("cases/asm.zig");
|
||||
_ = @import("cases/atomics.zig");
|
||||
_ = @import("cases/bitcast.zig");
|
||||
_ = @import("cases/bool.zig");
|
||||
_ = @import("cases/bswap.zig");
|
||||
_ = @import("cases/bitreverse.zig");
|
||||
_ = @import("cases/bugs/1076.zig");
|
||||
_ = @import("cases/bugs/1111.zig");
|
||||
_ = @import("cases/bugs/1277.zig");
|
||||
_ = @import("cases/bugs/1322.zig");
|
||||
_ = @import("cases/bugs/1381.zig");
|
||||
_ = @import("cases/bugs/1421.zig");
|
||||
_ = @import("cases/bugs/1442.zig");
|
||||
_ = @import("cases/bugs/1486.zig");
|
||||
_ = @import("cases/bugs/394.zig");
|
||||
_ = @import("cases/bugs/655.zig");
|
||||
_ = @import("cases/bugs/656.zig");
|
||||
_ = @import("cases/bugs/726.zig");
|
||||
_ = @import("cases/bugs/828.zig");
|
||||
_ = @import("cases/bugs/920.zig");
|
||||
_ = @import("cases/byval_arg_var.zig");
|
||||
_ = @import("cases/cancel.zig");
|
||||
_ = @import("cases/cast.zig");
|
||||
_ = @import("cases/const_slice_child.zig");
|
||||
_ = @import("cases/coroutine_await_struct.zig");
|
||||
_ = @import("cases/coroutines.zig");
|
||||
_ = @import("cases/defer.zig");
|
||||
_ = @import("cases/enum.zig");
|
||||
_ = @import("cases/enum_with_members.zig");
|
||||
_ = @import("cases/error.zig");
|
||||
_ = @import("cases/eval.zig");
|
||||
_ = @import("cases/field_parent_ptr.zig");
|
||||
_ = @import("cases/fn.zig");
|
||||
_ = @import("cases/fn_in_struct_in_comptime.zig");
|
||||
_ = @import("cases/for.zig");
|
||||
_ = @import("cases/generics.zig");
|
||||
_ = @import("cases/if.zig");
|
||||
_ = @import("cases/import.zig");
|
||||
_ = @import("cases/incomplete_struct_param_tld.zig");
|
||||
_ = @import("cases/inttoptr.zig");
|
||||
_ = @import("cases/ir_block_deps.zig");
|
||||
_ = @import("cases/math.zig");
|
||||
_ = @import("cases/merge_error_sets.zig");
|
||||
_ = @import("cases/misc.zig");
|
||||
_ = @import("cases/namespace_depends_on_compile_var/index.zig");
|
||||
_ = @import("cases/new_stack_call.zig");
|
||||
_ = @import("cases/null.zig");
|
||||
_ = @import("cases/optional.zig");
|
||||
_ = @import("cases/pointers.zig");
|
||||
_ = @import("cases/popcount.zig");
|
||||
_ = @import("cases/ptrcast.zig");
|
||||
_ = @import("cases/pub_enum/index.zig");
|
||||
_ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig");
|
||||
_ = @import("cases/reflection.zig");
|
||||
_ = @import("cases/sizeof_and_typeof.zig");
|
||||
_ = @import("cases/slice.zig");
|
||||
_ = @import("cases/struct.zig");
|
||||
_ = @import("cases/struct_contains_null_ptr_itself.zig");
|
||||
_ = @import("cases/struct_contains_slice_of_itself.zig");
|
||||
_ = @import("cases/switch.zig");
|
||||
_ = @import("cases/switch_prong_err_enum.zig");
|
||||
_ = @import("cases/switch_prong_implicit_cast.zig");
|
||||
_ = @import("cases/syntax.zig");
|
||||
_ = @import("cases/this.zig");
|
||||
_ = @import("cases/truncate.zig");
|
||||
_ = @import("cases/try.zig");
|
||||
_ = @import("cases/type_info.zig");
|
||||
_ = @import("cases/undefined.zig");
|
||||
_ = @import("cases/underscore.zig");
|
||||
_ = @import("cases/union.zig");
|
||||
_ = @import("cases/var_args.zig");
|
||||
_ = @import("cases/void.zig");
|
||||
_ = @import("cases/while.zig");
|
||||
_ = @import("cases/widening.zig");
|
||||
_ = @import("cases/bit_shifting.zig");
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const mem = @import("std").mem;
|
||||
|
||||
test "arrays" {
|
||||
var array: [5]u32 = undefined;
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < 5) {
|
||||
array[i] = i + 1;
|
||||
i = array[i];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
var accumulator = u32(0);
|
||||
while (i < 5) {
|
||||
accumulator += array[i];
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
assert(accumulator == 15);
|
||||
assert(getArrayLen(array) == 5);
|
||||
}
|
||||
fn getArrayLen(a: []const u32) usize {
|
||||
return a.len;
|
||||
}
|
||||
|
||||
test "void arrays" {
|
||||
var array: [4]void = undefined;
|
||||
array[0] = void{};
|
||||
array[1] = array[2];
|
||||
assert(@sizeOf(@typeOf(array)) == 0);
|
||||
assert(array.len == 4);
|
||||
}
|
||||
|
||||
test "array literal" {
|
||||
const hex_mult = []u16{
|
||||
4096,
|
||||
256,
|
||||
16,
|
||||
1,
|
||||
};
|
||||
|
||||
assert(hex_mult.len == 4);
|
||||
assert(hex_mult[1] == 256);
|
||||
}
|
||||
|
||||
test "array dot len const expr" {
|
||||
assert(comptime x: {
|
||||
break :x some_array.len == 4;
|
||||
});
|
||||
}
|
||||
|
||||
const ArrayDotLenConstExpr = struct {
|
||||
y: [some_array.len]u8,
|
||||
};
|
||||
const some_array = []u8{
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
};
|
||||
|
||||
test "nested arrays" {
|
||||
const array_of_strings = [][]const u8{
|
||||
"hello",
|
||||
"this",
|
||||
"is",
|
||||
"my",
|
||||
"thing",
|
||||
};
|
||||
for (array_of_strings) |s, i| {
|
||||
if (i == 0) assert(mem.eql(u8, s, "hello"));
|
||||
if (i == 1) assert(mem.eql(u8, s, "this"));
|
||||
if (i == 2) assert(mem.eql(u8, s, "is"));
|
||||
if (i == 3) assert(mem.eql(u8, s, "my"));
|
||||
if (i == 4) assert(mem.eql(u8, s, "thing"));
|
||||
}
|
||||
}
|
||||
|
||||
var s_array: [8]Sub = undefined;
|
||||
const Sub = struct {
|
||||
b: u8,
|
||||
};
|
||||
const Str = struct {
|
||||
a: []Sub,
|
||||
};
|
||||
test "set global var array via slice embedded in struct" {
|
||||
var s = Str{ .a = s_array[0..] };
|
||||
|
||||
s.a[0].b = 1;
|
||||
s.a[1].b = 2;
|
||||
s.a[2].b = 3;
|
||||
|
||||
assert(s_array[0].b == 1);
|
||||
assert(s_array[1].b == 2);
|
||||
assert(s_array[2].b == 3);
|
||||
}
|
||||
|
||||
test "array literal with specified size" {
|
||||
var array = [2]u8{
|
||||
1,
|
||||
2,
|
||||
};
|
||||
assert(array[0] == 1);
|
||||
assert(array[1] == 2);
|
||||
}
|
||||
|
||||
test "array child property" {
|
||||
var x: [5]i32 = undefined;
|
||||
assert(@typeOf(x).Child == i32);
|
||||
}
|
||||
|
||||
test "array len property" {
|
||||
var x: [5]i32 = undefined;
|
||||
assert(@typeOf(x).len == 5);
|
||||
}
|
||||
|
||||
test "array len field" {
|
||||
var arr = [4]u8{ 0, 0, 0, 0 };
|
||||
var ptr = &arr;
|
||||
assert(arr.len == 4);
|
||||
comptime assert(arr.len == 4);
|
||||
assert(ptr.len == 4);
|
||||
comptime assert(ptr.len == 4);
|
||||
}
|
||||
|
||||
test "single-item pointer to array indexing and slicing" {
|
||||
testSingleItemPtrArrayIndexSlice();
|
||||
comptime testSingleItemPtrArrayIndexSlice();
|
||||
}
|
||||
|
||||
fn testSingleItemPtrArrayIndexSlice() void {
|
||||
var array = "aaaa";
|
||||
doSomeMangling(&array);
|
||||
assert(mem.eql(u8, "azya", array));
|
||||
}
|
||||
|
||||
fn doSomeMangling(array: *[4]u8) void {
|
||||
array[1] = 'z';
|
||||
array[2..3][0] = 'y';
|
||||
}
|
||||
|
||||
test "implicit cast single-item pointer" {
|
||||
testImplicitCastSingleItemPtr();
|
||||
comptime testImplicitCastSingleItemPtr();
|
||||
}
|
||||
|
||||
fn testImplicitCastSingleItemPtr() void {
|
||||
var byte: u8 = 100;
|
||||
const slice = (*[1]u8)(&byte)[0..];
|
||||
slice[0] += 1;
|
||||
assert(byte == 101);
|
||||
}
|
||||
|
||||
fn testArrayByValAtComptime(b: [2]u8) u8 {
|
||||
return b[0];
|
||||
}
|
||||
|
||||
test "comptime evalutating function that takes array by value" {
|
||||
const arr = []u8{ 0, 1 };
|
||||
_ = comptime testArrayByValAtComptime(arr);
|
||||
_ = comptime testArrayByValAtComptime(arr);
|
||||
}
|
||||
|
||||
test "implicit comptime in array type size" {
|
||||
var arr: [plusOne(10)]bool = undefined;
|
||||
assert(arr.len == 11);
|
||||
}
|
||||
|
||||
fn plusOne(x: u32) u32 {
|
||||
return x + 1;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
const config = @import("builtin");
|
||||
const assert = @import("std").debug.assert;
|
||||
|
||||
comptime {
|
||||
if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) {
|
||||
asm volatile (
|
||||
\\.globl aoeu;
|
||||
\\.type aoeu, @function;
|
||||
\\.set aoeu, derp;
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test "module level assembly" {
|
||||
if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) {
|
||||
assert(aoeu() == 1234);
|
||||
}
|
||||
}
|
||||
|
||||
test "output constraint modifiers" {
|
||||
// This is only testing compilation.
|
||||
var a: u32 = 3;
|
||||
asm volatile ("" : [_]"=m,r"(a) : : "");
|
||||
asm volatile ("" : [_]"=r,m"(a) : : "");
|
||||
}
|
||||
|
||||
test "alternative constraints" {
|
||||
// Make sure we allow commas as a separator for alternative constraints.
|
||||
var a: u32 = 3;
|
||||
asm volatile ("" : [_]"=r,m"(a) : [_]"r,m"(a) : "");
|
||||
}
|
||||
|
||||
test "sized integer/float in asm input" {
|
||||
asm volatile ("" : : [_]"m"(usize(3)) : "");
|
||||
asm volatile ("" : : [_]"m"(i15(-3)) : "");
|
||||
asm volatile ("" : : [_]"m"(u3(3)) : "");
|
||||
asm volatile ("" : : [_]"m"(i3(3)) : "");
|
||||
asm volatile ("" : : [_]"m"(u121(3)) : "");
|
||||
asm volatile ("" : : [_]"m"(i121(3)) : "");
|
||||
asm volatile ("" : : [_]"m"(f32(3.17)) : "");
|
||||
asm volatile ("" : : [_]"m"(f64(3.17)) : "");
|
||||
}
|
||||
|
||||
extern fn aoeu() i32;
|
||||
|
||||
export fn derp() i32 {
|
||||
return 1234;
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const minInt = std.math.minInt;
|
||||
|
||||
test "@bitreverse" {
|
||||
comptime testBitReverse();
|
||||
testBitReverse();
|
||||
}
|
||||
|
||||
fn testBitReverse() void {
|
||||
// using comptime_ints, unsigned
|
||||
assert(@bitreverse(u0, 0) == 0);
|
||||
assert(@bitreverse(u5, 0x12) == 0x9);
|
||||
assert(@bitreverse(u8, 0x12) == 0x48);
|
||||
assert(@bitreverse(u16, 0x1234) == 0x2c48);
|
||||
assert(@bitreverse(u24, 0x123456) == 0x6a2c48);
|
||||
assert(@bitreverse(u32, 0x12345678) == 0x1e6a2c48);
|
||||
assert(@bitreverse(u40, 0x123456789a) == 0x591e6a2c48);
|
||||
assert(@bitreverse(u48, 0x123456789abc) == 0x3d591e6a2c48);
|
||||
assert(@bitreverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48);
|
||||
assert(@bitreverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48);
|
||||
assert(@bitreverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48);
|
||||
|
||||
// using runtime uints, unsigned
|
||||
var num0: u0 = 0;
|
||||
assert(@bitreverse(u0, num0) == 0);
|
||||
var num5: u5 = 0x12;
|
||||
assert(@bitreverse(u5, num5) == 0x9);
|
||||
var num8: u8 = 0x12;
|
||||
assert(@bitreverse(u8, num8) == 0x48);
|
||||
var num16: u16 = 0x1234;
|
||||
assert(@bitreverse(u16, num16) == 0x2c48);
|
||||
var num24: u24 = 0x123456;
|
||||
assert(@bitreverse(u24, num24) == 0x6a2c48);
|
||||
var num32: u32 = 0x12345678;
|
||||
assert(@bitreverse(u32, num32) == 0x1e6a2c48);
|
||||
var num40: u40 = 0x123456789a;
|
||||
assert(@bitreverse(u40, num40) == 0x591e6a2c48);
|
||||
var num48: u48 = 0x123456789abc;
|
||||
assert(@bitreverse(u48, num48) == 0x3d591e6a2c48);
|
||||
var num56: u56 = 0x123456789abcde;
|
||||
assert(@bitreverse(u56, num56) == 0x7b3d591e6a2c48);
|
||||
var num64: u64 = 0x123456789abcdef1;
|
||||
assert(@bitreverse(u64, num64) == 0x8f7b3d591e6a2c48);
|
||||
var num128: u128 = 0x123456789abcdef11121314151617181;
|
||||
assert(@bitreverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48);
|
||||
|
||||
// using comptime_ints, signed, positive
|
||||
assert(@bitreverse(i0, 0) == 0);
|
||||
assert(@bitreverse(i8, @bitCast(i8, u8(0x92))) == @bitCast(i8, u8( 0x49)));
|
||||
assert(@bitreverse(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16( 0x2c48)));
|
||||
assert(@bitreverse(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24( 0x6a2c48)));
|
||||
assert(@bitreverse(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32( 0x1e6a2c48)));
|
||||
assert(@bitreverse(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40( 0x591e6a2c48)));
|
||||
assert(@bitreverse(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48( 0x3d591e6a2c48)));
|
||||
assert(@bitreverse(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56( 0x7b3d591e6a2c48)));
|
||||
assert(@bitreverse(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64,u64(0x8f7b3d591e6a2c48)));
|
||||
assert(@bitreverse(i128, @bitCast(i128,u128(0x123456789abcdef11121314151617181))) == @bitCast(i128,u128(0x818e868a828c84888f7b3d591e6a2c48)));
|
||||
|
||||
// using comptime_ints, signed, negative. Compare to runtime ints returned from llvm.
|
||||
var neg5: i5 = minInt(i5) + 1;
|
||||
assert(@bitreverse(i5, minInt(i5) + 1) == @bitreverse(i5, neg5));
|
||||
var neg8: i8 = -18;
|
||||
assert(@bitreverse(i8, -18) == @bitreverse(i8, neg8));
|
||||
var neg16: i16 = -32694;
|
||||
assert(@bitreverse(i16, -32694) == @bitreverse(i16, neg16));
|
||||
var neg24: i24 = -6773785;
|
||||
assert(@bitreverse(i24, -6773785) == @bitreverse(i24, neg24));
|
||||
var neg32: i32 = -16773785;
|
||||
assert(@bitreverse(i32, -16773785) == @bitreverse(i32, neg32));
|
||||
var neg40: i40 = minInt(i40) + 12345;
|
||||
assert(@bitreverse(i40, minInt(i40) + 12345) == @bitreverse(i40, neg40));
|
||||
var neg48: i48 = minInt(i48) + 12345;
|
||||
assert(@bitreverse(i48, minInt(i48) + 12345) == @bitreverse(i48, neg48));
|
||||
var neg56: i56 = minInt(i56) + 12345;
|
||||
assert(@bitreverse(i56, minInt(i56) + 12345) == @bitreverse(i56, neg56));
|
||||
var neg64: i64 = minInt(i64) + 12345;
|
||||
assert(@bitreverse(i64, minInt(i64) + 12345) == @bitreverse(i64, neg64));
|
||||
var neg128: i128 = minInt(i128) + 12345;
|
||||
assert(@bitreverse(i128, minInt(i128) + 12345) == @bitreverse(i128, neg128));
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
test "@bswap" {
|
||||
comptime testByteSwap();
|
||||
testByteSwap();
|
||||
}
|
||||
|
||||
fn testByteSwap() void {
|
||||
assert(@bswap(u0, 0) == 0);
|
||||
assert(@bswap(u8, 0x12) == 0x12);
|
||||
assert(@bswap(u16, 0x1234) == 0x3412);
|
||||
assert(@bswap(u24, 0x123456) == 0x563412);
|
||||
assert(@bswap(u32, 0x12345678) == 0x78563412);
|
||||
assert(@bswap(u40, 0x123456789a) == 0x9a78563412);
|
||||
assert(@bswap(u48, 0x123456789abc) == 0xbc9a78563412);
|
||||
assert(@bswap(u56, 0x123456789abcde) == 0xdebc9a78563412);
|
||||
assert(@bswap(u64, 0x123456789abcdef1) == 0xf1debc9a78563412);
|
||||
assert(@bswap(u128, 0x123456789abcdef11121314151617181) == 0x8171615141312111f1debc9a78563412);
|
||||
|
||||
assert(@bswap(i0, 0) == 0);
|
||||
assert(@bswap(i8, -50) == -50);
|
||||
assert(@bswap(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x3412)));
|
||||
assert(@bswap(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x563412)));
|
||||
assert(@bswap(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x78563412)));
|
||||
assert(@bswap(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x9a78563412)));
|
||||
assert(@bswap(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0xbc9a78563412)));
|
||||
assert(@bswap(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0xdebc9a78563412)));
|
||||
assert(@bswap(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0xf1debc9a78563412)));
|
||||
assert(@bswap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) ==
|
||||
@bitCast(i128, u128(0x8171615141312111f1debc9a78563412)));
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const a_namespace = @import("import/a_namespace.zig");
|
||||
|
||||
test "call fn via namespace lookup" {
|
||||
assert(a_namespace.foo() == 1234);
|
||||
}
|
||||
|
||||
test "importing the same thing gives the same import" {
|
||||
assert(@import("std") == @import("std"));
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
|
||||
pub const EmptyStruct = struct {};
|
||||
|
||||
test "optional pointer to size zero struct" {
|
||||
var e = EmptyStruct{};
|
||||
var o: ?*EmptyStruct = &e;
|
||||
assert(o != null);
|
||||
}
|
||||
|
||||
test "equality compare nullable pointers" {
|
||||
testNullPtrsEql();
|
||||
comptime testNullPtrsEql();
|
||||
}
|
||||
|
||||
fn testNullPtrsEql() void {
|
||||
var number: i32 = 1234;
|
||||
|
||||
var x: ?*i32 = null;
|
||||
var y: ?*i32 = null;
|
||||
assert(x == y);
|
||||
y = &number;
|
||||
assert(x != y);
|
||||
assert(x != &number);
|
||||
assert(&number != x);
|
||||
x = &number;
|
||||
assert(x == y);
|
||||
assert(x == &number);
|
||||
assert(&number == x);
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
|
||||
test "@popCount" {
|
||||
comptime testPopCount();
|
||||
testPopCount();
|
||||
}
|
||||
|
||||
fn testPopCount() void {
|
||||
{
|
||||
var x: u32 = 0xaa;
|
||||
assert(@popCount(x) == 4);
|
||||
}
|
||||
{
|
||||
var x: u32 = 0xaaaaaaaa;
|
||||
assert(@popCount(x) == 16);
|
||||
}
|
||||
{
|
||||
var x: i16 = -1;
|
||||
assert(@popCount(x) == 16);
|
||||
}
|
||||
comptime {
|
||||
assert(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24);
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const mem = @import("std").mem;
|
||||
const reflection = @This();
|
||||
|
||||
test "reflection: array, pointer, optional, error union type child" {
|
||||
comptime {
|
||||
assert(([10]u8).Child == u8);
|
||||
assert((*u8).Child == u8);
|
||||
assert((anyerror!u8).Payload == u8);
|
||||
assert((?u8).Child == u8);
|
||||
}
|
||||
}
|
||||
|
||||
test "reflection: function return type, var args, and param types" {
|
||||
comptime {
|
||||
assert(@typeOf(dummy).ReturnType == i32);
|
||||
assert(!@typeOf(dummy).is_var_args);
|
||||
assert(@typeOf(dummy_varargs).is_var_args);
|
||||
assert(@typeOf(dummy).arg_count == 3);
|
||||
assert(@ArgType(@typeOf(dummy), 0) == bool);
|
||||
assert(@ArgType(@typeOf(dummy), 1) == i32);
|
||||
assert(@ArgType(@typeOf(dummy), 2) == f32);
|
||||
}
|
||||
}
|
||||
|
||||
fn dummy(a: bool, b: i32, c: f32) i32 {
|
||||
return 1234;
|
||||
}
|
||||
fn dummy_varargs(args: ...) void {}
|
||||
|
||||
test "reflection: struct member types and names" {
|
||||
comptime {
|
||||
assert(@memberCount(Foo) == 3);
|
||||
|
||||
assert(@memberType(Foo, 0) == i32);
|
||||
assert(@memberType(Foo, 1) == bool);
|
||||
assert(@memberType(Foo, 2) == void);
|
||||
|
||||
assert(mem.eql(u8, @memberName(Foo, 0), "one"));
|
||||
assert(mem.eql(u8, @memberName(Foo, 1), "two"));
|
||||
assert(mem.eql(u8, @memberName(Foo, 2), "three"));
|
||||
}
|
||||
}
|
||||
|
||||
test "reflection: enum member types and names" {
|
||||
comptime {
|
||||
assert(@memberCount(Bar) == 4);
|
||||
|
||||
assert(@memberType(Bar, 0) == void);
|
||||
assert(@memberType(Bar, 1) == i32);
|
||||
assert(@memberType(Bar, 2) == bool);
|
||||
assert(@memberType(Bar, 3) == f64);
|
||||
|
||||
assert(mem.eql(u8, @memberName(Bar, 0), "One"));
|
||||
assert(mem.eql(u8, @memberName(Bar, 1), "Two"));
|
||||
assert(mem.eql(u8, @memberName(Bar, 2), "Three"));
|
||||
assert(mem.eql(u8, @memberName(Bar, 3), "Four"));
|
||||
}
|
||||
}
|
||||
|
||||
test "reflection: @field" {
|
||||
var f = Foo{
|
||||
.one = 42,
|
||||
.two = true,
|
||||
.three = void{},
|
||||
};
|
||||
|
||||
assert(f.one == f.one);
|
||||
assert(@field(f, "o" ++ "ne") == f.one);
|
||||
assert(@field(f, "t" ++ "wo") == f.two);
|
||||
assert(@field(f, "th" ++ "ree") == f.three);
|
||||
assert(@field(Foo, "const" ++ "ant") == Foo.constant);
|
||||
assert(@field(Bar, "O" ++ "ne") == Bar.One);
|
||||
assert(@field(Bar, "T" ++ "wo") == Bar.Two);
|
||||
assert(@field(Bar, "Th" ++ "ree") == Bar.Three);
|
||||
assert(@field(Bar, "F" ++ "our") == Bar.Four);
|
||||
assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2));
|
||||
@field(f, "o" ++ "ne") = 4;
|
||||
assert(f.one == 4);
|
||||
}
|
||||
|
||||
const Foo = struct {
|
||||
const constant = 52;
|
||||
|
||||
one: i32,
|
||||
two: bool,
|
||||
three: void,
|
||||
};
|
||||
|
||||
const Bar = union(enum) {
|
||||
One: void,
|
||||
Two: i32,
|
||||
Three: bool,
|
||||
Four: f64,
|
||||
};
|
@ -1,69 +0,0 @@
|
||||
const builtin = @import("builtin");
|
||||
const assert = @import("std").debug.assert;
|
||||
|
||||
test "@sizeOf and @typeOf" {
|
||||
const y: @typeOf(x) = 120;
|
||||
assert(@sizeOf(@typeOf(y)) == 2);
|
||||
}
|
||||
const x: u16 = 13;
|
||||
const z: @typeOf(x) = 19;
|
||||
|
||||
const A = struct {
|
||||
a: u8,
|
||||
b: u32,
|
||||
c: u8,
|
||||
d: u3,
|
||||
e: u5,
|
||||
f: u16,
|
||||
g: u16,
|
||||
};
|
||||
|
||||
const P = packed struct {
|
||||
a: u8,
|
||||
b: u32,
|
||||
c: u8,
|
||||
d: u3,
|
||||
e: u5,
|
||||
f: u16,
|
||||
g: u16,
|
||||
};
|
||||
|
||||
test "@byteOffsetOf" {
|
||||
// Packed structs have fixed memory layout
|
||||
assert(@byteOffsetOf(P, "a") == 0);
|
||||
assert(@byteOffsetOf(P, "b") == 1);
|
||||
assert(@byteOffsetOf(P, "c") == 5);
|
||||
assert(@byteOffsetOf(P, "d") == 6);
|
||||
assert(@byteOffsetOf(P, "e") == 6);
|
||||
assert(@byteOffsetOf(P, "f") == 7);
|
||||
assert(@byteOffsetOf(P, "g") == 9);
|
||||
|
||||
// Normal struct fields can be moved/padded
|
||||
var a: A = undefined;
|
||||
assert(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a"));
|
||||
assert(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b"));
|
||||
assert(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c"));
|
||||
assert(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d"));
|
||||
assert(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e"));
|
||||
assert(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f"));
|
||||
assert(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g"));
|
||||
}
|
||||
|
||||
test "@bitOffsetOf" {
|
||||
// Packed structs have fixed memory layout
|
||||
assert(@bitOffsetOf(P, "a") == 0);
|
||||
assert(@bitOffsetOf(P, "b") == 8);
|
||||
assert(@bitOffsetOf(P, "c") == 40);
|
||||
assert(@bitOffsetOf(P, "d") == 48);
|
||||
assert(@bitOffsetOf(P, "e") == 51);
|
||||
assert(@bitOffsetOf(P, "f") == 56);
|
||||
assert(@bitOffsetOf(P, "g") == 72);
|
||||
|
||||
assert(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
|
||||
assert(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
|
||||
assert(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
|
||||
assert(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
|
||||
assert(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
|
||||
assert(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
|
||||
assert(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
|
||||
}
|
@ -1,264 +0,0 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const mem = @import("std").mem;
|
||||
const TypeInfo = @import("builtin").TypeInfo;
|
||||
const TypeId = @import("builtin").TypeId;
|
||||
|
||||
test "type info: tag type, void info" {
|
||||
testBasic();
|
||||
comptime testBasic();
|
||||
}
|
||||
|
||||
fn testBasic() void {
|
||||
assert(@TagType(TypeInfo) == TypeId);
|
||||
const void_info = @typeInfo(void);
|
||||
assert(TypeId(void_info) == TypeId.Void);
|
||||
assert(void_info.Void == {});
|
||||
}
|
||||
|
||||
test "type info: integer, floating point type info" {
|
||||
testIntFloat();
|
||||
comptime testIntFloat();
|
||||
}
|
||||
|
||||
fn testIntFloat() void {
|
||||
const u8_info = @typeInfo(u8);
|
||||
assert(TypeId(u8_info) == TypeId.Int);
|
||||
assert(!u8_info.Int.is_signed);
|
||||
assert(u8_info.Int.bits == 8);
|
||||
|
||||
const f64_info = @typeInfo(f64);
|
||||
assert(TypeId(f64_info) == TypeId.Float);
|
||||
assert(f64_info.Float.bits == 64);
|
||||
}
|
||||
|
||||
test "type info: pointer type info" {
|
||||
testPointer();
|
||||
comptime testPointer();
|
||||
}
|
||||
|
||||
fn testPointer() void {
|
||||
const u32_ptr_info = @typeInfo(*u32);
|
||||
assert(TypeId(u32_ptr_info) == TypeId.Pointer);
|
||||
assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One);
|
||||
assert(u32_ptr_info.Pointer.is_const == false);
|
||||
assert(u32_ptr_info.Pointer.is_volatile == false);
|
||||
assert(u32_ptr_info.Pointer.alignment == @alignOf(u32));
|
||||
assert(u32_ptr_info.Pointer.child == u32);
|
||||
}
|
||||
|
||||
test "type info: unknown length pointer type info" {
|
||||
testUnknownLenPtr();
|
||||
comptime testUnknownLenPtr();
|
||||
}
|
||||
|
||||
fn testUnknownLenPtr() void {
|
||||
const u32_ptr_info = @typeInfo([*]const volatile f64);
|
||||
assert(TypeId(u32_ptr_info) == TypeId.Pointer);
|
||||
assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
|
||||
assert(u32_ptr_info.Pointer.is_const == true);
|
||||
assert(u32_ptr_info.Pointer.is_volatile == true);
|
||||
assert(u32_ptr_info.Pointer.alignment == @alignOf(f64));
|
||||
assert(u32_ptr_info.Pointer.child == f64);
|
||||
}
|
||||
|
||||
test "type info: slice type info" {
|
||||
testSlice();
|
||||
comptime testSlice();
|
||||
}
|
||||
|
||||
fn testSlice() void {
|
||||
const u32_slice_info = @typeInfo([]u32);
|
||||
assert(TypeId(u32_slice_info) == TypeId.Pointer);
|
||||
assert(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice);
|
||||
assert(u32_slice_info.Pointer.is_const == false);
|
||||
assert(u32_slice_info.Pointer.is_volatile == false);
|
||||
assert(u32_slice_info.Pointer.alignment == 4);
|
||||
assert(u32_slice_info.Pointer.child == u32);
|
||||
}
|
||||
|
||||
test "type info: array type info" {
|
||||
testArray();
|
||||
comptime testArray();
|
||||
}
|
||||
|
||||
fn testArray() void {
|
||||
const arr_info = @typeInfo([42]bool);
|
||||
assert(TypeId(arr_info) == TypeId.Array);
|
||||
assert(arr_info.Array.len == 42);
|
||||
assert(arr_info.Array.child == bool);
|
||||
}
|
||||
|
||||
test "type info: optional type info" {
|
||||
testOptional();
|
||||
comptime testOptional();
|
||||
}
|
||||
|
||||
fn testOptional() void {
|
||||
const null_info = @typeInfo(?void);
|
||||
assert(TypeId(null_info) == TypeId.Optional);
|
||||
assert(null_info.Optional.child == void);
|
||||
}
|
||||
|
||||
test "type info: promise info" {
|
||||
testPromise();
|
||||
comptime testPromise();
|
||||
}
|
||||
|
||||
fn testPromise() void {
|
||||
const null_promise_info = @typeInfo(promise);
|
||||
assert(TypeId(null_promise_info) == TypeId.Promise);
|
||||
assert(null_promise_info.Promise.child == null);
|
||||
|
||||
const promise_info = @typeInfo(promise->usize);
|
||||
assert(TypeId(promise_info) == TypeId.Promise);
|
||||
assert(promise_info.Promise.child.? == usize);
|
||||
}
|
||||
|
||||
test "type info: error set, error union info" {
|
||||
testErrorSet();
|
||||
comptime testErrorSet();
|
||||
}
|
||||
|
||||
fn testErrorSet() void {
|
||||
const TestErrorSet = error{
|
||||
First,
|
||||
Second,
|
||||
Third,
|
||||
};
|
||||
|
||||
const error_set_info = @typeInfo(TestErrorSet);
|
||||
assert(TypeId(error_set_info) == TypeId.ErrorSet);
|
||||
assert(error_set_info.ErrorSet.errors.len == 3);
|
||||
assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First"));
|
||||
assert(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third));
|
||||
|
||||
const error_union_info = @typeInfo(TestErrorSet!usize);
|
||||
assert(TypeId(error_union_info) == TypeId.ErrorUnion);
|
||||
assert(error_union_info.ErrorUnion.error_set == TestErrorSet);
|
||||
assert(error_union_info.ErrorUnion.payload == usize);
|
||||
}
|
||||
|
||||
test "type info: enum info" {
|
||||
testEnum();
|
||||
comptime testEnum();
|
||||
}
|
||||
|
||||
fn testEnum() void {
|
||||
const Os = enum {
|
||||
Windows,
|
||||
Macos,
|
||||
Linux,
|
||||
FreeBSD,
|
||||
};
|
||||
|
||||
const os_info = @typeInfo(Os);
|
||||
assert(TypeId(os_info) == TypeId.Enum);
|
||||
assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto);
|
||||
assert(os_info.Enum.fields.len == 4);
|
||||
assert(mem.eql(u8, os_info.Enum.fields[1].name, "Macos"));
|
||||
assert(os_info.Enum.fields[3].value == 3);
|
||||
assert(os_info.Enum.tag_type == u2);
|
||||
assert(os_info.Enum.defs.len == 0);
|
||||
}
|
||||
|
||||
test "type info: union info" {
|
||||
testUnion();
|
||||
comptime testUnion();
|
||||
}
|
||||
|
||||
fn testUnion() void {
|
||||
const typeinfo_info = @typeInfo(TypeInfo);
|
||||
assert(TypeId(typeinfo_info) == TypeId.Union);
|
||||
assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);
|
||||
assert(typeinfo_info.Union.tag_type.? == TypeId);
|
||||
assert(typeinfo_info.Union.fields.len == 24);
|
||||
assert(typeinfo_info.Union.fields[4].enum_field != null);
|
||||
assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
|
||||
assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));
|
||||
assert(typeinfo_info.Union.defs.len == 20);
|
||||
|
||||
const TestNoTagUnion = union {
|
||||
Foo: void,
|
||||
Bar: u32,
|
||||
};
|
||||
|
||||
const notag_union_info = @typeInfo(TestNoTagUnion);
|
||||
assert(TypeId(notag_union_info) == TypeId.Union);
|
||||
assert(notag_union_info.Union.tag_type == null);
|
||||
assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto);
|
||||
assert(notag_union_info.Union.fields.len == 2);
|
||||
assert(notag_union_info.Union.fields[0].enum_field == null);
|
||||
assert(notag_union_info.Union.fields[1].field_type == u32);
|
||||
|
||||
const TestExternUnion = extern union {
|
||||
foo: *c_void,
|
||||
};
|
||||
|
||||
const extern_union_info = @typeInfo(TestExternUnion);
|
||||
assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern);
|
||||
assert(extern_union_info.Union.tag_type == null);
|
||||
assert(extern_union_info.Union.fields[0].enum_field == null);
|
||||
assert(extern_union_info.Union.fields[0].field_type == *c_void);
|
||||
}
|
||||
|
||||
test "type info: struct info" {
|
||||
testStruct();
|
||||
comptime testStruct();
|
||||
}
|
||||
|
||||
fn testStruct() void {
|
||||
const struct_info = @typeInfo(TestStruct);
|
||||
assert(TypeId(struct_info) == TypeId.Struct);
|
||||
assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed);
|
||||
assert(struct_info.Struct.fields.len == 3);
|
||||
assert(struct_info.Struct.fields[1].offset == null);
|
||||
assert(struct_info.Struct.fields[2].field_type == *TestStruct);
|
||||
assert(struct_info.Struct.defs.len == 2);
|
||||
assert(struct_info.Struct.defs[0].is_pub);
|
||||
assert(!struct_info.Struct.defs[0].data.Fn.is_extern);
|
||||
assert(struct_info.Struct.defs[0].data.Fn.lib_name == null);
|
||||
assert(struct_info.Struct.defs[0].data.Fn.return_type == void);
|
||||
assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void);
|
||||
}
|
||||
|
||||
const TestStruct = packed struct {
|
||||
const Self = @This();
|
||||
|
||||
fieldA: usize,
|
||||
fieldB: void,
|
||||
fieldC: *Self,
|
||||
|
||||
pub fn foo(self: *const Self) void {}
|
||||
};
|
||||
|
||||
test "type info: function type info" {
|
||||
testFunction();
|
||||
comptime testFunction();
|
||||
}
|
||||
|
||||
fn testFunction() void {
|
||||
const fn_info = @typeInfo(@typeOf(foo));
|
||||
assert(TypeId(fn_info) == TypeId.Fn);
|
||||
assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified);
|
||||
assert(fn_info.Fn.is_generic);
|
||||
assert(fn_info.Fn.args.len == 2);
|
||||
assert(fn_info.Fn.is_var_args);
|
||||
assert(fn_info.Fn.return_type == null);
|
||||
assert(fn_info.Fn.async_allocator_type == null);
|
||||
|
||||
const test_instance: TestStruct = undefined;
|
||||
const bound_fn_info = @typeInfo(@typeOf(test_instance.foo));
|
||||
assert(TypeId(bound_fn_info) == TypeId.BoundFn);
|
||||
assert(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct);
|
||||
}
|
||||
|
||||
fn foo(comptime a: usize, b: bool, args: ...) usize {
|
||||
return 0;
|
||||
}
|
||||
|
||||
test "typeInfo with comptime parameter in struct fn def" {
|
||||
const S = struct {
|
||||
pub fn func(comptime x: f32) void {}
|
||||
};
|
||||
comptime var info = @typeInfo(S);
|
||||
}
|
@ -1,6 +1,40 @@
|
||||
const tests = @import("tests.zig");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.add(
|
||||
"@bitCast same size but bit count mismatch",
|
||||
\\export fn entry(byte: u8) void {
|
||||
\\ var oops = @bitCast(u7, byte);
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:2:16: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"attempted `&&`",
|
||||
\\export fn entry(a: bool, b: bool) i32 {
|
||||
\\ if (a && b) {
|
||||
\\ return 1234;
|
||||
\\ }
|
||||
\\ return 5678;
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:2:12: error: `&&` is invalid. Note that `and` is boolean AND.",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"attempted `||` on boolean values",
|
||||
\\export fn entry(a: bool, b: bool) i32 {
|
||||
\\ if (a || b) {
|
||||
\\ return 1234;
|
||||
\\ }
|
||||
\\ return 5678;
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:2:9: error: expected error set type, found 'bool'",
|
||||
".tmp_source.zig:2:11: note: `||` merges error sets; `or` performs boolean OR",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"compile log a pointer to an opaque value",
|
||||
\\export fn entry() void {
|
||||
@ -267,8 +301,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\ const Errors = error{} || u16;
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:2:20: error: expected error set type, found 'u8'",
|
||||
".tmp_source.zig:5:31: error: expected error set type, found 'u16'",
|
||||
".tmp_source.zig:2:20: error: expected error set type, found type 'u8'",
|
||||
".tmp_source.zig:2:23: note: `||` merges error sets; `or` performs boolean OR",
|
||||
".tmp_source.zig:5:31: error: expected error set type, found type 'u16'",
|
||||
".tmp_source.zig:5:28: note: `||` merges error sets; `or` performs boolean OR",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
@ -2618,7 +2654,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\
|
||||
\\export fn entry() usize { return @sizeOf(@typeOf(foo)); }
|
||||
,
|
||||
".tmp_source.zig:1:13: error: newline not allowed in string literal",
|
||||
".tmp_source.zig:1:15: error: newline not allowed in string literal",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
@ -3193,7 +3229,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\ return 2;
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:2:15: error: unable to infer expression type",
|
||||
".tmp_source.zig:2:15: error: values of type 'comptime_int' must be comptime known",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
@ -3539,7 +3575,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\
|
||||
\\export fn entry() usize { return @sizeOf(@typeOf(a)); }
|
||||
,
|
||||
".tmp_source.zig:2:11: error: expected type, found 'i32'",
|
||||
".tmp_source.zig:2:11: error: expected type 'type', found 'i32'",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
@ -5331,4 +5367,32 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
,
|
||||
".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"runtime assignment to comptime struct type",
|
||||
\\const Foo = struct {
|
||||
\\ Bar: u8,
|
||||
\\ Baz: type,
|
||||
\\};
|
||||
\\export fn f() void {
|
||||
\\ var x: u8 = 0;
|
||||
\\ const foo = Foo { .Bar = x, .Baz = u8 };
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:7:30: error: unable to evaluate constant expression",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"runtime assignment to comptime union type",
|
||||
\\const Foo = union {
|
||||
\\ Bar: u8,
|
||||
\\ Baz: type,
|
||||
\\};
|
||||
\\export fn f() void {
|
||||
\\ var x: u8 = 0;
|
||||
\\ const foo = Foo { .Bar = x };
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:7:30: error: unable to evaluate constant expression",
|
||||
);
|
||||
}
|
||||
|
81
test/stage1/behavior.zig
Normal file
81
test/stage1/behavior.zig
Normal file
@ -0,0 +1,81 @@
|
||||
comptime {
|
||||
_ = @import("behavior/align.zig");
|
||||
_ = @import("behavior/alignof.zig");
|
||||
_ = @import("behavior/array.zig");
|
||||
_ = @import("behavior/asm.zig");
|
||||
_ = @import("behavior/atomics.zig");
|
||||
_ = @import("behavior/bit_shifting.zig");
|
||||
_ = @import("behavior/bitcast.zig");
|
||||
_ = @import("behavior/bitreverse.zig");
|
||||
_ = @import("behavior/bool.zig");
|
||||
_ = @import("behavior/bswap.zig");
|
||||
_ = @import("behavior/bugs/1076.zig");
|
||||
_ = @import("behavior/bugs/1111.zig");
|
||||
_ = @import("behavior/bugs/1277.zig");
|
||||
_ = @import("behavior/bugs/1322.zig");
|
||||
_ = @import("behavior/bugs/1381.zig");
|
||||
_ = @import("behavior/bugs/1421.zig");
|
||||
_ = @import("behavior/bugs/1442.zig");
|
||||
_ = @import("behavior/bugs/1486.zig");
|
||||
_ = @import("behavior/bugs/394.zig");
|
||||
_ = @import("behavior/bugs/655.zig");
|
||||
_ = @import("behavior/bugs/656.zig");
|
||||
_ = @import("behavior/bugs/726.zig");
|
||||
_ = @import("behavior/bugs/828.zig");
|
||||
_ = @import("behavior/bugs/920.zig");
|
||||
_ = @import("behavior/byval_arg_var.zig");
|
||||
_ = @import("behavior/cancel.zig");
|
||||
_ = @import("behavior/cast.zig");
|
||||
_ = @import("behavior/const_slice_child.zig");
|
||||
_ = @import("behavior/coroutine_await_struct.zig");
|
||||
_ = @import("behavior/coroutines.zig");
|
||||
_ = @import("behavior/defer.zig");
|
||||
_ = @import("behavior/enum.zig");
|
||||
_ = @import("behavior/enum_with_members.zig");
|
||||
_ = @import("behavior/error.zig");
|
||||
_ = @import("behavior/eval.zig");
|
||||
_ = @import("behavior/field_parent_ptr.zig");
|
||||
_ = @import("behavior/fn.zig");
|
||||
_ = @import("behavior/fn_in_struct_in_comptime.zig");
|
||||
_ = @import("behavior/for.zig");
|
||||
_ = @import("behavior/generics.zig");
|
||||
_ = @import("behavior/if.zig");
|
||||
_ = @import("behavior/import.zig");
|
||||
_ = @import("behavior/incomplete_struct_param_tld.zig");
|
||||
_ = @import("behavior/inttoptr.zig");
|
||||
_ = @import("behavior/ir_block_deps.zig");
|
||||
_ = @import("behavior/math.zig");
|
||||
_ = @import("behavior/merge_error_sets.zig");
|
||||
_ = @import("behavior/misc.zig");
|
||||
_ = @import("behavior/namespace_depends_on_compile_var/index.zig");
|
||||
_ = @import("behavior/new_stack_call.zig");
|
||||
_ = @import("behavior/null.zig");
|
||||
_ = @import("behavior/optional.zig");
|
||||
_ = @import("behavior/pointers.zig");
|
||||
_ = @import("behavior/popcount.zig");
|
||||
_ = @import("behavior/ptrcast.zig");
|
||||
_ = @import("behavior/pub_enum/index.zig");
|
||||
_ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
|
||||
_ = @import("behavior/reflection.zig");
|
||||
_ = @import("behavior/sizeof_and_typeof.zig");
|
||||
_ = @import("behavior/slice.zig");
|
||||
_ = @import("behavior/struct.zig");
|
||||
_ = @import("behavior/struct_contains_null_ptr_itself.zig");
|
||||
_ = @import("behavior/struct_contains_slice_of_itself.zig");
|
||||
_ = @import("behavior/switch.zig");
|
||||
_ = @import("behavior/switch_prong_err_enum.zig");
|
||||
_ = @import("behavior/switch_prong_implicit_cast.zig");
|
||||
_ = @import("behavior/syntax.zig");
|
||||
_ = @import("behavior/this.zig");
|
||||
_ = @import("behavior/truncate.zig");
|
||||
_ = @import("behavior/try.zig");
|
||||
_ = @import("behavior/type_info.zig");
|
||||
_ = @import("behavior/undefined.zig");
|
||||
_ = @import("behavior/underscore.zig");
|
||||
_ = @import("behavior/union.zig");
|
||||
_ = @import("behavior/var_args.zig");
|
||||
_ = @import("behavior/vector.zig");
|
||||
_ = @import("behavior/void.zig");
|
||||
_ = @import("behavior/while.zig");
|
||||
_ = @import("behavior/widening.zig");
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const assertOrPanic = @import("std").debug.assertOrPanic;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
var foo: u8 align(4) = 100;
|
||||
|
||||
test "global variable alignment" {
|
||||
assert(@typeOf(&foo).alignment == 4);
|
||||
assert(@typeOf(&foo) == *align(4) u8);
|
||||
assertOrPanic(@typeOf(&foo).alignment == 4);
|
||||
assertOrPanic(@typeOf(&foo) == *align(4) u8);
|
||||
const slice = (*[1]u8)(&foo)[0..];
|
||||
assert(@typeOf(slice) == []align(4) u8);
|
||||
assertOrPanic(@typeOf(slice) == []align(4) u8);
|
||||
}
|
||||
|
||||
fn derp() align(@sizeOf(usize) * 2) i32 {
|
||||
@ -17,9 +17,9 @@ fn noop1() align(1) void {}
|
||||
fn noop4() align(4) void {}
|
||||
|
||||
test "function alignment" {
|
||||
assert(derp() == 1234);
|
||||
assert(@typeOf(noop1) == fn () align(1) void);
|
||||
assert(@typeOf(noop4) == fn () align(4) void);
|
||||
assertOrPanic(derp() == 1234);
|
||||
assertOrPanic(@typeOf(noop1) == fn () align(1) void);
|
||||
assertOrPanic(@typeOf(noop4) == fn () align(4) void);
|
||||
noop1();
|
||||
noop4();
|
||||
}
|
||||
@ -30,7 +30,7 @@ var baz: packed struct {
|
||||
} = undefined;
|
||||
|
||||
test "packed struct alignment" {
|
||||
assert(@typeOf(&baz.b) == *align(1) u32);
|
||||
assertOrPanic(@typeOf(&baz.b) == *align(1) u32);
|
||||
}
|
||||
|
||||
const blah: packed struct {
|
||||
@ -40,17 +40,17 @@ const blah: packed struct {
|
||||
} = undefined;
|
||||
|
||||
test "bit field alignment" {
|
||||
assert(@typeOf(&blah.b) == *align(1:3:1) const u3);
|
||||
assertOrPanic(@typeOf(&blah.b) == *align(1:3:1) const u3);
|
||||
}
|
||||
|
||||
test "default alignment allows unspecified in type syntax" {
|
||||
assert(*u32 == *align(@alignOf(u32)) u32);
|
||||
assertOrPanic(*u32 == *align(@alignOf(u32)) u32);
|
||||
}
|
||||
|
||||
test "implicitly decreasing pointer alignment" {
|
||||
const a: u32 align(4) = 3;
|
||||
const b: u32 align(8) = 4;
|
||||
assert(addUnaligned(&a, &b) == 7);
|
||||
assertOrPanic(addUnaligned(&a, &b) == 7);
|
||||
}
|
||||
|
||||
fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 {
|
||||
@ -60,7 +60,7 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 {
|
||||
test "implicitly decreasing slice alignment" {
|
||||
const a: u32 align(4) = 3;
|
||||
const b: u32 align(8) = 4;
|
||||
assert(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7);
|
||||
assertOrPanic(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7);
|
||||
}
|
||||
fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
|
||||
return a[0] + b[0];
|
||||
@ -77,7 +77,7 @@ fn testBytesAlign(b: u8) void {
|
||||
b,
|
||||
};
|
||||
const ptr = @ptrCast(*u32, &bytes[0]);
|
||||
assert(ptr.* == 0x33333333);
|
||||
assertOrPanic(ptr.* == 0x33333333);
|
||||
}
|
||||
|
||||
test "specifying alignment allows slice cast" {
|
||||
@ -91,13 +91,13 @@ fn testBytesAlignSlice(b: u8) void {
|
||||
b,
|
||||
};
|
||||
const slice: []u32 = @bytesToSlice(u32, bytes[0..]);
|
||||
assert(slice[0] == 0x33333333);
|
||||
assertOrPanic(slice[0] == 0x33333333);
|
||||
}
|
||||
|
||||
test "@alignCast pointers" {
|
||||
var x: u32 align(4) = 1;
|
||||
expectsOnly1(&x);
|
||||
assert(x == 2);
|
||||
assertOrPanic(x == 2);
|
||||
}
|
||||
fn expectsOnly1(x: *align(1) u32) void {
|
||||
expects4(@alignCast(4, x));
|
||||
@ -113,7 +113,7 @@ test "@alignCast slices" {
|
||||
};
|
||||
const slice = array[0..];
|
||||
sliceExpectsOnly1(slice);
|
||||
assert(slice[0] == 2);
|
||||
assertOrPanic(slice[0] == 2);
|
||||
}
|
||||
fn sliceExpectsOnly1(slice: []align(1) u32) void {
|
||||
sliceExpects4(@alignCast(4, slice));
|
||||
@ -128,7 +128,7 @@ test "implicitly decreasing fn alignment" {
|
||||
}
|
||||
|
||||
fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void {
|
||||
assert(ptr() == answer);
|
||||
assertOrPanic(ptr() == answer);
|
||||
}
|
||||
|
||||
fn alignedSmall() align(8) i32 {
|
||||
@ -139,7 +139,7 @@ fn alignedBig() align(16) i32 {
|
||||
}
|
||||
|
||||
test "@alignCast functions" {
|
||||
assert(fnExpectsOnly1(simple4) == 0x19);
|
||||
assertOrPanic(fnExpectsOnly1(simple4) == 0x19);
|
||||
}
|
||||
fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 {
|
||||
return fnExpects4(@alignCast(4, ptr));
|
||||
@ -152,9 +152,9 @@ fn simple4() align(4) i32 {
|
||||
}
|
||||
|
||||
test "generic function with align param" {
|
||||
assert(whyWouldYouEverDoThis(1) == 0x1);
|
||||
assert(whyWouldYouEverDoThis(4) == 0x1);
|
||||
assert(whyWouldYouEverDoThis(8) == 0x1);
|
||||
assertOrPanic(whyWouldYouEverDoThis(1) == 0x1);
|
||||
assertOrPanic(whyWouldYouEverDoThis(4) == 0x1);
|
||||
assertOrPanic(whyWouldYouEverDoThis(8) == 0x1);
|
||||
}
|
||||
|
||||
fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {
|
||||
@ -164,28 +164,28 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {
|
||||
test "@ptrCast preserves alignment of bigger source" {
|
||||
var x: u32 align(16) = 1234;
|
||||
const ptr = @ptrCast(*u8, &x);
|
||||
assert(@typeOf(ptr) == *align(16) u8);
|
||||
assertOrPanic(@typeOf(ptr) == *align(16) u8);
|
||||
}
|
||||
|
||||
test "runtime known array index has best alignment possible" {
|
||||
// take full advantage of over-alignment
|
||||
var array align(4) = []u8{ 1, 2, 3, 4 };
|
||||
assert(@typeOf(&array[0]) == *align(4) u8);
|
||||
assert(@typeOf(&array[1]) == *u8);
|
||||
assert(@typeOf(&array[2]) == *align(2) u8);
|
||||
assert(@typeOf(&array[3]) == *u8);
|
||||
assertOrPanic(@typeOf(&array[0]) == *align(4) u8);
|
||||
assertOrPanic(@typeOf(&array[1]) == *u8);
|
||||
assertOrPanic(@typeOf(&array[2]) == *align(2) u8);
|
||||
assertOrPanic(@typeOf(&array[3]) == *u8);
|
||||
|
||||
// because align is too small but we still figure out to use 2
|
||||
var bigger align(2) = []u64{ 1, 2, 3, 4 };
|
||||
assert(@typeOf(&bigger[0]) == *align(2) u64);
|
||||
assert(@typeOf(&bigger[1]) == *align(2) u64);
|
||||
assert(@typeOf(&bigger[2]) == *align(2) u64);
|
||||
assert(@typeOf(&bigger[3]) == *align(2) u64);
|
||||
assertOrPanic(@typeOf(&bigger[0]) == *align(2) u64);
|
||||
assertOrPanic(@typeOf(&bigger[1]) == *align(2) u64);
|
||||
assertOrPanic(@typeOf(&bigger[2]) == *align(2) u64);
|
||||
assertOrPanic(@typeOf(&bigger[3]) == *align(2) u64);
|
||||
|
||||
// because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
|
||||
var smaller align(2) = []u32{ 1, 2, 3, 4 };
|
||||
comptime assert(@typeOf(smaller[0..]) == []align(2) u32);
|
||||
comptime assert(@typeOf(smaller[0..].ptr) == [*]align(2) u32);
|
||||
comptime assertOrPanic(@typeOf(smaller[0..]) == []align(2) u32);
|
||||
comptime assertOrPanic(@typeOf(smaller[0..].ptr) == [*]align(2) u32);
|
||||
testIndex(smaller[0..].ptr, 0, *align(2) u32);
|
||||
testIndex(smaller[0..].ptr, 1, *align(2) u32);
|
||||
testIndex(smaller[0..].ptr, 2, *align(2) u32);
|
||||
@ -198,14 +198,14 @@ test "runtime known array index has best alignment possible" {
|
||||
testIndex2(array[0..].ptr, 3, *u8);
|
||||
}
|
||||
fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void {
|
||||
comptime assert(@typeOf(&smaller[index]) == T);
|
||||
comptime assertOrPanic(@typeOf(&smaller[index]) == T);
|
||||
}
|
||||
fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void {
|
||||
comptime assert(@typeOf(&ptr[index]) == T);
|
||||
comptime assertOrPanic(@typeOf(&ptr[index]) == T);
|
||||
}
|
||||
|
||||
test "alignstack" {
|
||||
assert(fnWithAlignedStack() == 1234);
|
||||
assertOrPanic(fnWithAlignedStack() == 1234);
|
||||
}
|
||||
|
||||
fn fnWithAlignedStack() i32 {
|
||||
@ -214,7 +214,7 @@ fn fnWithAlignedStack() i32 {
|
||||
}
|
||||
|
||||
test "alignment of structs" {
|
||||
assert(@alignOf(struct {
|
||||
assertOrPanic(@alignOf(struct {
|
||||
a: i32,
|
||||
b: *i32,
|
||||
}) == @alignOf(usize));
|
@ -1,5 +1,5 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const assertOrPanic = std.debug.assertOrPanic;
|
||||
const builtin = @import("builtin");
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
@ -10,8 +10,9 @@ const Foo = struct {
|
||||
};
|
||||
|
||||
test "@alignOf(T) before referencing T" {
|
||||
comptime assert(@alignOf(Foo) != maxInt(usize));
|
||||
comptime assertOrPanic(@alignOf(Foo) != maxInt(usize));
|
||||
if (builtin.arch == builtin.Arch.x86_64) {
|
||||
comptime assert(@alignOf(Foo) == 4);
|
||||
comptime assertOrPanic(@alignOf(Foo) == 4);
|
||||
}
|
||||
}
|
||||
|
270
test/stage1/behavior/array.zig
Normal file
270
test/stage1/behavior/array.zig
Normal file
@ -0,0 +1,270 @@
|
||||
const assertOrPanic = @import("std").debug.assertOrPanic;
|
||||
const mem = @import("std").mem;
|
||||
|
||||
test "arrays" {
|
||||
var array: [5]u32 = undefined;
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < 5) {
|
||||
array[i] = i + 1;
|
||||
i = array[i];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
var accumulator = u32(0);
|
||||
while (i < 5) {
|
||||
accumulator += array[i];
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
assertOrPanic(accumulator == 15);
|
||||
assertOrPanic(getArrayLen(array) == 5);
|
||||
}
|
||||
fn getArrayLen(a: []const u32) usize {
|
||||
return a.len;
|
||||
}
|
||||
|
||||
test "void arrays" {
|
||||
var array: [4]void = undefined;
|
||||
array[0] = void{};
|
||||
array[1] = array[2];
|
||||
assertOrPanic(@sizeOf(@typeOf(array)) == 0);
|
||||
assertOrPanic(array.len == 4);
|
||||
}
|
||||
|
||||
test "array literal" {
|
||||
const hex_mult = []u16{
|
||||
4096,
|
||||
256,
|
||||
16,
|
||||
1,
|
||||
};
|
||||
|
||||
assertOrPanic(hex_mult.len == 4);
|
||||
assertOrPanic(hex_mult[1] == 256);
|
||||
}
|
||||
|
||||
test "array dot len const expr" {
|
||||
assertOrPanic(comptime x: {
|
||||
break :x some_array.len == 4;
|
||||
});
|
||||
}
|
||||
|
||||
const ArrayDotLenConstExpr = struct {
|
||||
y: [some_array.len]u8,
|
||||
};
|
||||
const some_array = []u8{
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
};
|
||||
|
||||
test "nested arrays" {
|
||||
const array_of_strings = [][]const u8{
|
||||
"hello",
|
||||
"this",
|
||||
"is",
|
||||
"my",
|
||||
"thing",
|
||||
};
|
||||
for (array_of_strings) |s, i| {
|
||||
if (i == 0) assertOrPanic(mem.eql(u8, s, "hello"));
|
||||
if (i == 1) assertOrPanic(mem.eql(u8, s, "this"));
|
||||
if (i == 2) assertOrPanic(mem.eql(u8, s, "is"));
|
||||
if (i == 3) assertOrPanic(mem.eql(u8, s, "my"));
|
||||
if (i == 4) assertOrPanic(mem.eql(u8, s, "thing"));
|
||||
}
|
||||
}
|
||||
|
||||
var s_array: [8]Sub = undefined;
|
||||
const Sub = struct {
|
||||
b: u8,
|
||||
};
|
||||
const Str = struct {
|
||||
a: []Sub,
|
||||
};
|
||||
test "set global var array via slice embedded in struct" {
|
||||
var s = Str{ .a = s_array[0..] };
|
||||
|
||||
s.a[0].b = 1;
|
||||
s.a[1].b = 2;
|
||||
s.a[2].b = 3;
|
||||
|
||||
assertOrPanic(s_array[0].b == 1);
|
||||
assertOrPanic(s_array[1].b == 2);
|
||||
assertOrPanic(s_array[2].b == 3);
|
||||
}
|
||||
|
||||
test "array literal with specified size" {
|
||||
var array = [2]u8{
|
||||
1,
|
||||
2,
|
||||
};
|
||||
assertOrPanic(array[0] == 1);
|
||||
assertOrPanic(array[1] == 2);
|
||||
}
|
||||
|
||||
test "array child property" {
|
||||
var x: [5]i32 = undefined;
|
||||
assertOrPanic(@typeOf(x).Child == i32);
|
||||
}
|
||||
|
||||
test "array len property" {
|
||||
var x: [5]i32 = undefined;
|
||||
assertOrPanic(@typeOf(x).len == 5);
|
||||
}
|
||||
|
||||
test "array len field" {
|
||||
var arr = [4]u8{ 0, 0, 0, 0 };
|
||||
var ptr = &arr;
|
||||
assertOrPanic(arr.len == 4);
|
||||
comptime assertOrPanic(arr.len == 4);
|
||||
assertOrPanic(ptr.len == 4);
|
||||
comptime assertOrPanic(ptr.len == 4);
|
||||
}
|
||||
|
||||
test "single-item pointer to array indexing and slicing" {
|
||||
testSingleItemPtrArrayIndexSlice();
|
||||
comptime testSingleItemPtrArrayIndexSlice();
|
||||
}
|
||||
|
||||
fn testSingleItemPtrArrayIndexSlice() void {
|
||||
var array = "aaaa";
|
||||
doSomeMangling(&array);
|
||||
assertOrPanic(mem.eql(u8, "azya", array));
|
||||
}
|
||||
|
||||
fn doSomeMangling(array: *[4]u8) void {
|
||||
array[1] = 'z';
|
||||
array[2..3][0] = 'y';
|
||||
}
|
||||
|
||||
test "implicit cast single-item pointer" {
|
||||
testImplicitCastSingleItemPtr();
|
||||
comptime testImplicitCastSingleItemPtr();
|
||||
}
|
||||
|
||||
fn testImplicitCastSingleItemPtr() void {
|
||||
var byte: u8 = 100;
|
||||
const slice = (*[1]u8)(&byte)[0..];
|
||||
slice[0] += 1;
|
||||
assertOrPanic(byte == 101);
|
||||
}
|
||||
|
||||
fn testArrayByValAtComptime(b: [2]u8) u8 {
|
||||
return b[0];
|
||||
}
|
||||
|
||||
test "comptime evalutating function that takes array by value" {
|
||||
const arr = []u8{ 0, 1 };
|
||||
_ = comptime testArrayByValAtComptime(arr);
|
||||
_ = comptime testArrayByValAtComptime(arr);
|
||||
}
|
||||
|
||||
test "implicit comptime in array type size" {
|
||||
var arr: [plusOne(10)]bool = undefined;
|
||||
assertOrPanic(arr.len == 11);
|
||||
}
|
||||
|
||||
fn plusOne(x: u32) u32 {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
test "array literal as argument to function" {
|
||||
const S = struct {
|
||||
fn entry(two: i32) void {
|
||||
foo([]i32{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
});
|
||||
foo([]i32{
|
||||
1,
|
||||
two,
|
||||
3,
|
||||
});
|
||||
foo2(true, []i32{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
});
|
||||
foo2(true, []i32{
|
||||
1,
|
||||
two,
|
||||
3,
|
||||
});
|
||||
}
|
||||
fn foo(x: []const i32) void {
|
||||
assertOrPanic(x[0] == 1);
|
||||
assertOrPanic(x[1] == 2);
|
||||
assertOrPanic(x[2] == 3);
|
||||
}
|
||||
fn foo2(trash: bool, x: []const i32) void {
|
||||
assertOrPanic(trash);
|
||||
assertOrPanic(x[0] == 1);
|
||||
assertOrPanic(x[1] == 2);
|
||||
assertOrPanic(x[2] == 3);
|
||||
}
|
||||
};
|
||||
S.entry(2);
|
||||
comptime S.entry(2);
|
||||
}
|
||||
|
||||
test "double nested array to const slice cast in array literal" {
|
||||
const S = struct {
|
||||
fn entry(two: i32) void {
|
||||
const cases = [][]const []const i32{
|
||||
[][]const i32{[]i32{1}},
|
||||
[][]const i32{[]i32{ 2, 3 }},
|
||||
[][]const i32{
|
||||
[]i32{4},
|
||||
[]i32{ 5, 6, 7 },
|
||||
},
|
||||
};
|
||||
check(cases);
|
||||
|
||||
const cases2 = [][]const i32{
|
||||
[]i32{1},
|
||||
[]i32{ two, 3 },
|
||||
};
|
||||
assertOrPanic(cases2.len == 2);
|
||||
assertOrPanic(cases2[0].len == 1);
|
||||
assertOrPanic(cases2[0][0] == 1);
|
||||
assertOrPanic(cases2[1].len == 2);
|
||||
assertOrPanic(cases2[1][0] == 2);
|
||||
assertOrPanic(cases2[1][1] == 3);
|
||||
|
||||
const cases3 = [][]const []const i32{
|
||||
[][]const i32{[]i32{1}},
|
||||
[][]const i32{[]i32{ two, 3 }},
|
||||
[][]const i32{
|
||||
[]i32{4},
|
||||
[]i32{ 5, 6, 7 },
|
||||
},
|
||||
};
|
||||
check(cases3);
|
||||
}
|
||||
|
||||
fn check(cases: []const []const []const i32) void {
|
||||
assertOrPanic(cases.len == 3);
|
||||
assertOrPanic(cases[0].len == 1);
|
||||
assertOrPanic(cases[0][0].len == 1);
|
||||
assertOrPanic(cases[0][0][0] == 1);
|
||||
assertOrPanic(cases[1].len == 1);
|
||||
assertOrPanic(cases[1][0].len == 2);
|
||||
assertOrPanic(cases[1][0][0] == 2);
|
||||
assertOrPanic(cases[1][0][1] == 3);
|
||||
assertOrPanic(cases[2].len == 2);
|
||||
assertOrPanic(cases[2][0].len == 1);
|
||||
assertOrPanic(cases[2][0][0] == 4);
|
||||
assertOrPanic(cases[2][1].len == 3);
|
||||
assertOrPanic(cases[2][1][0] == 5);
|
||||
assertOrPanic(cases[2][1][1] == 6);
|
||||
assertOrPanic(cases[2][1][2] == 7);
|
||||
}
|
||||
};
|
||||
S.entry(2);
|
||||
comptime S.entry(2);
|
||||
}
|
92
test/stage1/behavior/asm.zig
Normal file
92
test/stage1/behavior/asm.zig
Normal file
@ -0,0 +1,92 @@
|
||||
const config = @import("builtin");
|
||||
const assertOrPanic = @import("std").debug.assertOrPanic;
|
||||
|
||||
comptime {
|
||||
if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) {
|
||||
asm volatile (
|
||||
\\.globl aoeu;
|
||||
\\.type aoeu, @function;
|
||||
\\.set aoeu, derp;
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test "module level assembly" {
|
||||
if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) {
|
||||
assertOrPanic(aoeu() == 1234);
|
||||
}
|
||||
}
|
||||
|
||||
test "output constraint modifiers" {
|
||||
// This is only testing compilation.
|
||||
var a: u32 = 3;
|
||||
asm volatile (""
|
||||
: [_] "=m,r" (a)
|
||||
:
|
||||
: ""
|
||||
);
|
||||
asm volatile (""
|
||||
: [_] "=r,m" (a)
|
||||
:
|
||||
: ""
|
||||
);
|
||||
}
|
||||
|
||||
test "alternative constraints" {
|
||||
// Make sure we allow commas as a separator for alternative constraints.
|
||||
var a: u32 = 3;
|
||||
asm volatile (""
|
||||
: [_] "=r,m" (a)
|
||||
: [_] "r,m" (a)
|
||||
: ""
|
||||
);
|
||||
}
|
||||
|
||||
test "sized integer/float in asm input" {
|
||||
asm volatile (""
|
||||
:
|
||||
: [_] "m" (usize(3))
|
||||
: ""
|
||||
);
|
||||
asm volatile (""
|
||||
:
|
||||
: [_] "m" (i15(-3))
|
||||
: ""
|
||||
);
|
||||
asm volatile (""
|
||||
:
|
||||
: [_] "m" (u3(3))
|
||||
: ""
|
||||
);
|
||||
asm volatile (""
|
||||
:
|
||||
: [_] "m" (i3(3))
|
||||
: ""
|
||||
);
|
||||
asm volatile (""
|
||||
:
|
||||
: [_] "m" (u121(3))
|
||||
: ""
|
||||
);
|
||||
asm volatile (""
|
||||
:
|
||||
: [_] "m" (i121(3))
|
||||
: ""
|
||||
);
|
||||
asm volatile (""
|
||||
:
|
||||
: [_] "m" (f32(3.17))
|
||||
: ""
|
||||
);
|
||||
asm volatile (""
|
||||
:
|
||||
: [_] "m" (f64(3.17))
|
||||
: ""
|
||||
);
|
||||
}
|
||||
|
||||
extern fn aoeu() i32;
|
||||
|
||||
export fn derp() i32 {
|
||||
return 1234;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const assertOrPanic = std.debug.assertOrPanic;
|
||||
const builtin = @import("builtin");
|
||||
const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
@ -7,18 +7,18 @@ const AtomicOrder = builtin.AtomicOrder;
|
||||
test "cmpxchg" {
|
||||
var x: i32 = 1234;
|
||||
if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
|
||||
assert(x1 == 1234);
|
||||
assertOrPanic(x1 == 1234);
|
||||
} else {
|
||||
@panic("cmpxchg should have failed");
|
||||
}
|
||||
|
||||
while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
|
||||
assert(x1 == 1234);
|
||||
assertOrPanic(x1 == 1234);
|
||||
}
|
||||
assert(x == 5678);
|
||||
assertOrPanic(x == 5678);
|
||||
|
||||
assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
|
||||
assert(x == 42);
|
||||
assertOrPanic(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
|
||||
assertOrPanic(x == 42);
|
||||
}
|
||||
|
||||
test "fence" {
|
||||
@ -30,24 +30,24 @@ test "fence" {
|
||||
test "atomicrmw and atomicload" {
|
||||
var data: u8 = 200;
|
||||
testAtomicRmw(&data);
|
||||
assert(data == 42);
|
||||
assertOrPanic(data == 42);
|
||||
testAtomicLoad(&data);
|
||||
}
|
||||
|
||||
fn testAtomicRmw(ptr: *u8) void {
|
||||
const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst);
|
||||
assert(prev_value == 200);
|
||||
assertOrPanic(prev_value == 200);
|
||||
comptime {
|
||||
var x: i32 = 1234;
|
||||
const y: i32 = 12345;
|
||||
assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234);
|
||||
assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345);
|
||||
assertOrPanic(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234);
|
||||
assertOrPanic(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345);
|
||||
}
|
||||
}
|
||||
|
||||
fn testAtomicLoad(ptr: *u8) void {
|
||||
const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst);
|
||||
assert(x == 42);
|
||||
assertOrPanic(x == 42);
|
||||
}
|
||||
|
||||
test "cmpxchg with ptr" {
|
||||
@ -56,16 +56,16 @@ test "cmpxchg with ptr" {
|
||||
var data3: i32 = 9101;
|
||||
var x: *i32 = &data1;
|
||||
if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
|
||||
assert(x1 == &data1);
|
||||
assertOrPanic(x1 == &data1);
|
||||
} else {
|
||||
@panic("cmpxchg should have failed");
|
||||
}
|
||||
|
||||
while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
|
||||
assert(x1 == &data1);
|
||||
assertOrPanic(x1 == &data1);
|
||||
}
|
||||
assert(x == &data3);
|
||||
assertOrPanic(x == &data3);
|
||||
|
||||
assert(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
|
||||
assert(x == &data2);
|
||||
assertOrPanic(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
|
||||
assertOrPanic(x == &data2);
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const assertOrPanic = std.debug.assertOrPanic;
|
||||
|
||||
fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type {
|
||||
assert(Key == @IntType(false, Key.bit_count));
|
||||
assert(Key.bit_count >= mask_bit_count);
|
||||
assertOrPanic(Key == @IntType(false, Key.bit_count));
|
||||
assertOrPanic(Key.bit_count >= mask_bit_count);
|
||||
const ShardKey = @IntType(false, mask_bit_count);
|
||||
const shift_amount = Key.bit_count - ShardKey.bit_count;
|
||||
return struct {
|
||||
@ -77,12 +77,12 @@ fn testShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, c
|
||||
var node_buffer: [node_count]Table.Node = undefined;
|
||||
for (node_buffer) |*node, i| {
|
||||
const key = @intCast(Key, i);
|
||||
assert(table.get(key) == null);
|
||||
assertOrPanic(table.get(key) == null);
|
||||
node.init(key, {});
|
||||
table.put(node);
|
||||
}
|
||||
|
||||
for (node_buffer) |*node, i| {
|
||||
assert(table.get(@intCast(Key, i)) == node);
|
||||
assertOrPanic(table.get(@intCast(Key, i)) == node);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const assertOrPanic = std.debug.assertOrPanic;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
test "@bitCast i32 -> u32" {
|
||||
@ -8,8 +8,8 @@ test "@bitCast i32 -> u32" {
|
||||
}
|
||||
|
||||
fn testBitCast_i32_u32() void {
|
||||
assert(conv(-1) == maxInt(u32));
|
||||
assert(conv2(maxInt(u32)) == -1);
|
||||
assertOrPanic(conv(-1) == maxInt(u32));
|
||||
assertOrPanic(conv2(maxInt(u32)) == -1);
|
||||
}
|
||||
|
||||
fn conv(x: i32) u32 {
|
||||
@ -27,11 +27,10 @@ test "@bitCast extern enum to its integer type" {
|
||||
fn testBitCastExternEnum() void {
|
||||
var SOCK_DGRAM = @This().B;
|
||||
var sock_dgram = @bitCast(c_int, SOCK_DGRAM);
|
||||
assert(sock_dgram == 1);
|
||||
assertOrPanic(sock_dgram == 1);
|
||||
}
|
||||
};
|
||||
|
||||
SOCK.testBitCastExternEnum();
|
||||
comptime SOCK.testBitCastExternEnum();
|
||||
}
|
||||
|
81
test/stage1/behavior/bitreverse.zig
Normal file
81
test/stage1/behavior/bitreverse.zig
Normal file
@ -0,0 +1,81 @@
|
||||
const std = @import("std");
|
||||
const assertOrPanic = std.debug.assertOrPanic;
|
||||
const minInt = std.math.minInt;
|
||||
|
||||
test "@bitreverse" {
|
||||
comptime testBitReverse();
|
||||
testBitReverse();
|
||||
}
|
||||
|
||||
fn testBitReverse() void {
|
||||
// using comptime_ints, unsigned
|
||||
assertOrPanic(@bitreverse(u0, 0) == 0);
|
||||
assertOrPanic(@bitreverse(u5, 0x12) == 0x9);
|
||||
assertOrPanic(@bitreverse(u8, 0x12) == 0x48);
|
||||
assertOrPanic(@bitreverse(u16, 0x1234) == 0x2c48);
|
||||
assertOrPanic(@bitreverse(u24, 0x123456) == 0x6a2c48);
|
||||
assertOrPanic(@bitreverse(u32, 0x12345678) == 0x1e6a2c48);
|
||||
assertOrPanic(@bitreverse(u40, 0x123456789a) == 0x591e6a2c48);
|
||||
assertOrPanic(@bitreverse(u48, 0x123456789abc) == 0x3d591e6a2c48);
|
||||
assertOrPanic(@bitreverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48);
|
||||
assertOrPanic(@bitreverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48);
|
||||
assertOrPanic(@bitreverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48);
|
||||
|
||||
// using runtime uints, unsigned
|
||||
var num0: u0 = 0;
|
||||
assertOrPanic(@bitreverse(u0, num0) == 0);
|
||||
var num5: u5 = 0x12;
|
||||
assertOrPanic(@bitreverse(u5, num5) == 0x9);
|
||||
var num8: u8 = 0x12;
|
||||
assertOrPanic(@bitreverse(u8, num8) == 0x48);
|
||||
var num16: u16 = 0x1234;
|
||||
assertOrPanic(@bitreverse(u16, num16) == 0x2c48);
|
||||
var num24: u24 = 0x123456;
|
||||
assertOrPanic(@bitreverse(u24, num24) == 0x6a2c48);
|
||||
var num32: u32 = 0x12345678;
|
||||
assertOrPanic(@bitreverse(u32, num32) == 0x1e6a2c48);
|
||||
var num40: u40 = 0x123456789a;
|
||||
assertOrPanic(@bitreverse(u40, num40) == 0x591e6a2c48);
|
||||
var num48: u48 = 0x123456789abc;
|
||||
assertOrPanic(@bitreverse(u48, num48) == 0x3d591e6a2c48);
|
||||
var num56: u56 = 0x123456789abcde;
|
||||
assertOrPanic(@bitreverse(u56, num56) == 0x7b3d591e6a2c48);
|
||||
var num64: u64 = 0x123456789abcdef1;
|
||||
assertOrPanic(@bitreverse(u64, num64) == 0x8f7b3d591e6a2c48);
|
||||
var num128: u128 = 0x123456789abcdef11121314151617181;
|
||||
assertOrPanic(@bitreverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48);
|
||||
|
||||
// using comptime_ints, signed, positive
|
||||
assertOrPanic(@bitreverse(i0, 0) == 0);
|
||||
assertOrPanic(@bitreverse(i8, @bitCast(i8, u8(0x92))) == @bitCast(i8, u8(0x49)));
|
||||
assertOrPanic(@bitreverse(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x2c48)));
|
||||
assertOrPanic(@bitreverse(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x6a2c48)));
|
||||
assertOrPanic(@bitreverse(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x1e6a2c48)));
|
||||
assertOrPanic(@bitreverse(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x591e6a2c48)));
|
||||
assertOrPanic(@bitreverse(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0x3d591e6a2c48)));
|
||||
assertOrPanic(@bitreverse(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0x7b3d591e6a2c48)));
|
||||
assertOrPanic(@bitreverse(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0x8f7b3d591e6a2c48)));
|
||||
assertOrPanic(@bitreverse(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == @bitCast(i128, u128(0x818e868a828c84888f7b3d591e6a2c48)));
|
||||
|
||||
// using comptime_ints, signed, negative. Compare to runtime ints returned from llvm.
|
||||
var neg5: i5 = minInt(i5) + 1;
|
||||
assertOrPanic(@bitreverse(i5, minInt(i5) + 1) == @bitreverse(i5, neg5));
|
||||
var neg8: i8 = -18;
|
||||
assertOrPanic(@bitreverse(i8, -18) == @bitreverse(i8, neg8));
|
||||
var neg16: i16 = -32694;
|
||||
assertOrPanic(@bitreverse(i16, -32694) == @bitreverse(i16, neg16));
|
||||
var neg24: i24 = -6773785;
|
||||
assertOrPanic(@bitreverse(i24, -6773785) == @bitreverse(i24, neg24));
|
||||
var neg32: i32 = -16773785;
|
||||
assertOrPanic(@bitreverse(i32, -16773785) == @bitreverse(i32, neg32));
|
||||
var neg40: i40 = minInt(i40) + 12345;
|
||||
assertOrPanic(@bitreverse(i40, minInt(i40) + 12345) == @bitreverse(i40, neg40));
|
||||
var neg48: i48 = minInt(i48) + 12345;
|
||||
assertOrPanic(@bitreverse(i48, minInt(i48) + 12345) == @bitreverse(i48, neg48));
|
||||
var neg56: i56 = minInt(i56) + 12345;
|
||||
assertOrPanic(@bitreverse(i56, minInt(i56) + 12345) == @bitreverse(i56, neg56));
|
||||
var neg64: i64 = minInt(i64) + 12345;
|
||||
assertOrPanic(@bitreverse(i64, minInt(i64) + 12345) == @bitreverse(i64, neg64));
|
||||
var neg128: i128 = minInt(i128) + 12345;
|
||||
assertOrPanic(@bitreverse(i128, minInt(i128) + 12345) == @bitreverse(i128, neg128));
|
||||
}
|
@ -1,25 +1,25 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const assertOrPanic = @import("std").debug.assertOrPanic;
|
||||
|
||||
test "bool literals" {
|
||||
assert(true);
|
||||
assert(!false);
|
||||
assertOrPanic(true);
|
||||
assertOrPanic(!false);
|
||||
}
|
||||
|
||||
test "cast bool to int" {
|
||||
const t = true;
|
||||
const f = false;
|
||||
assert(@boolToInt(t) == u32(1));
|
||||
assert(@boolToInt(f) == u32(0));
|
||||
assertOrPanic(@boolToInt(t) == u32(1));
|
||||
assertOrPanic(@boolToInt(f) == u32(0));
|
||||
nonConstCastBoolToInt(t, f);
|
||||
}
|
||||
|
||||
fn nonConstCastBoolToInt(t: bool, f: bool) void {
|
||||
assert(@boolToInt(t) == u32(1));
|
||||
assert(@boolToInt(f) == u32(0));
|
||||
assertOrPanic(@boolToInt(t) == u32(1));
|
||||
assertOrPanic(@boolToInt(f) == u32(0));
|
||||
}
|
||||
|
||||
test "bool cmp" {
|
||||
assert(testBoolCmp(true, false) == false);
|
||||
assertOrPanic(testBoolCmp(true, false) == false);
|
||||
}
|
||||
fn testBoolCmp(a: bool, b: bool) bool {
|
||||
return a == b;
|
||||
@ -30,6 +30,6 @@ const global_t = true;
|
||||
const not_global_f = !global_f;
|
||||
const not_global_t = !global_t;
|
||||
test "compile time bool not" {
|
||||
assert(not_global_f);
|
||||
assert(!not_global_t);
|
||||
assertOrPanic(not_global_f);
|
||||
assertOrPanic(!not_global_t);
|
||||
}
|
32
test/stage1/behavior/bswap.zig
Normal file
32
test/stage1/behavior/bswap.zig
Normal file
@ -0,0 +1,32 @@
|
||||
const std = @import("std");
|
||||
const assertOrPanic = std.debug.assertOrPanic;
|
||||
|
||||
test "@bswap" {
|
||||
comptime testByteSwap();
|
||||
testByteSwap();
|
||||
}
|
||||
|
||||
fn testByteSwap() void {
|
||||
assertOrPanic(@bswap(u0, 0) == 0);
|
||||
assertOrPanic(@bswap(u8, 0x12) == 0x12);
|
||||
assertOrPanic(@bswap(u16, 0x1234) == 0x3412);
|
||||
assertOrPanic(@bswap(u24, 0x123456) == 0x563412);
|
||||
assertOrPanic(@bswap(u32, 0x12345678) == 0x78563412);
|
||||
assertOrPanic(@bswap(u40, 0x123456789a) == 0x9a78563412);
|
||||
assertOrPanic(@bswap(u48, 0x123456789abc) == 0xbc9a78563412);
|
||||
assertOrPanic(@bswap(u56, 0x123456789abcde) == 0xdebc9a78563412);
|
||||
assertOrPanic(@bswap(u64, 0x123456789abcdef1) == 0xf1debc9a78563412);
|
||||
assertOrPanic(@bswap(u128, 0x123456789abcdef11121314151617181) == 0x8171615141312111f1debc9a78563412);
|
||||
|
||||
assertOrPanic(@bswap(i0, 0) == 0);
|
||||
assertOrPanic(@bswap(i8, -50) == -50);
|
||||
assertOrPanic(@bswap(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x3412)));
|
||||
assertOrPanic(@bswap(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x563412)));
|
||||
assertOrPanic(@bswap(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x78563412)));
|
||||
assertOrPanic(@bswap(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x9a78563412)));
|
||||
assertOrPanic(@bswap(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0xbc9a78563412)));
|
||||
assertOrPanic(@bswap(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0xdebc9a78563412)));
|
||||
assertOrPanic(@bswap(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0xf1debc9a78563412)));
|
||||
assertOrPanic(@bswap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) ==
|
||||
@bitCast(i128, u128(0x8171615141312111f1debc9a78563412)));
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const assert = std.debug.assert;
|
||||
const assertOrPanic = std.debug.assertOrPanic;
|
||||
|
||||
test "comptime code should not modify constant data" {
|
||||
testCastPtrOfArrayToSliceAndPtr();
|
||||
@ -11,6 +11,6 @@ fn testCastPtrOfArrayToSliceAndPtr() void {
|
||||
var array = "aoeu";
|
||||
const x: [*]u8 = &array;
|
||||
x[0] += 1;
|
||||
assert(mem.eql(u8, array[0..], "boeu"));
|
||||
assertOrPanic(mem.eql(u8, array[0..], "boeu"));
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ const C = struct {};
|
||||
|
||||
test "tagged union with all void fields but a meaningful tag" {
|
||||
var a: A = A{ .b = B{ .c = C{} } };
|
||||
std.debug.assert(@TagType(B)(a.b) == @TagType(B).c);
|
||||
std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).c);
|
||||
a = A{ .b = B.None };
|
||||
std.debug.assert(@TagType(B)(a.b) == @TagType(B).None);
|
||||
std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).None);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const assertOrPanic = std.debug.assertOrPanic;
|
||||
|
||||
const S = struct {
|
||||
fn method() builtin.TypeInfo {
|
||||
@ -10,5 +10,5 @@ const S = struct {
|
||||
|
||||
test "functions with return type required to be comptime are generic" {
|
||||
const ti = S.method();
|
||||
assert(builtin.TypeId(ti) == builtin.TypeId.Struct);
|
||||
assertOrPanic(builtin.TypeId(ti) == builtin.TypeId.Struct);
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const assertOrPanic = @import("std").debug.assertOrPanic;
|
||||
|
||||
const ptr = &global;
|
||||
var global: u64 = 123;
|
||||
|
||||
test "constant pointer to global variable causes runtime load" {
|
||||
global = 1234;
|
||||
assert(&global == ptr);
|
||||
assert(ptr.* == 1234);
|
||||
assertOrPanic(&global == ptr);
|
||||
assertOrPanic(ptr.* == 1234);
|
||||
}
|
||||
|
@ -7,12 +7,12 @@ const S = struct {
|
||||
y: E,
|
||||
};
|
||||
|
||||
const assert = @import("std").debug.assert;
|
||||
const assertOrPanic = @import("std").debug.assertOrPanic;
|
||||
|
||||
test "bug 394 fixed" {
|
||||
const x = S{
|
||||
.x = 3,
|
||||
.y = E{ .B = 1 },
|
||||
};
|
||||
assert(x.x == 3);
|
||||
assertOrPanic(x.x == 3);
|
||||
}
|
@ -3,10 +3,10 @@ const other_file = @import("655_other_file.zig");
|
||||
|
||||
test "function with *const parameter with type dereferenced by namespace" {
|
||||
const x: other_file.Integer = 1234;
|
||||
comptime std.debug.assert(@typeOf(&x) == *const other_file.Integer);
|
||||
comptime std.debug.assertOrPanic(@typeOf(&x) == *const other_file.Integer);
|
||||
foo(&x);
|
||||
}
|
||||
|
||||
fn foo(x: *const other_file.Integer) void {
|
||||
std.debug.assert(x.* == 1234);
|
||||
std.debug.assertOrPanic(x.* == 1234);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const assertOrPanic = @import("std").debug.assertOrPanic;
|
||||
|
||||
const PrefixOp = union(enum) {
|
||||
Return,
|
||||
@ -22,7 +22,7 @@ fn foo(a: bool, b: bool) void {
|
||||
PrefixOp.AddrOf => |addr_of_info| {
|
||||
if (b) {}
|
||||
if (addr_of_info.align_expr) |align_expr| {
|
||||
assert(align_expr == 1234);
|
||||
assertOrPanic(align_expr == 1234);
|
||||
}
|
||||
},
|
||||
PrefixOp.Return => {},
|
@ -1,9 +1,9 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const assertOrPanic = @import("std").debug.assertOrPanic;
|
||||
|
||||
test "@ptrCast from const to nullable" {
|
||||
const c: u8 = 4;
|
||||
var x: ?*const u8 = @ptrCast(?*const u8, &c);
|
||||
assert(x.?.* == 4);
|
||||
assertOrPanic(x.?.* == 4);
|
||||
}
|
||||
|
||||
test "@ptrCast from var in empty struct to nullable" {
|
||||
@ -11,6 +11,6 @@ test "@ptrCast from var in empty struct to nullable" {
|
||||
var c: u8 = 4;
|
||||
};
|
||||
var x: ?*const u8 = @ptrCast(?*const u8, &container.c);
|
||||
assert(x.?.* == 4);
|
||||
assertOrPanic(x.?.* == 4);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user