diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index c7d92712b0..934b8a4f4a 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 3841519f29..4dd6a1dcfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" diff --git a/build.zig b/build.zig index 16185eebf4..a41a5f808b 100644 --- a/build.zig +++ b/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); diff --git a/doc/langref.html.in b/doc/langref.html.in index 6e03d3ec6d..144c8571c4 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1531,6 +1531,29 @@ test "array initialization with function calls" { {#code_end#} {#see_also|for|Slices#} {#header_close#} + + {#header_open|Vectors#} +

+ 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#}. +

+

+ TODO talk about C ABI interop +

+ {#header_open|SIMD#} +

+ 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 +

+ {#header_close#} + {#header_close#} + {#header_open|Pointers#}

Zig has two kinds of pointers: @@ -4327,7 +4350,7 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {

For example, if we were to introduce another function to the above snippet:

- {#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:

-
{#syntax#}for (source[0...byte_count]) |b, i| dest[i] = b;{#endsyntax#}
+
{#syntax#}for (source[0..byte_count]) |b, i| dest[i] = b;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memcpy.

There is also a standard library function for this:

{#syntax#}const mem = @import("std").mem;
-mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#}
+mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#} {#header_close#} {#header_open|@memset#} @@ -5923,7 +5946,7 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#} This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this:

-
{#syntax#}for (dest[0...byte_count]) |*b| b.* = c;{#endsyntax#}
+
{#syntax#}for (dest[0..byte_count]) |*b| b.* = c;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memset.

@@ -6592,9 +6615,10 @@ pub const TypeInfo = union(TypeId) { {#header_close#} {#header_open|@typeName#} -
{#syntax#}@typeName(T: type) []u8{#endsyntax#}
+
{#syntax#}@typeName(T: type) [N]u8{#endsyntax#}

- 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.

{#header_close#} @@ -6606,6 +6630,17 @@ pub const TypeInfo = union(TypeId) { expression passed as an argument. The expression is evaluated.

+ {#header_close#} + + {#header_open|@Vector#} +
{#syntax#}@Vector(comptime len: u32, comptime ElemType: type) type{#endsyntax#}
+

+ This function returns a vector type for {#link|SIMD#}. +

+

+ {#syntax#}ElemType{#endsyntax#} must be an {#link|integer|Integers#}, a {#link|float|Floats#}, or a + {#link|pointer|Pointers#}. +

{#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#} +

Zig has a compile option --single-threaded which has the following effects: +

+

+ {#header_close#} + {#header_open|Undefined Behavior#}

Zig has many instances of undefined behavior. If undefined behavior is diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index c00c7c1d41..d60892432e 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -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; diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index 0e552fde7e..fc49fad410 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -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); diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 562765b354..0362bb4ef8 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -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; diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 1c5d111c5a..18d2daf0c2 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -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; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 0742cbfe65..f6ee9a0513 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -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; } diff --git a/src-self-hosted/package.zig b/src-self-hosted/package.zig index 720b279651..0d31731b55 100644 --- a/src-self-hosted/package.zig +++ b/src-self-hosted/package.zig @@ -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 { diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 43d3b5a784..b14c073a9e 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -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, diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index aa00bb876d..8a05594b30 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -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, diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index e6dca4eff7..9431c614b9 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -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(); diff --git a/src/all_types.hpp b/src/all_types.hpp index 91b24e3110..c4c9e13cfb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -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 fn_type_table; HashMap error_table; HashMap generic_table; - HashMap memoized_fn_eval_table; + HashMap memoized_fn_eval_table; HashMap llvm_fn_table; HashMap exported_symbol_names; HashMap 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 *incoming_values; ZigList *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; diff --git a/src/analyze.cpp b/src/analyze.cpp index 15370983fc..ff961a7044 100644 --- a/src/analyze.cpp +++ b/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(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 diff --git a/src/analyze.hpp b/src/analyze.hpp index efbc065a63..f558fa44b0 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -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); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index f7eb646e16..994ba5f5b1 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -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); diff --git a/src/cache_hash.cpp b/src/cache_hash.cpp index 5e6c3b9a9d..4526a83c27 100644 --- a/src/cache_hash.cpp +++ b/src/cache_hash.cpp @@ -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 { diff --git a/src/codegen.cpp b/src/codegen.cpp index e180b69215..1a70901cf0 100644 --- a/src/codegen.cpp +++ b/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(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(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); diff --git a/src/ir.cpp b/src/ir.cpp index c651f30dd5..3cbbdc8103 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -167,6 +167,8 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ ZigType *dest_type, IrInstruction *dest_type_src); static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed); static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); +static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -178,15 +180,28 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c case ConstPtrSpecialRef: result = const_val->data.x_ptr.data.ref.pointee; break; - case ConstPtrSpecialBaseArray: - expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val); - result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.data.s_none.elements[ - const_val->data.x_ptr.data.base_array.elem_index]; + case ConstPtrSpecialBaseArray: { + ConstExprValue *array_val = const_val->data.x_ptr.data.base_array.array_val; + expand_undef_array(g, array_val); + result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; break; + } case ConstPtrSpecialBaseStruct: result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ const_val->data.x_ptr.data.base_struct.field_index]; break; + case ConstPtrSpecialBaseErrorUnionCode: + result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; + break; + case ConstPtrSpecialBaseErrorUnionPayload: + result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; + break; + case ConstPtrSpecialBaseOptionalPayload: + result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional; + break; + case ConstPtrSpecialNull: + result = const_val; + break; case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialDiscard: @@ -198,6 +213,11 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c return result; } +static bool is_opt_err_set(ZigType *ty) { + return ty->id == ZigTypeIdErrorSet || + (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); +} + static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; @@ -208,15 +228,10 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; - return false; -} + if (is_opt_err_set(a) && is_opt_err_set(b)) + return true; -ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { - ConstExprValue *result = const_ptr_pointee_unchecked(g, const_val); - if (const_val->type->id == ZigTypeIdPointer) { - assert(types_have_same_zig_comptime_repr(const_val->type->data.pointer.child_type, result->type)); - } - return result; + return false; } static bool ir_should_inline(IrExecutable *exec, Scope *scope) { @@ -305,6 +320,14 @@ static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) { return new_bb; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarSrc *) { + return IrInstructionIdDeclVarSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarGen *) { + return IrInstructionIdDeclVarGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) { return IrInstructionIdCondBr; } @@ -337,10 +360,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) { return IrInstructionIdBinOp; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) { - return IrInstructionIdDeclVar; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionExport *) { return IrInstructionIdExport; } @@ -449,8 +468,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) { return IrInstructionIdTestNonNull; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) { - return IrInstructionIdUnwrapOptional; +static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalUnwrapPtr *) { + return IrInstructionIdOptionalUnwrapPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { @@ -517,8 +536,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) { return IrInstructionIdEmbedFile; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchg *) { - return IrInstructionIdCmpxchg; +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgSrc *) { + return IrInstructionIdCmpxchgSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgGen *) { + return IrInstructionIdCmpxchgGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) { @@ -565,6 +588,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) { return IrInstructionIdIntType; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorType *) { + return IrInstructionIdVectorType; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) { return IrInstructionIdBoolNot; } @@ -649,8 +676,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) return IrInstructionIdTestComptime; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCast *) { - return IrInstructionIdPtrCast; +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastSrc *) { + return IrInstructionIdPtrCastSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastGen *) { + return IrInstructionIdPtrCastGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) { @@ -869,6 +900,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScop return IrInstructionIdCheckRuntimeScope; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorToArray *) { + return IrInstructionIdVectorToArray; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) { + return IrInstructionIdArrayToVector; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -915,7 +954,7 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *so ir_ref_instruction(condition, irb->current_basic_block); ir_ref_bb(then_block); ir_ref_bb(else_block); - if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); + if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); return &cond_br_instruction->base; } @@ -931,16 +970,6 @@ static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *sou return &return_instruction->base; } -static IrInstruction *ir_create_const(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigType *type_entry) -{ - assert(type_entry); - IrInstructionConst *const_instruction = ir_create_instruction(irb, scope, source_node); - const_instruction->base.value.type = type_entry; - const_instruction->base.value.special = ConstValSpecialStatic; - return &const_instruction->base; -} - static IrInstruction *ir_build_const_void(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_void; @@ -1188,14 +1217,11 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->async_allocator = async_allocator; call_instruction->new_stack = new_stack; - if (fn_ref) - ir_ref_instruction(fn_ref, irb->current_basic_block); + if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < arg_count; i += 1) ir_ref_instruction(args[i], irb->current_basic_block); - if (async_allocator) - ir_ref_instruction(async_allocator, irb->current_basic_block); - if (new_stack != nullptr) - ir_ref_instruction(new_stack, irb->current_basic_block); + if (async_allocator != nullptr) ir_ref_instruction(async_allocator, irb->current_basic_block); + if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); return &call_instruction->base; } @@ -1280,7 +1306,7 @@ static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, container_init_list_instruction->item_count = item_count; container_init_list_instruction->items = items; - ir_ref_instruction(container_type, irb->current_basic_block); + if (container_type != nullptr) ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < item_count; i += 1) { ir_ref_instruction(items[i], irb->current_basic_block); } @@ -1355,10 +1381,10 @@ static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) { - IrInstructionDeclVar *decl_var_instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionDeclVarSrc *decl_var_instruction = ir_build_instruction(irb, scope, source_node); decl_var_instruction->base.value.special = ConstValSpecialStatic; decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void; decl_var_instruction->var = var; @@ -1366,13 +1392,28 @@ static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *s decl_var_instruction->align_value = align_value; decl_var_instruction->init_value = init_value; - if (var_type) ir_ref_instruction(var_type, irb->current_basic_block); - if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); + if (var_type != nullptr) ir_ref_instruction(var_type, irb->current_basic_block); + if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(init_value, irb->current_basic_block); return &decl_var_instruction->base; } +static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *source_instruction, + ZigVar *var, IrInstruction *init_value) +{ + IrInstructionDeclVarGen *decl_var_instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + decl_var_instruction->base.value.special = ConstValSpecialStatic; + decl_var_instruction->base.value.type = ira->codegen->builtin_types.entry_void; + decl_var_instruction->var = var; + decl_var_instruction->init_value = init_value; + + ir_ref_instruction(init_value, ira->new_irb.current_basic_block); + + return &decl_var_instruction->base; +} + static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *target, IrInstruction *linkage) { @@ -1542,14 +1583,14 @@ static IrInstruction *ir_build_test_nonnull(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } -static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, - bool safety_check_on) +static IrInstruction *ir_build_optional_unwrap_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *base_ptr, bool safety_check_on) { - IrInstructionUnwrapOptional *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + IrInstructionOptionalUnwrapPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base_ptr = base_ptr; instruction->safety_check_on = safety_check_on; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(base_ptr, irb->current_basic_block); return &instruction->base; } @@ -1765,13 +1806,12 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, - IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, +static IrInstruction *ir_build_cmpxchg_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *type_value, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - bool is_weak, - ZigType *type, AtomicOrder success_order, AtomicOrder failure_order) + bool is_weak) { - IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionCmpxchgSrc *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; instruction->ptr = ptr; instruction->cmp_value = cmp_value; @@ -1779,16 +1819,33 @@ static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *so instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; instruction->is_weak = is_weak; - instruction->type = type; - instruction->success_order = success_order; - instruction->failure_order = failure_order; - if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(cmp_value, irb->current_basic_block); ir_ref_instruction(new_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); + ir_ref_instruction(success_order_value, irb->current_basic_block); + ir_ref_instruction(failure_order_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + AtomicOrder success_order, AtomicOrder failure_order, bool is_weak) +{ + IrInstructionCmpxchgGen *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->ptr = ptr; + instruction->cmp_value = cmp_value; + instruction->new_value = new_value; + instruction->success_order = success_order; + instruction->failure_order = failure_order; + instruction->is_weak = is_weak; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + ir_ref_instruction(cmp_value, ira->new_irb.current_basic_block); + ir_ref_instruction(new_value, ira->new_irb.current_basic_block); return &instruction->base; } @@ -1909,6 +1966,19 @@ static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } +static IrInstruction *ir_build_vector_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *len, + IrInstruction *elem_type) +{ + IrInstructionVectorType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->len = len; + instruction->elem_type = elem_type; + + ir_ref_instruction(len, irb->current_basic_block); + ir_ref_instruction(elem_type, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionBoolNot *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; @@ -2060,12 +2130,12 @@ static IrInstruction *ir_build_test_err(IrBuilder *irb, Scope *scope, AstNode *s } static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *value) + IrInstruction *err_union) { IrInstructionUnwrapErrCode *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + instruction->err_union = err_union; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(err_union, irb->current_basic_block); return &instruction->base; } @@ -2115,20 +2185,33 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } -static IrInstruction *ir_build_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *ptr) { - IrInstructionPtrCast *instruction = ir_build_instruction( + IrInstructionPtrCastSrc *instruction = ir_build_instruction( irb, scope, source_node); instruction->dest_type = dest_type; instruction->ptr = ptr; - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); return &instruction->base; } +static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction, + ZigType *ptr_type, IrInstruction *ptr) +{ + IrInstructionPtrCastGen *instruction = ir_build_instruction( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = ptr_type; + instruction->ptr = ptr; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *value) { @@ -2747,6 +2830,34 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, return &instruction->base; } +static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *vector, ZigType *result_type) +{ + IrInstructionVectorToArray *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = result_type; + instruction->vector = vector; + + ir_ref_instruction(vector, ira->new_irb.current_basic_block); + + ir_add_alloca(ira, &instruction->base, result_type); + + return &instruction->base; +} + +static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *array, ZigType *result_type) +{ + IrInstructionArrayToVector *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = result_type; + instruction->array = array; + + ir_ref_instruction(array, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -2807,10 +2918,13 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o Scope *defer_expr_scope = defer_node->data.defer.expr_scope; IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); if (defer_expr_value != irb->codegen->invalid_instruction) { - if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == ZigTypeIdUnreachable) { + if (defer_expr_value->value.type != nullptr && + defer_expr_value->value.type->id == ZigTypeIdUnreachable) + { is_noreturn = true; } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, + defer_expr_value)); } } } @@ -3065,7 +3179,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s variable_entry->mem_slot_index = SIZE_MAX; variable_entry->is_comptime = is_comptime; variable_entry->src_arg_index = SIZE_MAX; - variable_entry->value = create_const_vals(1); + variable_entry->const_value = create_const_vals(1); if (is_comptime != nullptr) { is_comptime->ref_count += 1; @@ -3080,20 +3194,20 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(codegen, node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { Tld *tld = find_decl(codegen, parent_scope, name); if (tld != nullptr) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } } } @@ -3156,7 +3270,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); - scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope)); + scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, + ir_should_inline(irb->exec, parent_scope)); } bool is_continuation_unreachable = false; @@ -3174,9 +3289,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode // defer starts a new scope child_scope = statement_node->data.defer.child_scope; assert(child_scope); - } else if (statement_value->id == IrInstructionIdDeclVar) { + } else if (statement_value->id == IrInstructionIdDeclVarSrc) { // variable declarations start a new scope - IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)statement_value; + IrInstructionDeclVarSrc *decl_var_instruction = (IrInstructionDeclVarSrc *)statement_value; child_scope = decl_var_instruction->var->child_scope; } else if (statement_value != irb->codegen->invalid_instruction && !is_continuation_unreachable) { // this statement's value must be void @@ -3331,7 +3446,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); AstNode *op1_node = node->data.bin_op_expr.op1; @@ -3365,7 +3480,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, ok_block); - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, parent_scope, node, maybe_ptr, false); + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false); IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); @@ -3483,7 +3598,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) case BinOpTypeMergeErrorSets: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeUnwrapOptional: - return ir_gen_maybe_ok_or(irb, scope, node); + return ir_gen_orelse(irb, scope, node); case BinOpTypeErrorUnion: return ir_gen_error_union(irb, scope, node); } @@ -3542,6 +3657,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, buf_ptr(variable_name))); return irb->codegen->invalid_instruction; } + assert(err == ErrorPrimitiveTypeNotFound); } else { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); if (lval == LValPtr) { @@ -3904,9 +4020,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; - IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), - nullptr, AtomicOrderUnordered, AtomicOrderUnordered); + IrInstruction *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak)); return ir_lval_wrap(irb, scope, cmpxchg, lval); } case BuiltinFnIdFence: @@ -4169,6 +4284,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, int_type, lval); } + case BuiltinFnIdVectorType: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, vector_type, lval); + } case BuiltinFnIdMemcpy: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4346,7 +4476,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, ptr_cast, lval); } case BuiltinFnIdBitCast: @@ -4784,7 +4914,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, + is_async, async_allocator, nullptr); return ir_lval_wrap(irb, scope, fn_call, lval); } @@ -4793,7 +4924,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); if (condition == irb->codegen->invalid_instruction) - return condition; + return irb->codegen->invalid_instruction; IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { @@ -4816,7 +4947,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope); if (then_expr_result == irb->codegen->invalid_instruction) - return then_expr_result; + return irb->codegen->invalid_instruction; IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -4826,7 +4957,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode if (else_node) { else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_expr_result == irb->codegen->invalid_instruction) - return else_expr_result; + return irb->codegen->invalid_instruction; } else { else_expr_result = ir_build_const_void(irb, scope, node); } @@ -4927,7 +5058,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode ptr_len, align_value, bit_offset_start, host_int_bytes); } -static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, +static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval) { IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); @@ -5053,11 +5184,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol, is_const, is_const, is_shadowable, is_comptime); - // we detect IrInstructionIdDeclVar in gen_block to make sure the next node + // we detect IrInstructionIdDeclVarSrc in gen_block to make sure the next node // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { - var->value->type = irb->codegen->builtin_types.entry_invalid; + var->var_type = irb->codegen->builtin_types.entry_invalid; add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); return irb->codegen->invalid_instruction; } @@ -5084,7 +5215,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod if (init_value == irb->codegen->invalid_instruction) return init_value; - return ir_build_var_decl(irb, scope, node, var, type_instruction, align_value, init_value); + return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, init_value); } static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5140,7 +5271,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n err_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); } ZigList incoming_values = {0}; @@ -5180,7 +5311,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n true, false, false, is_comptime); Scope *err_scope = err_var->child_scope; IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr); - ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); + ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope); if (else_result == irb->codegen->invalid_instruction) @@ -5220,10 +5351,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); + IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5380,7 +5511,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo Scope *child_scope = elem_var->child_scope; IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); - ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); + ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); AstNode *index_var_source_node; @@ -5398,7 +5529,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); - ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); + ir_build_var_decl_src(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); @@ -5622,8 +5753,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile); } -static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeTestExpr); +static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeIfOptional); Buf *var_symbol = node->data.test_expr.var_symbol; AstNode *expr_node = node->data.test_expr.target_node; @@ -5661,9 +5792,9 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); + IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5738,7 +5869,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5763,7 +5894,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * err_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstruction *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); err_var_scope = var->child_scope; } else { err_var_scope = subexpr_scope; @@ -5818,7 +5949,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); } IrInstruction *var_type = nullptr; // infer the type - ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); } else { child_scope = scope; } @@ -6228,7 +6359,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true); } -static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeUnwrapErrorExpr); AstNode *op1_node = node->data.unwrap_err_expr.op1; @@ -6242,7 +6373,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return irb->codegen->invalid_instruction; } - return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LValNone); + return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, LValNone); } @@ -6276,7 +6407,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr); - ir_build_var_decl(irb, err_scope, var_node, var, nullptr, nullptr, err_val); + ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, err_val); } else { err_scope = parent_scope; } @@ -6312,7 +6443,8 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope; if (need_comma) buf_append_char(name, ','); - render_const_value(codegen, name, var_scope->var->value); + // TODO: const ptr reinterpret here to make the var type agree with the value? + render_const_value(codegen, name, var_scope->var->const_value); return true; } @@ -6562,7 +6694,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6640,7 +6772,7 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6756,7 +6888,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); - ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); + ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); @@ -7050,7 +7182,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true); if (lval == LValPtr) return unwrapped_ptr; @@ -7074,8 +7206,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); case NodeTypeIfErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node), lval); - case NodeTypeTestExpr: - return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval); + case NodeTypeIfOptional: + return ir_lval_wrap(irb, scope, ir_gen_if_optional_expr(irb, scope, node), lval); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); case NodeTypeCompTime: @@ -7093,7 +7225,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSliceExpr: return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval); case NodeTypeUnwrapErrorExpr: - return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_catch(irb, scope, node), lval); case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: @@ -7152,6 +7284,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_ref_bb(irb->current_basic_block); ZigFn *fn_entry = exec_fn_entry(irb->exec); + bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; IrInstruction *coro_id; IrInstruction *u8_ptr_type; @@ -7172,27 +7305,27 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa - ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); + ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var); ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); + ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); u8_ptr_type = ir_build_const_type(irb, coro_scope, node, get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); - IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); + IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); - ir_build_var_decl(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); + ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); - ir_build_var_decl(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); + ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); @@ -7208,7 +7341,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_return(irb, coro_scope, node, undef); ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); @@ -7286,8 +7419,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); - IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); - IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, + IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); + IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node, fn_entry->type_entry->data.fn.fn_type_id.return_type); @@ -7303,7 +7436,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // Before we destroy the coroutine frame, we need to load the target promise into // a register or local variable which does not get spilled into the frame, // otherwise llvm tries to access memory inside the destroyed frame. - IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, + IrInstruction *unwrapped_await_handle_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, false); IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); @@ -7338,7 +7471,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); @@ -7435,7 +7568,7 @@ ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprVal return val; } -static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { +static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { IrBasicBlock *bb = exec->basic_block_list.at(0); for (size_t i = 0; i < bb->instruction_list.length; i += 1) { IrInstruction *instruction = bb->instruction_list.at(i); @@ -7445,16 +7578,16 @@ static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) if (value->value.special == ConstValSpecialRuntime) { exec_add_error_node(codegen, exec, value->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } - return value; + return &value->value; } else if (ir_has_side_effects(instruction)) { exec_add_error_node(codegen, exec, instruction->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } } - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) { @@ -8174,6 +8307,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt); bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat); + assert(const_val_is_int || const_val_is_float); if (other_type->id == ZigTypeIdFloat) { if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) { @@ -8768,13 +8902,13 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT size_t errors_count = 0; ZigType *err_set_type = nullptr; if (prev_inst->value.type->id == ZigTypeIdErrorSet) { + if (!resolve_inferred_error_set(ira->codegen, prev_inst->value.type, prev_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (type_is_global_error_set(prev_inst->value.type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; } else { err_set_type = prev_inst->value.type; - if (!resolve_inferred_error_set(ira->codegen, err_set_type, prev_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { @@ -8933,6 +9067,9 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (prev_type->id == ZigTypeIdArray) { convert_to_const_slice = true; } + if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; @@ -8940,9 +9077,6 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { continue; } - if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); @@ -9432,14 +9566,23 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ } return true; } + +static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + old_instruction->scope, old_instruction->source_node); + IrInstruction *new_instruction = &const_instruction->base; + new_instruction->value.type = ty; + new_instruction->value.special = ConstValSpecialStatic; + return new_instruction; +} + static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && cast_op != CastOpResizeSlice) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, &result->value, wanted_type)) { @@ -9476,9 +9619,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.data.x_ptr.special = ConstPtrSpecialBaseArray; result->value.data.x_ptr.mut = value->value.data.x_ptr.mut; result->value.data.x_ptr.data.base_array.array_val = pointee; @@ -9517,8 +9658,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc assert(is_slice(wanted_type)); bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const); result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = value->value.data.x_ptr.mut; @@ -9653,15 +9793,6 @@ static IrInstruction *ir_finish_anal(IrAnalyze *ira, IrInstruction *instruction) return instruction; } -static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { - IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - IrInstruction *new_instruction = &const_instruction->base; - new_instruction->value.type = ty; - new_instruction->value.special = ConstValSpecialStatic; - return new_instruction; -} - static IrInstruction *ir_const_type(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) { IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type); result->value.data.x_type = ty; @@ -9719,13 +9850,13 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un zig_unreachable(); } -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) { if (expected_type != nullptr && type_is_invalid(expected_type)) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; IrExecutable *ir_executable = allocate(1); ir_executable->source_node = source_node; @@ -9738,13 +9869,13 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node ir_gen(codegen, node, scope, ir_executable); if (ir_executable->invalid) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "\nSource: "); ast_render(codegen, stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); - ir_print(codegen, stderr, ir_executable, 4); + ir_print(codegen, stderr, ir_executable, 2); fprintf(stderr, "}\n"); } IrExecutable *analyzed_executable = allocate(1); @@ -9760,11 +9891,11 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node analyzed_executable->begin_scope = scope; ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node); if (type_is_invalid(result_type)) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); - ir_print(codegen, stderr, analyzed_executable, 4); + ir_print(codegen, stderr, analyzed_executable, 2); fprintf(stderr, "}\n"); } @@ -9789,6 +9920,34 @@ static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { return const_val->data.x_type; } +static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInstruction *op_source, IrInstruction *type_value) { + if (type_is_invalid(type_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (type_value->value.type->id != ZigTypeIdMetaType) { + ErrorMsg *msg = ir_add_error(ira, type_value, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value.type->name))); + add_error_note(ira->codegen, msg, op_source->source_node, + buf_sprintf("`||` merges error sets; `or` performs boolean OR")); + return ira->codegen->builtin_types.entry_invalid; + } + + ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad); + if (!const_val) + return ira->codegen->builtin_types.entry_invalid; + + assert(const_val->data.x_type != nullptr); + ZigType *result_type = const_val->data.x_type; + if (result_type->id != ZigTypeIdErrorSet) { + ErrorMsg *msg = ir_add_error(ira, type_value, + buf_sprintf("expected error set type, found type '%s'", buf_ptr(&result_type->name))); + add_error_note(ira->codegen, msg, op_source->source_node, + buf_sprintf("`||` merges error sets; `or` performs boolean OR")); + return ira->codegen->builtin_types.entry_invalid; + } + return result_type; +} + static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { if (fn_value == ira->codegen->invalid_instruction) return nullptr; @@ -9810,7 +9969,9 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { return const_val->data.x_ptr.data.fn.fn_entry; } -static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { +static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *wanted_type) +{ assert(wanted_type->id == ZigTypeIdOptional); if (instr_is_comptime(value)) { @@ -9826,7 +9987,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; - if (get_codegen_ptr_type(wanted_type) != nullptr) { + if (types_have_same_zig_comptime_repr(wanted_type, payload_type)) { copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); } else { const_instruction->base.value.data.x_optional = val; @@ -9857,11 +10018,16 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->type = wanted_type->data.error_union.err_set_type; + err_set_val->special = ConstValSpecialStatic; + err_set_val->data.x_err_set = nullptr; + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.err = nullptr; + const_instruction->base.value.data.x_err_union.error_set = err_set_val; const_instruction->base.value.data.x_err_union.payload = val; return &const_instruction->base; } @@ -9926,11 +10092,16 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->special = ConstValSpecialStatic; + err_set_val->type = wanted_type->data.error_union.err_set_type; + err_set_val->data.x_err_set = val->data.x_err_set; + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.err = val->data.x_err_set; + const_instruction->base.value.data.x_err_union.error_set = err_set_val; const_instruction->base.value.data.x_err_union.payload = nullptr; return &const_instruction->base; } @@ -9952,8 +10123,9 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; if (get_codegen_ptr_type(wanted_type) != nullptr) { - const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; + const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialNull; + } else if (is_opt_err_set(wanted_type)) { + const_instruction->base.value.data.x_err_set = nullptr; } else { const_instruction->base.value.data.x_optional = nullptr; } @@ -9986,7 +10158,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; - if (type_has_bits(ptr_type)) { + if (type_has_bits(ptr_type) && !handle_is_ptr(value->value.type)) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); fn_entry->alloca_list.append(new_instruction); @@ -10012,20 +10184,17 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s ZigType *array_type = array->value.type; assert(array_type->id == ZigTypeIdArray); - if (instr_is_comptime(array)) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + if (instr_is_comptime(array) || array_type->data.array.len == 0) { + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true); result->value.type = wanted_type; return result; } - IrInstruction *start = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + IrInstruction *start = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &start->value, 0); - IrInstruction *end = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + IrInstruction *end = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &end->value, array_type->data.array.len); if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); @@ -10064,8 +10233,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); return result; } @@ -10075,8 +10243,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour actual_type->data.enumeration.src_field_count == 1) { assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &actual_type->data.enumeration.fields[0].value); return result; @@ -10099,8 +10266,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_union.tag); @@ -10111,8 +10277,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou if (wanted_type->data.enumeration.layout == ContainerLayoutAuto && wanted_type->data.enumeration.src_field_count == 1) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; TypeEnumField *enum_field = target->value.type->data.unionation.fields[0].enum_field; @@ -10129,8 +10294,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_undefined(ira->codegen, &result->value); return result; } @@ -10162,8 +10326,7 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so buf_sprintf("field '%s' declared here", buf_ptr(union_field->name))); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag); @@ -10218,8 +10381,7 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; } } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = wanted_type; if (wanted_type->id == ZigTypeIdInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); @@ -10273,8 +10435,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint); return result; } @@ -10292,8 +10453,7 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (wanted_type->id == ZigTypeIdComptimeFloat) { float_init_float(&result->value, val); } else if (wanted_type->id == ZigTypeIdComptimeInt) { @@ -10316,8 +10476,7 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; @@ -10381,12 +10540,11 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err; if (err_type->id == ZigTypeIdErrorUnion) { - err = val->data.x_err_union.err; + err = val->data.x_err_union.error_set->data.x_err_set; } else if (err_type->id == ZigTypeIdErrorSet) { err = val->data.x_err_set; } else { @@ -10421,15 +10579,11 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return ira->codegen->invalid_instruction; } if (err_set_type->data.error_set.err_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); bigint_init_unsigned(&result->value.data.x_bigint, 0); return result; } else if (err_set_type->data.error_set.err_count == 1) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; bigint_init_unsigned(&result->value.data.x_bigint, err->value); return result; @@ -10476,8 +10630,8 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou array_val->type = array_type; array_val->data.x_array.special = ConstArraySpecialNone; array_val->data.x_array.data.s_none.elements = pointee; - array_val->data.x_array.data.s_none.parent.id = ConstParentIdScalar; - array_val->data.x_array.data.s_none.parent.data.p_scalar.scalar_val = pointee; + array_val->parent.id = ConstParentIdScalar; + array_val->parent.data.p_scalar.scalar_val = pointee; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); @@ -10598,6 +10752,32 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa } } +static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *array, ZigType *vector_type) +{ + if (instr_is_comptime(array)) { + // arrays and vectors have the same ConstExprValue representation + IrInstruction *result = ir_const(ira, source_instr, vector_type); + copy_const_val(&result->value, &array->value, false); + result->value.type = vector_type; + return result; + } + return ir_build_array_to_vector(ira, source_instr, array, vector_type); +} + +static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *vector, ZigType *array_type) +{ + if (instr_is_comptime(vector)) { + // arrays and vectors have the same ConstExprValue representation + IrInstruction *result = ir_const(ira, source_instr, array_type); + copy_const_val(&result->value, &vector->value, false); + result->value.type = array_type; + return result; + } + return ir_build_vector_to_array(ira, source_instr, vector, array_type); +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, ZigType *wanted_type, IrInstruction *value) { @@ -10625,12 +10805,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk) { - return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { - return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } else { return ira->codegen->invalid_instruction; } @@ -10654,7 +10834,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_child_type); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; - return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type); } } } @@ -10707,6 +10887,11 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat)) { + if (value->value.special == ConstValSpecialUndef) { + IrInstruction *result = ir_const(ira, source_instr, wanted_type); + result->value.special = ConstValSpecialUndef; + return result; + } if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); @@ -10760,6 +10945,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // cast from [N]T to []const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this if (is_slice(wanted_type) && actual_type->id == ZigTypeIdArray) { ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == ZigTypeIdPointer); @@ -10772,6 +10958,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // cast from [N]T to ?[]const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == ZigTypeIdArray) @@ -10866,7 +11053,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from error set to error union type + // cast from E to E!T if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorSet) { @@ -10979,6 +11166,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // cast from @Vector(N, T) to [N]T + if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector && + wanted_type->data.array.len == actual_type->data.vector.len && + types_match_const_cast_only(ira, wanted_type->data.array.child_type, + actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type); + } + + // cast from [N]T to @Vector(N, T) + if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector && + actual_type->data.array.len == wanted_type->data.vector.len && + types_match_const_cast_only(ira, actual_type->data.array.child_type, + wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type); + } // cast from undefined to anything if (actual_type->id == ZigTypeIdUndefined) { @@ -11018,8 +11222,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc ZigType *child_type = type_entry->data.pointer.child_type; // dereferencing a *u0 is comptime known to be 0 if (child_type->id == ZigTypeIdInt && child_type->data.integral.bit_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); init_const_unsigned_negative(&result->value, child_type, 0, false); return result; } @@ -11033,8 +11236,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc { ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, &result->value, &ptr->value))) @@ -11046,7 +11248,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } } } - // TODO if the instruction is a const ref instruction we can skip it + // if the instruction is a const ref instruction we can skip it + if (ptr->id == IrInstructionIdRef) { + IrInstructionRef *ref_inst = reinterpret_cast(ptr); + return ref_inst->value; + } IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, source_instruction->source_node, ptr); load_ptr_instruction->value.type = child_type; @@ -11293,8 +11499,7 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio static IrInstruction *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *instruction) { IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - // TODO determine if we need to use copy_const_val here - result->value = instruction->base.value; + copy_const_val(&result->value, &instruction->base.value, true); return result; } @@ -11369,6 +11574,8 @@ static bool optional_value_is_null(ConstExprValue *val) { if (get_codegen_ptr_type(val->type) != nullptr) { return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr == 0; + } else if (is_opt_err_set(val->type)) { + return val->data.x_err_set == nullptr; } else { return val->data.x_optional == nullptr; } @@ -11523,6 +11730,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * case ZigTypeIdComptimeInt: case ZigTypeIdInt: case ZigTypeIdFloat: + case ZigTypeIdVector: operator_allowed = true; break; @@ -11568,19 +11776,18 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->invalid_instruction; - bool requires_comptime; - switch (type_requires_comptime(ira->codegen, resolved_type)) { - case ReqCompTimeYes: - requires_comptime = true; - break; - case ReqCompTimeNo: - requires_comptime = false; - break; - case ReqCompTimeInvalid: + bool one_possible_value; + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + one_possible_value = true; + break; + case OnePossibleValueNo: + one_possible_value = false; + break; } - bool one_possible_value = !requires_comptime && !type_has_bits(resolved_type); if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) @@ -11654,8 +11861,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * return result; } -static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, - IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val) +static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry, + ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val) { bool is_int; bool is_float; @@ -11677,10 +11884,10 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod || op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ) { - return ErrorDivByZero; + return ir_add_error(ira, source_instr, buf_sprintf("division by zero")); } if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) { - return ErrorNegativeDenominator; + return ir_add_error(ira, source_instr, buf_sprintf("negative denominator")); } switch (op_id) { @@ -11726,7 +11933,7 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, BigInt orig_bigint; bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) { - return ErrorShiftedOutOneBits; + return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits")); } break; } @@ -11794,14 +12001,14 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, BigInt remainder; bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp_zero(&remainder) != CmpEQ) { - return ErrorExactDivRemainder; + return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); } } else { float_div_trunc(out_val, op1_val, op2_val); ConstExprValue remainder; float_rem(&remainder, op1_val, op2_val); if (float_cmp_zero(&remainder) != CmpEQ) { - return ErrorExactDivRemainder; + return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); } } break; @@ -11825,13 +12032,51 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed)) { - return ErrorOverflow; + return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow")); } } out_val->type = type_entry; out_val->special = ConstValSpecialStatic; - return 0; + return nullptr; +} + +// This works on operands that have already been checked to be comptime known. +static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_instr, + ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val) +{ + IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry); + ConstExprValue *out_val = &result_instruction->value; + if (type_entry->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, op1_val); + expand_undef_array(ira->codegen, op2_val); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + size_t len = type_entry->data.vector.len; + ZigType *scalar_type = type_entry->data.vector.elem_type; + for (size_t i = 0; i < len; i += 1) { + ConstExprValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i]; + ConstExprValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; + ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; + assert(scalar_op1_val->type == scalar_type); + assert(scalar_op2_val->type == scalar_type); + assert(scalar_out_val->type == scalar_type); + ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, + scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); + if (msg != nullptr) { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); + return ira->codegen->invalid_instruction; + } + } + out_val->type = type_entry; + out_val->special = ConstValSpecialStatic; + } else { + if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) { + return ira->codegen->invalid_instruction; + } + } + return ir_implicit_cast(ira, result_instruction, type_entry); } static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { @@ -11903,24 +12148,7 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b if (op2_val == nullptr) return ira->codegen->invalid_instruction; - IrInstruction *result_instruction = ir_const(ira, &bin_op_instruction->base, op1->value.type); - - int err; - if ((err = ir_eval_math_op(op1->value.type, op1_val, op_id, op2_val, &result_instruction->value))) { - if (err == ErrorOverflow) { - ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorShiftedOutOneBits) { - ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact shift shifted out 1 bits")); - return ira->codegen->invalid_instruction; - } else { - zig_unreachable(); - } - return ira->codegen->invalid_instruction; - } - - ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false); - return result_instruction; + return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value.type, op1_val, op_id, op2_val); } else if (op1->value.type->id == ZigTypeIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known")); @@ -11939,6 +12167,48 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b return result; } +static bool ok_float_op(IrBinOp op) { + switch (op) { + case IrBinOpInvalid: + zig_unreachable(); + case IrBinOpAdd: + case IrBinOpSub: + case IrBinOpMult: + case IrBinOpDivUnspecified: + case IrBinOpDivTrunc: + case IrBinOpDivFloor: + case IrBinOpDivExact: + case IrBinOpRemRem: + case IrBinOpRemMod: + return true; + + case IrBinOpBoolOr: + case IrBinOpBoolAnd: + case IrBinOpCmpEq: + case IrBinOpCmpNotEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpGreaterThan: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpGreaterOrEq: + case IrBinOpBinOr: + case IrBinOpBinXor: + case IrBinOpBinAnd: + case IrBinOpBitShiftLeftLossy: + case IrBinOpBitShiftLeftExact: + case IrBinOpBitShiftRightLossy: + case IrBinOpBitShiftRightExact: + case IrBinOpAddWrap: + case IrBinOpSubWrap: + case IrBinOpMultWrap: + case IrBinOpRemUnspecified: + case IrBinOpArrayCat: + case IrBinOpArrayMult: + case IrBinOpMergeErrorSets: + return false; + } + zig_unreachable(); +} + static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) { IrInstruction *op1 = instruction->op1->child; if (type_is_invalid(op1->value.type)) @@ -12076,21 +12346,20 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp op_id = IrBinOpRemRem; } + bool ok = false; if (is_int) { - // int - } else if (is_float && - (op_id == IrBinOpAdd || - op_id == IrBinOpSub || - op_id == IrBinOpMult || - op_id == IrBinOpDivUnspecified || - op_id == IrBinOpDivTrunc || - op_id == IrBinOpDivFloor || - op_id == IrBinOpDivExact || - op_id == IrBinOpRemRem || - op_id == IrBinOpRemMod)) - { - // float - } else { + ok = true; + } else if (is_float && ok_float_op(op_id)) { + ok = true; + } else if (resolved_type->id == ZigTypeIdVector) { + ZigType *elem_type = resolved_type->data.vector.elem_type; + if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) { + ok = true; + } else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) { + ok = true; + } + } + if (!ok) { AstNode *source_node = instruction->base.source_node; ir_add_error_node(ira, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", @@ -12125,30 +12394,7 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (op2_val == nullptr) return ira->codegen->invalid_instruction; - IrInstruction *result_instruction = ir_const(ira, &instruction->base, resolved_type); - - int err; - if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, &result_instruction->value))) { - if (err == ErrorDivByZero) { - ir_add_error(ira, &instruction->base, buf_sprintf("division by zero")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorOverflow) { - ir_add_error(ira, &instruction->base, buf_sprintf("operation caused overflow")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorExactDivRemainder) { - ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorNegativeDenominator) { - ir_add_error(ira, &instruction->base, buf_sprintf("negative denominator")); - return ira->codegen->invalid_instruction; - } else { - zig_unreachable(); - } - return ira->codegen->invalid_instruction; - } - - ir_num_lit_fits_in_other_type(ira, result_instruction, resolved_type, false); - return result_instruction; + return ir_analyze_math_op(ira, &instruction->base, resolved_type, op1_val, op_id, op2_val); } IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope, @@ -12202,7 +12448,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op1_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; ConstExprValue *len_val = &op1_val->data.x_struct.fields[slice_len_index]; - op1_array_end = bigint_as_unsigned(&len_val->data.x_bigint); + op1_array_end = op1_array_index + bigint_as_unsigned(&len_val->data.x_bigint); } else { ir_add_error(ira, op1, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name))); @@ -12212,13 +12458,9 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i ConstExprValue *op2_array_val; size_t op2_array_index; size_t op2_array_end; + bool op2_type_valid; if (op2_type->id == ZigTypeIdArray) { - if (op2_type->data.array.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", - buf_ptr(&child_type->name), - buf_ptr(&op2->value.type->name))); - return ira->codegen->invalid_instruction; - } + op2_type_valid = op2_type->data.array.child_type == child_type; op2_array_val = op2_val; op2_array_index = 0; op2_array_end = op2_array_val->type->data.array.len; @@ -12227,35 +12469,30 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray && op2_val->data.x_ptr.data.base_array.is_cstr) { - if (child_type != ira->codegen->builtin_types.entry_u8) { - ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", - buf_ptr(&child_type->name), - buf_ptr(&op2->value.type->name))); - return ira->codegen->invalid_instruction; - } + op2_type_valid = child_type == ira->codegen->builtin_types.entry_u8; op2_array_val = op2_val->data.x_ptr.data.base_array.array_val; op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index; op2_array_end = op2_array_val->type->data.array.len - 1; } else if (is_slice(op2_type)) { ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry; - if (ptr_type->data.pointer.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", - buf_ptr(&child_type->name), - buf_ptr(&op2->value.type->name))); - return ira->codegen->invalid_instruction; - } + op2_type_valid = ptr_type->data.pointer.child_type == child_type; ConstExprValue *ptr_val = &op2_val->data.x_struct.fields[slice_ptr_index]; assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); op2_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; - op2_array_end = op2_array_val->type->data.array.len; ConstExprValue *len_val = &op2_val->data.x_struct.fields[slice_len_index]; - op2_array_end = bigint_as_unsigned(&len_val->data.x_bigint); + op2_array_end = op2_array_index + bigint_as_unsigned(&len_val->data.x_bigint); } else { ir_add_error(ira, op2, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; } + if (!op2_type_valid) { + ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", + buf_ptr(&child_type->name), + buf_ptr(&op2->value.type->name))); + return ira->codegen->invalid_instruction; + } // The type of result is populated in the following if blocks IrInstruction *result = ir_const(ira, &instruction->base, nullptr); @@ -12388,26 +12625,14 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * } static IrInstruction *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) { - ZigType *op1_type = ir_resolve_type(ira, instruction->op1->child); + ZigType *op1_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op1->child); if (type_is_invalid(op1_type)) return ira->codegen->invalid_instruction; - if (op1_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, instruction->op1, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&op1_type->name))); - return ira->codegen->invalid_instruction; - } - - ZigType *op2_type = ir_resolve_type(ira, instruction->op2->child); + ZigType *op2_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op2->child); if (type_is_invalid(op2_type)) return ira->codegen->invalid_instruction; - if (op2_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, instruction->op2, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&op2_type->name))); - return ira->codegen->invalid_instruction; - } - if (type_is_global_error_set(op1_type) || type_is_global_error_set(op2_type)) { @@ -12481,13 +12706,15 @@ static IrInstruction *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructio zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { +static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, + IrInstructionDeclVarSrc *decl_var_instruction) +{ Error err; ZigVar *var = decl_var_instruction->var; IrInstruction *init_value = decl_var_instruction->init_value->child; if (type_is_invalid(init_value->value.type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -12498,7 +12725,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct ZigType *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (type_is_invalid(explicit_type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } } @@ -12523,7 +12750,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct case ReqCompTimeInvalid: result_type = ira->codegen->builtin_types.entry_invalid; break; - case ReqCompTimeYes: { + case ReqCompTimeYes: var_class_requires_const = true; if (!var->gen_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, @@ -12532,7 +12759,6 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct result_type = ira->codegen->builtin_types.entry_invalid; } break; - } case ReqCompTimeNo: if (casted_init_value->value.special == ConstValSpecialStatic && casted_init_value->value.type->id == ZigTypeIdFn && @@ -12551,7 +12777,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct break; } - if (var->value->type != nullptr && !is_comptime_var) { + if (var->var_type != nullptr && !is_comptime_var) { // This is at least the second time we've seen this variable declaration during analysis. // This means that this is actually a different variable due to, e.g. an inline while loop. // We make a new variable so that it can hold a different type, and so the debug info can @@ -12573,8 +12799,8 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct // This must be done after possibly creating a new variable above var->ref_count = 0; - var->value->type = result_type; - assert(var->value->type); + var->var_type = result_type; + assert(var->var_type); if (type_is_invalid(result_type)) { return ir_const_void(ira, &decl_var_instruction->base); @@ -12582,13 +12808,13 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct if (decl_var_instruction->align_value == nullptr) { if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ir_const_void(ira, &decl_var_instruction->base); } var->align_bytes = get_abi_alignment(ira->codegen, result_type); } else { if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, &var->align_bytes)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; } } @@ -12605,7 +12831,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -12613,11 +12839,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct if (fn_entry) fn_entry->variable_list.append(var); - IrInstruction *result = ir_build_var_decl(&ira->new_irb, - decl_var_instruction->base.scope, decl_var_instruction->base.source_node, - var, var_type, nullptr, casted_init_value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; + return ir_build_var_decl_gen(ira, &decl_var_instruction->base, var, casted_init_value); } static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) { @@ -12739,6 +12961,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdBool: + case ZigTypeIdVector: break; case ZigTypeIdMetaType: case ZigTypeIdVoid: @@ -12773,6 +12996,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: + case ZigTypeIdVector: zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); case ZigTypeIdNamespace: case ZigTypeIdBoundFn: @@ -12947,7 +13171,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node Buf *param_name = param_decl_node->data.param_decl.name; ZigVar *var = add_variable(ira->codegen, param_decl_node, - *exec_scope, param_name, true, arg_val, nullptr); + *exec_scope, param_name, true, arg_val, nullptr, arg_val->type); *exec_scope = var->child_scope; *next_proto_i += 1; @@ -13005,7 +13229,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod if (!param_name) return false; if (!is_var_args) { ZigVar *var = add_variable(ira->codegen, param_decl_node, - *child_scope, param_name, true, arg_val, nullptr); + *child_scope, param_name, true, arg_val, nullptr, arg_val->type); *child_scope = var->child_scope; var->shadowable = !comptime_arg; @@ -13059,9 +13283,7 @@ static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) { return fn_entry->variable_list.at(next_var_i); } -static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - ZigVar *var) -{ +static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { while (var->next_var != nullptr) { var = var->next_var; } @@ -13070,14 +13292,14 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(ira->codegen->errors.length != 0); return ira->codegen->invalid_instruction; } - if (var->value->type == nullptr || type_is_invalid(var->value->type)) + if (var->var_type == nullptr || type_is_invalid(var->var_type)) return ira->codegen->invalid_instruction; bool comptime_var_mem = ir_get_var_is_comptime(var); ConstExprValue *mem_slot = nullptr; - if (var->value->special == ConstValSpecialStatic) { - mem_slot = var->value; + if (var->const_value->special == ConstValSpecialStatic) { + mem_slot = var->const_value; } else { if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) { // find the relevant exec_context @@ -13106,7 +13328,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(!comptime_var_mem); ptr_mut = ConstPtrMutRuntimeVar; } - return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type, + return ir_get_const_ptr(ira, instruction, mem_slot, var->var_type, ptr_mut, is_const, is_volatile, var->align_bytes); } } @@ -13117,7 +13339,7 @@ no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); - var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, + var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->var_type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); @@ -13126,6 +13348,96 @@ no_mem_slot: return var_ptr_instruction; } +static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *ptr, IrInstruction *uncasted_value) +{ + if (ptr->value.type->id != ZigTypeIdPointer) { + ir_add_error(ira, ptr, + buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); + return ira->codegen->invalid_instruction; + } + + if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { + return ir_const_void(ira, source_instr); + } + + if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + + ZigType *child_type = ptr->value.type->data.pointer.child_type; + IrInstruction *value = ir_implicit_cast(ira, uncasted_value, child_type); + if (value == ira->codegen->invalid_instruction) + return ira->codegen->invalid_instruction; + + if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { + if (instr_is_comptime(value)) { + ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, source_instr->source_node); + if (dest_val == nullptr) + return ira->codegen->invalid_instruction; + if (dest_val->special != ConstValSpecialRuntime) { + // TODO this allows a value stored to have the original value modified and then + // have that affect what should be a copy. We need some kind of advanced copy-on-write + // system to make these two tests pass at the same time: + // * "string literal used as comptime slice is memoized" + // * "comptime modification of const struct field" - except modified to avoid + // ConstPtrMutComptimeVar, thus defeating the logic below. + bool same_global_refs = ptr->value.data.x_ptr.mut != ConstPtrMutComptimeVar; + copy_const_val(dest_val, &value->value, same_global_refs); + if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; + break; + case OnePossibleValueYes: + break; + } + } + return ir_const_void(ira, source_instr); + } + } + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in compile time variable")); + ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); + dest_val->type = ira->codegen->builtin_types.entry_invalid; + + return ira->codegen->invalid_instruction; + } + } + + switch (type_requires_comptime(ira->codegen, child_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_instruction; + case ReqCompTimeYes: + switch (type_has_one_possible_value(ira->codegen, ptr->value.type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const_void(ira, source_instr); + } + zig_unreachable(); + case ReqCompTimeNo: + break; + } + + IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, + ptr, value); + result->value.type = ira->codegen->builtin_types.entry_void; + return result; +} + static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) @@ -13270,7 +13582,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } bool cacheable = fn_eval_cacheable(exec_scope, return_type); - IrInstruction *result = nullptr; + ConstExprValue *result = nullptr; if (cacheable) { auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); if (entry) @@ -13286,18 +13598,19 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.infer_fn = nullptr; - if (result->value.type->id == ZigTypeIdErrorUnion) { - if (result->value.data.x_err_union.err != nullptr) { + if (result->type->id == ZigTypeIdErrorUnion) { + ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { inferred_err_set_type->data.error_set.err_count = 1; inferred_err_set_type->data.error_set.errors = allocate(1); - inferred_err_set_type->data.error_set.errors[0] = result->value.data.x_err_union.err; + inferred_err_set_type->data.error_set.errors[0] = err; } - ZigType *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type; + ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type; inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; - } else if (result->value.type->id == ZigTypeIdErrorSet) { - inferred_err_set_type->data.error_set.err_count = result->value.type->data.error_set.err_count; - inferred_err_set_type->data.error_set.errors = result->value.type->data.error_set.errors; + } else if (result->type->id == ZigTypeIdErrorSet) { + inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count; + inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors; } } @@ -13305,13 +13618,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->memoized_fn_eval_table.put(exec_scope, result); } - if (type_is_invalid(result->value.type)) + if (type_is_invalid(result->type)) return ira->codegen->invalid_instruction; } - IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->value.type); - // TODO should we use copy_const_val? - new_instruction->value = result->value; + IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->type); + copy_const_val(&new_instruction->value, result, true); new_instruction->value.type = return_type; return ir_finish_anal(ira, new_instruction); } @@ -13470,18 +13782,21 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, first_var_arg, inst_fn_type_id.param_count); ZigVar *var = add_variable(ira->codegen, param_decl_node, - impl_fn->child_scope, param_name, true, var_args_val, nullptr); + impl_fn->child_scope, param_name, true, var_args_val, nullptr, var_args_val->type); impl_fn->child_scope = var->child_scope; } if (fn_proto_node->data.fn_proto.align_expr != nullptr) { - IrInstruction *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, + ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen), ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec); + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); + const_instruction->base.value = *align_result; uint32_t align_bytes = 0; - ir_resolve_align(ira, align_result, &align_bytes); + ir_resolve_align(ira, &const_instruction->base, &align_bytes); impl_fn->align_bytes = align_bytes; inst_fn_type_id.alignment = align_bytes; } @@ -13559,12 +13874,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->fn_defs.append(impl_fn); } - ZigType *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; - if (fn_type_can_fail(&impl_fn->type_entry->data.fn.fn_type_id)) { + FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id; + if (fn_type_can_fail(impl_fn_type_id)) { parent_fn_entry->calls_or_awaits_errorable_fn = true; } - size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count; + size_t impl_param_count = impl_fn_type_id->param_count; if (call_instruction->is_async) { IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, fn_ref, casted_args, impl_param_count, async_allocator_inst); @@ -13577,9 +13892,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call call_instruction->base.scope, call_instruction->base.source_node, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, call_instruction->is_async, nullptr, casted_new_stack); - new_call_instruction->value.type = return_type; + new_call_instruction->value.type = impl_fn_type_id->return_type; - ir_add_alloca(ira, new_call_instruction, return_type); + ir_add_alloca(ira, new_call_instruction, impl_fn_type_id->return_type); return ir_finish_anal(ira, new_call_instruction); } @@ -13774,6 +14089,13 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source switch (ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); + case ConstPtrSpecialNull: + if (dst_size == 0) + return ErrorNone; + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %zu bytes from null pointer", + dst_size)); + return ErrorSemanticAnalyzeFail; case ConstPtrSpecialRef: { opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("attempt to read %zu bytes from pointer to %s which is %zu bytes", @@ -13806,6 +14128,9 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source return ErrorNone; } case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: case ConstPtrSpecialDiscard: case ConstPtrSpecialHardCodedAddr: case ConstPtrSpecialFunction: @@ -13830,6 +14155,7 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_ case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: + case ZigTypeIdVector: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: @@ -14001,9 +14327,14 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime)) return ir_unreach_error(ira); - if (is_comptime || instr_is_comptime(condition)) { + ZigType *bool_type = ira->codegen->builtin_types.entry_bool; + IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); + if (type_is_invalid(casted_condition->value.type)) + return ir_unreach_error(ira); + + if (is_comptime || instr_is_comptime(casted_condition)) { bool cond_is_true; - if (!ir_resolve_bool(ira, condition, &cond_is_true)) + if (!ir_resolve_bool(ira, casted_condition, &cond_is_true)) return ir_unreach_error(ira); IrBasicBlock *old_dest_block = cond_is_true ? @@ -14022,11 +14353,6 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi return ir_finish_anal(ira, result); } - ZigType *bool_type = ira->codegen->builtin_types.entry_bool; - IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); - if (casted_condition == ira->codegen->invalid_instruction) - return ir_unreach_error(ira); - assert(cond_br_instruction->then_block != cond_br_instruction->else_block); IrBasicBlock *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base); if (new_then_block == nullptr) @@ -14065,8 +14391,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (value->value.special != ConstValSpecialRuntime) { IrInstruction *result = ir_const(ira, &phi_instruction->base, nullptr); - // TODO use copy_const_val? - result->value = value->value; + copy_const_val(&result->value, &value->value, true); return result; } else { return value; @@ -14115,14 +14440,24 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (type_is_invalid(resolved_type)) return ira->codegen->invalid_instruction; - if (resolved_type->id == ZigTypeIdComptimeFloat || - resolved_type->id == ZigTypeIdComptimeInt || - resolved_type->id == ZigTypeIdNull || - resolved_type->id == ZigTypeIdUndefined) - { - ir_add_error_node(ira, phi_instruction->base.source_node, - buf_sprintf("unable to infer expression type")); + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const(ira, &phi_instruction->base, resolved_type); + case OnePossibleValueNo: + break; + } + + switch (type_requires_comptime(ira->codegen, resolved_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_instruction; + case ReqCompTimeYes: + ir_add_error_node(ira, phi_instruction->base.source_node, + buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name))); + return ira->codegen->invalid_instruction; + case ReqCompTimeNo: + break; } bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer); @@ -14412,10 +14747,18 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO element ptr of a function casted to a ptr"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a null pointer"); } if (new_index >= mem_size) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, @@ -14465,10 +14808,18 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a slice backed by const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a slice backed by const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a slice backed by const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a slice backed by const optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO elem ptr on a slice that was ptrcast from a function"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a slice has a null pointer"); } return result; } else if (array_type->id == ZigTypeIdArray) { @@ -15155,74 +15506,23 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc } } -static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { - IrInstruction *ptr = load_ptr_instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) - return ira->codegen->invalid_instruction; - return ir_get_deref(ira, &load_ptr_instruction->base, ptr); -} - -static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { - IrInstruction *ptr = store_ptr_instruction->ptr->child; +static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) { + IrInstruction *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *value = store_ptr_instruction->value->child; + IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - if (ptr->value.type->id != ZigTypeIdPointer) { - ir_add_error(ira, ptr, - buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); + return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); +} + +static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) { + IrInstruction *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - } - - if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { - return ir_const_void(ira, &store_ptr_instruction->base); - } - - if (ptr->value.type->data.pointer.is_const && !store_ptr_instruction->base.is_gen) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } - - ZigType *child_type = ptr->value.type->data.pointer.child_type; - IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type); - if (casted_value == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; - - if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { - if (instr_is_comptime(casted_value)) { - ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, store_ptr_instruction->base.source_node); - if (dest_val == nullptr) - return ira->codegen->invalid_instruction; - if (dest_val->special != ConstValSpecialRuntime) { - *dest_val = casted_value->value; - if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { - ira->new_irb.current_basic_block->must_be_comptime_source_instr = &store_ptr_instruction->base; - } - return ir_const_void(ira, &store_ptr_instruction->base); - } - } - ir_add_error(ira, &store_ptr_instruction->base, - buf_sprintf("cannot store runtime value in compile time variable")); - ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); - dest_val->type = ira->codegen->builtin_types.entry_invalid; - - return ira->codegen->invalid_instruction; - } - } - - IrInstruction *result = ir_build_store_ptr(&ira->new_irb, - store_ptr_instruction->base.scope, store_ptr_instruction->base.source_node, - ptr, casted_value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; + return ir_get_deref(ira, &instruction->base, ptr); } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { @@ -15230,37 +15530,7 @@ static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructio ZigType *type_entry = expr_value->value.type; if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - switch (type_entry->id) { - case ZigTypeIdInvalid: - zig_unreachable(); // handled above - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdNamespace: - case ZigTypeIdBoundFn: - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdUnreachable: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdArray: - case ZigTypeIdStruct: - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdEnum: - case ZigTypeIdUnion: - case ZigTypeIdFn: - case ZigTypeIdArgTuple: - case ZigTypeIdOpaque: - case ZigTypeIdPromise: - return ir_const_type(ira, &typeof_instruction->base, type_entry); - } - - zig_unreachable(); + return ir_const_type(ira, &typeof_instruction->base, type_entry); } static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, @@ -15499,6 +15769,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdPromise: + case ZigTypeIdVector: { if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; @@ -15619,6 +15890,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdPromise: + case ZigTypeIdVector: { if ((err = ensure_complete_type(ira->codegen, child_type))) return ira->codegen->invalid_instruction; @@ -15685,6 +15957,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: + case ZigTypeIdVector: { uint64_t size_in_bytes = type_size(ira->codegen, type_entry); return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes); @@ -15693,11 +15966,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - +static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) { ZigType *type_entry = value->value.type; if (type_entry->id == ZigTypeIdOptional) { @@ -15706,60 +15975,66 @@ static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIns if (!maybe_val) return ira->codegen->invalid_instruction; - return ir_const_bool(ira, &instruction->base, !optional_value_is_null(maybe_val)); + return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val)); } IrInstruction *result = ir_build_test_nonnull(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); + source_inst->scope, source_inst->source_node, value); result->value.type = ira->codegen->builtin_types.entry_bool; return result; } else if (type_entry->id == ZigTypeIdNull) { - return ir_const_bool(ira, &instruction->base, false); + return ir_const_bool(ira, source_inst, false); } else { - return ir_const_bool(ira, &instruction->base, true); + return ir_const_bool(ira, source_inst, true); } } -static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, - IrInstructionUnwrapOptional *unwrap_maybe_instruction) -{ - IrInstruction *value = unwrap_maybe_instruction->value->child; +static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { + IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; + return ir_analyze_test_non_null(ira, &instruction->base, value); +} + +static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, bool safety_check_on) +{ + ZigType *ptr_type = base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id != ZigTypeIdOptional) { - ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, + + if (type_entry->id != ZigTypeIdOptional) { + ir_add_error_node(ira, base_ptr->source_node, buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + ZigType *child_type = type_entry->data.maybe.child_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + if (instr_is_comptime(base_ptr)) { + ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); if (!val) return ira->codegen->invalid_instruction; - ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, unwrap_maybe_instruction->base.source_node); + ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (maybe_val == nullptr) return ira->codegen->invalid_instruction; if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { if (optional_value_is_null(maybe_val)) { - ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); + ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_const(ira, &unwrap_maybe_instruction->base, result_type); + IrInstruction *result = ir_const(ira, source_instr, result_type); ConstExprValue *out_val = &result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = val->data.x_ptr.mut; - if (type_is_codegen_pointer(child_type)) { + if (types_have_same_zig_comptime_repr(type_entry, child_type)) { out_val->data.x_ptr.data.ref.pointee = maybe_val; } else { out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional; @@ -15768,13 +16043,22 @@ static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, } } - IrInstruction *result = ir_build_unwrap_maybe(&ira->new_irb, - unwrap_maybe_instruction->base.scope, unwrap_maybe_instruction->base.source_node, - value, unwrap_maybe_instruction->safety_check_on); + IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, safety_check_on); result->value.type = result_type; return result; } +static IrInstruction *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira, + IrInstructionOptionalUnwrapPtr *instruction) +{ + IrInstruction *base_ptr = instruction->base_ptr->child; + if (type_is_invalid(base_ptr->value.type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_unwrap_optional_payload(ira, &instruction->base, base_ptr, instruction->safety_check_on); +} + static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) { IrInstruction *value = ctz_instruction->value->child; if (type_is_invalid(value->value.type)) { @@ -16075,9 +16359,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *result = ir_build_load_ptr(&ira->new_irb, - switch_target_instruction->base.scope, switch_target_instruction->base.source_node, - target_value_ptr); + IrInstruction *result = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); result->value.type = target_type; return result; } @@ -16107,8 +16389,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + IrInstruction *union_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, @@ -16132,8 +16413,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + IrInstruction *enum_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); enum_value->value.type = target_type; return enum_value; } @@ -16147,6 +16427,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: + case ZigTypeIdVector: ir_add_error(ira, &switch_target_instruction->base, buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_instruction; @@ -16290,7 +16571,7 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI Error err; assert(container_type->id == ZigTypeIdUnion); - if ((err = ensure_complete_type(ira->codegen, container_type))) + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; if (instr_field_count != 1) { @@ -16322,7 +16603,8 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope) + || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes; if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || !type_has_bits(casted_field_value->value.type)) { @@ -16334,12 +16616,8 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI ConstExprValue *out_val = &result->value; out_val->data.x_union.payload = field_val; out_val->data.x_union.tag = type_field->enum_field->value; - - ConstParent *parent = get_const_val_parent(ira->codegen, field_val); - if (parent != nullptr) { - parent->id = ConstParentIdUnion; - parent->data.p_union.union_val = out_val; - } + out_val->parent.id = ConstParentIdUnion; + out_val->parent.data.p_union.union_val = out_val; return result; } @@ -16366,7 +16644,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; } - if ((err = ensure_complete_type(ira->codegen, container_type))) + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; size_t actual_field_count = container_type->data.structure.src_field_count; @@ -16377,7 +16655,8 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc IrInstructionStructInitField *new_fields = allocate(actual_field_count); - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope) + || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes; ConstExprValue const_val = {}; const_val.special = ConstValSpecialStatic; @@ -16445,9 +16724,8 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (const_val.special == ConstValSpecialStatic) { IrInstruction *result = ir_const(ira, instruction, nullptr); ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = container_type; + copy_const_val(out_val, &const_val, true); + out_val->type = container_type; for (size_t i = 0; i < instr_field_count; i += 1) { ConstExprValue *field_val = &out_val->data.x_struct.fields[i]; @@ -16479,127 +16757,119 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) { - IrInstruction *container_type_value = instruction->container_type->child; - if (type_is_invalid(container_type_value->value.type)) + ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); + if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; size_t elem_count = instruction->item_count; - if (container_type_value->value.type->id == ZigTypeIdMetaType) { - ZigType *container_type = ir_resolve_type(ira, container_type_value); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_instruction; - if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { - return ir_analyze_container_init_fields(ira, &instruction->base, container_type, - 0, nullptr); - } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { - // array is same as slice init but we make a compile error if the length is wrong - ZigType *child_type; - if (container_type->id == ZigTypeIdArray) { - child_type = container_type->data.array.child_type; - if (container_type->data.array.len != elem_count) { - ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); + if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, + 0, nullptr); + } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { + // array is same as slice init but we make a compile error if the length is wrong + ZigType *child_type; + if (container_type->id == ZigTypeIdArray) { + child_type = container_type->data.array.child_type; + if (container_type->data.array.len != elem_count) { + ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); - ir_add_error(ira, &instruction->base, - buf_sprintf("expected %s literal, found %s literal", - buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); - return ira->codegen->invalid_instruction; - } - } else { - ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; - assert(pointer_type->id == ZigTypeIdPointer); - child_type = pointer_type->data.pointer.child_type; + ir_add_error(ira, &instruction->base, + buf_sprintf("expected %s literal, found %s literal", + buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); + return ira->codegen->invalid_instruction; } + } else { + ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; + assert(pointer_type->id == ZigTypeIdPointer); + child_type = pointer_type->data.pointer.child_type; + } - ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); + ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); - ConstExprValue const_val = {}; - const_val.special = ConstValSpecialStatic; - const_val.type = fixed_size_array_type; - const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count); + ConstExprValue const_val = {}; + const_val.special = ConstValSpecialStatic; + const_val.type = fixed_size_array_type; + const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count); - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); - IrInstruction **new_items = allocate(elem_count); + IrInstruction **new_items = allocate(elem_count); - IrInstruction *first_non_const_instruction = nullptr; + IrInstruction *first_non_const_instruction = nullptr; - for (size_t i = 0; i < elem_count; i += 1) { - IrInstruction *arg_value = instruction->items[i]->child; - if (type_is_invalid(arg_value->value.type)) - return ira->codegen->invalid_instruction; + for (size_t i = 0; i < elem_count; i += 1) { + IrInstruction *arg_value = instruction->items[i]->child; + if (type_is_invalid(arg_value->value.type)) + return ira->codegen->invalid_instruction; - IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); - if (casted_arg == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; + IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); + if (casted_arg == ira->codegen->invalid_instruction) + return ira->codegen->invalid_instruction; - new_items[i] = casted_arg; - - if (const_val.special == ConstValSpecialStatic) { - if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { - ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); - if (!elem_val) - return ira->codegen->invalid_instruction; - - copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true); - } else { - first_non_const_instruction = casted_arg; - const_val.special = ConstValSpecialRuntime; - } - } - } + new_items[i] = casted_arg; if (const_val.special == ConstValSpecialStatic) { - IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = fixed_size_array_type; - for (size_t i = 0; i < elem_count; i += 1) { - ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i]; - ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); - if (parent != nullptr) { - parent->id = ConstParentIdArray; - parent->data.p_array.array_val = out_val; - parent->data.p_array.elem_index = i; - } + if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { + ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); + if (!elem_val) + return ira->codegen->invalid_instruction; + + copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true); + } else { + first_non_const_instruction = casted_arg; + const_val.special = ConstValSpecialRuntime; } - return result; } + } - if (is_comptime) { - ir_add_error_node(ira, first_non_const_instruction->source_node, - buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->invalid_instruction; + if (const_val.special == ConstValSpecialStatic) { + IrInstruction *result = ir_const(ira, &instruction->base, nullptr); + ConstExprValue *out_val = &result->value; + copy_const_val(out_val, &const_val, true); + result->value.type = fixed_size_array_type; + for (size_t i = 0; i < elem_count; i += 1) { + ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i]; + ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); + if (parent != nullptr) { + parent->id = ConstParentIdArray; + parent->data.p_array.array_val = out_val; + parent->data.p_array.elem_index = i; + } } + return result; + } - IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, - container_type_value, elem_count, new_items); - new_instruction->value.type = fixed_size_array_type; - ir_add_alloca(ira, new_instruction, fixed_size_array_type); - return new_instruction; - } else if (container_type->id == ZigTypeIdVoid) { - if (elem_count != 0) { - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("void expression expects no arguments")); - return ira->codegen->invalid_instruction; - } - return ir_const_void(ira, &instruction->base); - } else { - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("type '%s' does not support array initialization", - buf_ptr(&container_type->name))); + if (is_comptime) { + ir_add_error_node(ira, first_non_const_instruction->source_node, + buf_sprintf("unable to evaluate constant expression")); return ira->codegen->invalid_instruction; } + + IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, + nullptr, elem_count, new_items); + new_instruction->value.type = fixed_size_array_type; + ir_add_alloca(ira, new_instruction, fixed_size_array_type); + return new_instruction; + } else if (container_type->id == ZigTypeIdVoid) { + if (elem_count != 0) { + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("void expression expects no arguments")); + return ira->codegen->invalid_instruction; + } + return ir_const_void(ira, &instruction->base); } else { - ir_add_error(ira, container_type_value, - buf_sprintf("expected type, found '%s' value", buf_ptr(&container_type_value->value.type->name))); + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("type '%s' does not support array initialization", + buf_ptr(&container_type->name))); return ira->codegen->invalid_instruction; } } -static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { +static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, + IrInstructionContainerInitFields *instruction) +{ IrInstruction *container_type_value = instruction->container_type->child; ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) @@ -16659,7 +16929,7 @@ static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruct if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *casted_value = ir_implicit_cast(ira, value, value->value.type); + IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set); if (type_is_invalid(casted_value->value.type)) return ira->codegen->invalid_instruction; @@ -16920,10 +17190,11 @@ static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, Zig ZigVar *var = tld->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ira->codegen->builtin_types.entry_invalid; - assert(var->value->type->id == ZigTypeIdMetaType); - return var->value->data.x_type; + + assert(var->const_value->type->id == ZigTypeIdMetaType); + return var->const_value->data.x_type; } static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { @@ -16978,7 +17249,6 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco definition_array->special = ConstValSpecialStatic; definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count); definition_array->data.x_array.special = ConstArraySpecialNone; - definition_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; definition_array->data.x_array.data.s_none.elements = create_const_vals(definition_count); init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false); @@ -17009,33 +17279,30 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub; inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = type_info_definition_data_type; - inner_fields[2].data.x_union.parent.id = ConstParentIdStruct; - inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; - inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; + inner_fields[2].parent.id = ConstParentIdStruct; + inner_fields[2].parent.data.p_struct.struct_val = definition_val; + inner_fields[2].parent.data.p_struct.field_index = 1; switch (curr_entry->value->id) { case TldIdVar: { ZigVar *var = ((TldVar *)curr_entry->value)->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ErrorSemanticAnalyzeFail; - if (var->value->type->id == ZigTypeIdMetaType) - { + if (var->const_value->type->id == ZigTypeIdMetaType) { // We have a variable of type 'type', so it's actually a type definition. // 0: Data.Type: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); - inner_fields[2].data.x_union.payload = var->value; - } - else - { + inner_fields[2].data.x_union.payload = var->const_value; + } else { // We have a variable of another type, so we store the type of the variable. // 1: Data.Var: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1); ConstExprValue *payload = create_const_vals(1); payload->type = ira->codegen->builtin_types.entry_type; - payload->data.x_type = var->value->type; + payload->data.x_type = var->const_value->type; inner_fields[2].data.x_union.payload = payload; } @@ -17055,8 +17322,8 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco ConstExprValue *fn_def_val = create_const_vals(1); fn_def_val->special = ConstValSpecialStatic; fn_def_val->type = type_info_fn_def_type; - fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; - fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; + fn_def_val->parent.id = ConstParentIdUnion; + fn_def_val->parent.data.p_union.union_val = &inner_fields[2]; ConstExprValue *fn_def_fields = create_const_vals(9); fn_def_val->data.x_struct.fields = fn_def_fields; @@ -17120,20 +17387,18 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr), fn_arg_count); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_name_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false); - for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) - { + for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index); ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index]; ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name); init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true); - fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_name_val->data.x_struct.parent.data.p_array.array_val = fn_arg_name_array; - fn_arg_name_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + fn_arg_name_val->parent.id = ConstParentIdArray; + fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array; + fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index; } inner_fields[2].data.x_union.payload = fn_def_val; @@ -17354,6 +17619,27 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE break; } + case ZigTypeIdVector: { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Vector", nullptr); + + ConstExprValue *fields = create_const_vals(2); + result->data.x_struct.fields = fields; + + // len: usize + ensure_field_index(result->type, "len", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.vector.len); + // child: type + ensure_field_index(result->type, "child", 1); + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_type; + fields[1].data.x_type = type_entry->data.vector.elem_type; + + break; + } case ZigTypeIdOptional: { result = create_const_vals(1); @@ -17426,7 +17712,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE enum_field_array->special = ConstValSpecialStatic; enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count); enum_field_array->data.x_array.special = ConstArraySpecialNone; - enum_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count); init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false); @@ -17436,9 +17721,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ConstExprValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index]; make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); - enum_field_val->data.x_struct.parent.id = ConstParentIdArray; - enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; - enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; + enum_field_val->parent.id = ConstParentIdArray; + enum_field_val->parent.data.p_array.array_val = enum_field_array; + enum_field_val->parent.data.p_array.elem_index = enum_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -17465,7 +17750,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE error_array->special = ConstValSpecialStatic; error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count); error_array->data.x_array.special = ConstArraySpecialNone; - error_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; error_array->data.x_array.data.s_none.elements = create_const_vals(error_count); init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); @@ -17489,9 +17773,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value); error_val->data.x_struct.fields = inner_fields; - error_val->data.x_struct.parent.id = ConstParentIdArray; - error_val->data.x_struct.parent.data.p_array.array_val = error_array; - error_val->data.x_struct.parent.data.p_array.elem_index = error_index; + error_val->parent.id = ConstParentIdArray; + error_val->parent.data.p_array.array_val = error_array; + error_val->parent.data.p_array.elem_index = error_index; } break; @@ -17560,7 +17844,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE union_field_array->special = ConstValSpecialStatic; union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count); union_field_array->data.x_array.special = ConstArraySpecialNone; - union_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count); init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); @@ -17593,9 +17876,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); union_field_val->data.x_struct.fields = inner_fields; - union_field_val->data.x_struct.parent.id = ConstParentIdArray; - union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; - union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; + union_field_val->parent.id = ConstParentIdArray; + union_field_val->parent.data.p_array.array_val = union_field_array; + union_field_val->parent.data.p_array.elem_index = union_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -17635,7 +17918,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE struct_field_array->special = ConstValSpecialStatic; struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count); struct_field_array->data.x_array.special = ConstArraySpecialNone; - struct_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count); init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); @@ -17669,9 +17951,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true); struct_field_val->data.x_struct.fields = inner_fields; - struct_field_val->data.x_struct.parent.id = ConstParentIdArray; - struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; - struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; + struct_field_val->parent.id = ConstParentIdArray; + struct_field_val->parent.data.p_array.array_val = struct_field_array; + struct_field_val->parent.data.p_array.elem_index = struct_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 2); @@ -17741,7 +18023,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE fn_arg_array->special = ConstValSpecialStatic; fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count); fn_arg_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false); @@ -17778,9 +18059,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE } fn_arg_val->data.x_struct.fields = inner_fields; - fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_val->data.x_struct.parent.data.p_array.array_val = fn_arg_array; - fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + fn_arg_val->parent.id = ConstParentIdArray; + fn_arg_val->parent.data.p_array.array_val = fn_arg_array; + fn_arg_val->parent.data.p_array.elem_index = fn_arg_index; } break; @@ -17824,8 +18105,8 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, if (payload != nullptr) { assert(payload->type->id == ZigTypeIdStruct); - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = out_val; + payload->parent.id = ConstParentIdUnion; + payload->parent.data.p_union.union_val = out_val; } return result; @@ -17897,10 +18178,10 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct // Execute the C import block like an inline function ZigType *void_type = ira->codegen->builtin_types.entry_void; - IrInstruction *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, + ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf, block_node, nullptr, nullptr); - if (type_is_invalid(cimport_result->value.type)) + if (type_is_invalid(cimport_result->type)) return ira->codegen->invalid_instruction; find_libc_include_path(ira->codegen); @@ -18050,7 +18331,7 @@ static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstru return result; } -static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { +static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchgSrc *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child); if (type_is_invalid(operand_type)) return ira->codegen->invalid_instruction; @@ -18122,9 +18403,9 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi zig_panic("TODO compile-time execution of cmpxchg"); } - IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, - operand_type, success_order, failure_order); + IrInstruction *result = ir_build_cmpxchg_gen(ira, &instruction->base, + casted_ptr, casted_cmp_value, casted_new_value, + success_order, failure_order, instruction->is_weak); result->value.type = get_optional_type(ira->codegen, operand_type); ir_add_alloca(ira, result, result->value.type); return result; @@ -18296,18 +18577,6 @@ static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInst return ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type); } -static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { - Error err; - - if (ty->id == ZigTypeIdPointer) { - if ((err = type_resolve(ira->codegen, ty->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return err; - } - - *result_align = get_ptr_align(ira->codegen, ty); - return ErrorNone; -} - static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { Error err; @@ -18426,6 +18695,20 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); } +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { + Error err; + + ZigType *ptr_type = get_src_ptr_type(ty); + assert(ptr_type != nullptr); + if (ptr_type->id == ZigTypeIdPointer) { + if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return err; + } + + *result_align = get_ptr_align(ira->codegen, ty); + return ErrorNone; +} + static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) @@ -18532,6 +18815,27 @@ static IrInstruction *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstruct return ir_const_type(ira, &instruction->base, get_int_type(ira->codegen, is_signed, (uint32_t)bit_count)); } +static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstructionVectorType *instruction) { + uint64_t len; + if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len)) + return ira->codegen->invalid_instruction; + + ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction; + + if (!is_valid_vector_elem_type(elem_type)) { + ir_add_error(ira, instruction->elem_type, + buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid", + buf_ptr(&elem_type->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type); + + return ir_const_type(ira, &instruction->base, vector_type); +} + static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) { IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) @@ -18630,10 +18934,18 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memset on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memset on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memset on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memset on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memset on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memset on null ptr"); } size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint); @@ -18745,10 +19057,18 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (dest_start + count > dest_end) { @@ -18781,10 +19101,18 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (src_start + count > src_end) { @@ -18812,9 +19140,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (type_is_invalid(ptr_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = ptr_ptr->value.type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = ptr_type->data.pointer.child_type; + ZigType *ptr_ptr_type = ptr_ptr->value.type; + assert(ptr_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = ptr_ptr_type->data.pointer.child_type; IrInstruction *start = instruction->start->child; if (type_is_invalid(start->value.type)) @@ -18843,10 +19171,10 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, - ptr_type->data.pointer.is_const || is_comptime_const, - ptr_type->data.pointer.is_volatile, + ptr_ptr_type->data.pointer.is_const || is_comptime_const, + ptr_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - ptr_type->data.pointer.explicit_alignment, 0, 0); + ptr_ptr_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { @@ -18944,6 +19272,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -18951,6 +19285,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null ptr"); } } else if (is_slice(array_type)) { ConstExprValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node); @@ -18981,6 +19317,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -18988,6 +19330,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of slice cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null"); } } else { zig_unreachable(); @@ -19053,6 +19397,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_unreachable(); case ConstPtrSpecialBaseStruct: zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO"); case ConstPtrSpecialHardCodedAddr: init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, @@ -19061,6 +19411,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO"); + case ConstPtrSpecialNull: + zig_panic("TODO"); } ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; @@ -19287,6 +19639,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: + case ZigTypeIdVector: { uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry); return ir_const_unsigned(ira, &instruction->base, align_in_bytes); @@ -19416,7 +19769,8 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; if (err_union_val->special != ConstValSpecialRuntime) { - return ir_const_bool(ira, &instruction->base, (err_union_val->data.x_err_union.err != nullptr)); + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + return ir_const_bool(ira, &instruction->base, (err != nullptr)); } } @@ -19442,48 +19796,47 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct } } -static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, - IrInstructionUnwrapErrCode *instruction) -{ - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) +static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) { + IrInstruction *base_ptr = instruction->err_union->child; + if (type_is_invalid(base_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; + ZigType *ptr_type = base_ptr->value.type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; - assert(err); - IrInstruction *result = ir_const(ira, &instruction->base, - type_entry->data.error_union.err_set_type); - result->value.data.x_err_set = err; - return result; - } - } - - IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); - result->value.type = type_entry->data.error_union.err_set_type; - return result; - } else { - ir_add_error(ira, value, + if (type_entry->id != ZigTypeIdErrorUnion) { + ir_add_error(ira, base_ptr, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + + if (instr_is_comptime(base_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_instruction; + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + assert(err); + + IrInstruction *result = ir_const(ira, &instruction->base, + type_entry->data.error_union.err_set_type); + result->value.data.x_err_set = err; + return result; + } + } + + IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, base_ptr); + result->value.type = type_entry->data.error_union.err_set_type; + return result; } static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, @@ -19499,48 +19852,48 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - ZigType *payload_type = type_entry->data.error_union.payload_type; - if (type_is_invalid(payload_type)) { - return ira->codegen->invalid_instruction; - } - ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - PtrLenSingle, 0, 0, 0); - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; - if (err != nullptr) { - ir_add_error(ira, &instruction->base, - buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); - return ira->codegen->invalid_instruction; - } - IrInstruction *result = ir_const(ira, &instruction->base, result_type); - result->value.data.x_ptr.special = ConstPtrSpecialRef; - result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; - return result; - } - } - - IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on); - result->value.type = result_type; - return result; - } else { + if (type_entry->id != ZigTypeIdErrorUnion) { ir_add_error(ira, value, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + ZigType *payload_type = type_entry->data.error_union.payload_type; + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_instruction; + + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(value)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_instruction; + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { + ir_add_error(ira, &instruction->base, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_instruction; + } + + IrInstruction *result = ir_const(ira, &instruction->base, result_type); + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; + return result; + } + } + + IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on); + result->value.type = result_type; + return result; } static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) { @@ -19957,7 +20310,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type); + IrInstruction *result = ir_const(ira, target, result_type); copy_const_val(&result->value, val, false); result->value.type = result_type; return result; @@ -20005,8 +20358,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, - dest_type); + IrInstruction *result = ir_const(ira, source_instr, dest_type); copy_const_val(&result->value, val, false); result->value.type = dest_type; return result; @@ -20029,9 +20381,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return ira->codegen->invalid_instruction; } - IrInstruction *casted_ptr = ir_build_ptr_cast(&ira->new_irb, source_instr->scope, - source_instr->source_node, nullptr, ptr); - casted_ptr->value.type = dest_type; + IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr); if (type_has_bits(dest_type) && !type_has_bits(src_type)) { ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -20057,7 +20407,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return result; } -static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { +static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCastSrc *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) @@ -20071,6 +20421,17 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value); } +static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) { + size_t buf_i = 0; + // TODO optimize the buf case + expand_undef_array(codegen, val); + for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) { + ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i]; + buf_write_value_bytes(codegen, &buf[buf_i], elem); + buf_i += type_size(codegen, elem->type); + } +} + static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) { if (val->special == ConstValSpecialUndef) val->special = ConstValSpecialStatic; @@ -20116,17 +20477,9 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_unreachable(); } case ZigTypeIdArray: - { - size_t buf_i = 0; - // TODO optimize the buf case - expand_undef_array(codegen, val); - for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) { - ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i]; - buf_write_value_bytes(codegen, &buf[buf_i], elem); - buf_i += type_size(codegen, elem->type); - } - } - return; + return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len); + case ZigTypeIdVector: + return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len); case ZigTypeIdStruct: zig_panic("TODO buf_write_value_bytes struct type"); case ZigTypeIdOptional: @@ -20143,6 +20496,31 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_unreachable(); } +static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, + ConstExprValue *val, ZigType *elem_type, size_t len) +{ + Error err; + uint64_t elem_size = type_size(codegen, elem_type); + + switch (val->data.x_array.special) { + case ConstArraySpecialNone: + val->data.x_array.data.s_none.elements = create_const_vals(len); + for (size_t i = 0; i < len; i++) { + ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i]; + elem->special = ConstValSpecialStatic; + elem->type = elem_type; + if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) + return err; + } + return ErrorNone; + case ConstArraySpecialUndef: + zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); + case ConstArraySpecialBuf: + zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); + } + zig_unreachable(); +} + static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) { Error err; assert(val->special == ConstValSpecialStatic); @@ -20181,29 +20559,27 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn); return ErrorNone; } - case ZigTypeIdArray: { - uint64_t elem_size = type_size(codegen, val->type->data.array.child_type); - size_t len = val->type->data.array.len; - - switch (val->data.x_array.special) { - case ConstArraySpecialNone: - val->data.x_array.data.s_none.elements = create_const_vals(len); - for (size_t i = 0; i < len; i++) { - ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i]; - elem->special = ConstValSpecialStatic; - elem->type = val->type->data.array.child_type; - if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) - return err; - } - break; - case ConstArraySpecialUndef: - zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); - case ConstArraySpecialBuf: - zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); + case ZigTypeIdArray: + return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type, + val->type->data.array.len); + case ZigTypeIdVector: + return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type, + val->type->data.vector.len); + case ZigTypeIdEnum: + switch (val->type->data.enumeration.layout) { + case ContainerLayoutAuto: + zig_panic("TODO buf_read_value_bytes enum auto"); + case ContainerLayoutPacked: + zig_panic("TODO buf_read_value_bytes enum packed"); + case ContainerLayoutExtern: { + ZigType *tag_int_type = val->type->data.enumeration.tag_int_type; + assert(tag_int_type->id == ZigTypeIdInt); + bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count, + codegen->is_big_endian, tag_int_type->data.integral.is_signed); + return ErrorNone; + } } - - return ErrorNone; - } + zig_unreachable(); case ZigTypeIdStruct: switch (val->type->data.structure.layout) { case ContainerLayoutAuto: { @@ -20242,8 +20618,6 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou zig_panic("TODO buf_read_value_bytes error union"); case ZigTypeIdErrorSet: zig_panic("TODO buf_read_value_bytes pure error type"); - case ZigTypeIdEnum: - zig_panic("TODO buf_read_value_bytes enum type"); case ZigTypeIdFn: zig_panic("TODO buf_read_value_bytes fn type"); case ZigTypeIdUnion: @@ -20264,10 +20638,10 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct if (type_is_invalid(src_type)) return ira->codegen->invalid_instruction; - if ((err = ensure_complete_type(ira->codegen, dest_type))) + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; - if ((err = ensure_complete_type(ira->codegen, src_type))) + if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; if (get_codegen_ptr_type(src_type) != nullptr) { @@ -20330,6 +20704,16 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } + uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type); + uint64_t src_size_bits = type_size_bits(ira->codegen, src_type); + if (dest_size_bits != src_size_bits) { + ir_add_error(ira, &instruction->base, + buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits", + buf_ptr(&dest_type->name), dest_size_bits, + buf_ptr(&src_type->name), src_size_bits)); + return ira->codegen->invalid_instruction; + } + if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) @@ -20410,8 +20794,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, case TldIdContainer: case TldIdCompTime: zig_unreachable(); - case TldIdVar: - { + case TldIdVar: { TldVar *tld_var = (TldVar *)tld; ZigVar *var = tld_var->var; @@ -20429,8 +20812,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, return ir_get_deref(ira, &instruction->base, var_ptr); } } - case TldIdFn: - { + case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); @@ -20476,8 +20858,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru if (!val) return ira->codegen->invalid_instruction; if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, usize); + IrInstruction *result = ir_const(ira, &instruction->base, usize); bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); result->value.type = usize; return result; @@ -21315,6 +21696,11 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: + case IrInstructionIdDeclVarGen: + case IrInstructionIdPtrCastGen: + case IrInstructionIdCmpxchgGen: + case IrInstructionIdArrayToVector: + case IrInstructionIdVectorToArray: zig_unreachable(); case IrInstructionIdReturn: @@ -21325,8 +21711,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); case IrInstructionIdBinOp: return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); - case IrInstructionIdDeclVar: - return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction); + case IrInstructionIdDeclVarSrc: + return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVarSrc *)instruction); case IrInstructionIdLoadPtr: return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); case IrInstructionIdStorePtr: @@ -21371,8 +21757,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapOptional: - return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstructionOptionalUnwrapPtr *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -21413,8 +21799,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_c_undef(ira, (IrInstructionCUndef *)instruction); case IrInstructionIdEmbedFile: return ir_analyze_instruction_embed_file(ira, (IrInstructionEmbedFile *)instruction); - case IrInstructionIdCmpxchg: - return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgSrc: + return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchgSrc *)instruction); case IrInstructionIdFence: return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: @@ -21437,6 +21823,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction); case IrInstructionIdIntType: return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); + case IrInstructionIdVectorType: + return ir_analyze_instruction_vector_type(ira, (IrInstructionVectorType *)instruction); case IrInstructionIdBoolNot: return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction); case IrInstructionIdMemset: @@ -21481,8 +21869,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction); case IrInstructionIdPanic: return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction); - case IrInstructionIdPtrCast: - return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastSrc: + return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction); case IrInstructionIdBitCast: return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)instruction); case IrInstructionIdIntToPtr: @@ -21666,7 +22054,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBr: case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: - case IrInstructionIdDeclVar: + case IrInstructionIdDeclVarSrc: + case IrInstructionIdDeclVarGen: case IrInstructionIdStorePtr: case IrInstructionIdCall: case IrInstructionIdReturn: @@ -21681,7 +22070,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCInclude: case IrInstructionIdCDefine: case IrInstructionIdCUndef: - case IrInstructionIdCmpxchg: case IrInstructionIdFence: case IrInstructionIdMemset: case IrInstructionIdMemcpy: @@ -21709,6 +22097,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: case IrInstructionIdAtomicRmw: + case IrInstructionIdCmpxchgGen: + case IrInstructionIdCmpxchgSrc: return true; case IrInstructionIdPhi: @@ -21734,7 +22124,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: - case IrInstructionIdUnwrapOptional: + case IrInstructionIdOptionalUnwrapPtr: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdPopCount: @@ -21745,6 +22135,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdEmbedFile: case IrInstructionIdTruncate: case IrInstructionIdIntType: + case IrInstructionIdVectorType: case IrInstructionIdBoolNot: case IrInstructionIdSlice: case IrInstructionIdMemberCount: @@ -21761,7 +22152,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: - case IrInstructionIdPtrCast: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdPtrCastGen: case IrInstructionIdBitCast: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: @@ -21804,6 +22196,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFromBytes: case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: + case IrInstructionIdVectorToArray: + case IrInstructionIdArrayToVector: return false; case IrInstructionIdAsm: diff --git a/src/ir.hpp b/src/ir.hpp index 7af1d7f52b..0a7c614812 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -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); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b5099db86a..e19aa6dda8 100644 --- a/src/ir_print.cpp +++ b/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"); } diff --git a/src/main.cpp b/src/main.cpp index fd8e3db2fa..81f49089be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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); diff --git a/src/os.cpp b/src/os.cpp index 2f0379c09b..8eb6b34654 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1808,7 +1808,7 @@ Error os_self_exe_shared_libs(ZigList &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; diff --git a/src/os.hpp b/src/os.hpp index d81a2362a5..68ac6e0171 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -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); diff --git a/src/parser.cpp b/src/parser.cpp index 077365995e..81bd469d1c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -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 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); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 921ee4de09..d43bfabf6d 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -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); diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 6ba8356ddb..7c16d3af02 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -263,6 +263,19 @@ ZigLLVMDIType *ZigLLVMCreateDebugBasicType(ZigLLVMDIBuilder *dibuilder, const ch return reinterpret_cast(di_type); } +struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder, + uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty) +{ + SmallVector subrange; + subrange.push_back(reinterpret_cast(dibuilder)->getOrCreateSubrange(0, Size)); + DIType *di_type = reinterpret_cast(dibuilder)->createVectorType( + Size, + AlignInBits, + reinterpret_cast(Ty), + reinterpret_cast(dibuilder)->getOrCreateArray(subrange)); + return reinterpret_cast(di_type); +} + ZigLLVMDIType *ZigLLVMCreateDebugArrayType(ZigLLVMDIBuilder *dibuilder, uint64_t size_in_bits, uint64_t align_in_bits, ZigLLVMDIType *elem_type, int elem_count) { diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 8588b7e272..8247368240 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -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); diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 1aab4c32de..183c434dc5 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -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); } diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index b69a93733c..503fa0c0ce 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -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); } diff --git a/std/build.zig b/std/build.zig index 90f5bec656..5246d97339 100644 --- a/std/build.zig +++ b/std/build.zig @@ -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) { diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 86c5e4828e..4f0ea4a02b 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -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. diff --git a/std/c/index.zig b/std/c/index.zig index 4aab39d931..058d879c09 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -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; diff --git a/std/debug/index.zig b/std/debug/index.zig index 445f943594..838bd0c166 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -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; diff --git a/std/event/channel.zig b/std/event/channel.zig index 133ce1c69c..f8cdae6208 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -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(); diff --git a/std/event/fs.zig b/std/event/fs.zig index 1b8e1aa5dc..097f2beddc 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -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 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 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); diff --git a/std/event/future.zig b/std/event/future.zig index d61768b198..55ed01046d 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -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(); diff --git a/std/event/group.zig b/std/event/group.zig index 9f2687a5b3..7f6b5d953b 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -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(); diff --git a/std/event/lock.zig b/std/event/lock.zig index 46e0d13468..01978e1909 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -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(); diff --git a/std/event/loop.zig b/std/event/loop.zig index 43965e8293..d5228db604 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -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(); diff --git a/std/event/net.zig b/std/event/net.zig index 6838704084..9dac6aa566 100644 --- a/std/event/net.zig +++ b/std/event/net.zig @@ -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; diff --git a/std/event/rwlock.zig b/std/event/rwlock.zig index 5d48ea893e..f272ac71ea 100644 --- a/std/event/rwlock.zig +++ b/std/event/rwlock.zig @@ -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(); diff --git a/std/fmt/index.zig b/std/fmt/index.zig index b010072273..6097a12c23 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -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")); } { diff --git a/std/hash_map.zig b/std/hash_map.zig index 99237047e0..a63a549814 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -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"), } } diff --git a/std/heap.zig b/std/heap.zig index 46b247fa7e..fd2ce1e965 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -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); diff --git a/std/index.zig b/std/index.zig index 33eec14b0e..80d1e46bb6 100644 --- a/std/index.zig +++ b/std/index.zig @@ -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"); diff --git a/std/io.zig b/std/io.zig index 428d95725d..c8701aeda6 100644 --- a/std/io.zig +++ b/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"); + } +} diff --git a/std/io_test.zig b/std/io_test.zig index 5224199527..0bee0ddaf0 100644 --- a/std/io_test.zig +++ b/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); +} \ No newline at end of file diff --git a/std/linked_list.zig b/std/linked_list.zig index c3db55b5a6..7021cac707 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -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. diff --git a/std/mem.zig b/std/mem.zig index fb5f6fd5da..26ae4ef089 100644 --- a/std/mem.zig +++ b/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 { diff --git a/std/meta/index.zig b/std/meta/index.zig index 69a3097288..4d195a6a12 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -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{ diff --git a/std/meta/trait.zig b/std/meta/trait.zig index caf7f1be04..c9d9e971c9 100644 --- a/std/meta/trait.zig +++ b/std/meta/trait.zig @@ -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)) { diff --git a/std/mutex.zig b/std/mutex.zig index e35bd81bc4..54173fa38a 100644 --- a/std/mutex.zig +++ b/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(); diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 0aa896ff1b..7aa8582369 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -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); diff --git a/std/os/index.zig b/std/os/index.zig index 78d543583d..451c0a3436 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -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 { diff --git a/std/os/path.zig b/std/os/path.zig index 31f949f0d6..0b960fa2da 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -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(); diff --git a/std/os/test.zig b/std/os/test.zig index 5142920687..f14cf47786 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -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); diff --git a/std/os/time.zig b/std/os/time.zig index 42f32f8fee..4b11a69376 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -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 { diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 3d6ee67113..3f19905835 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -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; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 202b8bffeb..66b9552c5f 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -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, +}; diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig new file mode 100644 index 0000000000..37582d49c1 --- /dev/null +++ b/std/statically_initialized_mutex.zig @@ -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); + } +} diff --git a/std/zig/parse.zig b/std/zig/parse.zig index a216484d7d..783464c620 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -17,14 +17,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { defer stack.deinit(); const arena = &tree_arena.allocator; - const root_node = try arena.create(ast.Node.Root{ + const root_node = try arena.create(ast.Node.Root); + root_node.* = ast.Node.Root{ .base = ast.Node{ .id = ast.Node.Id.Root }, .decls = ast.Node.Root.DeclList.init(arena), .doc_comments = null, .shebang = null, // initialized when we get the eof token .eof_token = undefined, - }); + }; var tree = ast.Tree{ .source = source, @@ -75,20 +76,22 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); - const test_node = try arena.create(ast.Node.TestDecl{ + }; + const test_node = try arena.create(ast.Node.TestDecl); + test_node.* = ast.Node.TestDecl{ .base = ast.Node{ .id = ast.Node.Id.TestDecl }, .doc_comments = comments, .test_token = token_index, .name = undefined, .body_node = &block.base, - }); + }; try root_node.decls.push(&test_node.base); try stack.append(State{ .Block = block }); try stack.append(State{ @@ -119,19 +122,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_comptime => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); - const node = try arena.create(ast.Node.Comptime{ + }; + const node = try arena.create(ast.Node.Comptime); + node.* = ast.Node.Comptime{ .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = &block.base, .doc_comments = comments, - }); + }; try root_node.decls.push(&node.base); stack.append(State.TopLevel) catch unreachable; @@ -235,14 +240,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { return tree; } - const node = try arena.create(ast.Node.Use{ + const node = try arena.create(ast.Node.Use); + node.* = ast.Node.Use{ .base = ast.Node{ .id = ast.Node.Id.Use }, .use_token = token_index, .visib_token = ctx.visib_token, .expr = undefined, .semicolon_token = undefined, .doc_comments = ctx.comments, - }); + }; try ctx.decls.push(&node.base); stack.append(State{ @@ -276,7 +282,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, @@ -292,7 +299,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = ctx.lib_name, .align_expr = null, .section_expr = null, - }); + }; try ctx.decls.push(&fn_proto.base); stack.append(State{ .FnDef = fn_proto }) catch unreachable; try stack.append(State{ .FnProto = fn_proto }); @@ -309,12 +316,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_async => { - const async_node = try arena.create(ast.Node.AsyncAttribute{ + const async_node = try arena.create(ast.Node.AsyncAttribute); + async_node.* = ast.Node.AsyncAttribute{ .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = token_index, .allocator_type = null, .rangle_bracket = null, - }); + }; fn_proto.async_attr = async_node; try stack.append(State{ @@ -341,13 +349,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.TopLevelExternOrField => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |identifier| { - const node = try arena.create(ast.Node.StructField{ + const node = try arena.create(ast.Node.StructField); + node.* = ast.Node.StructField{ .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, .name_token = identifier, .type_expr = undefined, - }); + }; const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); node_ptr.* = &node.base; @@ -391,7 +400,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - const node = try arena.create(ast.Node.ContainerDecl{ + const node = try arena.create(ast.Node.ContainerDecl); + node.* = ast.Node.ContainerDecl{ .base = ast.Node{ .id = ast.Node.Id.ContainerDecl }, .layout_token = ctx.layout_token, .kind_token = switch (token_ptr.id) { @@ -405,7 +415,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena), .lbrace_token = undefined, .rbrace_token = undefined, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .ContainerDecl = node }) catch unreachable; @@ -464,13 +474,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Identifier => { switch (tree.tokens.at(container_decl.kind_token).id) { Token.Id.Keyword_struct => { - const node = try arena.create(ast.Node.StructField{ + const node = try arena.create(ast.Node.StructField); + node.* = ast.Node.StructField{ .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = comments, .visib_token = null, .name_token = token_index, .type_expr = undefined, - }); + }; const node_ptr = try container_decl.fields_and_decls.addOne(); node_ptr.* = &node.base; @@ -485,13 +496,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_union => { - const node = try arena.create(ast.Node.UnionTag{ + const node = try arena.create(ast.Node.UnionTag); + node.* = ast.Node.UnionTag{ .base = ast.Node{ .id = ast.Node.Id.UnionTag }, .name_token = token_index, .type_expr = null, .value_expr = null, .doc_comments = comments, - }); + }; try container_decl.fields_and_decls.push(&node.base); try stack.append(State{ @@ -506,12 +518,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_enum => { - const node = try arena.create(ast.Node.EnumTag{ + const node = try arena.create(ast.Node.EnumTag); + node.* = ast.Node.EnumTag{ .base = ast.Node{ .id = ast.Node.Id.EnumTag }, .name_token = token_index, .value = null, .doc_comments = comments, - }); + }; try container_decl.fields_and_decls.push(&node.base); try stack.append(State{ @@ -593,7 +606,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.VarDecl => |ctx| { - const var_decl = try arena.create(ast.Node.VarDecl{ + const var_decl = try arena.create(ast.Node.VarDecl); + var_decl.* = ast.Node.VarDecl{ .base = ast.Node{ .id = ast.Node.Id.VarDecl }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, @@ -609,7 +623,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .name_token = undefined, .eq_token = undefined, .semicolon_token = undefined, - }); + }; try ctx.list.push(&var_decl.base); try stack.append(State{ .VarDeclAlign = var_decl }); @@ -708,13 +722,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; fn_proto.body_node = &block.base; stack.append(State{ .Block = block }) catch unreachable; continue; @@ -770,10 +785,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { // TODO: this is a special case. Remove this when #760 is fixed if (token_ptr.id == Token.Id.Keyword_anyerror) { if (tok_it.peek().?.id == Token.Id.LBrace) { - const error_type_node = try arena.create(ast.Node.ErrorType{ + const error_type_node = try arena.create(ast.Node.ErrorType); + error_type_node.* = ast.Node.ErrorType{ .base = ast.Node{ .id = ast.Node.Id.ErrorType }, .token = token_index, - }); + }; fn_proto.return_type = ast.Node.FnProto.ReturnType{ .Explicit = &error_type_node.base }; continue; } @@ -791,14 +807,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { if (eatToken(&tok_it, &tree, Token.Id.RParen)) |_| { continue; } - const param_decl = try arena.create(ast.Node.ParamDecl{ + const param_decl = try arena.create(ast.Node.ParamDecl); + param_decl.* = ast.Node.ParamDecl{ .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, .comptime_token = null, .noalias_token = null, .name_token = null, .type_node = undefined, .var_args_token = null, - }); + }; try fn_proto.params.push(¶m_decl.base); stack.append(State{ @@ -877,13 +894,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = ctx.label, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; ctx.opt_ctx.store(&block.base); stack.append(State{ .Block = block }) catch unreachable; continue; @@ -970,7 +988,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } }, State.While => |ctx| { - const node = try arena.create(ast.Node.While{ + const node = try arena.create(ast.Node.While); + node.* = ast.Node.While{ .base = ast.Node{ .id = ast.Node.Id.While }, .label = ctx.label, .inline_token = ctx.inline_token, @@ -980,7 +999,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .continue_expr = null, .body = undefined, .@"else" = null, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .Else = &node.@"else" }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }); @@ -999,7 +1018,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.For => |ctx| { - const node = try arena.create(ast.Node.For{ + const node = try arena.create(ast.Node.For); + node.* = ast.Node.For{ .base = ast.Node{ .id = ast.Node.Id.For }, .label = ctx.label, .inline_token = ctx.inline_token, @@ -1008,7 +1028,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .payload = null, .body = undefined, .@"else" = null, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .Else = &node.@"else" }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }); @@ -1020,12 +1040,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.Else => |dest| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| { - const node = try arena.create(ast.Node.Else{ + const node = try arena.create(ast.Node.Else); + node.* = ast.Node.Else{ .base = ast.Node{ .id = ast.Node.Id.Else }, .else_token = else_token, .payload = null, .body = undefined, - }); + }; dest.* = node; stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }) catch unreachable; @@ -1083,11 +1104,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try arena.create(ast.Node.Defer{ + const node = try arena.create(ast.Node.Defer); + node.* = ast.Node.Defer{ .base = ast.Node{ .id = ast.Node.Id.Defer }, .defer_token = token_index, .expr = undefined, - }); + }; const node_ptr = try block.statements.addOne(); node_ptr.* = &node.base; @@ -1096,13 +1118,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBrace => { - const inner_block = try arena.create(ast.Node.Block{ + const inner_block = try arena.create(ast.Node.Block); + inner_block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; try block.statements.push(&inner_block.base); stack.append(State{ .Block = inner_block }) catch unreachable; @@ -1164,14 +1187,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.AsmOutput{ + const node = try arena.create(ast.Node.AsmOutput); + node.* = ast.Node.AsmOutput{ .base = ast.Node{ .id = ast.Node.Id.AsmOutput }, .lbracket = lbracket_index, .symbolic_name = undefined, .constraint = undefined, .kind = undefined, .rparen = undefined, - }); + }; try items.push(node); stack.append(State{ .AsmOutputItems = items }) catch unreachable; @@ -1218,14 +1242,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.AsmInput{ + const node = try arena.create(ast.Node.AsmInput); + node.* = ast.Node.AsmInput{ .base = ast.Node{ .id = ast.Node.Id.AsmInput }, .lbracket = lbracket_index, .symbolic_name = undefined, .constraint = undefined, .expr = undefined, .rparen = undefined, - }); + }; try items.push(node); stack.append(State{ .AsmInputItems = items }) catch unreachable; @@ -1283,12 +1308,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.FieldInitializer{ + const node = try arena.create(ast.Node.FieldInitializer); + node.* = ast.Node.FieldInitializer{ .base = ast.Node{ .id = ast.Node.Id.FieldInitializer }, .period_token = undefined, .name_token = undefined, .expr = undefined, - }); + }; try list_state.list.push(&node.base); stack.append(State{ .FieldInitListCommaOrEnd = list_state }) catch unreachable; @@ -1390,13 +1416,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } const comments = try eatDocComments(arena, &tok_it, &tree); - const node = try arena.create(ast.Node.SwitchCase{ + const node = try arena.create(ast.Node.SwitchCase); + node.* = ast.Node.SwitchCase{ .base = ast.Node{ .id = ast.Node.Id.SwitchCase }, .items = ast.Node.SwitchCase.ItemList.init(arena), .payload = null, .expr = undefined, .arrow_token = undefined, - }); + }; try list_state.list.push(&node.base); try stack.append(State{ .SwitchCaseCommaOrEnd = list_state }); try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); @@ -1427,10 +1454,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id == Token.Id.Keyword_else) { - const else_node = try arena.create(ast.Node.SwitchElse{ + const else_node = try arena.create(ast.Node.SwitchElse); + else_node.* = ast.Node.SwitchElse{ .base = ast.Node{ .id = ast.Node.Id.SwitchElse }, .token = token_index, - }); + }; try switch_case.items.push(&else_node.base); try stack.append(State{ @@ -1537,7 +1565,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.ExternType => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = null, @@ -1553,7 +1582,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = null, .align_expr = null, .section_expr = null, - }); + }; ctx.opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; continue; @@ -1711,12 +1740,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.Payload{ + const node = try arena.create(ast.Node.Payload); + node.* = ast.Node.Payload{ .base = ast.Node{ .id = ast.Node.Id.Payload }, .lpipe = token_index, .error_symbol = undefined, .rpipe = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -1747,13 +1777,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.PointerPayload{ + const node = try arena.create(ast.Node.PointerPayload); + node.* = ast.Node.PointerPayload{ .base = ast.Node{ .id = ast.Node.Id.PointerPayload }, .lpipe = token_index, .ptr_token = null, .value_symbol = undefined, .rpipe = undefined, - }); + }; opt_ctx.store(&node.base); try stack.append(State{ @@ -1790,14 +1821,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.PointerIndexPayload{ + const node = try arena.create(ast.Node.PointerIndexPayload); + node.* = ast.Node.PointerIndexPayload{ .base = ast.Node{ .id = ast.Node.Id.PointerIndexPayload }, .lpipe = token_index, .ptr_token = null, .value_symbol = undefined, .index_symbol = null, .rpipe = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -1824,12 +1856,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try arena.create(ast.Node.ControlFlowExpression{ + const node = try arena.create(ast.Node.ControlFlowExpression); + node.* = ast.Node.ControlFlowExpression{ .base = ast.Node{ .id = ast.Node.Id.ControlFlowExpression }, .ltoken = token_index, .kind = undefined, .rhs = null, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .Expression = OptionalCtx{ .Optional = &node.rhs } }) catch unreachable; @@ -1853,7 +1886,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try arena.create(ast.Node.PrefixOp{ + const node = try arena.create(ast.Node.PrefixOp); + node.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = switch (token_ptr.id) { @@ -1863,7 +1897,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { else => unreachable, }, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; @@ -1887,13 +1921,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ellipsis3, .op = ast.Node.InfixOp.Op.Range, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; continue; @@ -1912,13 +1947,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAssignment(token_ptr.id)) |ass_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = ass_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }); @@ -1942,13 +1978,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = unwrap_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -1974,13 +2011,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = or_token, .op = ast.Node.InfixOp.Op.BoolOr, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BoolAndExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -1998,13 +2036,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = and_token, .op = ast.Node.InfixOp.Op.BoolAnd, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .ComparisonExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2025,13 +2064,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToComparison(token_ptr.id)) |comp_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = comp_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BinaryOrExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2052,13 +2092,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = pipe, .op = ast.Node.InfixOp.Op.BitOr, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BinaryXorExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2076,13 +2117,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = caret, .op = ast.Node.InfixOp.Op.BitXor, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BinaryAndExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2100,13 +2142,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ampersand, .op = ast.Node.InfixOp.Op.BitAnd, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BitShiftExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2127,13 +2170,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = bitshift_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .AdditionExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2157,13 +2201,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAddition(token_ptr.id)) |add_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = add_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .MultiplyExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2187,13 +2232,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToMultiply(token_ptr.id)) |mult_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = mult_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .CurlySuffixExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2215,12 +2261,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (tok_it.peek().?.id == Token.Id.Period) { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2234,12 +2281,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .IfToken = Token.Id.LBrace }); @@ -2263,13 +2311,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = bang, .op = ast.Node.InfixOp.Op.ErrorUnion, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .PrefixOpExpression = OptionalCtx{ .Required = &node.rhs } }); @@ -2282,22 +2331,24 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { - var node = try arena.create(ast.Node.PrefixOp{ + var node = try arena.create(ast.Node.PrefixOp); + node.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); // Treat '**' token as two pointer types if (token_ptr.id == Token.Id.AsteriskAsterisk) { - const child = try arena.create(ast.Node.PrefixOp{ + const child = try arena.create(ast.Node.PrefixOp); + child.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, .rhs = undefined, - }); + }; node.rhs = &child.base; node = child; } @@ -2316,12 +2367,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.SuffixOpExpressionBegin => |opt_ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| { - const async_node = try arena.create(ast.Node.AsyncAttribute{ + const async_node = try arena.create(ast.Node.AsyncAttribute); + async_node.* = ast.Node.AsyncAttribute{ .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = async_token, .allocator_type = null, .rangle_bracket = null, - }); + }; stack.append(State{ .AsyncEnd = AsyncEndCtx{ .ctx = opt_ctx, @@ -2347,7 +2399,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LParen => { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ @@ -2357,7 +2410,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2371,12 +2424,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .ArrayAccess = undefined }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2386,34 +2440,37 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, Token.Id.Period => { if (eatToken(&tok_it, &tree, Token.Id.Asterisk)) |asterisk_token| { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op.Deref, .rtoken = asterisk_token, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; continue; } if (eatToken(&tok_it, &tree, Token.Id.QuestionMark)) |question_token| { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op.UnwrapOptional, .rtoken = question_token, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; continue; } - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = ast.Node.InfixOp.Op.Period, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2467,11 +2524,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_promise => { - const node = try arena.create(ast.Node.PromiseType{ + const node = try arena.create(ast.Node.PromiseType); + node.* = ast.Node.PromiseType{ .base = ast.Node{ .id = ast.Node.Id.PromiseType }, .promise_token = token.index, .result = null, - }); + }; opt_ctx.store(&node.base); const next_token = nextToken(&tok_it, &tree); const next_token_index = next_token.index; @@ -2493,12 +2551,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LParen => { - const node = try arena.create(ast.Node.GroupedExpression{ + const node = try arena.create(ast.Node.GroupedExpression); + node.* = ast.Node.GroupedExpression{ .base = ast.Node{ .id = ast.Node.Id.GroupedExpression }, .lparen = token.index, .expr = undefined, .rparen = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -2511,12 +2570,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Builtin => { - const node = try arena.create(ast.Node.BuiltinCall{ + const node = try arena.create(ast.Node.BuiltinCall); + node.* = ast.Node.BuiltinCall{ .base = ast.Node{ .id = ast.Node.Id.BuiltinCall }, .builtin_token = token.index, .params = ast.Node.BuiltinCall.ParamList.init(arena), .rparen_token = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -2530,12 +2590,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try arena.create(ast.Node.PrefixOp{ + const node = try arena.create(ast.Node.PrefixOp); + node.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token.index, .op = undefined, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SliceOrArrayType = node }) catch unreachable; @@ -2593,7 +2654,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_fn => { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, @@ -2609,13 +2671,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = null, .align_expr = null, .section_expr = null, - }); + }; opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; continue; }, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, @@ -2631,7 +2694,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = null, .align_expr = null, .section_expr = null, - }); + }; opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; try stack.append(State{ @@ -2643,7 +2706,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_asm => { - const node = try arena.create(ast.Node.Asm{ + const node = try arena.create(ast.Node.Asm); + node.* = ast.Node.Asm{ .base = ast.Node{ .id = ast.Node.Id.Asm }, .asm_token = token.index, .volatile_token = null, @@ -2652,7 +2716,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .inputs = ast.Node.Asm.InputList.init(arena), .clobbers = ast.Node.Asm.ClobberList.init(arena), .rparen = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -2701,13 +2765,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.ErrorTypeOrSetDecl => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.LBrace) == null) { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = &(try createLiteral(arena, ast.Node.ErrorType, ctx.error_token)).base, .op_token = undefined, .op = ast.Node.InfixOp.Op.Period, .rhs = undefined, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; try stack.append(State{ @@ -2719,12 +2784,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.ErrorSetDecl{ + const node = try arena.create(ast.Node.ErrorSetDecl); + node.* = ast.Node.ErrorSetDecl{ .base = ast.Node{ .id = ast.Node.Id.ErrorSetDecl }, .error_token = ctx.error_token, .decls = ast.Node.ErrorSetDecl.DeclList.init(arena), .rbrace_token = undefined, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ @@ -2785,11 +2851,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { return tree; } - const node = try arena.create(ast.Node.ErrorTag{ + const node = try arena.create(ast.Node.ErrorTag); + node.* = ast.Node.ErrorTag{ .base = ast.Node{ .id = ast.Node.Id.ErrorTag }, .doc_comments = comments, .name_token = ident_token_index, - }); + }; node_ptr.* = &node.base; continue; }, @@ -3129,10 +3196,11 @@ fn pushDocComment(arena: *mem.Allocator, line_comment: TokenIndex, result: *?*as if (result.*) |comment_node| { break :blk comment_node; } else { - const comment_node = try arena.create(ast.Node.DocComment{ + const comment_node = try arena.create(ast.Node.DocComment); + comment_node.* = ast.Node.DocComment{ .base = ast.Node{ .id = ast.Node.Id.DocComment }, .lines = ast.Node.DocComment.LineList.init(arena), - }); + }; result.* = comment_node; break :blk comment_node; } @@ -3158,10 +3226,11 @@ fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterato return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; }, Token.Id.MultilineStringLiteralLine => { - const node = try arena.create(ast.Node.MultilineStringLiteral{ + const node = try arena.create(ast.Node.MultilineStringLiteral); + node.* = ast.Node.MultilineStringLiteral{ .base = ast.Node{ .id = ast.Node.Id.MultilineStringLiteral }, .lines = ast.Node.MultilineStringLiteral.LineList.init(arena), - }); + }; try node.lines.push(token_index); while (true) { const multiline_str = nextToken(tok_it, tree); @@ -3186,25 +3255,27 @@ fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterato fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: OptionalCtx, token_ptr: Token, token_index: TokenIndex) !bool { switch (token_ptr.id) { Token.Id.Keyword_suspend => { - const node = try arena.create(ast.Node.Suspend{ + const node = try arena.create(ast.Node.Suspend); + node.* = ast.Node.Suspend{ .base = ast.Node{ .id = ast.Node.Id.Suspend }, .suspend_token = token_index, .body = null, - }); + }; ctx.store(&node.base); stack.append(State{ .SuspendBody = node }) catch unreachable; return true; }, Token.Id.Keyword_if => { - const node = try arena.create(ast.Node.If{ + const node = try arena.create(ast.Node.If); + node.* = ast.Node.If{ .base = ast.Node{ .id = ast.Node.Id.If }, .if_token = token_index, .condition = undefined, .payload = null, .body = undefined, .@"else" = null, - }); + }; ctx.store(&node.base); stack.append(State{ .Else = &node.@"else" }) catch unreachable; @@ -3238,13 +3309,14 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: Opti return true; }, Token.Id.Keyword_switch => { - const node = try arena.create(ast.Node.Switch{ + const node = try arena.create(ast.Node.Switch); + node.* = ast.Node.Switch{ .base = ast.Node{ .id = ast.Node.Id.Switch }, .switch_token = token_index, .expr = undefined, .cases = ast.Node.Switch.CaseList.init(arena), .rbrace = undefined, - }); + }; ctx.store(&node.base); stack.append(State{ @@ -3260,25 +3332,27 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: Opti return true; }, Token.Id.Keyword_comptime => { - const node = try arena.create(ast.Node.Comptime{ + const node = try arena.create(ast.Node.Comptime); + node.* = ast.Node.Comptime{ .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = undefined, .doc_comments = null, - }); + }; ctx.store(&node.base); try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); return true; }, Token.Id.LBrace => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; ctx.store(&block.base); stack.append(State{ .Block = block }) catch unreachable; return true; @@ -3412,10 +3486,12 @@ fn tokenIdToPrefixOp(id: Token.Id) ?ast.Node.PrefixOp.Op { } fn createLiteral(arena: *mem.Allocator, comptime T: type, token_index: TokenIndex) !*T { - return arena.create(T{ + const result = try arena.create(T); + result.* = T{ .base = ast.Node{ .id = ast.Node.typeToId(T) }, .token = token_index, - }); + }; + return result; } fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: OptionalCtx, comptime T: type, token_index: TokenIndex) !*T { diff --git a/test/behavior.zig b/test/behavior.zig deleted file mode 100644 index 10cd08dad7..0000000000 --- a/test/behavior.zig +++ /dev/null @@ -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"); -} diff --git a/test/cases/array.zig b/test/cases/array.zig deleted file mode 100644 index 7c63a649a8..0000000000 --- a/test/cases/array.zig +++ /dev/null @@ -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; -} diff --git a/test/cases/asm.zig b/test/cases/asm.zig deleted file mode 100644 index 63e37c857c..0000000000 --- a/test/cases/asm.zig +++ /dev/null @@ -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; -} diff --git a/test/cases/bitreverse.zig b/test/cases/bitreverse.zig deleted file mode 100644 index 3721e68a94..0000000000 --- a/test/cases/bitreverse.zig +++ /dev/null @@ -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)); -} diff --git a/test/cases/bswap.zig b/test/cases/bswap.zig deleted file mode 100644 index 57993077e1..0000000000 --- a/test/cases/bswap.zig +++ /dev/null @@ -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))); -} diff --git a/test/cases/import.zig b/test/cases/import.zig deleted file mode 100644 index 6d6d4b0208..0000000000 --- a/test/cases/import.zig +++ /dev/null @@ -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")); -} diff --git a/test/cases/optional.zig b/test/cases/optional.zig deleted file mode 100644 index d43682bbec..0000000000 --- a/test/cases/optional.zig +++ /dev/null @@ -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); -} diff --git a/test/cases/popcount.zig b/test/cases/popcount.zig deleted file mode 100644 index 7dc7f28c0e..0000000000 --- a/test/cases/popcount.zig +++ /dev/null @@ -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); - } -} diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig deleted file mode 100644 index b9b8aff4e1..0000000000 --- a/test/cases/reflection.zig +++ /dev/null @@ -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, -}; diff --git a/test/cases/sizeof_and_typeof.zig b/test/cases/sizeof_and_typeof.zig deleted file mode 100644 index 11c6b2f6ba..0000000000 --- a/test/cases/sizeof_and_typeof.zig +++ /dev/null @@ -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")); -} diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig deleted file mode 100644 index cec532d5d3..0000000000 --- a/test/cases/type_info.zig +++ /dev/null @@ -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); -} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d9603ba4ff..30d9ca5d70 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -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", + ); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig new file mode 100644 index 0000000000..1fa00b34fd --- /dev/null +++ b/test/stage1/behavior.zig @@ -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"); +} diff --git a/test/cases/align.zig b/test/stage1/behavior/align.zig similarity index 69% rename from test/cases/align.zig rename to test/stage1/behavior/align.zig index 3dff57feb8..aa7a93ad84 100644 --- a/test/cases/align.zig +++ b/test/stage1/behavior/align.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)); diff --git a/test/cases/alignof.zig b/test/stage1/behavior/alignof.zig similarity index 61% rename from test/cases/alignof.zig rename to test/stage1/behavior/alignof.zig index 433e86e45e..98c805908b 100644 --- a/test/cases/alignof.zig +++ b/test/stage1/behavior/alignof.zig @@ -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); } } + diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig new file mode 100644 index 0000000000..1183305209 --- /dev/null +++ b/test/stage1/behavior/array.zig @@ -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); +} diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig new file mode 100644 index 0000000000..48701c5836 --- /dev/null +++ b/test/stage1/behavior/asm.zig @@ -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; +} diff --git a/test/cases/atomics.zig b/test/stage1/behavior/atomics.zig similarity index 65% rename from test/cases/atomics.zig rename to test/stage1/behavior/atomics.zig index 67c9ab3dd1..fa3c5f29a6 100644 --- a/test/cases/atomics.zig +++ b/test/stage1/behavior/atomics.zig @@ -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); } diff --git a/test/cases/bit_shifting.zig b/test/stage1/behavior/bit_shifting.zig similarity index 90% rename from test/cases/bit_shifting.zig rename to test/stage1/behavior/bit_shifting.zig index 325e765bb0..3290688358 100644 --- a/test/cases/bit_shifting.zig +++ b/test/stage1/behavior/bit_shifting.zig @@ -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); } } diff --git a/test/cases/bitcast.zig b/test/stage1/behavior/bitcast.zig similarity index 78% rename from test/cases/bitcast.zig rename to test/stage1/behavior/bitcast.zig index d85a84ed22..19030255e4 100644 --- a/test/cases/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -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(); } - diff --git a/test/stage1/behavior/bitreverse.zig b/test/stage1/behavior/bitreverse.zig new file mode 100644 index 0000000000..97787ace84 --- /dev/null +++ b/test/stage1/behavior/bitreverse.zig @@ -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)); +} diff --git a/test/cases/bool.zig b/test/stage1/behavior/bool.zig similarity index 50% rename from test/cases/bool.zig rename to test/stage1/behavior/bool.zig index 3e4ac9c1cf..2d7241526f 100644 --- a/test/cases/bool.zig +++ b/test/stage1/behavior/bool.zig @@ -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); } diff --git a/test/stage1/behavior/bswap.zig b/test/stage1/behavior/bswap.zig new file mode 100644 index 0000000000..8084538e03 --- /dev/null +++ b/test/stage1/behavior/bswap.zig @@ -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))); +} diff --git a/test/cases/bugs/1076.zig b/test/stage1/behavior/bugs/1076.zig similarity index 75% rename from test/cases/bugs/1076.zig rename to test/stage1/behavior/bugs/1076.zig index 7b84312310..69a7e70f7d 100644 --- a/test/cases/bugs/1076.zig +++ b/test/stage1/behavior/bugs/1076.zig @@ -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")); } diff --git a/test/cases/bugs/1111.zig b/test/stage1/behavior/bugs/1111.zig similarity index 100% rename from test/cases/bugs/1111.zig rename to test/stage1/behavior/bugs/1111.zig diff --git a/test/cases/bugs/1277.zig b/test/stage1/behavior/bugs/1277.zig similarity index 100% rename from test/cases/bugs/1277.zig rename to test/stage1/behavior/bugs/1277.zig diff --git a/test/cases/bugs/1322.zig b/test/stage1/behavior/bugs/1322.zig similarity index 66% rename from test/cases/bugs/1322.zig rename to test/stage1/behavior/bugs/1322.zig index 2de92191ec..2e67f4473f 100644 --- a/test/cases/bugs/1322.zig +++ b/test/stage1/behavior/bugs/1322.zig @@ -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); } diff --git a/test/cases/bugs/1381.zig b/test/stage1/behavior/bugs/1381.zig similarity index 100% rename from test/cases/bugs/1381.zig rename to test/stage1/behavior/bugs/1381.zig diff --git a/test/cases/bugs/1421.zig b/test/stage1/behavior/bugs/1421.zig similarity index 70% rename from test/cases/bugs/1421.zig rename to test/stage1/behavior/bugs/1421.zig index fcbb8b70e4..fbc932781a 100644 --- a/test/cases/bugs/1421.zig +++ b/test/stage1/behavior/bugs/1421.zig @@ -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); } diff --git a/test/cases/bugs/1442.zig b/test/stage1/behavior/bugs/1442.zig similarity index 100% rename from test/cases/bugs/1442.zig rename to test/stage1/behavior/bugs/1442.zig diff --git a/test/cases/bugs/1486.zig b/test/stage1/behavior/bugs/1486.zig similarity index 51% rename from test/cases/bugs/1486.zig rename to test/stage1/behavior/bugs/1486.zig index 98fae36d3a..0483e3828c 100644 --- a/test/cases/bugs/1486.zig +++ b/test/stage1/behavior/bugs/1486.zig @@ -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); } diff --git a/test/cases/bugs/394.zig b/test/stage1/behavior/bugs/394.zig similarity index 68% rename from test/cases/bugs/394.zig rename to test/stage1/behavior/bugs/394.zig index b0afec2357..766ad9e157 100644 --- a/test/cases/bugs/394.zig +++ b/test/stage1/behavior/bugs/394.zig @@ -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); } diff --git a/test/cases/bugs/655.zig b/test/stage1/behavior/bugs/655.zig similarity index 67% rename from test/cases/bugs/655.zig rename to test/stage1/behavior/bugs/655.zig index ebb8da0658..67ba6a231f 100644 --- a/test/cases/bugs/655.zig +++ b/test/stage1/behavior/bugs/655.zig @@ -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); } diff --git a/test/cases/bugs/655_other_file.zig b/test/stage1/behavior/bugs/655_other_file.zig similarity index 100% rename from test/cases/bugs/655_other_file.zig rename to test/stage1/behavior/bugs/655_other_file.zig diff --git a/test/cases/bugs/656.zig b/test/stage1/behavior/bugs/656.zig similarity index 84% rename from test/cases/bugs/656.zig rename to test/stage1/behavior/bugs/656.zig index f93f0ac4d5..cb37fe67fe 100644 --- a/test/cases/bugs/656.zig +++ b/test/stage1/behavior/bugs/656.zig @@ -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 => {}, diff --git a/test/cases/bugs/726.zig b/test/stage1/behavior/bugs/726.zig similarity index 71% rename from test/cases/bugs/726.zig rename to test/stage1/behavior/bugs/726.zig index 2acc91eb26..ce20480c63 100644 --- a/test/cases/bugs/726.zig +++ b/test/stage1/behavior/bugs/726.zig @@ -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); } diff --git a/test/cases/bugs/828.zig b/test/stage1/behavior/bugs/828.zig similarity index 100% rename from test/cases/bugs/828.zig rename to test/stage1/behavior/bugs/828.zig diff --git a/test/cases/bugs/920.zig b/test/stage1/behavior/bugs/920.zig similarity index 95% rename from test/cases/bugs/920.zig rename to test/stage1/behavior/bugs/920.zig index 2903f05a29..e29c5c4acf 100644 --- a/test/cases/bugs/920.zig +++ b/test/stage1/behavior/bugs/920.zig @@ -60,6 +60,6 @@ test "bug 920 fixed" { }; for (NormalDist1.f) |_, i| { - std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]); + std.debug.assertOrPanic(NormalDist1.f[i] == NormalDist.f[i]); } } diff --git a/test/cases/byval_arg_var.zig b/test/stage1/behavior/byval_arg_var.zig similarity index 70% rename from test/cases/byval_arg_var.zig rename to test/stage1/behavior/byval_arg_var.zig index 826b9cc9e5..14ee212ce0 100644 --- a/test/cases/byval_arg_var.zig +++ b/test/stage1/behavior/byval_arg_var.zig @@ -2,11 +2,11 @@ const std = @import("std"); var result: []const u8 = "wrong"; -test "aoeu" { +test "pass string literal byvalue to a generic var param" { start(); blowUpStack(10); - std.debug.assert(std.mem.eql(u8, result, "string literal")); + std.debug.assertOrPanic(std.mem.eql(u8, result, "string literal")); } fn start() void { diff --git a/test/cases/cancel.zig b/test/stage1/behavior/cancel.zig similarity index 84% rename from test/cases/cancel.zig rename to test/stage1/behavior/cancel.zig index c0f74fd34f..863da4bdb8 100644 --- a/test/cases/cancel.zig +++ b/test/stage1/behavior/cancel.zig @@ -10,9 +10,9 @@ test "cancel forwards" { const p = async<&da.allocator> f1() catch unreachable; cancel p; - std.debug.assert(defer_f1); - std.debug.assert(defer_f2); - std.debug.assert(defer_f3); + std.debug.assertOrPanic(defer_f1); + std.debug.assertOrPanic(defer_f2); + std.debug.assertOrPanic(defer_f3); } async fn f1() void { @@ -47,10 +47,10 @@ test "cancel backwards" { const p = async<&da.allocator> b1() catch unreachable; cancel p; - std.debug.assert(defer_b1); - std.debug.assert(defer_b2); - std.debug.assert(defer_b3); - std.debug.assert(defer_b4); + std.debug.assertOrPanic(defer_b1); + std.debug.assertOrPanic(defer_b2); + std.debug.assertOrPanic(defer_b3); + std.debug.assertOrPanic(defer_b4); } async fn b1() void { diff --git a/test/cases/cast.zig b/test/stage1/behavior/cast.zig similarity index 67% rename from test/cases/cast.zig rename to test/stage1/behavior/cast.zig index bd45bbc00f..61ddcd8135 100644 --- a/test/cases/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; @@ -7,12 +7,12 @@ test "int to ptr cast" { const x = usize(13); const y = @intToPtr(*u8, x); const z = @ptrToInt(y); - assert(z == 13); + assertOrPanic(z == 13); } test "integer literal to pointer cast" { const vga_mem = @intToPtr(*u16, 0xB8000); - assert(@ptrToInt(vga_mem) == 0xB8000); + assertOrPanic(@ptrToInt(vga_mem) == 0xB8000); } test "pointer reinterpret const float to int" { @@ -20,7 +20,7 @@ test "pointer reinterpret const float to int" { const float_ptr = &float; const int_ptr = @ptrCast(*const i32, float_ptr); const int_val = int_ptr.*; - assert(int_val == 858993411); + assertOrPanic(int_val == 858993411); } test "implicitly cast indirect pointer to maybe-indirect pointer" { @@ -44,10 +44,10 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { const p = &s; const q = &p; const r = &q; - assert(42 == S.constConst(q)); - assert(42 == S.maybeConstConst(q)); - assert(42 == S.constConstConst(r)); - assert(42 == S.maybeConstConstConst(r)); + assertOrPanic(42 == S.constConst(q)); + assertOrPanic(42 == S.maybeConstConst(q)); + assertOrPanic(42 == S.constConstConst(r)); + assertOrPanic(42 == S.maybeConstConstConst(r)); } test "explicit cast from integer to error type" { @@ -57,14 +57,14 @@ test "explicit cast from integer to error type" { fn testCastIntToErr(err: anyerror) void { const x = @errorToInt(err); const y = @intToError(x); - assert(error.ItBroke == y); + assertOrPanic(error.ItBroke == y); } test "peer resolve arrays of different size to const slice" { - assert(mem.eql(u8, boolToStr(true), "true")); - assert(mem.eql(u8, boolToStr(false), "false")); - comptime assert(mem.eql(u8, boolToStr(true), "true")); - comptime assert(mem.eql(u8, boolToStr(false), "false")); + assertOrPanic(mem.eql(u8, boolToStr(true), "true")); + assertOrPanic(mem.eql(u8, boolToStr(false), "false")); + comptime assertOrPanic(mem.eql(u8, boolToStr(true), "true")); + comptime assertOrPanic(mem.eql(u8, boolToStr(false), "false")); } fn boolToStr(b: bool) []const u8 { return if (b) "true" else "false"; @@ -77,28 +77,29 @@ test "peer resolve array and const slice" { fn testPeerResolveArrayConstSlice(b: bool) void { const value1 = if (b) "aoeu" else ([]const u8)("zz"); const value2 = if (b) ([]const u8)("zz") else "aoeu"; - assert(mem.eql(u8, value1, "aoeu")); - assert(mem.eql(u8, value2, "zz")); + assertOrPanic(mem.eql(u8, value1, "aoeu")); + assertOrPanic(mem.eql(u8, value2, "zz")); } test "implicitly cast from T to anyerror!?T" { castToOptionalTypeError(1); comptime castToOptionalTypeError(1); } + const A = struct { a: i32, }; fn castToOptionalTypeError(z: i32) void { const x = i32(1); const y: anyerror!?i32 = x; - assert((try y).? == 1); + assertOrPanic((try y).? == 1); const f = z; const g: anyerror!?i32 = f; const a = A{ .a = z }; const b: anyerror!?A = a; - assert((b catch unreachable).?.a == 1); + assertOrPanic((b catch unreachable).?.a == 1); } test "implicitly cast from int to anyerror!?T" { @@ -113,7 +114,7 @@ fn implicitIntLitToOptional() void { test "return null from fn() anyerror!?&T" { const a = returnNullFromOptionalTypeErrorRef(); const b = returnNullLitFromOptionalTypeErrorRef(); - assert((try a) == null and (try b) == null); + assertOrPanic((try a) == null and (try b) == null); } fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { const a: ?*A = null; @@ -124,11 +125,11 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { } test "peer type resolution: ?T and T" { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); comptime { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); } } fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { @@ -140,11 +141,11 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { } test "peer type resolution: [0]u8 and []const u8" { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); comptime { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); } } fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { @@ -156,8 +157,8 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "implicitly cast from [N]T to ?[]const T" { - assert(mem.eql(u8, castToOptionalSlice().?, "hi")); - comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi")); + assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); } fn castToOptionalSlice() ?[]const u8 { @@ -170,7 +171,7 @@ test "implicitly cast from [0]T to anyerror![]T" { } fn testCastZeroArrayToErrSliceMut() void { - assert((gimmeErrOrSlice() catch unreachable).len == 0); + assertOrPanic((gimmeErrOrSlice() catch unreachable).len == 0); } fn gimmeErrOrSlice() anyerror![]u8 { @@ -181,14 +182,14 @@ test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { { var data = "hi"; const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } comptime { var data = "hi"; const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } } fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { @@ -206,7 +207,7 @@ test "resolve undefined with integer" { fn testResolveUndefWithInt(b: bool, x: i32) void { const value = if (b) x else undefined; if (b) { - assert(value == x); + assertOrPanic(value == x); } } @@ -218,17 +219,17 @@ test "implicit cast from &const [N]T to []const T" { fn testCastConstArrayRefToConstSlice() void { const blah = "aoeu"; const const_array_ref = &blah; - assert(@typeOf(const_array_ref) == *const [4]u8); + assertOrPanic(@typeOf(const_array_ref) == *const [4]u8); const slice: []const u8 = const_array_ref; - assert(mem.eql(u8, slice, "aoeu")); + assertOrPanic(mem.eql(u8, slice, "aoeu")); } test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U - //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); - comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); + //assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + //comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); + comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); } //fn testPeerErrorAndArray(x: u8) error![]const u8 { @@ -252,9 +253,9 @@ test "@floatToInt" { fn testFloatToInts() void { const x = i32(1e4); - assert(x == 10000); + assertOrPanic(x == 10000); const y = @floatToInt(i32, f32(1e4)); - assert(y == 10000); + assertOrPanic(y == 10000); expectFloatToInt(f16, 255.1, u8, 255); expectFloatToInt(f16, 127.2, i8, 127); expectFloatToInt(f16, -128.2, i8, -128); @@ -265,7 +266,7 @@ fn testFloatToInts() void { } fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { - assert(@floatToInt(I, f) == i); + assertOrPanic(@floatToInt(I, f) == i); } test "cast u128 to f128 and back" { @@ -274,7 +275,7 @@ test "cast u128 to f128 and back" { } fn testCast128() void { - assert(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); + assertOrPanic(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); } fn cast128Int(x: f128) u128 { @@ -294,9 +295,9 @@ test "const slice widen cast" { }; const u32_value = @bytesToSlice(u32, bytes[0..])[0]; - assert(u32_value == 0x12121212); + assertOrPanic(u32_value == 0x12121212); - assert(@bitCast(u32, bytes) == 0x12121212); + assertOrPanic(@bitCast(u32, bytes) == 0x12121212); } test "single-item pointer of array to slice and to unknown length pointer" { @@ -308,76 +309,76 @@ 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")); const y: []u8 = &array; y[0] += 1; - assert(mem.eql(u8, array[0..], "coeu")); + assertOrPanic(mem.eql(u8, array[0..], "coeu")); } test "cast *[1][*]const u8 to [*]const ?[*]const u8" { const window_name = [1][*]const u8{c"window name"}; const x: [*]const ?[*]const u8 = &window_name; - assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); + assertOrPanic(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); } test "@intCast comptime_int" { const result = @intCast(i32, 1234); - assert(@typeOf(result) == i32); - assert(result == 1234); + assertOrPanic(@typeOf(result) == i32); + assertOrPanic(result == 1234); } test "@floatCast comptime_int and comptime_float" { { const result = @floatCast(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f16, 1234.0); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f32, 1234.0); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } } test "comptime_int @intToFloat" { { const result = @intToFloat(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @intToFloat(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } } test "@bytesToSlice keeps pointer alignment" { var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; const numbers = @bytesToSlice(u32, bytes[0..]); - comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); + comptime assertOrPanic(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); } test "@intCast i32 to u7" { var x: u128 = maxInt(u128); var y: i32 = 120; var z = x >> @intCast(u7, y); - assert(z == 0xff); + assertOrPanic(z == 0xff); } test "implicit cast undefined to optional" { - assert(MakeType(void).getNull() == null); - assert(MakeType(void).getNonNull() != null); + assertOrPanic(MakeType(void).getNull() == null); + assertOrPanic(MakeType(void).getNonNull() != null); } fn MakeType(comptime T: type) type { @@ -397,16 +398,16 @@ test "implicit cast from *[N]T to ?[*]T" { var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; x = &y; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); x.?[0] = 8; y[3] = 6; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); } test "implicit cast from *T to ?*c_void" { var a: u8 = 1; incrementVoidPtrValue(&a); - std.debug.assert(a == 2); + std.debug.assertOrPanic(a == 2); } fn incrementVoidPtrValue(value: ?*c_void) void { @@ -416,7 +417,7 @@ fn incrementVoidPtrValue(value: ?*c_void) void { test "implicit cast from [*]T to ?*c_void" { var a = []u8{ 3, 2, 1 }; incrementVoidPtrArray(a[0..].ptr, 3); - assert(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); + assertOrPanic(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); } fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { @@ -440,27 +441,27 @@ pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); pub const PFN_void = extern fn (*c_void) void; fn foobar(func: PFN_void) void { - std.debug.assert(@ptrToInt(func) == maxInt(usize)); + std.debug.assertOrPanic(@ptrToInt(func) == maxInt(usize)); } test "implicit ptr to *c_void" { var a: u32 = 1; var ptr: *c_void = &a; var b: *u32 = @ptrCast(*u32, ptr); - assert(b.* == 1); + assertOrPanic(b.* == 1); var ptr2: ?*c_void = &a; var c: *u32 = @ptrCast(*u32, ptr2.?); - assert(c.* == 1); + assertOrPanic(c.* == 1); } test "@intCast to comptime_int" { - assert(@intCast(comptime_int, 0) == 0); + assertOrPanic(@intCast(comptime_int, 0) == 0); } test "implicit cast comptime numbers to any type when the value fits" { const a: u64 = 255; var b: u8 = a; - assert(b == 255); + assertOrPanic(b == 255); } test "@intToEnum passed a comptime_int to an enum with one item" { @@ -468,5 +469,5 @@ test "@intToEnum passed a comptime_int to an enum with one item" { A, }; const x = @intToEnum(E, 0); - assert(x == E.A); + assertOrPanic(x == E.A); } diff --git a/test/cases/const_slice_child.zig b/test/stage1/behavior/const_slice_child.zig similarity index 80% rename from test/cases/const_slice_child.zig rename to test/stage1/behavior/const_slice_child.zig index 07d02d5df0..5b9b70a558 100644 --- a/test/cases/const_slice_child.zig +++ b/test/stage1/behavior/const_slice_child.zig @@ -1,5 +1,5 @@ const debug = @import("std").debug; -const assert = debug.assert; +const assertOrPanic = debug.assertOrPanic; var argv: [*]const [*]const u8 = undefined; @@ -15,10 +15,10 @@ test "const slice child" { } fn foo(args: [][]const u8) void { - assert(args.len == 3); - assert(streql(args[0], "one")); - assert(streql(args[1], "two")); - assert(streql(args[2], "three")); + assertOrPanic(args.len == 3); + assertOrPanic(streql(args[0], "one")); + assertOrPanic(streql(args[1], "two")); + assertOrPanic(streql(args[2], "three")); } fn bar(argc: usize) void { diff --git a/test/cases/coroutine_await_struct.zig b/test/stage1/behavior/coroutine_await_struct.zig similarity index 86% rename from test/cases/coroutine_await_struct.zig rename to test/stage1/behavior/coroutine_await_struct.zig index 79168715d8..6ca2a301ec 100644 --- a/test/cases/coroutine_await_struct.zig +++ b/test/stage1/behavior/coroutine_await_struct.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const Foo = struct { x: i32, @@ -18,8 +18,8 @@ test "coroutine await struct" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result.x == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + assertOrPanic(await_final_result.x == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); diff --git a/test/cases/coroutines.zig b/test/stage1/behavior/coroutines.zig similarity index 86% rename from test/cases/coroutines.zig rename to test/stage1/behavior/coroutines.zig index 89490ebc2c..a2327c5060 100644 --- a/test/cases/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; var x: i32 = 1; @@ -9,9 +9,9 @@ test "create a coroutine and cancel it" { defer da.deinit(); const p = try async<&da.allocator> simpleAsyncFn(); - comptime assert(@typeOf(p) == promise->void); + comptime assertOrPanic(@typeOf(p) == promise->void); cancel p; - assert(x == 2); + assertOrPanic(x == 2); } async fn simpleAsyncFn() void { x += 1; @@ -31,7 +31,7 @@ test "coroutine suspend, resume, cancel" { cancel p; seq('g'); - assert(std.mem.eql(u8, points, "abcdefg")); + assertOrPanic(std.mem.eql(u8, points, "abcdefg")); } async fn testAsyncSeq() void { defer seq('e'); @@ -53,9 +53,9 @@ test "coroutine suspend with block" { defer da.deinit(); const p = try async<&da.allocator> testSuspendBlock(); - std.debug.assert(!result); + std.debug.assertOrPanic(!result); resume a_promise; - std.debug.assert(result); + std.debug.assertOrPanic(result); cancel p; } @@ -63,13 +63,13 @@ var a_promise: promise = undefined; var result = false; async fn testSuspendBlock() void { suspend { - comptime assert(@typeOf(@handle()) == promise->void); + comptime assertOrPanic(@typeOf(@handle()) == promise->void); a_promise = @handle(); } //Test to make sure that @handle() works as advertised (issue #1296) //var our_handle: promise = @handle(); - assert( a_promise == @handle() ); + assertOrPanic(a_promise == @handle()); result = true; } @@ -86,8 +86,8 @@ test "coroutine await" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + assertOrPanic(await_final_result == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); @@ -123,8 +123,8 @@ test "coroutine await early return" { early_seq('a'); const p = async<&da.allocator> early_amain() catch @panic("out of memory"); early_seq('f'); - assert(early_final_result == 1234); - assert(std.mem.eql(u8, early_points, "abcdef")); + assertOrPanic(early_final_result == 1234); + assertOrPanic(std.mem.eql(u8, early_points, "abcdef")); } async fn early_amain() void { early_seq('b'); @@ -170,7 +170,7 @@ test "async function with dot syntax" { defer da.deinit(); const p = try async<&da.allocator> S.foo(); cancel p; - assert(S.y == 2); + assertOrPanic(S.y == 2); } test "async fn pointer in a struct field" { @@ -182,9 +182,9 @@ test "async fn pointer in a struct field" { var da = std.heap.DirectAllocator.init(); defer da.deinit(); const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; - assert(data == 2); + assertOrPanic(data == 2); cancel p; - assert(data == 4); + assertOrPanic(data == 4); } async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { defer y.* += 2; @@ -199,6 +199,7 @@ test "async fn with inferred error set" { resume p; cancel p; } + async fn failing() !void { suspend; return error.Fail; @@ -220,8 +221,7 @@ test "error return trace across suspend points - async return" { cancel p2; } -// TODO https://github.com/ziglang/zig/issues/760 -fn nonFailing() promise->(anyerror!void) { +fn nonFailing() (promise->anyerror!void) { return async suspendThenFail() catch unreachable; } async fn suspendThenFail() anyerror!void { @@ -230,9 +230,9 @@ async fn suspendThenFail() anyerror!void { } async fn printTrace(p: promise->(anyerror!void)) void { (await p) catch |e| { - std.debug.assert(e == error.Fail); + std.debug.assertOrPanic(e == error.Fail); if (@errorReturnTrace()) |trace| { - assert(trace.index == 1); + assertOrPanic(trace.index == 1); } else switch (builtin.mode) { builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, @@ -246,7 +246,7 @@ test "break from suspend" { var my_result: i32 = 1; const p = try async testBreakFromSuspend(&my_result); cancel p; - std.debug.assert(my_result == 2); + std.debug.assertOrPanic(my_result == 2); } async fn testBreakFromSuspend(my_result: *i32) void { suspend { diff --git a/test/cases/defer.zig b/test/stage1/behavior/defer.zig similarity index 72% rename from test/cases/defer.zig rename to test/stage1/behavior/defer.zig index f9a2b69cd9..6c6c60311e 100644 --- a/test/cases/defer.zig +++ b/test/stage1/behavior/defer.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; var result: [3]u8 = undefined; var index: usize = undefined; @@ -21,18 +21,18 @@ fn runSomeErrorDefers(x: bool) !bool { } test "mixing normal and error defers" { - assert(runSomeErrorDefers(true) catch unreachable); - assert(result[0] == 'c'); - assert(result[1] == 'a'); + assertOrPanic(runSomeErrorDefers(true) catch unreachable); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'a'); const ok = runSomeErrorDefers(false) catch |err| x: { - assert(err == error.FalseNotAllowed); + assertOrPanic(err == error.FalseNotAllowed); break :x true; }; - assert(ok); - assert(result[0] == 'c'); - assert(result[1] == 'b'); - assert(result[2] == 'a'); + assertOrPanic(ok); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'b'); + assertOrPanic(result[2] == 'a'); } test "break and continue inside loop inside defer expression" { @@ -47,7 +47,7 @@ fn testBreakContInDefer(x: usize) void { if (i < 5) continue; if (i == 5) break; } - assert(i == 5); + assertOrPanic(i == 5); } } @@ -59,11 +59,11 @@ test "defer and labeled break" { break :blk; } - assert(i == 1); + assertOrPanic(i == 1); } test "errdefer does not apply to fn inside fn" { - if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assertOrPanic(e == error.Bad); } fn testNestedFnErrDefer() anyerror!void { diff --git a/test/cases/enum.zig b/test/stage1/behavior/enum.zig similarity index 84% rename from test/cases/enum.zig rename to test/stage1/behavior/enum.zig index 2dd552488c..9de138ef78 100644 --- a/test/cases/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; test "enum type" { @@ -11,16 +11,16 @@ test "enum type" { }; const bar = Bar.B; - assert(bar == Bar.B); - assert(@memberCount(Foo) == 3); - assert(@memberCount(Bar) == 4); - assert(@sizeOf(Foo) == @sizeOf(FooNoVoid)); - assert(@sizeOf(Bar) == 1); + assertOrPanic(bar == Bar.B); + assertOrPanic(@memberCount(Foo) == 3); + assertOrPanic(@memberCount(Bar) == 4); + assertOrPanic(@sizeOf(Foo) == @sizeOf(FooNoVoid)); + assertOrPanic(@sizeOf(Bar) == 1); } test "enum as return value" { switch (returnAnInt(13)) { - Foo.One => |value| assert(value == 13), + Foo.One => |value| assertOrPanic(value == 13), else => unreachable, } } @@ -92,14 +92,14 @@ test "enum to int" { } fn shouldEqual(n: Number, expected: u3) void { - assert(@enumToInt(n) == expected); + assertOrPanic(@enumToInt(n) == expected); } test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) void { - assert(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); + assertOrPanic(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -110,8 +110,8 @@ const IntToEnumNumber = enum { }; test "@tagName" { - assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); - comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + comptime assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); } fn testEnumTagNameBare(n: BareNumber) []const u8 { @@ -126,8 +126,8 @@ const BareNumber = enum { test "enum alignment" { comptime { - assert(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); - assert(@alignOf(AlignTestEnum) >= @alignOf(u64)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf(u64)); } } @@ -663,10 +663,10 @@ const ValueCount257 = enum { test "enum sizes" { comptime { - assert(@sizeOf(ValueCount1) == 0); - assert(@sizeOf(ValueCount2) == 1); - assert(@sizeOf(ValueCount256) == 1); - assert(@sizeOf(ValueCount257) == 2); + assertOrPanic(@sizeOf(ValueCount1) == 0); + assertOrPanic(@sizeOf(ValueCount2) == 1); + assertOrPanic(@sizeOf(ValueCount256) == 1); + assertOrPanic(@sizeOf(ValueCount257) == 2); } } @@ -685,12 +685,12 @@ test "set enum tag type" { { var x = Small.One; x = Small.Two; - comptime assert(@TagType(Small) == u2); + comptime assertOrPanic(@TagType(Small) == u2); } { var x = Small2.One; x = Small2.Two; - comptime assert(@TagType(Small2) == u2); + comptime assertOrPanic(@TagType(Small2) == u2); } } @@ -737,17 +737,17 @@ const bit_field_1 = BitFieldOfEnums{ test "bit field access with enum fields" { var data = bit_field_1; - assert(getA(&data) == A.Two); - assert(getB(&data) == B.Three3); - assert(getC(&data) == C.Four4); - comptime assert(@sizeOf(BitFieldOfEnums) == 1); + assertOrPanic(getA(&data) == A.Two); + assertOrPanic(getB(&data) == B.Three3); + assertOrPanic(getC(&data) == C.Four4); + comptime assertOrPanic(@sizeOf(BitFieldOfEnums) == 1); data.b = B.Four3; - assert(data.b == B.Four3); + assertOrPanic(data.b == B.Four3); data.a = A.Three; - assert(data.a == A.Three); - assert(data.b == B.Four3); + assertOrPanic(data.a == A.Three); + assertOrPanic(data.b == B.Four3); } fn getA(data: *const BitFieldOfEnums) A { @@ -768,7 +768,7 @@ test "casting enum to its tag type" { } fn testCastEnumToTagType(value: Small2) void { - assert(@enumToInt(value) == 1); + assertOrPanic(@enumToInt(value) == 1); } const MultipleChoice = enum(u32) { @@ -784,8 +784,8 @@ test "enum with specified tag values" { } fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { - assert(@enumToInt(x) == 60); - assert(1234 == switch (x) { + assertOrPanic(@enumToInt(x) == 60); + assertOrPanic(1234 == switch (x) { MultipleChoice.A => 1, MultipleChoice.B => 2, MultipleChoice.C => u32(1234), @@ -811,8 +811,8 @@ test "enum with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(x) == 1000); - assert(1234 == switch (x) { + assertOrPanic(@enumToInt(x) == 1000); + assertOrPanic(1234 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => 3, @@ -826,8 +826,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { } test "cast integer literal to enum" { - assert(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); - assert(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); + assertOrPanic(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + assertOrPanic(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); } const EnumWithOneMember = enum { @@ -865,14 +865,14 @@ const EnumWithTagValues = enum(u4) { D = 1 << 3, }; test "enum with tag values don't require parens" { - assert(@enumToInt(EnumWithTagValues.C) == 0b0100); + assertOrPanic(@enumToInt(EnumWithTagValues.C) == 0b0100); } test "enum with 1 field but explicit tag type should still have the tag type" { const Enum = enum(u8) { B = 2, }; - comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8)); + comptime @import("std").debug.assertOrPanic(@sizeOf(Enum) == @sizeOf(u8)); } test "empty extern enum with members" { @@ -881,14 +881,14 @@ test "empty extern enum with members" { B, C, }; - assert(@sizeOf(E) == @sizeOf(c_int)); + assertOrPanic(@sizeOf(E) == @sizeOf(c_int)); } -test "aoeu" { +test "tag name with assigned enum values" { const LocalFoo = enum { A = 1, B = 0, }; var b = LocalFoo.B; - assert(mem.eql(u8, @tagName(b), "B")); + assertOrPanic(mem.eql(u8, @tagName(b), "B")); } diff --git a/test/cases/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig similarity index 63% rename from test/cases/enum_with_members.zig rename to test/stage1/behavior/enum_with_members.zig index 088496bd2f..49af1ceae7 100644 --- a/test/cases/enum_with_members.zig +++ b/test/stage1/behavior/enum_with_members.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; const fmt = @import("std").fmt; @@ -19,9 +19,9 @@ test "enum with members" { const b = ET{ .UINT = 42 }; var buf: [20]u8 = undefined; - assert((a.print(buf[0..]) catch unreachable) == 3); - assert(mem.eql(u8, buf[0..3], "-42")); + assertOrPanic((a.print(buf[0..]) catch unreachable) == 3); + assertOrPanic(mem.eql(u8, buf[0..3], "-42")); - assert((b.print(buf[0..]) catch unreachable) == 2); - assert(mem.eql(u8, buf[0..2], "42")); + assertOrPanic((b.print(buf[0..]) catch unreachable) == 2); + assertOrPanic(mem.eql(u8, buf[0..2], "42")); } diff --git a/test/cases/error.zig b/test/stage1/behavior/error.zig similarity index 58% rename from test/cases/error.zig rename to test/stage1/behavior/error.zig index a731f39021..c7e38712bc 100644 --- a/test/cases/error.zig +++ b/test/stage1/behavior/error.zig @@ -1,5 +1,6 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; +const assertError = std.debug.assertError; const mem = std.mem; const builtin = @import("builtin"); @@ -18,7 +19,7 @@ pub fn baz() anyerror!i32 { } test "error wrapping" { - assert((baz() catch unreachable) == 15); + assertOrPanic((baz() catch unreachable) == 15); } fn gimmeItBroke() []const u8 { @@ -26,14 +27,14 @@ fn gimmeItBroke() []const u8 { } test "@errorName" { - assert(mem.eql(u8, @errorName(error.AnError), "AnError")); - assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); + assertOrPanic(mem.eql(u8, @errorName(error.AnError), "AnError")); + assertOrPanic(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); } test "error values" { const a = @errorToInt(error.err1); const b = @errorToInt(error.err2); - assert(a != b); + assertOrPanic(a != b); } test "redefinition of error values allowed" { @@ -46,8 +47,8 @@ fn shouldBeNotEqual(a: anyerror, b: anyerror) void { test "error binary operator" { const a = errBinaryOperatorG(true) catch 3; const b = errBinaryOperatorG(false) catch 3; - assert(a == 3); - assert(b == 10); + assertOrPanic(a == 3); + assertOrPanic(b == 10); } fn errBinaryOperatorG(x: bool) anyerror!isize { return if (x) error.ItBroke else isize(10); @@ -55,7 +56,7 @@ fn errBinaryOperatorG(x: bool) anyerror!isize { test "unwrap simple value from error" { const i = unwrapSimpleValueFromErrorDo() catch unreachable; - assert(i == 13); + assertOrPanic(i == 13); } fn unwrapSimpleValueFromErrorDo() anyerror!isize { return 13; @@ -81,13 +82,13 @@ test "error union type " { fn testErrorUnionType() void { const x: anyerror!i32 = 1234; - if (x) |value| assert(value == 1234) else |_| unreachable; - assert(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); - assert(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); - assert(@typeOf(x).ErrorSet == anyerror); + if (x) |value| assertOrPanic(value == 1234) else |_| unreachable; + assertOrPanic(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); + assertOrPanic(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); + assertOrPanic(@typeOf(x).ErrorSet == anyerror); } -test "error set type " { +test "error set type" { testErrorSetType(); comptime testErrorSetType(); } @@ -98,12 +99,12 @@ const MyErrSet = error{ }; fn testErrorSetType() void { - assert(@memberCount(MyErrSet) == 2); + assertOrPanic(@memberCount(MyErrSet) == 2); const a: MyErrSet!i32 = 5678; const b: MyErrSet!i32 = MyErrSet.OutOfMemory; - if (a) |value| assert(value == 5678) else |err| switch (err) { + if (a) |value| assertOrPanic(value == 5678) else |err| switch (err) { error.OutOfMemory => unreachable, error.FileNotFound => unreachable, } @@ -126,7 +127,7 @@ const Set2 = error{ fn testExplicitErrorSetCast(set1: Set1) void { var x = @errSetCast(Set2, set1); var y = @errSetCast(Set1, x); - assert(y == error.A); + assertOrPanic(y == error.A); } test "comptime test error for empty error set" { @@ -137,12 +138,12 @@ test "comptime test error for empty error set" { const EmptyErrorSet = error{}; fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { - if (x) |v| assert(v == 1234) else |err| @compileError("bad"); + if (x) |v| assertOrPanic(v == 1234) else |err| @compileError("bad"); } test "syntax: optional operator in front of error union operator" { comptime { - assert(?(anyerror!i32) == ?(anyerror!i32)); + assertOrPanic(?(anyerror!i32) == ?(anyerror!i32)); } } @@ -161,7 +162,6 @@ fn testErrToIntWithOnePossibleValue( test "error union peer type resolution" { testErrorUnionPeerTypeResolution(1); - comptime testErrorUnionPeerTypeResolution(1); } fn testErrorUnionPeerTypeResolution(x: i32) void { @@ -170,6 +170,11 @@ fn testErrorUnionPeerTypeResolution(x: i32) void { 2 => baz_1(), else => quux_1(), }; + if (y) |_| { + @panic("expected error"); + } else |e| { + assertOrPanic(e == error.A); + } } fn bar_1() anyerror { @@ -243,3 +248,85 @@ fn intLiteral(str: []const u8) !?i64 { return error.T; } + +test "nested error union function call in optional unwrap" { + const S = struct { + const Foo = struct { + a: i32, + }; + + fn errorable() !i32 { + var x: Foo = (try getFoo()) orelse return error.Other; + return x.a; + } + + fn errorable2() !i32 { + var x: Foo = (try getFoo2()) orelse return error.Other; + return x.a; + } + + fn errorable3() !i32 { + var x: Foo = (try getFoo3()) orelse return error.Other; + return x.a; + } + + fn getFoo() anyerror!?Foo { + return Foo{ .a = 1234 }; + } + + fn getFoo2() anyerror!?Foo { + return error.Failure; + } + + fn getFoo3() anyerror!?Foo { + return null; + } + }; + assertOrPanic((try S.errorable()) == 1234); + assertError(S.errorable2(), error.Failure); + assertError(S.errorable3(), error.Other); + comptime { + assertOrPanic((try S.errorable()) == 1234); + assertError(S.errorable2(), error.Failure); + assertError(S.errorable3(), error.Other); + } +} + +test "widen cast integer payload of error union function call" { + const S = struct { + fn errorable() !u64 { + var x = u64(try number()); + return x; + } + + fn number() anyerror!u32 { + return 1234; + } + }; + assertOrPanic((try S.errorable()) == 1234); +} + +test "return function call to error set from error union function" { + const S = struct { + fn errorable() anyerror!i32 { + return fail(); + } + + fn fail() anyerror { + return error.Failure; + } + }; + assertError(S.errorable(), error.Failure); + comptime assertError(S.errorable(), error.Failure); +} + +test "optional error set is the same size as error set" { + comptime assertOrPanic(@sizeOf(?anyerror) == @sizeOf(anyerror)); + const S = struct { + fn returnsOptErrSet() ?anyerror { + return null; + } + }; + assertOrPanic(S.returnsOptErrSet() == null); + comptime assertOrPanic(S.returnsOptErrSet() == null); +} diff --git a/test/cases/eval.zig b/test/stage1/behavior/eval.zig similarity index 70% rename from test/cases/eval.zig rename to test/stage1/behavior/eval.zig index a9eded151e..3e8af0524f 100644 --- a/test/cases/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); test "compile time recursion" { - assert(some_data.len == 21); + assertOrPanic(some_data.len == 21); } var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; fn fibonacci(x: i32) i32 { @@ -16,7 +16,7 @@ fn unwrapAndAddOne(blah: ?i32) i32 { } const should_be_1235 = unwrapAndAddOne(1234); test "static add one" { - assert(should_be_1235 == 1235); + assertOrPanic(should_be_1235 == 1235); } test "inlined loop" { @@ -24,7 +24,7 @@ test "inlined loop" { comptime var sum = 0; inline while (i <= 5) : (i += 1) sum += i; - assert(sum == 15); + assertOrPanic(sum == 15); } fn gimme1or2(comptime a: bool) i32 { @@ -34,12 +34,12 @@ fn gimme1or2(comptime a: bool) i32 { return z; } test "inline variable gets result of const if" { - assert(gimme1or2(true) == 1); - assert(gimme1or2(false) == 2); + assertOrPanic(gimme1or2(true) == 1); + assertOrPanic(gimme1or2(false) == 2); } test "static function evaluation" { - assert(statically_added_number == 3); + assertOrPanic(statically_added_number == 3); } const statically_added_number = staticAdd(1, 2); fn staticAdd(a: i32, b: i32) i32 { @@ -47,7 +47,8 @@ fn staticAdd(a: i32, b: i32) i32 { } test "const expr eval on single expr blocks" { - assert(constExprEvalOnSingleExprBlocksFn(1, true) == 3); + assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, true) == 3); + comptime assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, true) == 3); } fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { @@ -63,10 +64,10 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { } test "statically initialized list" { - assert(static_point_list[0].x == 1); - assert(static_point_list[0].y == 2); - assert(static_point_list[1].x == 3); - assert(static_point_list[1].y == 4); + assertOrPanic(static_point_list[0].x == 1); + assertOrPanic(static_point_list[0].y == 2); + assertOrPanic(static_point_list[1].x == 3); + assertOrPanic(static_point_list[1].y == 4); } const Point = struct { x: i32, @@ -84,8 +85,8 @@ fn makePoint(x: i32, y: i32) Point { } test "static eval list init" { - assert(static_vec3.data[2] == 1.0); - assert(vec3(0.0, 0.0, 3.0).data[2] == 3.0); + assertOrPanic(static_vec3.data[2] == 1.0); + assertOrPanic(vec3(0.0, 0.0, 3.0).data[2] == 3.0); } const static_vec3 = vec3(0.0, 0.0, 1.0); pub const Vec3 = struct { @@ -101,12 +102,12 @@ pub fn vec3(x: f32, y: f32, z: f32) Vec3 { test "constant expressions" { var array: [array_size]u8 = undefined; - assert(@sizeOf(@typeOf(array)) == 20); + assertOrPanic(@sizeOf(@typeOf(array)) == 20); } const array_size: u8 = 20; test "constant struct with negation" { - assert(vertices[0].x == -0.6); + assertOrPanic(vertices[0].x == -0.6); } const Vertex = struct { x: f32, @@ -141,7 +142,7 @@ const vertices = []Vertex{ test "statically initialized struct" { st_init_str_foo.x += 1; - assert(st_init_str_foo.x == 14); + assertOrPanic(st_init_str_foo.x == 14); } const StInitStrFoo = struct { x: i32, @@ -154,7 +155,7 @@ var st_init_str_foo = StInitStrFoo{ test "statically initalized array literal" { const y: [4]u8 = st_init_arr_lit_x; - assert(y[3] == 4); + assertOrPanic(y[3] == 4); } const st_init_arr_lit_x = []u8{ 1, @@ -166,15 +167,15 @@ const st_init_arr_lit_x = []u8{ test "const slice" { comptime { const a = "1234567890"; - assert(a.len == 10); + assertOrPanic(a.len == 10); const b = a[1..2]; - assert(b.len == 1); - assert(b[0] == '2'); + assertOrPanic(b.len == 1); + assertOrPanic(b[0] == '2'); } } test "try to trick eval with runtime if" { - assert(testTryToTrickEvalWithRuntimeIf(true) == 10); + assertOrPanic(testTryToTrickEvalWithRuntimeIf(true) == 10); } fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { @@ -200,16 +201,16 @@ fn letsTryToCompareBools(a: bool, b: bool) bool { return max(bool, a, b); } test "inlined block and runtime block phi" { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); comptime { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); } } @@ -254,14 +255,14 @@ fn performFn(comptime prefix_char: u8, start_value: i32) i32 { } test "comptime iterate over fn ptr list" { - assert(performFn('t', 1) == 6); - assert(performFn('o', 0) == 1); - assert(performFn('w', 99) == 99); + assertOrPanic(performFn('t', 1) == 6); + assertOrPanic(performFn('o', 0) == 1); + assertOrPanic(performFn('w', 99) == 99); } test "eval @setRuntimeSafety at compile-time" { const result = comptime fnWithSetRuntimeSafety(); - assert(result == 1234); + assertOrPanic(result == 1234); } fn fnWithSetRuntimeSafety() i32 { @@ -271,7 +272,7 @@ fn fnWithSetRuntimeSafety() i32 { test "eval @setFloatMode at compile-time" { const result = comptime fnWithFloatMode(); - assert(result == 1234.0); + assertOrPanic(result == 1234.0); } fn fnWithFloatMode() f32 { @@ -292,15 +293,15 @@ var simple_struct = SimpleStruct{ .field = 1234 }; const bound_fn = simple_struct.method; test "call method on bound fn referring to var instance" { - assert(bound_fn() == 1237); + assertOrPanic(bound_fn() == 1237); } test "ptr to local array argument at comptime" { comptime { var bytes: [10]u8 = undefined; modifySomeBytes(bytes[0..]); - assert(bytes[0] == 'a'); - assert(bytes[9] == 'b'); + assertOrPanic(bytes[0] == 'a'); + assertOrPanic(bytes[9] == 'b'); } } @@ -328,9 +329,9 @@ fn testCompTimeUIntComparisons(x: u32) void { } test "const ptr to variable data changes at runtime" { - assert(foo_ref.name[0] == 'a'); + assertOrPanic(foo_ref.name[0] == 'a'); foo_ref.name = "b"; - assert(foo_ref.name[0] == 'b'); + assertOrPanic(foo_ref.name[0] == 'b'); } const Foo = struct { @@ -341,8 +342,8 @@ var foo_contents = Foo{ .name = "a" }; const foo_ref = &foo_contents; test "create global array with for loop" { - assert(global_array[5] == 5 * 5); - assert(global_array[9] == 9 * 9); + assertOrPanic(global_array[5] == 5 * 5); + assertOrPanic(global_array[9] == 9 * 9); } const global_array = x: { @@ -357,7 +358,7 @@ test "compile-time downcast when the bits fit" { comptime { const spartan_count: u16 = 255; const byte = @intCast(u8, spartan_count); - assert(byte == 255); + assertOrPanic(byte == 255); } } @@ -365,44 +366,45 @@ const hi1 = "hi"; const hi2 = hi1; test "const global shares pointer with other same one" { assertEqualPtrs(&hi1[0], &hi2[0]); - comptime assert(&hi1[0] == &hi2[0]); + comptime assertOrPanic(&hi1[0] == &hi2[0]); } fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { - assert(ptr1 == ptr2); + assertOrPanic(ptr1 == ptr2); } test "@setEvalBranchQuota" { comptime { - // 1001 for the loop and then 1 more for the assert fn call + // 1001 for the loop and then 1 more for the assertOrPanic fn call @setEvalBranchQuota(1002); var i = 0; var sum = 0; while (i < 1001) : (i += 1) { sum += i; } - assert(sum == 500500); + assertOrPanic(sum == 500500); } } // TODO test "float literal at compile time not lossy" { -// TODO assert(16777216.0 + 1.0 == 16777217.0); -// TODO assert(9007199254740992.0 + 1.0 == 9007199254740993.0); +// TODO assertOrPanic(16777216.0 + 1.0 == 16777217.0); +// TODO assertOrPanic(9007199254740992.0 + 1.0 == 9007199254740993.0); // TODO } test "f32 at compile time is lossy" { - assert(f32(1 << 24) + 1 == 1 << 24); + assertOrPanic(f32(1 << 24) + 1 == 1 << 24); } test "f64 at compile time is lossy" { - assert(f64(1 << 53) + 1 == 1 << 53); + assertOrPanic(f64(1 << 53) + 1 == 1 << 53); } test "f128 at compile time is lossy" { - assert(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); + assertOrPanic(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); } -// TODO need a better implementation of bigfloat_init_bigint -// assert(f128(1 << 113) == 10384593717069655257060992658440192); +comptime { + assertOrPanic(f128(1 << 113) == 10384593717069655257060992658440192); +} pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { return struct { @@ -413,15 +415,15 @@ pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { test "string literal used as comptime slice is memoized" { const a = "link"; const b = "link"; - comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); - comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); + comptime assertOrPanic(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); + comptime assertOrPanic(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); } test "comptime slice of undefined pointer of length 0" { const slice1 = ([*]i32)(undefined)[0..0]; - assert(slice1.len == 0); + assertOrPanic(slice1.len == 0); const slice2 = ([*]i32)(undefined)[100..100]; - assert(slice2.len == 0); + assertOrPanic(slice2.len == 0); } fn copyWithPartialInline(s: []u32, b: []u8) void { @@ -443,16 +445,16 @@ test "binary math operator in partially inlined function" { r.* = @intCast(u8, i + 1); copyWithPartialInline(s[0..], b[0..]); - assert(s[0] == 0x1020304); - assert(s[1] == 0x5060708); - assert(s[2] == 0x90a0b0c); - assert(s[3] == 0xd0e0f10); + assertOrPanic(s[0] == 0x1020304); + assertOrPanic(s[1] == 0x5060708); + assertOrPanic(s[2] == 0x90a0b0c); + assertOrPanic(s[3] == 0xd0e0f10); } test "comptime function with the same args is memoized" { comptime { - assert(MakeType(i32) == MakeType(i32)); - assert(MakeType(i32) != MakeType(f64)); + assertOrPanic(MakeType(i32) == MakeType(i32)); + assertOrPanic(MakeType(i32) != MakeType(f64)); } } @@ -468,7 +470,7 @@ test "comptime function with mutable pointer is not memoized" { const ptr = &x; increment(ptr); increment(ptr); - assert(x == 3); + assertOrPanic(x == 3); } } @@ -494,14 +496,14 @@ fn doesAlotT(comptime T: type, value: usize) T { } test "@setEvalBranchQuota at same scope as generic function call" { - assert(doesAlotT(u32, 2) == 2); + assertOrPanic(doesAlotT(u32, 2) == 2); } test "comptime slice of slice preserves comptime var" { comptime { var buff: [10]u8 = undefined; buff[0..][0..][0] = 1; - assert(buff[0..][0..][0] == 1); + assertOrPanic(buff[0..][0..][0] == 1); } } @@ -510,7 +512,7 @@ test "comptime slice of pointer preserves comptime var" { var buff: [10]u8 = undefined; var a = buff[0..].ptr; a[0..1][0] = 1; - assert(buff[0..][0..][0] == 1); + assertOrPanic(buff[0..][0..][0] == 1); } } @@ -524,9 +526,9 @@ const SingleFieldStruct = struct { test "const ptr to comptime mutable data is not memoized" { comptime { var foo = SingleFieldStruct{ .x = 1 }; - assert(foo.read_x() == 1); + assertOrPanic(foo.read_x() == 1); foo.x = 2; - assert(foo.read_x() == 2); + assertOrPanic(foo.read_x() == 2); } } @@ -535,7 +537,7 @@ test "array concat of slices gives slice" { var a: []const u8 = "aoeu"; var b: []const u8 = "asdf"; const c = a ++ b; - assert(std.mem.eql(u8, c, "aoeuasdf")); + assertOrPanic(std.mem.eql(u8, c, "aoeuasdf")); } } @@ -552,14 +554,14 @@ test "comptime shlWithOverflow" { break :amt amt; }; - assert(ct_shifted == rt_shifted); + assertOrPanic(ct_shifted == rt_shifted); } test "runtime 128 bit integer division" { var a: u128 = 152313999999999991610955792383; var b: u128 = 10000000000000000000; var c = a / b; - assert(c == 15231399999); + assertOrPanic(c == 15231399999); } pub const Info = struct { @@ -572,20 +574,20 @@ test "comptime modification of const struct field" { comptime { var res = diamond_info; res.version = 1; - assert(diamond_info.version == 0); - assert(res.version == 1); + assertOrPanic(diamond_info.version == 0); + assertOrPanic(res.version == 1); } } test "pointer to type" { comptime { var T: type = i32; - assert(T == i32); + assertOrPanic(T == i32); var ptr = &T; - assert(@typeOf(ptr) == *type); + assertOrPanic(@typeOf(ptr) == *type); ptr.* = f32; - assert(T == f32); - assert(*T == *f32); + assertOrPanic(T == f32); + assertOrPanic(*T == *f32); } } @@ -594,17 +596,17 @@ test "slice of type" { var types_array = []type{ i32, f64, type }; for (types_array) |T, i| { switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), else => unreachable, } } for (types_array[0..]) |T, i| { switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), else => unreachable, } } @@ -621,7 +623,7 @@ fn wrap(comptime T: type) Wrapper { test "function which returns struct with type field causes implicit comptime" { const ty = wrap(i32).T; - assert(ty == i32); + assertOrPanic(ty == i32); } test "call method with comptime pass-by-non-copying-value self parameter" { @@ -635,12 +637,12 @@ test "call method with comptime pass-by-non-copying-value self parameter" { const s = S{ .a = 2 }; var b = s.b(); - assert(b == 2); + assertOrPanic(b == 2); } test "@tagName of @typeId" { const str = @tagName(@typeId(u8)); - assert(std.mem.eql(u8, str, "Int")); + assertOrPanic(std.mem.eql(u8, str, "Int")); } test "setting backward branch quota just before a generic fn call" { @@ -661,8 +663,8 @@ fn testVarInsideInlineLoop(args: ...) void { comptime var i = 0; inline while (i < args.len) : (i += 1) { const x = args[i]; - if (i == 0) assert(x); - if (i == 1) assert(x == 42); + if (i == 0) assertOrPanic(x); + if (i == 1) assertOrPanic(x == 42); } } @@ -672,7 +674,7 @@ test "inline for with same type but different values" { var a: T = undefined; res += a.len; } - assert(res == 5); + assertOrPanic(res == 5); } test "refer to the type of a generic function" { @@ -686,19 +688,19 @@ fn doNothingWithType(comptime T: type) void {} test "zero extend from u0 to u1" { var zero_u0: u0 = 0; var zero_u1: u1 = zero_u0; - assert(zero_u1 == 0); + assertOrPanic(zero_u1 == 0); } test "bit shift a u1" { var x: u1 = 1; var y = x << 0; - assert(y == 1); + assertOrPanic(y == 1); } test "@intCast to a u0" { var x: u8 = 0; var y: u0 = @intCast(u0, x); - assert(y == 0); + assertOrPanic(y == 0); } test "@bytesToslice on a packed struct" { @@ -708,7 +710,7 @@ test "@bytesToslice on a packed struct" { var b = [1]u8{9}; var f = @bytesToSlice(F, b); - assert(f[0].a == 9); + assertOrPanic(f[0].a == 9); } test "comptime pointer cast array and then slice" { @@ -720,57 +722,57 @@ test "comptime pointer cast array and then slice" { const ptrB: [*]const u8 = &array; const sliceB: []const u8 = ptrB[0..2]; - assert(sliceA[1] == 2); - assert(sliceB[1] == 2); + assertOrPanic(sliceA[1] == 2); + assertOrPanic(sliceB[1] == 2); } test "slice bounds in comptime concatenation" { const bs = comptime blk: { - const b = c"11"; - break :blk b[0..1]; + const b = c"........1........"; + break :blk b[8..9]; }; const str = "" ++ bs; - assert(str.len == 1); - assert(std.mem.eql(u8, str, "1")); + assertOrPanic(str.len == 1); + assertOrPanic(std.mem.eql(u8, str, "1")); const str2 = bs ++ ""; - assert(str2.len == 1); - assert(std.mem.eql(u8, str2, "1")); + assertOrPanic(str2.len == 1); + assertOrPanic(std.mem.eql(u8, str2, "1")); } test "comptime bitwise operators" { comptime { - assert(3 & 1 == 1); - assert(3 & -1 == 3); - assert(-3 & -1 == -3); - assert(3 | -1 == -1); - assert(-3 | -1 == -1); - assert(3 ^ -1 == -4); - assert(-3 ^ -1 == 2); - assert(~i8(-1) == 0); - assert(~i128(-1) == 0); - assert(18446744073709551615 & 18446744073709551611 == 18446744073709551611); - assert(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); - assert(~u128(0) == 0xffffffffffffffffffffffffffffffff); + assertOrPanic(3 & 1 == 1); + assertOrPanic(3 & -1 == 3); + assertOrPanic(-3 & -1 == -3); + assertOrPanic(3 | -1 == -1); + assertOrPanic(-3 | -1 == -1); + assertOrPanic(3 ^ -1 == -4); + assertOrPanic(-3 ^ -1 == 2); + assertOrPanic(~i8(-1) == 0); + assertOrPanic(~i128(-1) == 0); + assertOrPanic(18446744073709551615 & 18446744073709551611 == 18446744073709551611); + assertOrPanic(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); + assertOrPanic(~u128(0) == 0xffffffffffffffffffffffffffffffff); } } test "*align(1) u16 is the same as *align(1:0:2) u16" { comptime { - assert(*align(1:0:2) u16 == *align(1) u16); + assertOrPanic(*align(1:0:2) u16 == *align(1) u16); // TODO add parsing support for this syntax - //assert(*align(:0:2) u16 == *u16); + //assertOrPanic(*align(:0:2) u16 == *u16); } } test "array concatenation forces comptime" { var a = oneItem(3) ++ oneItem(4); - assert(std.mem.eql(i32, a, []i32{3, 4})); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 4 })); } test "array multiplication forces comptime" { var a = oneItem(3) ** scalar(2); - assert(std.mem.eql(i32, a, []i32{3, 3})); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 3 })); } fn oneItem(x: i32) [1]i32 { diff --git a/test/cases/field_parent_ptr.zig b/test/stage1/behavior/field_parent_ptr.zig similarity index 69% rename from test/cases/field_parent_ptr.zig rename to test/stage1/behavior/field_parent_ptr.zig index 00d4e0f367..ed2487c020 100644 --- a/test/cases/field_parent_ptr.zig +++ b/test/stage1/behavior/field_parent_ptr.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "@fieldParentPtr non-first field" { testParentFieldPtr(&foo.c); @@ -25,17 +25,17 @@ const foo = Foo{ }; fn testParentFieldPtr(c: *const i32) void { - assert(c == &foo.c); + assertOrPanic(c == &foo.c); const base = @fieldParentPtr(Foo, "c", c); - assert(base == &foo); - assert(&base.c == c); + assertOrPanic(base == &foo); + assertOrPanic(&base.c == c); } fn testParentFieldPtrFirst(a: *const bool) void { - assert(a == &foo.a); + assertOrPanic(a == &foo.a); const base = @fieldParentPtr(Foo, "a", a); - assert(base == &foo); - assert(&base.a == a); + assertOrPanic(base == &foo); + assertOrPanic(&base.a == a); } diff --git a/test/cases/fn.zig b/test/stage1/behavior/fn.zig similarity index 78% rename from test/cases/fn.zig rename to test/stage1/behavior/fn.zig index 8908bd7854..3011bc41d0 100644 --- a/test/cases/fn.zig +++ b/test/stage1/behavior/fn.zig @@ -1,7 +1,7 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "params" { - assert(testParamsAdd(22, 11) == 33); + assertOrPanic(testParamsAdd(22, 11) == 33); } fn testParamsAdd(a: i32, b: i32) i32 { return a + b; @@ -21,32 +21,32 @@ test "void parameters" { fn voidFun(a: i32, b: void, c: i32, d: void) void { const v = b; const vv: void = if (a == 1) v else {}; - assert(a + c == 3); + assertOrPanic(a + c == 3); return vv; } test "mutable local variables" { var zero: i32 = 0; - assert(zero == 0); + assertOrPanic(zero == 0); var i = i32(0); while (i != 3) { i += 1; } - assert(i == 3); + assertOrPanic(i == 3); } test "separate block scopes" { { const no_conflict: i32 = 5; - assert(no_conflict == 5); + assertOrPanic(no_conflict == 5); } const c = x: { const no_conflict = i32(10); break :x no_conflict; }; - assert(c == 10); + assertOrPanic(c == 10); } test "call function with empty string" { @@ -59,7 +59,7 @@ fn @"weird function name"() i32 { return 1234; } test "weird function name" { - assert(@"weird function name"() == 1234); + assertOrPanic(@"weird function name"() == 1234); } test "implicit cast function unreachable return" { @@ -80,7 +80,7 @@ test "function pointers" { fn4, }; for (fns) |f, i| { - assert(f() == @intCast(u32, i) + 5); + assertOrPanic(f() == @intCast(u32, i) + 5); } } fn fn1() u32 { @@ -97,7 +97,7 @@ fn fn4() u32 { } test "inline function call" { - assert(@inlineCall(add, 3, 9) == 12); + assertOrPanic(@inlineCall(add, 3, 9) == 12); } fn add(a: i32, b: i32) i32 { @@ -110,7 +110,7 @@ test "number literal as an argument" { } fn numberLiteralArg(a: var) void { - assert(a == 3); + assertOrPanic(a == 3); } test "assign inline fn to const variable" { @@ -121,7 +121,7 @@ test "assign inline fn to const variable" { inline fn inlineFn() void {} test "pass by non-copying value" { - assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); + assertOrPanic(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } const Point = struct { @@ -134,17 +134,17 @@ fn addPointCoords(pt: Point) i32 { } test "pass by non-copying value through var arg" { - assert(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); + assertOrPanic(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); } fn addPointCoordsVar(pt: var) i32 { - comptime assert(@typeOf(pt) == Point); + comptime assertOrPanic(@typeOf(pt) == Point); return pt.x + pt.y; } test "pass by non-copying value as method" { var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); + assertOrPanic(pt.addPointCoords() == 3); } const Point2 = struct { @@ -158,7 +158,7 @@ const Point2 = struct { test "pass by non-copying value as method, which is generic" { var pt = Point3{ .x = 1, .y = 2 }; - assert(pt.addPointCoords(i32) == 3); + assertOrPanic(pt.addPointCoords(i32) == 3); } const Point3 = struct { @@ -173,7 +173,7 @@ const Point3 = struct { test "pass by non-copying value as method, at comptime" { comptime { var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); + assertOrPanic(pt.addPointCoords() == 3); } } @@ -189,7 +189,7 @@ fn outer(y: u32) fn (u32) u32 { test "return inner function which references comptime variable of outer function" { var func = outer(10); - assert(func(3) == 7); + assertOrPanic(func(3) == 7); } test "extern struct with stdcallcc fn pointer" { @@ -203,5 +203,6 @@ test "extern struct with stdcallcc fn pointer" { var s: S = undefined; s.ptr = S.foo; - assert(s.ptr() == 1234); + assertOrPanic(s.ptr() == 1234); } + diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/stage1/behavior/fn_in_struct_in_comptime.zig similarity index 72% rename from test/cases/fn_in_struct_in_comptime.zig rename to test/stage1/behavior/fn_in_struct_in_comptime.zig index fabb57e9cb..0af076d40a 100644 --- a/test/cases/fn_in_struct_in_comptime.zig +++ b/test/stage1/behavior/fn_in_struct_in_comptime.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn get_foo() fn (*u8) usize { comptime { @@ -13,5 +13,5 @@ fn get_foo() fn (*u8) usize { test "define a function in an anonymous struct in comptime" { const foo = get_foo(); - assert(foo(@intToPtr(*u8, 12345)) == 12345); + assertOrPanic(foo(@intToPtr(*u8, 12345)) == 12345); } diff --git a/test/cases/for.zig b/test/stage1/behavior/for.zig similarity index 89% rename from test/cases/for.zig rename to test/stage1/behavior/for.zig index aecd8b9a07..b6d1ef24c4 100644 --- a/test/cases/for.zig +++ b/test/stage1/behavior/for.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; test "continue in for loop" { @@ -26,7 +26,7 @@ test "for loop with pointer elem var" { var target: [source.len]u8 = undefined; mem.copy(u8, target[0..], source); mangleString(target[0..]); - assert(mem.eql(u8, target, "bcdefgh")); + assertOrPanic(mem.eql(u8, target, "bcdefgh")); } fn mangleString(s: []u8) void { for (s) |*c| { @@ -68,7 +68,7 @@ test "basic for loop" { buf_index += 1; } - assert(mem.eql(u8, buffer[0..buf_index], expected_result)); + assertOrPanic(mem.eql(u8, buffer[0..buf_index], expected_result)); } test "break from outer for loop" { @@ -85,7 +85,7 @@ fn testBreakOuter() void { break :outer; } } - assert(count == 1); + assertOrPanic(count == 1); } test "continue outer for loop" { @@ -102,5 +102,5 @@ fn testContinueOuter() void { continue :outer; } } - assert(counter == array.len); + assertOrPanic(counter == array.len); } diff --git a/test/cases/generics.zig b/test/stage1/behavior/generics.zig similarity index 68% rename from test/cases/generics.zig rename to test/stage1/behavior/generics.zig index 52aa013989..a0928634a7 100644 --- a/test/cases/generics.zig +++ b/test/stage1/behavior/generics.zig @@ -1,9 +1,9 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "simple generic fn" { - assert(max(i32, 3, -1) == 3); - assert(max(f32, 0.123, 0.456) == 0.456); - assert(add(2, 3) == 5); + assertOrPanic(max(i32, 3, -1) == 3); + assertOrPanic(max(f32, 0.123, 0.456) == 0.456); + assertOrPanic(add(2, 3) == 5); } fn max(comptime T: type, a: T, b: T) T { @@ -16,7 +16,7 @@ fn add(comptime a: i32, b: i32) i32 { const the_max = max(u32, 1234, 5678); test "compile time generic eval" { - assert(the_max == 5678); + assertOrPanic(the_max == 5678); } fn gimmeTheBigOne(a: u32, b: u32) u32 { @@ -32,19 +32,19 @@ fn sameButWithFloats(a: f64, b: f64) f64 { } test "fn with comptime args" { - assert(gimmeTheBigOne(1234, 5678) == 5678); - assert(shouldCallSameInstance(34, 12) == 34); - assert(sameButWithFloats(0.43, 0.49) == 0.49); + assertOrPanic(gimmeTheBigOne(1234, 5678) == 5678); + assertOrPanic(shouldCallSameInstance(34, 12) == 34); + assertOrPanic(sameButWithFloats(0.43, 0.49) == 0.49); } test "var params" { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); + assertOrPanic(max_i32(12, 34) == 34); + assertOrPanic(max_f64(1.2, 3.4) == 3.4); } comptime { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); + assertOrPanic(max_i32(12, 34) == 34); + assertOrPanic(max_f64(1.2, 3.4) == 3.4); } fn max_var(a: var, b: var) @typeOf(a + b) { @@ -76,8 +76,8 @@ test "function with return type type" { var list2: List(i32) = undefined; list.length = 10; list2.length = 10; - assert(list.prealloc_items.len == 8); - assert(list2.prealloc_items.len == 8); + assertOrPanic(list.prealloc_items.len == 8); + assertOrPanic(list2.prealloc_items.len == 8); } test "generic struct" { @@ -89,9 +89,9 @@ test "generic struct" { .value = true, .next = null, }; - assert(a1.value == 13); - assert(a1.value == a1.getVal()); - assert(b1.getVal()); + assertOrPanic(a1.value == 13); + assertOrPanic(a1.value == a1.getVal()); + assertOrPanic(b1.getVal()); } fn GenNode(comptime T: type) type { return struct { @@ -104,7 +104,7 @@ fn GenNode(comptime T: type) type { } test "const decls in struct" { - assert(GenericDataThing(3).count_plus_one == 4); + assertOrPanic(GenericDataThing(3).count_plus_one == 4); } fn GenericDataThing(comptime count: isize) type { return struct { @@ -113,15 +113,15 @@ fn GenericDataThing(comptime count: isize) type { } test "use generic param in generic param" { - assert(aGenericFn(i32, 3, 4) == 7); + assertOrPanic(aGenericFn(i32, 3, 4) == 7); } fn aGenericFn(comptime T: type, comptime a: T, b: T) T { return a + b; } test "generic fn with implicit cast" { - assert(getFirstByte(u8, []u8{13}) == 13); - assert(getFirstByte(u16, []u16{ + assertOrPanic(getFirstByte(u8, []u8{13}) == 13); + assertOrPanic(getFirstByte(u16, []u16{ 0, 13, }) == 0); @@ -146,6 +146,6 @@ fn foo2(arg: var) bool { } test "array of generic fns" { - assert(foos[0](true)); - assert(!foos[1](true)); + assertOrPanic(foos[0](true)); + assertOrPanic(!foos[1](true)); } diff --git a/test/cases/if.zig b/test/stage1/behavior/if.zig similarity index 85% rename from test/cases/if.zig rename to test/stage1/behavior/if.zig index 808936bfa5..58d1b8fd73 100644 --- a/test/cases/if.zig +++ b/test/stage1/behavior/if.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "if statements" { shouldBeEqual(1, 1); @@ -24,7 +24,7 @@ fn firstEqlThird(a: i32, b: i32, c: i32) void { } test "else if expression" { - assert(elseIfExpressionF(1) == 1); + assertOrPanic(elseIfExpressionF(1) == 1); } fn elseIfExpressionF(c: u8) u8 { if (c == 0) { diff --git a/test/stage1/behavior/import.zig b/test/stage1/behavior/import.zig new file mode 100644 index 0000000000..736e4c219d --- /dev/null +++ b/test/stage1/behavior/import.zig @@ -0,0 +1,10 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const a_namespace = @import("import/a_namespace.zig"); + +test "call fn via namespace lookup" { + assertOrPanic(a_namespace.foo() == 1234); +} + +test "importing the same thing gives the same import" { + assertOrPanic(@import("std") == @import("std")); +} diff --git a/test/cases/import/a_namespace.zig b/test/stage1/behavior/import/a_namespace.zig similarity index 100% rename from test/cases/import/a_namespace.zig rename to test/stage1/behavior/import/a_namespace.zig diff --git a/test/cases/incomplete_struct_param_tld.zig b/test/stage1/behavior/incomplete_struct_param_tld.zig similarity index 78% rename from test/cases/incomplete_struct_param_tld.zig rename to test/stage1/behavior/incomplete_struct_param_tld.zig index f1ac03a292..d062311b2e 100644 --- a/test/cases/incomplete_struct_param_tld.zig +++ b/test/stage1/behavior/incomplete_struct_param_tld.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const A = struct { b: B, @@ -26,5 +26,5 @@ test "incomplete struct param top level declaration" { .c = C{ .x = 13 }, }, }; - assert(foo(a) == 13); + assertOrPanic(foo(a) == 13); } diff --git a/test/cases/inttoptr.zig b/test/stage1/behavior/inttoptr.zig similarity index 99% rename from test/cases/inttoptr.zig rename to test/stage1/behavior/inttoptr.zig index ba3cc52f09..bf657fc86a 100644 --- a/test/cases/inttoptr.zig +++ b/test/stage1/behavior/inttoptr.zig @@ -24,4 +24,3 @@ fn forceCompilerAnalyzeBranchHardCodedPtrDereference(x: bool) void { return; } } - diff --git a/test/cases/ir_block_deps.zig b/test/stage1/behavior/ir_block_deps.zig similarity index 64% rename from test/cases/ir_block_deps.zig rename to test/stage1/behavior/ir_block_deps.zig index 5c1b18c00e..bc61e11df7 100644 --- a/test/cases/ir_block_deps.zig +++ b/test/stage1/behavior/ir_block_deps.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn foo(id: u64) !i32 { return switch (id) { @@ -16,6 +16,6 @@ fn getErrInt() anyerror!i32 { } test "ir block deps" { - assert((foo(1) catch unreachable) == 0); - assert((foo(2) catch unreachable) == 0); + assertOrPanic((foo(1) catch unreachable) == 0); + assertOrPanic((foo(2) catch unreachable) == 0); } diff --git a/test/cases/math.zig b/test/stage1/behavior/math.zig similarity index 56% rename from test/cases/math.zig rename to test/stage1/behavior/math.zig index 7d6b1bd9ac..9d6a5a4997 100644 --- a/test/cases/math.zig +++ b/test/stage1/behavior/math.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const maxInt = std.math.maxInt; const minInt = std.math.minInt; @@ -8,57 +8,57 @@ test "division" { comptime testDivision(); } fn testDivision() void { - assert(div(u32, 13, 3) == 4); - assert(div(f16, 1.0, 2.0) == 0.5); - assert(div(f32, 1.0, 2.0) == 0.5); + assertOrPanic(div(u32, 13, 3) == 4); + assertOrPanic(div(f16, 1.0, 2.0) == 0.5); + assertOrPanic(div(f32, 1.0, 2.0) == 0.5); - assert(divExact(u32, 55, 11) == 5); - assert(divExact(i32, -55, 11) == -5); - assert(divExact(f16, 55.0, 11.0) == 5.0); - assert(divExact(f16, -55.0, 11.0) == -5.0); - assert(divExact(f32, 55.0, 11.0) == 5.0); - assert(divExact(f32, -55.0, 11.0) == -5.0); + assertOrPanic(divExact(u32, 55, 11) == 5); + assertOrPanic(divExact(i32, -55, 11) == -5); + assertOrPanic(divExact(f16, 55.0, 11.0) == 5.0); + assertOrPanic(divExact(f16, -55.0, 11.0) == -5.0); + assertOrPanic(divExact(f32, 55.0, 11.0) == 5.0); + assertOrPanic(divExact(f32, -55.0, 11.0) == -5.0); - assert(divFloor(i32, 5, 3) == 1); - assert(divFloor(i32, -5, 3) == -2); - assert(divFloor(f16, 5.0, 3.0) == 1.0); - assert(divFloor(f16, -5.0, 3.0) == -2.0); - assert(divFloor(f32, 5.0, 3.0) == 1.0); - assert(divFloor(f32, -5.0, 3.0) == -2.0); - assert(divFloor(i32, -0x80000000, -2) == 0x40000000); - assert(divFloor(i32, 0, -0x80000000) == 0); - assert(divFloor(i32, -0x40000001, 0x40000000) == -2); - assert(divFloor(i32, -0x80000000, 1) == -0x80000000); + assertOrPanic(divFloor(i32, 5, 3) == 1); + assertOrPanic(divFloor(i32, -5, 3) == -2); + assertOrPanic(divFloor(f16, 5.0, 3.0) == 1.0); + assertOrPanic(divFloor(f16, -5.0, 3.0) == -2.0); + assertOrPanic(divFloor(f32, 5.0, 3.0) == 1.0); + assertOrPanic(divFloor(f32, -5.0, 3.0) == -2.0); + assertOrPanic(divFloor(i32, -0x80000000, -2) == 0x40000000); + assertOrPanic(divFloor(i32, 0, -0x80000000) == 0); + assertOrPanic(divFloor(i32, -0x40000001, 0x40000000) == -2); + assertOrPanic(divFloor(i32, -0x80000000, 1) == -0x80000000); - assert(divTrunc(i32, 5, 3) == 1); - assert(divTrunc(i32, -5, 3) == -1); - assert(divTrunc(f16, 5.0, 3.0) == 1.0); - assert(divTrunc(f16, -5.0, 3.0) == -1.0); - assert(divTrunc(f32, 5.0, 3.0) == 1.0); - assert(divTrunc(f32, -5.0, 3.0) == -1.0); - assert(divTrunc(f64, 5.0, 3.0) == 1.0); - assert(divTrunc(f64, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(i32, 5, 3) == 1); + assertOrPanic(divTrunc(i32, -5, 3) == -1); + assertOrPanic(divTrunc(f16, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f16, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(f32, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f32, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(f64, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f64, -5.0, 3.0) == -1.0); comptime { - assert( + assertOrPanic( 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, ); - assert( + assertOrPanic( @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, ); - assert( + assertOrPanic( 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, ); - assert( + assertOrPanic( @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, ); - assert( + assertOrPanic( @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, ); - assert( + assertOrPanic( @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, ); - assert( + assertOrPanic( 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, ); } @@ -78,9 +78,9 @@ fn divTrunc(comptime T: type, a: T, b: T) T { test "@addWithOverflow" { var result: u8 = undefined; - assert(@addWithOverflow(u8, 250, 100, &result)); - assert(!@addWithOverflow(u8, 100, 150, &result)); - assert(result == 250); + assertOrPanic(@addWithOverflow(u8, 250, 100, &result)); + assertOrPanic(!@addWithOverflow(u8, 100, 150, &result)); + assertOrPanic(result == 250); } // TODO test mulWithOverflow @@ -88,9 +88,9 @@ test "@addWithOverflow" { test "@shlWithOverflow" { var result: u16 = undefined; - assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); - assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); - assert(result == 0b1011111111111100); + assertOrPanic(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); + assertOrPanic(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); + assertOrPanic(result == 0b1011111111111100); } test "@clz" { @@ -99,11 +99,11 @@ test "@clz" { } fn testClz() void { - assert(clz(u8(0b00001010)) == 4); - assert(clz(u8(0b10001010)) == 0); - assert(clz(u8(0b00000000)) == 8); - assert(clz(u128(0xffffffffffffffff)) == 64); - assert(clz(u128(0x10000000000000000)) == 63); + assertOrPanic(clz(u8(0b00001010)) == 4); + assertOrPanic(clz(u8(0b10001010)) == 0); + assertOrPanic(clz(u8(0b00000000)) == 8); + assertOrPanic(clz(u128(0xffffffffffffffff)) == 64); + assertOrPanic(clz(u128(0x10000000000000000)) == 63); } fn clz(x: var) usize { @@ -116,9 +116,9 @@ test "@ctz" { } fn testCtz() void { - assert(ctz(u8(0b10100000)) == 5); - assert(ctz(u8(0b10001010)) == 1); - assert(ctz(u8(0b00000000)) == 8); + assertOrPanic(ctz(u8(0b10100000)) == 5); + assertOrPanic(ctz(u8(0b10001010)) == 1); + assertOrPanic(ctz(u8(0b00000000)) == 8); } fn ctz(x: var) usize { @@ -128,27 +128,27 @@ fn ctz(x: var) usize { test "assignment operators" { var i: u32 = 0; i += 5; - assert(i == 5); + assertOrPanic(i == 5); i -= 2; - assert(i == 3); + assertOrPanic(i == 3); i *= 20; - assert(i == 60); + assertOrPanic(i == 60); i /= 3; - assert(i == 20); + assertOrPanic(i == 20); i %= 11; - assert(i == 9); + assertOrPanic(i == 9); i <<= 1; - assert(i == 18); + assertOrPanic(i == 18); i >>= 2; - assert(i == 4); + assertOrPanic(i == 4); i = 6; i &= 5; - assert(i == 4); + assertOrPanic(i == 4); i ^= 6; - assert(i == 2); + assertOrPanic(i == 2); i = 6; i |= 3; - assert(i == 7); + assertOrPanic(i == 7); } test "three expr in a row" { @@ -170,14 +170,14 @@ fn testThreeExprInARow(f: bool, t: bool) void { assertFalse(i32(7) != --(i32(7))); } fn assertFalse(b: bool) void { - assert(!b); + assertOrPanic(!b); } test "const number literal" { const one = 1; const eleven = ten + one; - assert(eleven == 11); + assertOrPanic(eleven == 11); } const ten = 10; @@ -187,9 +187,9 @@ test "unsigned wrapping" { } fn testUnsignedWrappingEval(x: u32) void { const zero = x +% 1; - assert(zero == 0); + assertOrPanic(zero == 0); const orig = zero -% 1; - assert(orig == maxInt(u32)); + assertOrPanic(orig == maxInt(u32)); } test "signed wrapping" { @@ -198,9 +198,9 @@ test "signed wrapping" { } fn testSignedWrappingEval(x: i32) void { const min_val = x +% 1; - assert(min_val == minInt(i32)); + assertOrPanic(min_val == minInt(i32)); const max_val = min_val -% 1; - assert(max_val == maxInt(i32)); + assertOrPanic(max_val == maxInt(i32)); } test "negation wrapping" { @@ -208,9 +208,9 @@ test "negation wrapping" { comptime testNegationWrappingEval(minInt(i16)); } fn testNegationWrappingEval(x: i16) void { - assert(x == -32768); + assertOrPanic(x == -32768); const neg = -%x; - assert(neg == -32768); + assertOrPanic(neg == -32768); } test "unsigned 64-bit division" { @@ -219,8 +219,8 @@ test "unsigned 64-bit division" { } fn test_u64_div() void { const result = divWithResult(1152921504606846976, 34359738365); - assert(result.quotient == 33554432); - assert(result.remainder == 100663296); + assertOrPanic(result.quotient == 33554432); + assertOrPanic(result.remainder == 100663296); } fn divWithResult(a: u64, b: u64) DivResult { return DivResult{ @@ -234,36 +234,36 @@ const DivResult = struct { }; test "binary not" { - assert(comptime x: { + assertOrPanic(comptime x: { break :x ~u16(0b1010101010101010) == 0b0101010101010101; }); - assert(comptime x: { + assertOrPanic(comptime x: { break :x ~u64(2147483647) == 18446744071562067968; }); testBinaryNot(0b1010101010101010); } fn testBinaryNot(x: u16) void { - assert(~x == 0b0101010101010101); + assertOrPanic(~x == 0b0101010101010101); } test "small int addition" { var x: @IntType(false, 2) = 0; - assert(x == 0); + assertOrPanic(x == 0); x += 1; - assert(x == 1); + assertOrPanic(x == 1); x += 1; - assert(x == 2); + assertOrPanic(x == 2); x += 1; - assert(x == 3); + assertOrPanic(x == 3); var result: @typeOf(x) = 3; - assert(@addWithOverflow(@typeOf(x), x, 1, &result)); + assertOrPanic(@addWithOverflow(@typeOf(x), x, 1, &result)); - assert(result == 0); + assertOrPanic(result == 0); } test "float equality" { @@ -276,20 +276,20 @@ test "float equality" { fn testFloatEqualityImpl(x: f64, y: f64) void { const y2 = x + 1.0; - assert(y == y2); + assertOrPanic(y == y2); } test "allow signed integer division/remainder when values are comptime known and positive or exact" { - assert(5 / 3 == 1); - assert(-5 / -3 == 1); - assert(-6 / 3 == -2); + assertOrPanic(5 / 3 == 1); + assertOrPanic(-5 / -3 == 1); + assertOrPanic(-6 / 3 == -2); - assert(5 % 3 == 2); - assert(-6 % 3 == 0); + assertOrPanic(5 % 3 == 2); + assertOrPanic(-6 % 3 == 0); } test "hex float literal parsing" { - comptime assert(0x1.0 == 1.0); + comptime assertOrPanic(0x1.0 == 1.0); } test "quad hex float literal parsing in range" { @@ -304,7 +304,7 @@ test "quad hex float literal parsing accurate" { // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. const expected: u128 = 0x3fff1111222233334444555566667777; - assert(@bitCast(u128, a) == expected); + assertOrPanic(@bitCast(u128, a) == expected); } test "hex float literal within range" { @@ -319,7 +319,7 @@ test "truncating shift left" { } fn testShlTrunc(x: u16) void { const shifted = x << 1; - assert(shifted == 65534); + assertOrPanic(shifted == 65534); } test "truncating shift right" { @@ -328,7 +328,7 @@ test "truncating shift right" { } fn testShrTrunc(x: u16) void { const shifted = x >> 1; - assert(shifted == 32767); + assertOrPanic(shifted == 32767); } test "exact shift left" { @@ -337,7 +337,7 @@ test "exact shift left" { } fn testShlExact(x: u8) void { const shifted = @shlExact(x, 2); - assert(shifted == 0b11010100); + assertOrPanic(shifted == 0b11010100); } test "exact shift right" { @@ -346,22 +346,22 @@ test "exact shift right" { } fn testShrExact(x: u8) void { const shifted = @shrExact(x, 2); - assert(shifted == 0b00101101); + assertOrPanic(shifted == 0b00101101); } test "comptime_int addition" { comptime { - assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); - assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); + assertOrPanic(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); + assertOrPanic(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); } } test "comptime_int multiplication" { comptime { - assert( + assertOrPanic( 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, ); - assert( + assertOrPanic( 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, ); } @@ -369,7 +369,7 @@ test "comptime_int multiplication" { test "comptime_int shifting" { comptime { - assert((u128(1) << 127) == 0x80000000000000000000000000000000); + assertOrPanic((u128(1) << 127) == 0x80000000000000000000000000000000); } } @@ -377,16 +377,16 @@ test "comptime_int multi-limb shift and mask" { comptime { var a = 0xefffffffa0000001eeeeeeefaaaaaaab; - assert(u32(a & 0xffffffff) == 0xaaaaaaab); + assertOrPanic(u32(a & 0xffffffff) == 0xaaaaaaab); a >>= 32; - assert(u32(a & 0xffffffff) == 0xeeeeeeef); + assertOrPanic(u32(a & 0xffffffff) == 0xeeeeeeef); a >>= 32; - assert(u32(a & 0xffffffff) == 0xa0000001); + assertOrPanic(u32(a & 0xffffffff) == 0xa0000001); a >>= 32; - assert(u32(a & 0xffffffff) == 0xefffffff); + assertOrPanic(u32(a & 0xffffffff) == 0xefffffff); a >>= 32; - assert(a == 0); + assertOrPanic(a == 0); } } @@ -394,7 +394,7 @@ test "comptime_int multi-limb partial shift right" { comptime { var a = 0x1ffffffffeeeeeeee; a >>= 16; - assert(a == 0x1ffffffffeeee); + assertOrPanic(a == 0x1ffffffffeeee); } } @@ -404,23 +404,23 @@ test "xor" { } fn test_xor() void { - assert(0xFF ^ 0x00 == 0xFF); - assert(0xF0 ^ 0x0F == 0xFF); - assert(0xFF ^ 0xF0 == 0x0F); - assert(0xFF ^ 0x0F == 0xF0); - assert(0xFF ^ 0xFF == 0x00); + assertOrPanic(0xFF ^ 0x00 == 0xFF); + assertOrPanic(0xF0 ^ 0x0F == 0xFF); + assertOrPanic(0xFF ^ 0xF0 == 0x0F); + assertOrPanic(0xFF ^ 0x0F == 0xF0); + assertOrPanic(0xFF ^ 0xFF == 0x00); } test "comptime_int xor" { comptime { - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); - assert(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); - assert(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); + assertOrPanic(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); + assertOrPanic(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); + assertOrPanic(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); + assertOrPanic(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); + assertOrPanic(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); } } @@ -434,23 +434,23 @@ fn make_f128(x: f128) f128 { } fn test_f128() void { - assert(@sizeOf(f128) == 16); - assert(make_f128(1.0) == 1.0); - assert(make_f128(1.0) != 1.1); - assert(make_f128(1.0) > 0.9); - assert(make_f128(1.0) >= 0.9); - assert(make_f128(1.0) >= 1.0); + assertOrPanic(@sizeOf(f128) == 16); + assertOrPanic(make_f128(1.0) == 1.0); + assertOrPanic(make_f128(1.0) != 1.1); + assertOrPanic(make_f128(1.0) > 0.9); + assertOrPanic(make_f128(1.0) >= 0.9); + assertOrPanic(make_f128(1.0) >= 1.0); should_not_be_zero(1.0); } fn should_not_be_zero(x: f128) void { - assert(x != 0.0); + assertOrPanic(x != 0.0); } test "comptime float rem int" { comptime { var x = f32(1) % 2; - assert(x == 1.0); + assertOrPanic(x == 1.0); } } @@ -465,8 +465,8 @@ test "remainder division" { } fn remdiv(comptime T: type) void { - assert(T(1) == T(1) % T(2)); - assert(T(1) == T(7) % T(3)); + assertOrPanic(T(1) == T(1) % T(2)); + assertOrPanic(T(1) == T(7) % T(3)); } test "@sqrt" { @@ -480,19 +480,19 @@ test "@sqrt" { const x = 14.0; const y = x * x; const z = @sqrt(@typeOf(y), y); - comptime assert(z == x); + comptime assertOrPanic(z == x); } fn testSqrt(comptime T: type, x: T) void { - assert(@sqrt(T, x * x) == x); + assertOrPanic(@sqrt(T, x * x) == x); } test "comptime_int param and return" { const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); - assert(a == 137114567242441932203689521744947848950); + assertOrPanic(a == 137114567242441932203689521744947848950); const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); - assert(b == 985095453608931032642182098849559179469148836107390954364380); + assertOrPanic(b == 985095453608931032642182098849559179469148836107390954364380); } fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { diff --git a/test/cases/merge_error_sets.zig b/test/stage1/behavior/merge_error_sets.zig similarity index 100% rename from test/cases/merge_error_sets.zig rename to test/stage1/behavior/merge_error_sets.zig diff --git a/test/cases/misc.zig b/test/stage1/behavior/misc.zig similarity index 61% rename from test/cases/misc.zig rename to test/stage1/behavior/misc.zig index 1a34d54e9e..8d2555dddd 100644 --- a/test/cases/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const cstr = std.cstr; const builtin = @import("builtin"); @@ -26,38 +26,38 @@ test "call disabled extern fn" { } test "@IntType builtin" { - assert(@IntType(true, 8) == i8); - assert(@IntType(true, 16) == i16); - assert(@IntType(true, 32) == i32); - assert(@IntType(true, 64) == i64); + assertOrPanic(@IntType(true, 8) == i8); + assertOrPanic(@IntType(true, 16) == i16); + assertOrPanic(@IntType(true, 32) == i32); + assertOrPanic(@IntType(true, 64) == i64); - assert(@IntType(false, 8) == u8); - assert(@IntType(false, 16) == u16); - assert(@IntType(false, 32) == u32); - assert(@IntType(false, 64) == u64); + assertOrPanic(@IntType(false, 8) == u8); + assertOrPanic(@IntType(false, 16) == u16); + assertOrPanic(@IntType(false, 32) == u32); + assertOrPanic(@IntType(false, 64) == u64); - assert(i8.bit_count == 8); - assert(i16.bit_count == 16); - assert(i32.bit_count == 32); - assert(i64.bit_count == 64); + assertOrPanic(i8.bit_count == 8); + assertOrPanic(i16.bit_count == 16); + assertOrPanic(i32.bit_count == 32); + assertOrPanic(i64.bit_count == 64); - assert(i8.is_signed); - assert(i16.is_signed); - assert(i32.is_signed); - assert(i64.is_signed); - assert(isize.is_signed); + assertOrPanic(i8.is_signed); + assertOrPanic(i16.is_signed); + assertOrPanic(i32.is_signed); + assertOrPanic(i64.is_signed); + assertOrPanic(isize.is_signed); - assert(!u8.is_signed); - assert(!u16.is_signed); - assert(!u32.is_signed); - assert(!u64.is_signed); - assert(!usize.is_signed); + assertOrPanic(!u8.is_signed); + assertOrPanic(!u16.is_signed); + assertOrPanic(!u32.is_signed); + assertOrPanic(!u64.is_signed); + assertOrPanic(!usize.is_signed); } test "floating point primitive bit counts" { - assert(f16.bit_count == 16); - assert(f32.bit_count == 32); - assert(f64.bit_count == 64); + assertOrPanic(f16.bit_count == 16); + assertOrPanic(f32.bit_count == 32); + assertOrPanic(f64.bit_count == 64); } test "short circuit" { @@ -72,7 +72,7 @@ fn testShortCircuit(f: bool, t: bool) void { var hit_4 = f; if (t or x: { - assert(f); + assertOrPanic(f); break :x f; }) { hit_1 = t; @@ -81,31 +81,31 @@ fn testShortCircuit(f: bool, t: bool) void { hit_2 = t; break :x f; }) { - assert(f); + assertOrPanic(f); } if (t and x: { hit_3 = t; break :x f; }) { - assert(f); + assertOrPanic(f); } if (f and x: { - assert(f); + assertOrPanic(f); break :x f; }) { - assert(f); + assertOrPanic(f); } else { hit_4 = t; } - assert(hit_1); - assert(hit_2); - assert(hit_3); - assert(hit_4); + assertOrPanic(hit_1); + assertOrPanic(hit_2); + assertOrPanic(hit_3); + assertOrPanic(hit_4); } test "truncate" { - assert(testTruncate(0x10fd) == 0xfd); + assertOrPanic(testTruncate(0x10fd) == 0xfd); } fn testTruncate(x: u32) u8 { return @truncate(u8, x); @@ -116,16 +116,16 @@ fn first4KeysOfHomeRow() []const u8 { } test "return string from function" { - assert(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); + assertOrPanic(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); } const g1: i32 = 1233 + 1; var g2: i32 = 0; test "global variables" { - assert(g2 == 0); + assertOrPanic(g2 == 0); g2 = g1; - assert(g2 == 1234); + assertOrPanic(g2 == 1234); } test "memcpy and memset intrinsics" { @@ -142,7 +142,7 @@ test "builtin static eval" { const x: i32 = comptime x: { break :x 1 + 2 + 3; }; - assert(x == comptime 6); + assertOrPanic(x == comptime 6); } test "slicing" { @@ -163,7 +163,7 @@ test "slicing" { test "constant equal function pointers" { const alias = emptyFn; - assert(comptime x: { + assertOrPanic(comptime x: { break :x emptyFn == alias; }); } @@ -171,25 +171,25 @@ test "constant equal function pointers" { fn emptyFn() void {} test "hex escape" { - assert(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); + assertOrPanic(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } test "string concatenation" { - assert(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); + assertOrPanic(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); } test "array mult operator" { - assert(mem.eql(u8, "ab" ** 5, "ababababab")); + assertOrPanic(mem.eql(u8, "ab" ** 5, "ababababab")); } test "string escapes" { - assert(mem.eql(u8, "\"", "\x22")); - assert(mem.eql(u8, "\'", "\x27")); - assert(mem.eql(u8, "\n", "\x0a")); - assert(mem.eql(u8, "\r", "\x0d")); - assert(mem.eql(u8, "\t", "\x09")); - assert(mem.eql(u8, "\\", "\x5c")); - assert(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); + assertOrPanic(mem.eql(u8, "\"", "\x22")); + assertOrPanic(mem.eql(u8, "\'", "\x27")); + assertOrPanic(mem.eql(u8, "\n", "\x0a")); + assertOrPanic(mem.eql(u8, "\r", "\x0d")); + assertOrPanic(mem.eql(u8, "\t", "\x09")); + assertOrPanic(mem.eql(u8, "\\", "\x5c")); + assertOrPanic(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); } test "multiline string" { @@ -199,7 +199,7 @@ test "multiline string" { \\three ; const s2 = "one\ntwo)\nthree"; - assert(mem.eql(u8, s1, s2)); + assertOrPanic(mem.eql(u8, s1, s2)); } test "multiline C string" { @@ -209,11 +209,11 @@ test "multiline C string" { c\\three ; const s2 = c"one\ntwo)\nthree"; - assert(cstr.cmp(s1, s2) == 0); + assertOrPanic(cstr.cmp(s1, s2) == 0); } test "type equality" { - assert(*const u8 != *u8); + assertOrPanic(*const u8 != *u8); } const global_a: i32 = 1234; @@ -221,7 +221,7 @@ const global_b: *const i32 = &global_a; const global_c: *const f32 = @ptrCast(*const f32, global_b); test "compile time global reinterpret" { const d = @ptrCast(*const i32, global_c); - assert(d.* == 1234); + assertOrPanic(d.* == 1234); } test "explicit cast maybe pointers" { @@ -247,8 +247,8 @@ test "cast undefined" { fn testCastUndefined(x: []const u8) void {} test "cast small unsigned to larger signed" { - assert(castSmallUnsignedToLargerSigned1(200) == i16(200)); - assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); + assertOrPanic(castSmallUnsignedToLargerSigned1(200) == i16(200)); + assertOrPanic(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); } fn castSmallUnsignedToLargerSigned1(x: u8) i16 { return x; @@ -258,7 +258,7 @@ fn castSmallUnsignedToLargerSigned2(x: u16) i64 { } test "implicit cast after unreachable" { - assert(outer() == 1234); + assertOrPanic(outer() == 1234); } fn inner() i32 { return 1234; @@ -273,13 +273,13 @@ test "pointer dereferencing" { y.* += 1; - assert(x == 4); - assert(y.* == 4); + assertOrPanic(x == 4); + assertOrPanic(y.* == 4); } test "call result of if else expression" { - assert(mem.eql(u8, f2(true), "a")); - assert(mem.eql(u8, f2(false), "b")); + assertOrPanic(mem.eql(u8, f2(true), "a")); + assertOrPanic(mem.eql(u8, f2(false), "b")); } fn f2(x: bool) []const u8 { return (if (x) fA else fB)(); @@ -321,8 +321,8 @@ const test3_bar = Test3Foo{ .Two = 13 }; fn test3_1(f: Test3Foo) void { switch (f) { Test3Foo.Three => |pt| { - assert(pt.x == 3); - assert(pt.y == 4); + assertOrPanic(pt.x == 3); + assertOrPanic(pt.y == 4); }, else => unreachable, } @@ -330,14 +330,14 @@ fn test3_1(f: Test3Foo) void { fn test3_2(f: Test3Foo) void { switch (f) { Test3Foo.Two => |x| { - assert(x == 13); + assertOrPanic(x == 13); }, else => unreachable, } } test "character literals" { - assert('\'' == single_quote); + assertOrPanic('\'' == single_quote); } const single_quote = '\''; @@ -346,13 +346,13 @@ test "take address of parameter" { } fn testTakeAddressOfParameter(f: f32) void { const f_ptr = &f; - assert(f_ptr.* == 12.34); + assertOrPanic(f_ptr.* == 12.34); } test "pointer comparison" { const a = ([]const u8)("a"); const b = &a; - assert(ptrEql(b, b)); + assertOrPanic(ptrEql(b, b)); } fn ptrEql(a: *const []const u8, b: *const []const u8) bool { return a == b; @@ -367,36 +367,31 @@ test "C string concatenation" { { var i: u32 = 0; while (i < len_with_null) : (i += 1) { - assert(a[i] == b[i]); + assertOrPanic(a[i] == b[i]); } } - assert(a[len] == 0); - assert(b[len] == 0); + assertOrPanic(a[len] == 0); + assertOrPanic(b[len] == 0); } test "cast slice to u8 slice" { - assert(@sizeOf(i32) == 4); - var big_thing_array = []i32{ - 1, - 2, - 3, - 4, - }; + assertOrPanic(@sizeOf(i32) == 4); + var big_thing_array = []i32{ 1, 2, 3, 4 }; const big_thing_slice: []i32 = big_thing_array[0..]; const bytes = @sliceToBytes(big_thing_slice); - assert(bytes.len == 4 * 4); + assertOrPanic(bytes.len == 4 * 4); bytes[4] = 0; bytes[5] = 0; bytes[6] = 0; bytes[7] = 0; - assert(big_thing_slice[1] == 0); + assertOrPanic(big_thing_slice[1] == 0); const big_thing_again = @bytesToSlice(i32, bytes); - assert(big_thing_again[2] == 3); + assertOrPanic(big_thing_again[2] == 3); big_thing_again[2] = -1; - assert(bytes[8] == maxInt(u8)); - assert(bytes[9] == maxInt(u8)); - assert(bytes[10] == maxInt(u8)); - assert(bytes[11] == maxInt(u8)); + assertOrPanic(bytes[8] == maxInt(u8)); + assertOrPanic(bytes[9] == maxInt(u8)); + assertOrPanic(bytes[10] == maxInt(u8)); + assertOrPanic(bytes[11] == maxInt(u8)); } test "pointer to void return type" { @@ -413,7 +408,7 @@ fn testPointerToVoidReturnType2() *const void { test "non const ptr to aliased type" { const int = i32; - assert(?*int == ?*i32); + assertOrPanic(?*int == ?*i32); } test "array 2D const double ptr" { @@ -426,8 +421,8 @@ test "array 2D const double ptr" { fn testArray2DConstDoublePtr(ptr: *const f32) void { const ptr2 = @ptrCast([*]const f32, ptr); - assert(ptr2[0] == 1.0); - assert(ptr2[1] == 2.0); + assertOrPanic(ptr2[0] == 1.0); + assertOrPanic(ptr2[1] == 2.0); } const Tid = builtin.TypeId; @@ -449,32 +444,32 @@ const AUnion = union { test "@typeId" { comptime { - assert(@typeId(type) == Tid.Type); - assert(@typeId(void) == Tid.Void); - assert(@typeId(bool) == Tid.Bool); - assert(@typeId(noreturn) == Tid.NoReturn); - assert(@typeId(i8) == Tid.Int); - assert(@typeId(u8) == Tid.Int); - assert(@typeId(i64) == Tid.Int); - assert(@typeId(u64) == Tid.Int); - assert(@typeId(f32) == Tid.Float); - assert(@typeId(f64) == Tid.Float); - assert(@typeId(*f32) == Tid.Pointer); - assert(@typeId([2]u8) == Tid.Array); - assert(@typeId(AStruct) == Tid.Struct); - assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); - assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); - assert(@typeId(@typeOf(undefined)) == Tid.Undefined); - assert(@typeId(@typeOf(null)) == Tid.Null); - assert(@typeId(?i32) == Tid.Optional); - assert(@typeId(anyerror!i32) == Tid.ErrorUnion); - assert(@typeId(anyerror) == Tid.ErrorSet); - assert(@typeId(AnEnum) == Tid.Enum); - assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); - assert(@typeId(AUnionEnum) == Tid.Union); - assert(@typeId(AUnion) == Tid.Union); - assert(@typeId(fn () void) == Tid.Fn); - assert(@typeId(@typeOf(builtin)) == Tid.Namespace); + assertOrPanic(@typeId(type) == Tid.Type); + assertOrPanic(@typeId(void) == Tid.Void); + assertOrPanic(@typeId(bool) == Tid.Bool); + assertOrPanic(@typeId(noreturn) == Tid.NoReturn); + assertOrPanic(@typeId(i8) == Tid.Int); + assertOrPanic(@typeId(u8) == Tid.Int); + assertOrPanic(@typeId(i64) == Tid.Int); + assertOrPanic(@typeId(u64) == Tid.Int); + assertOrPanic(@typeId(f32) == Tid.Float); + assertOrPanic(@typeId(f64) == Tid.Float); + assertOrPanic(@typeId(*f32) == Tid.Pointer); + assertOrPanic(@typeId([2]u8) == Tid.Array); + assertOrPanic(@typeId(AStruct) == Tid.Struct); + assertOrPanic(@typeId(@typeOf(1)) == Tid.ComptimeInt); + assertOrPanic(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); + assertOrPanic(@typeId(@typeOf(undefined)) == Tid.Undefined); + assertOrPanic(@typeId(@typeOf(null)) == Tid.Null); + assertOrPanic(@typeId(?i32) == Tid.Optional); + assertOrPanic(@typeId(anyerror!i32) == Tid.ErrorUnion); + assertOrPanic(@typeId(anyerror) == Tid.ErrorSet); + assertOrPanic(@typeId(AnEnum) == Tid.Enum); + assertOrPanic(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); + assertOrPanic(@typeId(AUnionEnum) == Tid.Union); + assertOrPanic(@typeId(AUnion) == Tid.Union); + assertOrPanic(@typeId(fn () void) == Tid.Fn); + assertOrPanic(@typeId(@typeOf(builtin)) == Tid.Namespace); // TODO bound fn // TODO arg tuple // TODO opaque @@ -490,13 +485,13 @@ test "@typeName" { Unused, }; comptime { - assert(mem.eql(u8, @typeName(i64), "i64")); - assert(mem.eql(u8, @typeName(*usize), "*usize")); + assertOrPanic(mem.eql(u8, @typeName(i64), "i64")); + assertOrPanic(mem.eql(u8, @typeName(*usize), "*usize")); // https://github.com/ziglang/zig/issues/675 - assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); - assert(mem.eql(u8, @typeName(Struct), "Struct")); - assert(mem.eql(u8, @typeName(Union), "Union")); - assert(mem.eql(u8, @typeName(Enum), "Enum")); + assertOrPanic(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); + assertOrPanic(mem.eql(u8, @typeName(Struct), "Struct")); + assertOrPanic(mem.eql(u8, @typeName(Union), "Union")); + assertOrPanic(mem.eql(u8, @typeName(Enum), "Enum")); } } @@ -504,28 +499,16 @@ fn TypeFromFn(comptime T: type) type { return struct {}; } -test "volatile load and store" { - var number: i32 = 1234; - const ptr = (*volatile i32)(&number); - ptr.* += 1; - assert(ptr.* == 1235); +test "double implicit cast in same expression" { + var x = i32(u16(nine())); + assertOrPanic(x == 9); } - -test "slice string literal has type []const u8" { - comptime { - assert(@typeOf("aoeu"[0..]) == []const u8); - const array = []i32{ - 1, - 2, - 3, - 4, - }; - assert(@typeOf(array[0..]) == []const i32); - } +fn nine() u8 { + return 9; } test "global variable initialized to global variable array element" { - assert(global_ptr == &gdt[0]); + assertOrPanic(global_ptr == &gdt[0]); } const GDTEntry = struct { field: i32, @@ -543,16 +526,12 @@ export fn writeToVRam() void { vram[0] = 'X'; } -test "pointer child field" { - assert((*u32).Child == u32); -} - const OpaqueA = @OpaqueType(); const OpaqueB = @OpaqueType(); test "@OpaqueType" { - assert(*OpaqueA != *OpaqueB); - assert(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); - assert(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); + assertOrPanic(*OpaqueA != *OpaqueB); + assertOrPanic(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); + assertOrPanic(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); } test "variable is allowed to be a pointer to an opaque type" { @@ -581,25 +560,6 @@ test "implicit comptime while" { } } -test "struct inside function" { - testStructInFn(); - comptime testStructInFn(); -} - -fn testStructInFn() void { - const BlockKind = u32; - - const Block = struct { - kind: BlockKind, - }; - - var block = Block{ .kind = 1234 }; - - block.kind += 1; - - assert(block.kind == 1235); -} - fn fnThatClosesOverLocalConst() type { const c = 1; return struct { @@ -611,7 +571,7 @@ fn fnThatClosesOverLocalConst() type { test "function closes over local const" { const x = fnThatClosesOverLocalConst().g(); - assert(x == 1); + assertOrPanic(x == 1); } test "cold function" { @@ -648,21 +608,21 @@ export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: Pack test "slicing zero length array" { const s1 = ""[0..]; const s2 = ([]u32{})[0..]; - assert(s1.len == 0); - assert(s2.len == 0); - assert(mem.eql(u8, s1, "")); - assert(mem.eql(u32, s2, []u32{})); + assertOrPanic(s1.len == 0); + assertOrPanic(s2.len == 0); + assertOrPanic(mem.eql(u8, s1, "")); + assertOrPanic(mem.eql(u32, s2, []u32{})); } const addr1 = @ptrCast(*const u8, emptyFn); test "comptime cast fn to ptr" { const addr2 = @ptrCast(*const u8, emptyFn); - comptime assert(addr1 == addr2); + comptime assertOrPanic(addr1 == addr2); } test "equality compare fn ptrs" { var a = emptyFn; - assert(a == a); + assertOrPanic(a == a); } test "self reference through fn ptr field" { @@ -677,5 +637,51 @@ test "self reference through fn ptr field" { }; var a: S.A = undefined; a.f = S.foo; - assert(a.f(a) == 12); + assertOrPanic(a.f(a) == 12); +} + +test "volatile load and store" { + var number: i32 = 1234; + const ptr = (*volatile i32)(&number); + ptr.* += 1; + assertOrPanic(ptr.* == 1235); +} + +test "slice string literal has type []const u8" { + comptime { + assertOrPanic(@typeOf("aoeu"[0..]) == []const u8); + const array = []i32{ 1, 2, 3, 4 }; + assertOrPanic(@typeOf(array[0..]) == []const i32); + } +} + +test "pointer child field" { + assertOrPanic((*u32).Child == u32); +} + +test "struct inside function" { + testStructInFn(); + comptime testStructInFn(); +} + +fn testStructInFn() void { + const BlockKind = u32; + + const Block = struct { + kind: BlockKind, + }; + + var block = Block{ .kind = 1234 }; + + block.kind += 1; + + assertOrPanic(block.kind == 1235); +} + +test "fn call returning scalar optional in equality expression" { + assertOrPanic(getNull() == null); +} + +fn getNull() ?*i32 { + return null; } diff --git a/test/cases/namespace_depends_on_compile_var/a.zig b/test/stage1/behavior/namespace_depends_on_compile_var/a.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/a.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/a.zig diff --git a/test/cases/namespace_depends_on_compile_var/b.zig b/test/stage1/behavior/namespace_depends_on_compile_var/b.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/b.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/b.zig diff --git a/test/cases/namespace_depends_on_compile_var/index.zig b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig similarity index 62% rename from test/cases/namespace_depends_on_compile_var/index.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/index.zig index ccc49d9367..fe3e0cc020 100644 --- a/test/cases/namespace_depends_on_compile_var/index.zig +++ b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig @@ -1,11 +1,11 @@ const builtin = @import("builtin"); -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "namespace depends on compile var" { if (some_namespace.a_bool) { - assert(some_namespace.a_bool); + assertOrPanic(some_namespace.a_bool); } else { - assert(!some_namespace.a_bool); + assertOrPanic(!some_namespace.a_bool); } } const some_namespace = switch (builtin.os) { diff --git a/test/cases/new_stack_call.zig b/test/stage1/behavior/new_stack_call.zig similarity index 72% rename from test/cases/new_stack_call.zig rename to test/stage1/behavior/new_stack_call.zig index 5912550d54..b9ae2d27cd 100644 --- a/test/cases/new_stack_call.zig +++ b/test/stage1/behavior/new_stack_call.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; var new_stack_bytes: [1024]u8 = undefined; @@ -10,17 +10,17 @@ test "calling a function with a new stack" { const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); _ = targetFunction(arg); - assert(arg == 1234); - assert(a < b); + assertOrPanic(arg == 1234); + assertOrPanic(a < b); } fn targetFunction(x: i32) usize { - assert(x == 1234); + assertOrPanic(x == 1234); var local_variable: i32 = 42; const ptr = &local_variable; ptr.* += 1; - assert(local_variable == 43); + assertOrPanic(local_variable == 43); return @ptrToInt(ptr); } diff --git a/test/cases/null.zig b/test/stage1/behavior/null.zig similarity index 80% rename from test/cases/null.zig rename to test/stage1/behavior/null.zig index 825db88b1e..e2f86a05ba 100644 --- a/test/cases/null.zig +++ b/test/stage1/behavior/null.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "optional type" { const x: ?bool = true; @@ -17,13 +17,13 @@ test "optional type" { const z = next_x orelse 1234; - assert(z == 1234); + assertOrPanic(z == 1234); const final_x: ?i32 = 13; const num = final_x orelse unreachable; - assert(num == 13); + assertOrPanic(num == 13); } test "test maybe object and get a pointer to the inner value" { @@ -33,7 +33,7 @@ test "test maybe object and get a pointer to the inner value" { b.* = false; } - assert(maybe_bool.? == false); + assertOrPanic(maybe_bool.? == false); } test "rhs maybe unwrap return" { @@ -47,9 +47,9 @@ test "maybe return" { } fn maybeReturnImpl() void { - assert(foo(1235).?); + assertOrPanic(foo(1235).?); if (foo(null) != null) unreachable; - assert(!foo(1234).?); + assertOrPanic(!foo(1234).?); } fn foo(x: ?i32) ?bool { @@ -58,7 +58,7 @@ fn foo(x: ?i32) ?bool { } test "if var maybe pointer" { - assert(shouldBeAPlus1(Particle{ + assertOrPanic(shouldBeAPlus1(Particle{ .a = 14, .b = 1, .c = 1, @@ -84,10 +84,10 @@ const Particle = struct { test "null literal outside function" { const is_null = here_is_a_null_literal.context == null; - assert(is_null); + assertOrPanic(is_null); const is_non_null = here_is_a_null_literal.context != null; - assert(!is_non_null); + assertOrPanic(!is_non_null); } const SillyStruct = struct { context: ?i32, @@ -98,8 +98,8 @@ test "test null runtime" { testTestNullRuntime(null); } fn testTestNullRuntime(x: ?i32) void { - assert(x == null); - assert(!(x != null)); + assertOrPanic(x == null); + assertOrPanic(!(x != null)); } test "optional void" { @@ -108,8 +108,8 @@ test "optional void" { } fn optionalVoidImpl() void { - assert(bar(null) == null); - assert(bar({}) != null); + assertOrPanic(bar(null) == null); + assertOrPanic(bar({}) != null); } fn bar(x: ?void) ?void { @@ -133,7 +133,7 @@ test "unwrap optional which is field of global var" { } struct_with_optional.field = 1234; if (struct_with_optional.field) |payload| { - assert(payload == 1234); + assertOrPanic(payload == 1234); } else { unreachable; } @@ -141,13 +141,13 @@ test "unwrap optional which is field of global var" { test "null with default unwrap" { const x: i32 = null orelse 1; - assert(x == 1); + assertOrPanic(x == 1); } test "optional types" { comptime { const opt_type_struct = StructWithOptionalType{ .t = u8 }; - assert(opt_type_struct.t != null and opt_type_struct.t.? == u8); + assertOrPanic(opt_type_struct.t != null and opt_type_struct.t.? == u8); } } @@ -158,5 +158,5 @@ const StructWithOptionalType = struct { test "optional pointer to 0 bit type null value at runtime" { const EmptyStruct = struct {}; var x: ?*EmptyStruct = null; - assert(x == null); + assertOrPanic(x == null); } diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig new file mode 100644 index 0000000000..14692cb1ea --- /dev/null +++ b/test/stage1/behavior/optional.zig @@ -0,0 +1,81 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +pub const EmptyStruct = struct {}; + +test "optional pointer to size zero struct" { + var e = EmptyStruct{}; + var o: ?*EmptyStruct = &e; + assertOrPanic(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; + assertOrPanic(x == y); + y = &number; + assertOrPanic(x != y); + assertOrPanic(x != &number); + assertOrPanic(&number != x); + x = &number; + assertOrPanic(x == y); + assertOrPanic(x == &number); + assertOrPanic(&number == x); +} + +test "address of unwrap optional" { + const S = struct { + const Foo = struct { + a: i32, + }; + + var global: ?Foo = null; + + pub fn getFoo() anyerror!*Foo { + return &global.?; + } + }; + S.global = S.Foo{ .a = 1234 }; + const foo = S.getFoo() catch unreachable; + assertOrPanic(foo.a == 1234); +} + +test "passing an optional integer as a parameter" { + const S = struct { + fn entry() bool { + var x: i32 = 1234; + return foo(x); + } + + fn foo(x: ?i32) bool { + return x.? == 1234; + } + }; + assertOrPanic(S.entry()); + comptime assertOrPanic(S.entry()); +} + +test "unwrap function call with optional pointer return value" { + const S = struct { + fn entry() void { + assertOrPanic(foo().?.* == 1234); + assertOrPanic(bar() == null); + } + const global: i32 = 1234; + fn foo() ?*const i32 { + return &global; + } + fn bar() ?*i32 { + return null; + } + }; + S.entry(); + // TODO https://github.com/ziglang/zig/issues/1901 + //comptime S.entry(); +} diff --git a/test/cases/pointers.zig b/test/stage1/behavior/pointers.zig similarity index 50% rename from test/cases/pointers.zig rename to test/stage1/behavior/pointers.zig index 47afb60a2e..1142d89ab5 100644 --- a/test/cases/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "dereference pointer" { comptime testDerefPtr(); @@ -10,33 +10,33 @@ fn testDerefPtr() void { var x: i32 = 1234; var y = &x; y.* += 1; - assert(x == 1235); + assertOrPanic(x == 1235); } test "pointer arithmetic" { var ptr = c"abcd"; - assert(ptr[0] == 'a'); + assertOrPanic(ptr[0] == 'a'); ptr += 1; - assert(ptr[0] == 'b'); + assertOrPanic(ptr[0] == 'b'); ptr += 1; - assert(ptr[0] == 'c'); + assertOrPanic(ptr[0] == 'c'); ptr += 1; - assert(ptr[0] == 'd'); + assertOrPanic(ptr[0] == 'd'); ptr += 1; - assert(ptr[0] == 0); + assertOrPanic(ptr[0] == 0); ptr -= 1; - assert(ptr[0] == 'd'); + assertOrPanic(ptr[0] == 'd'); ptr -= 1; - assert(ptr[0] == 'c'); + assertOrPanic(ptr[0] == 'c'); ptr -= 1; - assert(ptr[0] == 'b'); + assertOrPanic(ptr[0] == 'b'); ptr -= 1; - assert(ptr[0] == 'a'); + assertOrPanic(ptr[0] == 'a'); } test "double pointer parsing" { - comptime assert(PtrOf(PtrOf(i32)) == **i32); + comptime assertOrPanic(PtrOf(PtrOf(i32)) == **i32); } fn PtrOf(comptime T: type) type { diff --git a/test/stage1/behavior/popcount.zig b/test/stage1/behavior/popcount.zig new file mode 100644 index 0000000000..f7f8bb523b --- /dev/null +++ b/test/stage1/behavior/popcount.zig @@ -0,0 +1,25 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@popCount" { + comptime testPopCount(); + testPopCount(); +} + +fn testPopCount() void { + { + var x: u32 = 0xaa; + assertOrPanic(@popCount(x) == 4); + } + { + var x: u32 = 0xaaaaaaaa; + assertOrPanic(@popCount(x) == 16); + } + { + var x: i16 = -1; + assertOrPanic(@popCount(x) == 16); + } + comptime { + assertOrPanic(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); + } +} + diff --git a/test/cases/ptrcast.zig b/test/stage1/behavior/ptrcast.zig similarity index 100% rename from test/cases/ptrcast.zig rename to test/stage1/behavior/ptrcast.zig diff --git a/test/cases/pub_enum/index.zig b/test/stage1/behavior/pub_enum/index.zig similarity index 54% rename from test/cases/pub_enum/index.zig rename to test/stage1/behavior/pub_enum/index.zig index 7fdd07b8a3..181113f6bf 100644 --- a/test/cases/pub_enum/index.zig +++ b/test/stage1/behavior/pub_enum/index.zig @@ -1,13 +1,13 @@ const other = @import("other.zig"); -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "pub enum" { pubEnumTest(other.APubEnum.Two); } fn pubEnumTest(foo: other.APubEnum) void { - assert(foo == other.APubEnum.Two); + assertOrPanic(foo == other.APubEnum.Two); } test "cast with imported symbol" { - assert(other.size_t(42) == 42); + assertOrPanic(other.size_t(42) == 42); } diff --git a/test/cases/pub_enum/other.zig b/test/stage1/behavior/pub_enum/other.zig similarity index 100% rename from test/cases/pub_enum/other.zig rename to test/stage1/behavior/pub_enum/other.zig diff --git a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig similarity index 77% rename from test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig rename to test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig index 3c94bb0d49..acbe6b2459 100644 --- a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig +++ b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig @@ -1,14 +1,14 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; var ok: bool = false; test "reference a variable in an if after an if in the 2nd switch prong" { foo(true, Num.Two, false, "aoeu"); - assert(!ok); + assertOrPanic(!ok); foo(false, Num.One, false, "aoeu"); - assert(!ok); + assertOrPanic(!ok); foo(true, Num.One, false, "aoeu"); - assert(ok); + assertOrPanic(ok); } const Num = enum { @@ -32,6 +32,6 @@ fn foo(c: bool, k: Num, c2: bool, b: []const u8) void { } fn a(x: []const u8) void { - assert(mem.eql(u8, x, "aoeu")); + assertOrPanic(mem.eql(u8, x, "aoeu")); ok = true; } diff --git a/test/stage1/behavior/reflection.zig b/test/stage1/behavior/reflection.zig new file mode 100644 index 0000000000..f4c142e0f7 --- /dev/null +++ b/test/stage1/behavior/reflection.zig @@ -0,0 +1,96 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; +const reflection = @This(); + +test "reflection: array, pointer, optional, error union type child" { + comptime { + assertOrPanic(([10]u8).Child == u8); + assertOrPanic((*u8).Child == u8); + assertOrPanic((anyerror!u8).Payload == u8); + assertOrPanic((?u8).Child == u8); + } +} + +test "reflection: function return type, var args, and param types" { + comptime { + assertOrPanic(@typeOf(dummy).ReturnType == i32); + assertOrPanic(!@typeOf(dummy).is_var_args); + assertOrPanic(@typeOf(dummy_varargs).is_var_args); + assertOrPanic(@typeOf(dummy).arg_count == 3); + assertOrPanic(@ArgType(@typeOf(dummy), 0) == bool); + assertOrPanic(@ArgType(@typeOf(dummy), 1) == i32); + assertOrPanic(@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 { + assertOrPanic(@memberCount(Foo) == 3); + + assertOrPanic(@memberType(Foo, 0) == i32); + assertOrPanic(@memberType(Foo, 1) == bool); + assertOrPanic(@memberType(Foo, 2) == void); + + assertOrPanic(mem.eql(u8, @memberName(Foo, 0), "one")); + assertOrPanic(mem.eql(u8, @memberName(Foo, 1), "two")); + assertOrPanic(mem.eql(u8, @memberName(Foo, 2), "three")); + } +} + +test "reflection: enum member types and names" { + comptime { + assertOrPanic(@memberCount(Bar) == 4); + + assertOrPanic(@memberType(Bar, 0) == void); + assertOrPanic(@memberType(Bar, 1) == i32); + assertOrPanic(@memberType(Bar, 2) == bool); + assertOrPanic(@memberType(Bar, 3) == f64); + + assertOrPanic(mem.eql(u8, @memberName(Bar, 0), "One")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 1), "Two")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 2), "Three")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 3), "Four")); + } +} + +test "reflection: @field" { + var f = Foo{ + .one = 42, + .two = true, + .three = void{}, + }; + + assertOrPanic(f.one == f.one); + assertOrPanic(@field(f, "o" ++ "ne") == f.one); + assertOrPanic(@field(f, "t" ++ "wo") == f.two); + assertOrPanic(@field(f, "th" ++ "ree") == f.three); + assertOrPanic(@field(Foo, "const" ++ "ant") == Foo.constant); + assertOrPanic(@field(Bar, "O" ++ "ne") == Bar.One); + assertOrPanic(@field(Bar, "T" ++ "wo") == Bar.Two); + assertOrPanic(@field(Bar, "Th" ++ "ree") == Bar.Three); + assertOrPanic(@field(Bar, "F" ++ "our") == Bar.Four); + assertOrPanic(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); + @field(f, "o" ++ "ne") = 4; + assertOrPanic(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, +}; + diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig new file mode 100644 index 0000000000..ddaea4c242 --- /dev/null +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@sizeOf and @typeOf" { + const y: @typeOf(x) = 120; + assertOrPanic(@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 + assertOrPanic(@byteOffsetOf(P, "a") == 0); + assertOrPanic(@byteOffsetOf(P, "b") == 1); + assertOrPanic(@byteOffsetOf(P, "c") == 5); + assertOrPanic(@byteOffsetOf(P, "d") == 6); + assertOrPanic(@byteOffsetOf(P, "e") == 6); + assertOrPanic(@byteOffsetOf(P, "f") == 7); + assertOrPanic(@byteOffsetOf(P, "g") == 9); + + // Normal struct fields can be moved/padded + var a: A = undefined; + assertOrPanic(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); + assertOrPanic(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); + assertOrPanic(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); + assertOrPanic(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); + assertOrPanic(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); + assertOrPanic(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); + assertOrPanic(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); +} + +test "@bitOffsetOf" { + // Packed structs have fixed memory layout + assertOrPanic(@bitOffsetOf(P, "a") == 0); + assertOrPanic(@bitOffsetOf(P, "b") == 8); + assertOrPanic(@bitOffsetOf(P, "c") == 40); + assertOrPanic(@bitOffsetOf(P, "d") == 48); + assertOrPanic(@bitOffsetOf(P, "e") == 51); + assertOrPanic(@bitOffsetOf(P, "f") == 56); + assertOrPanic(@bitOffsetOf(P, "g") == 72); + + assertOrPanic(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); + assertOrPanic(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); + assertOrPanic(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); + assertOrPanic(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); + assertOrPanic(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); + assertOrPanic(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); + assertOrPanic(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); +} diff --git a/test/cases/slice.zig b/test/stage1/behavior/slice.zig similarity index 62% rename from test/cases/slice.zig rename to test/stage1/behavior/slice.zig index b4b43bdd19..cc29e43485 100644 --- a/test/cases/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -1,20 +1,20 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { - assert(@ptrToInt(x.ptr) == 0x1000); - assert(x.len == 0x500); + assertOrPanic(@ptrToInt(x.ptr) == 0x1000); + assertOrPanic(x.len == 0x500); - assert(@ptrToInt(y.ptr) == 0x1100); - assert(y.len == 0x400); + assertOrPanic(@ptrToInt(y.ptr) == 0x1100); + assertOrPanic(y.len == 0x400); } test "slice child property" { var array: [5]i32 = undefined; var slice = array[0..]; - assert(@typeOf(slice).Child == i32); + assertOrPanic(@typeOf(slice).Child == i32); } test "runtime safety lets us slice from len..len" { @@ -23,7 +23,7 @@ test "runtime safety lets us slice from len..len" { 2, 3, }; - assert(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); + assertOrPanic(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); } fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { @@ -36,5 +36,5 @@ test "implicitly cast array of size 0 to slice" { } fn assertLenIsZero(msg: []const u8) void { - assert(msg.len == 0); + assertOrPanic(msg.len == 0); } diff --git a/test/cases/struct.zig b/test/stage1/behavior/struct.zig similarity index 64% rename from test/cases/struct.zig rename to test/stage1/behavior/struct.zig index bbbd21912c..92ae2baa15 100644 --- a/test/cases/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -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; @@ -12,7 +12,7 @@ const empty_global_instance = StructWithNoFields{}; test "call struct static method" { const result = StructWithNoFields.add(3, 4); - assert(result == 7); + assertOrPanic(result == 7); } test "return empty struct instance" { @@ -24,8 +24,8 @@ fn returnEmptyStructInstance() StructWithNoFields { const should_be_11 = StructWithNoFields.add(5, 6); -test "invake static method in global scope" { - assert(should_be_11 == 11); +test "invoke static method in global scope" { + assertOrPanic(should_be_11 == 11); } test "void struct fields" { @@ -34,8 +34,8 @@ test "void struct fields" { .b = 1, .c = void{}, }; - assert(foo.b == 1); - assert(@sizeOf(VoidStructFieldsFoo) == 4); + assertOrPanic(foo.b == 1); + assertOrPanic(@sizeOf(VoidStructFieldsFoo) == 4); } const VoidStructFieldsFoo = struct { a: void, @@ -50,7 +50,7 @@ test "structs" { foo.b = foo.a == 1; testFoo(foo); testMutation(&foo); - assert(foo.c == 100); + assertOrPanic(foo.c == 100); } const StructFoo = struct { a: i32, @@ -58,7 +58,7 @@ const StructFoo = struct { c: f32, }; fn testFoo(foo: StructFoo) void { - assert(foo.b); + assertOrPanic(foo.b); } fn testMutation(foo: *StructFoo) void { foo.c = 100; @@ -83,7 +83,7 @@ test "struct point to self" { root.next = &node; - assert(node.next.next.next.val.x == 1); + assertOrPanic(node.next.next.next.val.x == 1); } test "struct byval assign" { @@ -92,18 +92,18 @@ test "struct byval assign" { foo1.a = 1234; foo2.a = 0; - assert(foo2.a == 0); + assertOrPanic(foo2.a == 0); foo2 = foo1; - assert(foo2.a == 1234); + assertOrPanic(foo2.a == 1234); } fn structInitializer() void { const val = Val{ .x = 42 }; - assert(val.x == 42); + assertOrPanic(val.x == 42); } test "fn call of struct field" { - assert(callStructField(Foo{ .ptr = aFunc }) == 13); + assertOrPanic(callStructField(Foo{ .ptr = aFunc }) == 13); } const Foo = struct { @@ -122,7 +122,7 @@ test "store member function in variable" { const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); - assert(result == 1234); + assertOrPanic(result == 1234); } const MemberFnTestFoo = struct { x: i32, @@ -134,12 +134,12 @@ const MemberFnTestFoo = struct { test "call member function directly" { const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); - assert(result == 1234); + assertOrPanic(result == 1234); } test "member functions" { const r = MemberFnRand{ .seed = 1234 }; - assert(r.getSeed() == 1234); + assertOrPanic(r.getSeed() == 1234); } const MemberFnRand = struct { seed: u32, @@ -150,7 +150,7 @@ const MemberFnRand = struct { test "return struct byval from function" { const bar = makeBar(1234, 5678); - assert(bar.y == 5678); + assertOrPanic(bar.y == 5678); } const Bar = struct { x: i32, @@ -165,7 +165,7 @@ fn makeBar(x: i32, y: i32) Bar { test "empty struct method call" { const es = EmptyStruct{}; - assert(es.method() == 1234); + assertOrPanic(es.method() == 1234); } const EmptyStruct = struct { fn method(es: *const EmptyStruct) i32 { @@ -182,7 +182,7 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { } test "pass slice of empty struct to fn" { - assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); + assertOrPanic(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { return slice.len; @@ -200,7 +200,7 @@ test "packed struct" { }; foo.y += 1; const four = foo.x + foo.y; - assert(four == 4); + assertOrPanic(four == 4); } const BitField1 = packed struct { @@ -217,17 +217,17 @@ const bit_field_1 = BitField1{ test "bit field access" { var data = bit_field_1; - assert(getA(&data) == 1); - assert(getB(&data) == 2); - assert(getC(&data) == 3); - comptime assert(@sizeOf(BitField1) == 1); + assertOrPanic(getA(&data) == 1); + assertOrPanic(getB(&data) == 2); + assertOrPanic(getC(&data) == 3); + comptime assertOrPanic(@sizeOf(BitField1) == 1); data.b += 1; - assert(data.b == 3); + assertOrPanic(data.b == 3); data.a += 1; - assert(data.a == 2); - assert(data.b == 3); + assertOrPanic(data.a == 2); + assertOrPanic(data.b == 3); } fn getA(data: *const BitField1) u3 { @@ -254,8 +254,8 @@ const Foo96Bits = packed struct { test "packed struct 24bits" { comptime { - assert(@sizeOf(Foo24Bits) == 3); - assert(@sizeOf(Foo96Bits) == 12); + assertOrPanic(@sizeOf(Foo24Bits) == 3); + assertOrPanic(@sizeOf(Foo96Bits) == 12); } var value = Foo96Bits{ @@ -265,28 +265,28 @@ test "packed struct 24bits" { .d = 0, }; value.a += 1; - assert(value.a == 1); - assert(value.b == 0); - assert(value.c == 0); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 0); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); value.b += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 0); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); value.c += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 0); value.d += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 1); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 1); } const FooArray24Bits = packed struct { @@ -297,43 +297,43 @@ const FooArray24Bits = packed struct { test "packed array 24bits" { comptime { - assert(@sizeOf([9]Foo24Bits) == 9 * 3); - assert(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); + assertOrPanic(@sizeOf([9]Foo24Bits) == 9 * 3); + assertOrPanic(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); } var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; - assert(ptr.a == 0); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + assertOrPanic(ptr.a == 0); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); ptr.a = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); ptr.b[0].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); ptr.b[1].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == 0); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == 0); ptr.c = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == maxInt(u16)); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == maxInt(u16)); - assert(bytes[bytes.len - 1] == 0xaa); + assertOrPanic(bytes[bytes.len - 1] == 0xaa); } const FooStructAligned = packed struct { @@ -347,17 +347,17 @@ const FooArrayOfAligned = packed struct { test "aligned array of packed struct" { comptime { - assert(@sizeOf(FooStructAligned) == 2); - assert(@sizeOf(FooArrayOfAligned) == 2 * 2); + assertOrPanic(@sizeOf(FooStructAligned) == 2); + assertOrPanic(@sizeOf(FooArrayOfAligned) == 2 * 2); } var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; - assert(ptr.a[0].a == 0xbb); - assert(ptr.a[0].b == 0xbb); - assert(ptr.a[1].a == 0xbb); - assert(ptr.a[1].b == 0xbb); + assertOrPanic(ptr.a[0].a == 0xbb); + assertOrPanic(ptr.a[0].b == 0xbb); + assertOrPanic(ptr.a[1].a == 0xbb); + assertOrPanic(ptr.a[1].b == 0xbb); } test "runtime struct initialization of bitfield" { @@ -370,10 +370,10 @@ test "runtime struct initialization of bitfield" { .y = @intCast(u4, x2), }; - assert(s1.x == x1); - assert(s1.y == x1); - assert(s2.x == @intCast(u4, x2)); - assert(s2.y == @intCast(u4, x2)); + assertOrPanic(s1.x == x1); + assertOrPanic(s1.y == x1); + assertOrPanic(s2.x == @intCast(u4, x2)); + assertOrPanic(s2.y == @intCast(u4, x2)); } var x1 = u4(1); @@ -400,18 +400,18 @@ test "native bit field understands endianness" { @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; - assert(bitfields.f1 == 0x1111); - assert(bitfields.f2 == 0x2222); - assert(bitfields.f3 == 0x33); - assert(bitfields.f4 == 0x44); - assert(bitfields.f5 == 0x5); - assert(bitfields.f6 == 0x6); - assert(bitfields.f7 == 0x77); + assertOrPanic(bitfields.f1 == 0x1111); + assertOrPanic(bitfields.f2 == 0x2222); + assertOrPanic(bitfields.f3 == 0x33); + assertOrPanic(bitfields.f4 == 0x44); + assertOrPanic(bitfields.f5 == 0x5); + assertOrPanic(bitfields.f6 == 0x6); + assertOrPanic(bitfields.f7 == 0x77); } test "align 1 field before self referential align 8 field as slice return type" { const result = alloc(Expr); - assert(result.len == 0); + assertOrPanic(result.len == 0); } const Expr = union(enum) { @@ -434,10 +434,10 @@ test "call method with mutable reference to struct with no fields" { }; var s = S{}; - assert(S.doC(&s)); - assert(s.doC()); - assert(S.do(&s)); - assert(s.do()); + assertOrPanic(S.doC(&s)); + assertOrPanic(s.doC()); + assertOrPanic(S.do(&s)); + assertOrPanic(s.do()); } test "implicit cast packed struct field to const ptr" { @@ -453,7 +453,7 @@ test "implicit cast packed struct field to const ptr" { var lup: LevelUpMove = undefined; lup.level = 12; const res = LevelUpMove.toInt(lup.level); - assert(res == 12); + assertOrPanic(res == 12); } test "pointer to packed struct member in a stack variable" { @@ -464,7 +464,7 @@ test "pointer to packed struct member in a stack variable" { var s = S{ .a = 2, .b = 0 }; var b_ptr = &s.b; - assert(s.b == 0); + assertOrPanic(s.b == 0); b_ptr.* = 2; - assert(s.b == 2); + assertOrPanic(s.b == 2); } diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/stage1/behavior/struct_contains_null_ptr_itself.zig similarity index 81% rename from test/cases/struct_contains_null_ptr_itself.zig rename to test/stage1/behavior/struct_contains_null_ptr_itself.zig index 21175974b3..4cc479f31c 100644 --- a/test/cases/struct_contains_null_ptr_itself.zig +++ b/test/stage1/behavior/struct_contains_null_ptr_itself.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "struct contains null pointer which contains original struct" { var x: ?*NodeLineComment = null; - assert(x == null); + assertOrPanic(x == null); } pub const Node = struct { diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/stage1/behavior/struct_contains_slice_of_itself.zig similarity index 68% rename from test/cases/struct_contains_slice_of_itself.zig rename to test/stage1/behavior/struct_contains_slice_of_itself.zig index aa3075312c..15780a7c44 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/stage1/behavior/struct_contains_slice_of_itself.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Node = struct { payload: i32, @@ -39,12 +39,12 @@ test "struct contains slice of itself" { .payload = 1234, .children = nodes[0..], }; - assert(root.payload == 1234); - assert(root.children[0].payload == 1); - assert(root.children[1].payload == 2); - assert(root.children[2].payload == 3); - assert(root.children[2].children[0].payload == 31); - assert(root.children[2].children[1].payload == 32); + assertOrPanic(root.payload == 1234); + assertOrPanic(root.children[0].payload == 1); + assertOrPanic(root.children[1].payload == 2); + assertOrPanic(root.children[2].payload == 3); + assertOrPanic(root.children[2].children[0].payload == 31); + assertOrPanic(root.children[2].children[1].payload == 32); } test "struct contains aligned slice of itself" { @@ -76,10 +76,10 @@ test "struct contains aligned slice of itself" { .payload = 1234, .children = nodes[0..], }; - assert(root.payload == 1234); - assert(root.children[0].payload == 1); - assert(root.children[1].payload == 2); - assert(root.children[2].payload == 3); - assert(root.children[2].children[0].payload == 31); - assert(root.children[2].children[1].payload == 32); + assertOrPanic(root.payload == 1234); + assertOrPanic(root.children[0].payload == 1); + assertOrPanic(root.children[1].payload == 2); + assertOrPanic(root.children[2].payload == 3); + assertOrPanic(root.children[2].children[0].payload == 31); + assertOrPanic(root.children[2].children[1].payload == 32); } diff --git a/test/cases/switch.zig b/test/stage1/behavior/switch.zig similarity index 72% rename from test/cases/switch.zig rename to test/stage1/behavior/switch.zig index 1162fdd4b2..4ac971397e 100644 --- a/test/cases/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "switch with numbers" { testSwitchWithNumbers(13); @@ -10,14 +10,14 @@ fn testSwitchWithNumbers(x: u32) void { 13 => true, else => false, }; - assert(result); + assertOrPanic(result); } test "switch with all ranges" { - assert(testSwitchWithAllRanges(50, 3) == 1); - assert(testSwitchWithAllRanges(101, 0) == 2); - assert(testSwitchWithAllRanges(300, 5) == 3); - assert(testSwitchWithAllRanges(301, 6) == 6); + assertOrPanic(testSwitchWithAllRanges(50, 3) == 1); + assertOrPanic(testSwitchWithAllRanges(101, 0) == 2); + assertOrPanic(testSwitchWithAllRanges(300, 5) == 3); + assertOrPanic(testSwitchWithAllRanges(301, 6) == 6); } fn testSwitchWithAllRanges(x: u32, y: u32) u32 { @@ -40,7 +40,7 @@ test "implicit comptime switch" { }; comptime { - assert(result + 1 == 14); + assertOrPanic(result + 1 == 14); } } @@ -71,7 +71,7 @@ fn nonConstSwitch(foo: SwitchStatmentFoo) void { SwitchStatmentFoo.C => 3, SwitchStatmentFoo.D => 4, }; - assert(val == 3); + assertOrPanic(val == 3); } const SwitchStatmentFoo = enum { A, @@ -93,10 +93,10 @@ const SwitchProngWithVarEnum = union(enum) { fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void { switch (a) { SwitchProngWithVarEnum.One => |x| { - assert(x == 13); + assertOrPanic(x == 13); }, SwitchProngWithVarEnum.Two => |x| { - assert(x == 13.0); + assertOrPanic(x == 13.0); }, SwitchProngWithVarEnum.Meh => |x| { const v: void = x; @@ -116,7 +116,7 @@ fn testSwitchEnumPtrCapture() void { else => unreachable, } switch (value) { - SwitchProngWithVarEnum.One => |x| assert(x == 1235), + SwitchProngWithVarEnum.One => |x| assertOrPanic(x == 1235), else => unreachable, } } @@ -127,7 +127,7 @@ test "switch with multiple expressions" { 4, 5, 6 => 2, else => i32(3), }; - assert(x == 2); + assertOrPanic(x == 2); } fn returnsFive() i32 { return 5; @@ -149,12 +149,12 @@ fn returnsFalse() bool { } } test "switch on const enum with var" { - assert(!returnsFalse()); + assertOrPanic(!returnsFalse()); } test "switch on type" { - assert(trueIfBoolFalseOtherwise(bool)); - assert(!trueIfBoolFalseOtherwise(i32)); + assertOrPanic(trueIfBoolFalseOtherwise(bool)); + assertOrPanic(!trueIfBoolFalseOtherwise(i32)); } fn trueIfBoolFalseOtherwise(comptime T: type) bool { @@ -170,16 +170,16 @@ test "switch handles all cases of number" { } fn testSwitchHandleAllCases() void { - assert(testSwitchHandleAllCasesExhaustive(0) == 3); - assert(testSwitchHandleAllCasesExhaustive(1) == 2); - assert(testSwitchHandleAllCasesExhaustive(2) == 1); - assert(testSwitchHandleAllCasesExhaustive(3) == 0); + assertOrPanic(testSwitchHandleAllCasesExhaustive(0) == 3); + assertOrPanic(testSwitchHandleAllCasesExhaustive(1) == 2); + assertOrPanic(testSwitchHandleAllCasesExhaustive(2) == 1); + assertOrPanic(testSwitchHandleAllCasesExhaustive(3) == 0); - assert(testSwitchHandleAllCasesRange(100) == 0); - assert(testSwitchHandleAllCasesRange(200) == 1); - assert(testSwitchHandleAllCasesRange(201) == 2); - assert(testSwitchHandleAllCasesRange(202) == 4); - assert(testSwitchHandleAllCasesRange(230) == 3); + assertOrPanic(testSwitchHandleAllCasesRange(100) == 0); + assertOrPanic(testSwitchHandleAllCasesRange(200) == 1); + assertOrPanic(testSwitchHandleAllCasesRange(201) == 2); + assertOrPanic(testSwitchHandleAllCasesRange(202) == 4); + assertOrPanic(testSwitchHandleAllCasesRange(230) == 3); } fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { @@ -207,8 +207,8 @@ test "switch all prongs unreachable" { } fn testAllProngsUnreachable() void { - assert(switchWithUnreachable(1) == 2); - assert(switchWithUnreachable(2) == 10); + assertOrPanic(switchWithUnreachable(1) == 2); + assertOrPanic(switchWithUnreachable(2) == 10); } fn switchWithUnreachable(x: i32) i32 { @@ -230,7 +230,7 @@ test "capture value of switch with all unreachable prongs" { const x = return_a_number() catch |err| switch (err) { else => unreachable, }; - assert(x == 1); + assertOrPanic(x == 1); } test "switching on booleans" { @@ -239,14 +239,14 @@ test "switching on booleans" { } fn testSwitchOnBools() void { - assert(testSwitchOnBoolsTrueAndFalse(true) == false); - assert(testSwitchOnBoolsTrueAndFalse(false) == true); + assertOrPanic(testSwitchOnBoolsTrueAndFalse(true) == false); + assertOrPanic(testSwitchOnBoolsTrueAndFalse(false) == true); - assert(testSwitchOnBoolsTrueWithElse(true) == false); - assert(testSwitchOnBoolsTrueWithElse(false) == true); + assertOrPanic(testSwitchOnBoolsTrueWithElse(true) == false); + assertOrPanic(testSwitchOnBoolsTrueWithElse(false) == true); - assert(testSwitchOnBoolsFalseWithElse(true) == false); - assert(testSwitchOnBoolsFalseWithElse(false) == true); + assertOrPanic(testSwitchOnBoolsFalseWithElse(true) == false); + assertOrPanic(testSwitchOnBoolsFalseWithElse(false) == true); } fn testSwitchOnBoolsTrueAndFalse(x: bool) bool { diff --git a/test/cases/switch_prong_err_enum.zig b/test/stage1/behavior/switch_prong_err_enum.zig similarity index 79% rename from test/cases/switch_prong_err_enum.zig rename to test/stage1/behavior/switch_prong_err_enum.zig index 89060690fc..6ac1919f0d 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/stage1/behavior/switch_prong_err_enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; var read_count: u64 = 0; @@ -22,9 +22,9 @@ fn doThing(form_id: u64) anyerror!FormValue { test "switch prong returns error enum" { switch (doThing(17) catch unreachable) { FormValue.Address => |payload| { - assert(payload == 1); + assertOrPanic(payload == 1); }, else => unreachable, } - assert(read_count == 1); + assertOrPanic(read_count == 1); } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/stage1/behavior/switch_prong_implicit_cast.zig similarity index 82% rename from test/cases/switch_prong_implicit_cast.zig rename to test/stage1/behavior/switch_prong_implicit_cast.zig index 56d37e290f..4ca031e2e1 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/stage1/behavior/switch_prong_implicit_cast.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const FormValue = union(enum) { One: void, @@ -18,5 +18,5 @@ test "switch prong implicit cast" { FormValue.One => false, FormValue.Two => |x| x, }; - assert(result); + assertOrPanic(result); } diff --git a/test/cases/syntax.zig b/test/stage1/behavior/syntax.zig similarity index 99% rename from test/cases/syntax.zig rename to test/stage1/behavior/syntax.zig index 0c8c3c5ed3..451e396142 100644 --- a/test/cases/syntax.zig +++ b/test/stage1/behavior/syntax.zig @@ -57,3 +57,4 @@ fn asm_lists() void { :::"a","b",); } } + diff --git a/test/cases/this.zig b/test/stage1/behavior/this.zig similarity index 74% rename from test/cases/this.zig rename to test/stage1/behavior/this.zig index c7be074f36..0e3a7a03ae 100644 --- a/test/cases/this.zig +++ b/test/stage1/behavior/this.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const module = @This(); @@ -20,7 +20,7 @@ fn add(x: i32, y: i32) i32 { } test "this refer to module call private fn" { - assert(module.add(1, 2) == 3); + assertOrPanic(module.add(1, 2) == 3); } test "this refer to container" { @@ -29,6 +29,7 @@ test "this refer to container" { .y = 34, }; pt.addOne(); - assert(pt.x == 13); - assert(pt.y == 35); + assertOrPanic(pt.x == 13); + assertOrPanic(pt.y == 35); } + diff --git a/test/cases/truncate.zig b/test/stage1/behavior/truncate.zig similarity index 65% rename from test/cases/truncate.zig rename to test/stage1/behavior/truncate.zig index 02b5085ccd..b7904bc7fb 100644 --- a/test/cases/truncate.zig +++ b/test/stage1/behavior/truncate.zig @@ -1,8 +1,8 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "truncate u0 to larger integer allowed and has comptime known result" { var x: u0 = 0; const y = @truncate(u8, x); - comptime assert(y == 0); + comptime assertOrPanic(y == 0); } diff --git a/test/cases/try.zig b/test/stage1/behavior/try.zig similarity index 79% rename from test/cases/try.zig rename to test/stage1/behavior/try.zig index 450a9af6ac..ed48875eb4 100644 --- a/test/cases/try.zig +++ b/test/stage1/behavior/try.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "try on error union" { tryOnErrorUnionImpl(); @@ -11,7 +11,7 @@ fn tryOnErrorUnionImpl() void { error.CrappedOut => i32(2), else => unreachable, }; - assert(x == 11); + assertOrPanic(x == 11); } fn returnsTen() anyerror!i32 { @@ -20,10 +20,10 @@ fn returnsTen() anyerror!i32 { test "try without vars" { const result1 = if (failIfTrue(true)) 1 else |_| i32(2); - assert(result1 == 2); + assertOrPanic(result1 == 2); const result2 = if (failIfTrue(false)) 1 else |_| i32(2); - assert(result2 == 1); + assertOrPanic(result2 == 1); } fn failIfTrue(ok: bool) anyerror!void { @@ -38,6 +38,6 @@ test "try then not executed with assignment" { if (failIfTrue(true)) { unreachable; } else |err| { - assert(err == error.ItBroke); + assertOrPanic(err == error.ItBroke); } } diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig new file mode 100644 index 0000000000..f3bb17b282 --- /dev/null +++ b/test/stage1/behavior/type_info.zig @@ -0,0 +1,276 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +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 { + assertOrPanic(@TagType(TypeInfo) == TypeId); + const void_info = @typeInfo(void); + assertOrPanic(TypeId(void_info) == TypeId.Void); + assertOrPanic(void_info.Void == {}); +} + +test "type info: integer, floating point type info" { + testIntFloat(); + comptime testIntFloat(); +} + +fn testIntFloat() void { + const u8_info = @typeInfo(u8); + assertOrPanic(TypeId(u8_info) == TypeId.Int); + assertOrPanic(!u8_info.Int.is_signed); + assertOrPanic(u8_info.Int.bits == 8); + + const f64_info = @typeInfo(f64); + assertOrPanic(TypeId(f64_info) == TypeId.Float); + assertOrPanic(f64_info.Float.bits == 64); +} + +test "type info: pointer type info" { + testPointer(); + comptime testPointer(); +} + +fn testPointer() void { + const u32_ptr_info = @typeInfo(*u32); + assertOrPanic(TypeId(u32_ptr_info) == TypeId.Pointer); + assertOrPanic(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); + assertOrPanic(u32_ptr_info.Pointer.is_const == false); + assertOrPanic(u32_ptr_info.Pointer.is_volatile == false); + assertOrPanic(u32_ptr_info.Pointer.alignment == @alignOf(u32)); + assertOrPanic(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); + assertOrPanic(TypeId(u32_ptr_info) == TypeId.Pointer); + assertOrPanic(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); + assertOrPanic(u32_ptr_info.Pointer.is_const == true); + assertOrPanic(u32_ptr_info.Pointer.is_volatile == true); + assertOrPanic(u32_ptr_info.Pointer.alignment == @alignOf(f64)); + assertOrPanic(u32_ptr_info.Pointer.child == f64); +} + +test "type info: slice type info" { + testSlice(); + comptime testSlice(); +} + +fn testSlice() void { + const u32_slice_info = @typeInfo([]u32); + assertOrPanic(TypeId(u32_slice_info) == TypeId.Pointer); + assertOrPanic(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); + assertOrPanic(u32_slice_info.Pointer.is_const == false); + assertOrPanic(u32_slice_info.Pointer.is_volatile == false); + assertOrPanic(u32_slice_info.Pointer.alignment == 4); + assertOrPanic(u32_slice_info.Pointer.child == u32); +} + +test "type info: array type info" { + testArray(); + comptime testArray(); +} + +fn testArray() void { + const arr_info = @typeInfo([42]bool); + assertOrPanic(TypeId(arr_info) == TypeId.Array); + assertOrPanic(arr_info.Array.len == 42); + assertOrPanic(arr_info.Array.child == bool); +} + +test "type info: optional type info" { + testOptional(); + comptime testOptional(); +} + +fn testOptional() void { + const null_info = @typeInfo(?void); + assertOrPanic(TypeId(null_info) == TypeId.Optional); + assertOrPanic(null_info.Optional.child == void); +} + +test "type info: promise info" { + testPromise(); + comptime testPromise(); +} + +fn testPromise() void { + const null_promise_info = @typeInfo(promise); + assertOrPanic(TypeId(null_promise_info) == TypeId.Promise); + assertOrPanic(null_promise_info.Promise.child == null); + + const promise_info = @typeInfo(promise->usize); + assertOrPanic(TypeId(promise_info) == TypeId.Promise); + assertOrPanic(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); + assertOrPanic(TypeId(error_set_info) == TypeId.ErrorSet); + assertOrPanic(error_set_info.ErrorSet.errors.len == 3); + assertOrPanic(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); + assertOrPanic(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); + + const error_union_info = @typeInfo(TestErrorSet!usize); + assertOrPanic(TypeId(error_union_info) == TypeId.ErrorUnion); + assertOrPanic(error_union_info.ErrorUnion.error_set == TestErrorSet); + assertOrPanic(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); + assertOrPanic(TypeId(os_info) == TypeId.Enum); + assertOrPanic(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(os_info.Enum.fields.len == 4); + assertOrPanic(mem.eql(u8, os_info.Enum.fields[1].name, "Macos")); + assertOrPanic(os_info.Enum.fields[3].value == 3); + assertOrPanic(os_info.Enum.tag_type == u2); + assertOrPanic(os_info.Enum.defs.len == 0); +} + +test "type info: union info" { + testUnion(); + comptime testUnion(); +} + +fn testUnion() void { + const typeinfo_info = @typeInfo(TypeInfo); + assertOrPanic(TypeId(typeinfo_info) == TypeId.Union); + assertOrPanic(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(typeinfo_info.Union.tag_type.? == TypeId); + assertOrPanic(typeinfo_info.Union.fields.len == 25); + assertOrPanic(typeinfo_info.Union.fields[4].enum_field != null); + assertOrPanic(typeinfo_info.Union.fields[4].enum_field.?.value == 4); + assertOrPanic(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); + assertOrPanic(typeinfo_info.Union.defs.len == 21); + + const TestNoTagUnion = union { + Foo: void, + Bar: u32, + }; + + const notag_union_info = @typeInfo(TestNoTagUnion); + assertOrPanic(TypeId(notag_union_info) == TypeId.Union); + assertOrPanic(notag_union_info.Union.tag_type == null); + assertOrPanic(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(notag_union_info.Union.fields.len == 2); + assertOrPanic(notag_union_info.Union.fields[0].enum_field == null); + assertOrPanic(notag_union_info.Union.fields[1].field_type == u32); + + const TestExternUnion = extern union { + foo: *c_void, + }; + + const extern_union_info = @typeInfo(TestExternUnion); + assertOrPanic(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); + assertOrPanic(extern_union_info.Union.tag_type == null); + assertOrPanic(extern_union_info.Union.fields[0].enum_field == null); + assertOrPanic(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); + assertOrPanic(TypeId(struct_info) == TypeId.Struct); + assertOrPanic(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); + assertOrPanic(struct_info.Struct.fields.len == 3); + assertOrPanic(struct_info.Struct.fields[1].offset == null); + assertOrPanic(struct_info.Struct.fields[2].field_type == *TestStruct); + assertOrPanic(struct_info.Struct.defs.len == 2); + assertOrPanic(struct_info.Struct.defs[0].is_pub); + assertOrPanic(!struct_info.Struct.defs[0].data.Fn.is_extern); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.lib_name == null); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.return_type == void); + assertOrPanic(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)); + assertOrPanic(TypeId(fn_info) == TypeId.Fn); + assertOrPanic(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); + assertOrPanic(fn_info.Fn.is_generic); + assertOrPanic(fn_info.Fn.args.len == 2); + assertOrPanic(fn_info.Fn.is_var_args); + assertOrPanic(fn_info.Fn.return_type == null); + assertOrPanic(fn_info.Fn.async_allocator_type == null); + + const test_instance: TestStruct = undefined; + const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); + assertOrPanic(TypeId(bound_fn_info) == TypeId.BoundFn); + assertOrPanic(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); +} + +test "type info: vectors" { + testVector(); + comptime testVector(); +} + +fn testVector() void { + const vec_info = @typeInfo(@Vector(4, i32)); + assertOrPanic(TypeId(vec_info) == TypeId.Vector); + assertOrPanic(vec_info.Vector.len == 4); + assertOrPanic(vec_info.Vector.child == i32); +} diff --git a/test/cases/undefined.zig b/test/stage1/behavior/undefined.zig similarity index 59% rename from test/cases/undefined.zig rename to test/stage1/behavior/undefined.zig index 83c620d211..333e217d49 100644 --- a/test/cases/undefined.zig +++ b/test/stage1/behavior/undefined.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; fn initStaticArray() [10]i32 { @@ -11,16 +11,16 @@ fn initStaticArray() [10]i32 { } const static_array = initStaticArray(); test "init static array to undefined" { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(static_array[9] == 4); comptime { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(static_array[9] == 4); } } @@ -40,12 +40,12 @@ test "assign undefined to struct" { comptime { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + assertOrPanic(foo.x == 2); } { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + assertOrPanic(foo.x == 2); } } @@ -53,16 +53,17 @@ test "assign undefined to struct with method" { comptime { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + assertOrPanic(foo.x == 3); } { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + assertOrPanic(foo.x == 3); } } test "type name of undefined" { const x = undefined; - assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); + assertOrPanic(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); } + diff --git a/test/cases/underscore.zig b/test/stage1/behavior/underscore.zig similarity index 91% rename from test/cases/underscore.zig rename to test/stage1/behavior/underscore.zig index da1c97659c..7443319336 100644 --- a/test/cases/underscore.zig +++ b/test/stage1/behavior/underscore.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "ignore lval with underscore" { _ = false; diff --git a/test/cases/union.zig b/test/stage1/behavior/union.zig similarity index 77% rename from test/cases/union.zig rename to test/stage1/behavior/union.zig index 019a7012da..c8e8feb11e 100644 --- a/test/cases/union.zig +++ b/test/stage1/behavior/union.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Value = union(enum) { Int: u64, @@ -27,11 +27,11 @@ const array = []Value{ test "unions embedded in aggregate types" { switch (array[1]) { - Value.Array => |arr| assert(arr[4] == 3), + Value.Array => |arr| assertOrPanic(arr[4] == 3), else => unreachable, } switch ((err catch unreachable).val1) { - Value.Int => |x| assert(x == 1234), + Value.Int => |x| assertOrPanic(x == 1234), else => unreachable, } } @@ -43,18 +43,18 @@ const Foo = union { test "basic unions" { var foo = Foo{ .int = 1 }; - assert(foo.int == 1); + assertOrPanic(foo.int == 1); foo = Foo{ .float = 12.34 }; - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); } test "comptime union field access" { comptime { var foo = Foo{ .int = 0 }; - assert(foo.int == 0); + assertOrPanic(foo.int == 0); foo = Foo{ .float = 42.42 }; - assert(foo.float == 42.42); + assertOrPanic(foo.float == 42.42); } } @@ -62,10 +62,10 @@ test "init union with runtime value" { var foo: Foo = undefined; setFloat(&foo, 12.34); - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); setInt(&foo, 42); - assert(foo.int == 42); + assertOrPanic(foo.int == 42); } fn setFloat(foo: *Foo, x: f64) void { @@ -83,9 +83,9 @@ const FooExtern = extern union { test "basic extern unions" { var foo = FooExtern{ .int = 1 }; - assert(foo.int == 1); + assertOrPanic(foo.int == 1); foo.float = 12.34; - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); } const Letter = enum { @@ -105,11 +105,11 @@ test "union with specified enum tag" { } fn doTest() void { - assert(bar(Payload{ .A = 1234 }) == -10); + assertOrPanic(bar(Payload{ .A = 1234 }) == -10); } fn bar(value: Payload) i32 { - assert(Letter(value) == Letter.A); + assertOrPanic(Letter(value) == Letter.A); return switch (value) { Payload.A => |x| return x - 1244, Payload.B => |x| if (x == 12.34) i32(20) else 21, @@ -125,8 +125,8 @@ const MultipleChoice = union(enum(u32)) { }; test "simple union(enum(u32))" { var x = MultipleChoice.C; - assert(x == MultipleChoice.C); - assert(@enumToInt(@TagType(MultipleChoice)(x)) == 60); + assertOrPanic(x == MultipleChoice.C); + assertOrPanic(@enumToInt(@TagType(MultipleChoice)(x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -142,14 +142,14 @@ const MultipleChoice2 = union(enum(u32)) { }; test "union(enum(u32)) with specified and unspecified tag values" { - comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); + comptime assertOrPanic(@TagType(@TagType(MultipleChoice2)) == u32); testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); - assert(1123 == switch (x) { + assertOrPanic(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); + assertOrPanic(1123 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => |v| i32(1000) + v, @@ -167,7 +167,7 @@ const ExternPtrOrInt = extern union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(ExternPtrOrInt) == 8); + comptime assertOrPanic(@sizeOf(ExternPtrOrInt) == 8); } const PackedPtrOrInt = packed union { @@ -175,14 +175,14 @@ const PackedPtrOrInt = packed union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(PackedPtrOrInt) == 8); + comptime assertOrPanic(@sizeOf(PackedPtrOrInt) == 8); } const ZeroBits = union { OnlyField: void, }; test "union with only 1 field which is void should be zero bits" { - comptime assert(@sizeOf(ZeroBits) == 0); + comptime assertOrPanic(@sizeOf(ZeroBits) == 0); } const TheTag = enum { @@ -196,9 +196,9 @@ const TheUnion = union(TheTag) { C: i32, }; test "union field access gives the enum values" { - assert(TheUnion.A == TheTag.A); - assert(TheUnion.B == TheTag.B); - assert(TheUnion.C == TheTag.C); + assertOrPanic(TheUnion.A == TheTag.A); + assertOrPanic(TheUnion.B == TheTag.B); + assertOrPanic(TheUnion.C == TheTag.C); } test "cast union to tag type of union" { @@ -207,12 +207,12 @@ test "cast union to tag type of union" { } fn testCastUnionToTagType(x: TheUnion) void { - assert(TheTag(x) == TheTag.B); + assertOrPanic(TheTag(x) == TheTag.B); } test "cast tag type of union to union" { var x: Value2 = Letter2.B; - assert(Letter2(x) == Letter2.B); + assertOrPanic(Letter2(x) == Letter2.B); } const Letter2 = enum { A, @@ -227,11 +227,11 @@ const Value2 = union(Letter2) { test "implicit cast union to its tag type" { var x: Value2 = Letter2.B; - assert(x == Letter2.B); + assertOrPanic(x == Letter2.B); giveMeLetterB(x); } fn giveMeLetterB(x: Letter2) void { - assert(x == Value2.B); + assertOrPanic(x == Value2.B); } pub const PackThis = union(enum) { @@ -244,7 +244,7 @@ test "constant packed union" { } fn testConstPackedUnion(expected_tokens: []const PackThis) void { - assert(expected_tokens[0].StringLiteral == 1); + assertOrPanic(expected_tokens[0].StringLiteral == 1); } test "switch on union with only 1 field" { @@ -256,7 +256,7 @@ test "switch on union with only 1 field" { z = PartialInstWithPayload{ .Compiled = 1234 }; switch (z) { PartialInstWithPayload.Compiled => |x| { - assert(x == 1234); + assertOrPanic(x == 1234); return; }, } @@ -282,11 +282,11 @@ test "access a member of tagged union with conflicting enum tag name" { const B = void; }; - comptime assert(Bar.A == u8); + comptime assertOrPanic(Bar.A == u8); } test "tagged union initialization with runtime void" { - assert(testTaggedUnionInit({})); + assertOrPanic(testTaggedUnionInit({})); } const TaggedUnionWithAVoid = union(enum) { @@ -324,9 +324,9 @@ test "union with only 1 field casted to its enum type" { var e = Expr{ .Literal = Literal{ .Bool = true } }; const Tag = @TagType(Expr); - comptime assert(@TagType(Tag) == comptime_int); + comptime assertOrPanic(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); + assertOrPanic(t == Expr.Literal); } test "union with only 1 field casted to its enum type which has enum value specified" { @@ -344,9 +344,9 @@ test "union with only 1 field casted to its enum type which has enum value speci }; var e = Expr{ .Literal = Literal{ .Bool = true } }; - comptime assert(@TagType(Tag) == comptime_int); + comptime assertOrPanic(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); - assert(@enumToInt(t) == 33); - comptime assert(@enumToInt(t) == 33); + assertOrPanic(t == Expr.Literal); + assertOrPanic(@enumToInt(t) == 33); + comptime assertOrPanic(@enumToInt(t) == 33); } diff --git a/test/cases/var_args.zig b/test/stage1/behavior/var_args.zig similarity index 56% rename from test/cases/var_args.zig rename to test/stage1/behavior/var_args.zig index 3eb6e30448..1f782a3bb3 100644 --- a/test/cases/var_args.zig +++ b/test/stage1/behavior/var_args.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn add(args: ...) i32 { var sum = i32(0); @@ -12,9 +12,9 @@ fn add(args: ...) i32 { } test "add arbitrary args" { - assert(add(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(add(i32(1234)) == 1234); - assert(add() == 0); + assertOrPanic(add(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(add(i32(1234)) == 1234); + assertOrPanic(add() == 0); } fn readFirstVarArg(args: ...) void { @@ -26,9 +26,9 @@ test "send void arg to var args" { } test "pass args directly" { - assert(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(addSomeStuff(i32(1234)) == 1234); - assert(addSomeStuff() == 0); + assertOrPanic(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(addSomeStuff(i32(1234)) == 1234); + assertOrPanic(addSomeStuff() == 0); } fn addSomeStuff(args: ...) i32 { @@ -36,24 +36,24 @@ fn addSomeStuff(args: ...) i32 { } test "runtime parameter before var args" { - assert(extraFn(10) == 0); - assert(extraFn(10, false) == 1); - assert(extraFn(10, false, true) == 2); + assertOrPanic(extraFn(10) == 0); + assertOrPanic(extraFn(10, false) == 1); + assertOrPanic(extraFn(10, false, true) == 2); // TODO issue #313 //comptime { - // assert(extraFn(10) == 0); - // assert(extraFn(10, false) == 1); - // assert(extraFn(10, false, true) == 2); + // assertOrPanic(extraFn(10) == 0); + // assertOrPanic(extraFn(10, false) == 1); + // assertOrPanic(extraFn(10, false, true) == 2); //} } fn extraFn(extra: u32, args: ...) usize { if (args.len >= 1) { - assert(args[0] == false); + assertOrPanic(args[0] == false); } if (args.len >= 2) { - assert(args[1] == true); + assertOrPanic(args[1] == true); } return args.len; } @@ -71,8 +71,8 @@ fn foo2(args: ...) bool { } test "array of var args functions" { - assert(foos[0]()); - assert(!foos[1]()); + assertOrPanic(foos[0]()); + assertOrPanic(!foos[1]()); } test "pass zero length array to var args param" { diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig new file mode 100644 index 0000000000..53c5d01381 --- /dev/null +++ b/test/stage1/behavior/vector.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "implicit array to vector and vector to array" { + const S = struct { + fn doTheTest() void { + var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40}; + const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4}; + v +%= x; + const result: [4]i32 = v; + assertOrPanic(result[0] == 11); + assertOrPanic(result[1] == 22); + assertOrPanic(result[2] == 33); + assertOrPanic(result[3] == 44); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + diff --git a/test/cases/void.zig b/test/stage1/behavior/void.zig similarity index 67% rename from test/cases/void.zig rename to test/stage1/behavior/void.zig index 7121ac664b..431d3f4eb1 100644 --- a/test/cases/void.zig +++ b/test/stage1/behavior/void.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Foo = struct { a: void, @@ -13,14 +13,14 @@ test "compare void with void compile time known" { .b = 1, .c = {}, }; - assert(foo.a == {}); + assertOrPanic(foo.a == {}); } } test "iterate over a void slice" { var j: usize = 0; for (times(10)) |_, i| { - assert(i == j); + assertOrPanic(i == j); j += 1; } } @@ -28,3 +28,8 @@ test "iterate over a void slice" { fn times(n: usize) []const void { return ([*]void)(undefined)[0..n]; } + +test "void optional" { + var x: ?void = {}; + assertOrPanic(x != null); +} diff --git a/test/cases/while.zig b/test/stage1/behavior/while.zig similarity index 83% rename from test/cases/while.zig rename to test/stage1/behavior/while.zig index f774e0ec6b..579b4e4db8 100644 --- a/test/cases/while.zig +++ b/test/stage1/behavior/while.zig @@ -1,12 +1,12 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "while loop" { var i: i32 = 0; while (i < 4) { i += 1; } - assert(i == 4); - assert(whileLoop1() == 1); + assertOrPanic(i == 4); + assertOrPanic(whileLoop1() == 1); } fn whileLoop1() i32 { return whileLoop2(); @@ -16,8 +16,9 @@ fn whileLoop2() i32 { return 1; } } + test "static eval while" { - assert(static_eval_while_number == 1); + assertOrPanic(static_eval_while_number == 1); } const static_eval_while_number = staticWhileLoop1(); fn staticWhileLoop1() i32 { @@ -31,7 +32,7 @@ fn staticWhileLoop2() i32 { test "continue and break" { runContinueAndBreakTest(); - assert(continue_and_break_counter == 8); + assertOrPanic(continue_and_break_counter == 8); } var continue_and_break_counter: i32 = 0; fn runContinueAndBreakTest() void { @@ -44,7 +45,7 @@ fn runContinueAndBreakTest() void { } break; } - assert(i == 4); + assertOrPanic(i == 4); } test "return with implicit cast from while loop" { @@ -65,7 +66,7 @@ test "while with continue expression" { sum += i; } } - assert(sum == 40); + assertOrPanic(sum == 40); } test "while with else" { @@ -77,8 +78,8 @@ test "while with else" { } else { got_else += 1; } - assert(sum == 10); - assert(got_else == 1); + assertOrPanic(sum == 10); + assertOrPanic(got_else == 1); } test "while with optional as condition" { @@ -87,7 +88,7 @@ test "while with optional as condition" { while (getNumberOrNull()) |value| { sum += value; } - assert(sum == 45); + assertOrPanic(sum == 45); } test "while with optional as condition with else" { @@ -96,12 +97,12 @@ test "while with optional as condition with else" { var got_else: i32 = 0; while (getNumberOrNull()) |value| { sum += value; - assert(got_else == 0); + assertOrPanic(got_else == 0); } else { got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); } test "while with error union condition" { @@ -111,11 +112,11 @@ test "while with error union condition" { while (getNumberOrErr()) |value| { sum += value; } else |err| { - assert(err == error.OutOfNumbers); + assertOrPanic(err == error.OutOfNumbers); got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); } var numbers_left: i32 = undefined; @@ -137,7 +138,7 @@ test "while on optional with else result follow else prong" { break value; } else i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on optional with else result follow break prong" { @@ -145,7 +146,7 @@ test "while on optional with else result follow break prong" { break value; } else i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "while on error union with else result follow else prong" { @@ -153,7 +154,7 @@ test "while on error union with else result follow else prong" { break value; } else |err| i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on error union with else result follow break prong" { @@ -161,7 +162,7 @@ test "while on error union with else result follow break prong" { break value; } else |err| i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "while on bool with else result follow else prong" { @@ -169,7 +170,7 @@ test "while on bool with else result follow else prong" { break i32(10); } else i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on bool with else result follow break prong" { @@ -177,7 +178,7 @@ test "while on bool with else result follow break prong" { break i32(10); } else i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "break from outer while loop" { diff --git a/test/cases/widening.zig b/test/stage1/behavior/widening.zig similarity index 75% rename from test/cases/widening.zig rename to test/stage1/behavior/widening.zig index cf6ab4ca0f..7577868aff 100644 --- a/test/cases/widening.zig +++ b/test/stage1/behavior/widening.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; test "integer widening" { @@ -9,13 +9,13 @@ test "integer widening" { var d: u64 = c; var e: u64 = d; var f: u128 = e; - assert(f == a); + assertOrPanic(f == a); } test "implicit unsigned integer to signed integer" { var a: u8 = 250; var b: i16 = a; - assert(b == 250); + assertOrPanic(b == 250); } test "float widening" { @@ -23,5 +23,6 @@ test "float widening" { var b: f32 = a; var c: f64 = b; var d: f128 = c; - assert(d == a); + assertOrPanic(d == a); } + diff --git a/test/tests.zig b/test/tests.zig index 1ca06b4b34..548496fa2f 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -48,13 +48,14 @@ const test_targets = []TestTarget{ const max_stdout_size = 1 * 1024 * 1024; // 1 MB pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext) catch unreachable; + cases.* = CompareOutputContext{ .b = b, .step = b.step("test-compare-output", "Run the compare output tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; compare_output.addCases(cases); @@ -62,13 +63,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: } pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext) catch unreachable; + cases.* = CompareOutputContext{ .b = b, .step = b.step("test-runtime-safety", "Run the runtime safety tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; runtime_safety.addCases(cases); @@ -76,13 +78,14 @@ pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: } pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompileErrorContext{ + const cases = b.allocator.create(CompileErrorContext) catch unreachable; + cases.* = CompileErrorContext{ .b = b, .step = b.step("test-compile-errors", "Run the compile error tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; compile_errors.addCases(cases); @@ -90,13 +93,14 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: } pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(BuildExamplesContext{ + const cases = b.allocator.create(BuildExamplesContext) catch unreachable; + cases.* = BuildExamplesContext{ .b = b, .step = b.step("test-build-examples", "Build the examples"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; build_examples.addCases(cases); @@ -119,13 +123,14 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const M } pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext) catch unreachable; + cases.* = CompareOutputContext{ .b = b, .step = b.step("test-asm-link", "Run the assemble and link tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; assemble_and_link.addCases(cases); @@ -133,12 +138,13 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode } pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(TranslateCContext{ + const cases = b.allocator.create(TranslateCContext) catch unreachable; + cases.* = TranslateCContext{ .b = b, .step = b.step("test-translate-c", "Run the C transation tests"), .test_index = 0, .test_filter = test_filter, - }) catch unreachable; + }; translate_c.addCases(cases); @@ -146,12 +152,13 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St } pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(GenHContext{ + const cases = b.allocator.create(GenHContext) catch unreachable; + cases.* = GenHContext{ .b = b, .step = b.step("test-gen-h", "Run the C header file generation tests"), .test_index = 0, .test_filter = test_filter, - }) catch unreachable; + }; gen_h.addCases(cases); @@ -163,25 +170,32 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons for (test_targets) |test_target| { const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch); for (modes) |mode| { - for ([]bool{ - false, - true, - }) |link_libc| { - if (link_libc and !is_native) { - // don't assume we have a cross-compiling libc set up - continue; + for ([]bool{ false, true }) |link_libc| { + for ([]bool{ false, true }) |single_threaded| { + if (link_libc and !is_native) { + // don't assume we have a cross-compiling libc set up + continue; + } + const these_tests = b.addTest(root_src); + these_tests.setNamePrefix(b.fmt( + "{}-{}-{}-{}-{}-{} ", + name, + @tagName(test_target.os), + @tagName(test_target.arch), + @tagName(mode), + if (link_libc) "c" else "bare", + if (single_threaded) "single" else "multi", + )); + these_tests.setFilter(test_filter); + these_tests.setBuildMode(mode); + if (!is_native) { + these_tests.setTarget(test_target.arch, test_target.os, test_target.environ); + } + if (link_libc) { + these_tests.linkSystemLibrary("c"); + } + step.dependOn(&these_tests.step); } - const these_tests = b.addTest(root_src); - these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare")); - these_tests.setFilter(test_filter); - these_tests.setBuildMode(mode); - if (!is_native) { - these_tests.setTarget(test_target.arch, test_target.os, test_target.environ); - } - if (link_libc) { - these_tests.linkSystemLibrary("c"); - } - step.dependOn(&these_tests.step); } } } @@ -237,7 +251,8 @@ pub const CompareOutputContext = struct { pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, cli_args: []const []const u8) *RunCompareOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(RunCompareOutputStep{ + const ptr = allocator.create(RunCompareOutputStep) catch unreachable; + ptr.* = RunCompareOutputStep{ .context = context, .exe_path = exe_path, .name = name, @@ -245,7 +260,7 @@ pub const CompareOutputContext = struct { .test_index = context.test_index, .step = build.Step.init("RunCompareOutput", allocator, make), .cli_args = cli_args, - }) catch unreachable; + }; context.test_index += 1; return ptr; } @@ -324,13 +339,14 @@ pub const CompareOutputContext = struct { pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8) *RuntimeSafetyRunStep { const allocator = context.b.allocator; - const ptr = allocator.create(RuntimeSafetyRunStep{ + const ptr = allocator.create(RuntimeSafetyRunStep) catch unreachable; + ptr.* = RuntimeSafetyRunStep{ .context = context, .exe_path = exe_path, .name = name, .test_index = context.test_index, .step = build.Step.init("RuntimeSafetyRun", allocator, make), - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -535,14 +551,15 @@ pub const CompileErrorContext = struct { pub fn create(context: *CompileErrorContext, name: []const u8, case: *const TestCase, build_mode: Mode) *CompileCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(CompileCmpOutputStep{ + const ptr = allocator.create(CompileCmpOutputStep) catch unreachable; + ptr.* = CompileCmpOutputStep{ .step = build.Step.init("CompileCmpOutput", allocator, make), .context = context, .name = name, .test_index = context.test_index, .case = case, .build_mode = build_mode, - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -654,13 +671,14 @@ pub const CompileErrorContext = struct { } pub fn create(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase{ + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_errors = ArrayList([]const u8).init(self.b.allocator), .link_libc = false, .is_exe = false, - }) catch unreachable; + }; tc.addSourceFile(".tmp_source.zig", source); comptime var arg_i = 0; @@ -814,13 +832,14 @@ pub const TranslateCContext = struct { pub fn create(context: *TranslateCContext, name: []const u8, case: *const TestCase) *TranslateCCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(TranslateCCmpOutputStep{ + const ptr = allocator.create(TranslateCCmpOutputStep) catch unreachable; + ptr.* = TranslateCCmpOutputStep{ .step = build.Step.init("ParseCCmpOutput", allocator, make), .context = context, .name = name, .test_index = context.test_index, .case = case, - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -921,12 +940,13 @@ pub const TranslateCContext = struct { } pub fn create(self: *TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase{ + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_lines = ArrayList([]const u8).init(self.b.allocator), .allow_warnings = allow_warnings, - }) catch unreachable; + }; tc.addSourceFile(filename, source); comptime var arg_i = 0; @@ -1008,14 +1028,15 @@ pub const GenHContext = struct { pub fn create(context: *GenHContext, h_path: []const u8, name: []const u8, case: *const TestCase) *GenHCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(GenHCmpOutputStep{ + const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; + ptr.* = GenHCmpOutputStep{ .step = build.Step.init("ParseCCmpOutput", allocator, make), .context = context, .h_path = h_path, .name = name, .test_index = context.test_index, .case = case, - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -1055,11 +1076,12 @@ pub const GenHContext = struct { } pub fn create(self: *GenHContext, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase{ + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_lines = ArrayList([]const u8).init(self.b.allocator), - }) catch unreachable; + }; tc.addSourceFile(filename, source); comptime var arg_i = 0;