diff --git a/src/Compilation.zig b/src/Compilation.zig index 50d9376c58..f458fe9f61 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -46,6 +46,7 @@ stage1_cache_manifest: *Cache.Manifest = undefined, link_error_flags: link.File.ErrorFlags = .{}, work_queue: std.fifo.LinearFifo(Job, .Dynamic), +anon_work_queue: std.fifo.LinearFifo(Job, .Dynamic), /// These jobs are to invoke the Clang compiler to create an object file, which /// gets linked with the Compilation. @@ -1460,6 +1461,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .emit_analysis = options.emit_analysis, .emit_docs = options.emit_docs, .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), + .anon_work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa), .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa), .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa), @@ -1646,6 +1648,7 @@ pub fn destroy(self: *Compilation) void { const gpa = self.gpa; self.work_queue.deinit(); + self.anon_work_queue.deinit(); self.c_object_work_queue.deinit(); self.astgen_work_queue.deinit(); self.embed_file_work_queue.deinit(); @@ -2072,7 +2075,6 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 { } pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void { - const gpa = self.gpa; // If the terminal is dumb, we dont want to show the user all the // output. var progress: std.Progress = .{ .dont_print_on_dumb = true }; @@ -2146,7 +2148,24 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor } } - while (self.work_queue.readItem()) |work_item| switch (work_item) { + // In this main loop we give priority to non-anonymous Decls in the work queue, so + // that they can establish references to anonymous Decls, setting alive=true in the + // backend, preventing anonymous Decls from being prematurely destroyed. + while (true) { + if (self.work_queue.readItem()) |work_item| { + try processOneJob(self, work_item, main_progress_node); + continue; + } + if (self.anon_work_queue.readItem()) |work_item| { + try processOneJob(self, work_item, main_progress_node); + continue; + } + break; + } +} + +fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress.Node) !void { + switch (job) { .codegen_decl => |decl| switch (decl.analysis) { .unreferenced => unreachable, .in_progress => unreachable, @@ -2157,24 +2176,25 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .codegen_failure, .dependency_failure, .sema_failure_retryable, - => continue, + => return, .complete, .codegen_failure_retryable => { if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); - const module = self.bin_file.options.module.?; + const module = comp.bin_file.options.module.?; assert(decl.has_tv); assert(decl.ty.hasCodeGenBits()); if (decl.alive) { try module.linkerUpdateDecl(decl); - continue; + return; } // Instead of sending this decl to the linker, we actually will delete it // because we found out that it in fact was never referenced. module.deleteUnusedDecl(decl); + return; }, }, .codegen_func => |func| switch (func.owner_decl.analysis) { @@ -2187,20 +2207,21 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .codegen_failure, .dependency_failure, .sema_failure_retryable, - => continue, + => return, .complete, .codegen_failure_retryable => { if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); switch (func.state) { - .sema_failure, .dependency_failure => continue, + .sema_failure, .dependency_failure => return, .queued => {}, .in_progress => unreachable, .inline_only => unreachable, // don't queue work for this .success => unreachable, // don't queue it twice } - const module = self.bin_file.options.module.?; + const gpa = comp.gpa; + const module = comp.bin_file.options.module.?; const decl = func.owner_decl; var tmp_arena = std.heap.ArenaAllocator.init(gpa); @@ -2210,7 +2231,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor var air = module.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) { error.AnalysisFail => { assert(func.state != .in_progress); - continue; + return; }, error.OutOfMemory => return error.OutOfMemory, }; @@ -2220,17 +2241,17 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor var liveness = try Liveness.analyze(gpa, air, decl.getFileScope().zir); defer liveness.deinit(gpa); - if (builtin.mode == .Debug and self.verbose_air) { + if (builtin.mode == .Debug and comp.verbose_air) { std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); @import("print_air.zig").dump(gpa, air, decl.getFileScope().zir, liveness); std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); } - self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) { + comp.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => { decl.analysis = .codegen_failure; - continue; + return; }, else => { try module.failed_decls.ensureUnusedCapacity(gpa, 1); @@ -2241,10 +2262,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .{@errorName(err)}, )); decl.analysis = .codegen_failure_retryable; - continue; + return; }, }; - continue; + return; }, }, .emit_h_decl => |decl| switch (decl.analysis) { @@ -2256,14 +2277,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .sema_failure, .dependency_failure, .sema_failure_retryable, - => continue, + => return, // emit-h only requires semantic analysis of the Decl to be complete, // it does not depend on machine code generation to succeed. .codegen_failure, .codegen_failure_retryable, .complete => { if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); - const module = self.bin_file.options.module.?; + const gpa = comp.gpa; + const module = comp.bin_file.options.module.?; const emit_h = module.emit_h.?; _ = try emit_h.decl_table.getOrPut(gpa, decl); const decl_emit_h = decl.getEmitH(module); @@ -2287,7 +2309,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor c_codegen.genHeader(&dg) catch |err| switch (err) { error.AnalysisFail => { try emit_h.failed_decls.put(gpa, decl, dg.error_msg.?); - continue; + return; }, else => |e| return e, }; @@ -2299,26 +2321,27 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .analyze_decl => |decl| { if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); - const module = self.bin_file.options.module.?; + const module = comp.bin_file.options.module.?; module.ensureDeclAnalyzed(decl) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => continue, + error.AnalysisFail => return, }; }, .update_embed_file => |embed_file| { if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); - const module = self.bin_file.options.module.?; + const module = comp.bin_file.options.module.?; module.updateEmbedFile(embed_file) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => continue, + error.AnalysisFail => return, }; }, .update_line_number => |decl| { if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); - const module = self.bin_file.options.module.?; - self.bin_file.updateDeclLineNumber(module, decl) catch |err| { + const gpa = comp.gpa; + const module = comp.bin_file.options.module.?; + comp.bin_file.updateDeclLineNumber(module, decl) catch |err| { try module.failed_decls.ensureUnusedCapacity(gpa, 1); module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( gpa, @@ -2332,31 +2355,31 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .analyze_pkg => |pkg| { if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); - const module = self.bin_file.options.module.?; + const module = comp.bin_file.options.module.?; module.semaPkg(pkg) catch |err| switch (err) { error.CurrentWorkingDirectoryUnlinked, error.Unexpected, - => try self.setMiscFailure( + => try comp.setMiscFailure( .analyze_pkg, "unexpected problem analyzing package '{s}'", .{pkg.root_src_path}, ), error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => continue, + error.AnalysisFail => return, }; }, .glibc_crt_file => |crt_file| { - glibc.buildCRTFile(self, crt_file) catch |err| { + glibc.buildCRTFile(comp, crt_file) catch |err| { // TODO Surface more error details. - try self.setMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ + try comp.setMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ @errorName(err), }); }; }, .glibc_shared_objects => { - glibc.buildSharedObjects(self) catch |err| { + glibc.buildSharedObjects(comp) catch |err| { // TODO Surface more error details. - try self.setMiscFailure( + try comp.setMiscFailure( .glibc_shared_objects, "unable to build glibc shared objects: {s}", .{@errorName(err)}, @@ -2364,9 +2387,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .musl_crt_file => |crt_file| { - musl.buildCRTFile(self, crt_file) catch |err| { + musl.buildCRTFile(comp, crt_file) catch |err| { // TODO Surface more error details. - try self.setMiscFailure( + try comp.setMiscFailure( .musl_crt_file, "unable to build musl CRT file: {s}", .{@errorName(err)}, @@ -2374,9 +2397,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .mingw_crt_file => |crt_file| { - mingw.buildCRTFile(self, crt_file) catch |err| { + mingw.buildCRTFile(comp, crt_file) catch |err| { // TODO Surface more error details. - try self.setMiscFailure( + try comp.setMiscFailure( .mingw_crt_file, "unable to build mingw-w64 CRT file: {s}", .{@errorName(err)}, @@ -2384,10 +2407,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .windows_import_lib => |index| { - const link_lib = self.bin_file.options.system_libs.keys()[index]; - mingw.buildImportLib(self, link_lib) catch |err| { + const link_lib = comp.bin_file.options.system_libs.keys()[index]; + mingw.buildImportLib(comp, link_lib) catch |err| { // TODO Surface more error details. - try self.setMiscFailure( + try comp.setMiscFailure( .windows_import_lib, "unable to generate DLL import .lib file: {s}", .{@errorName(err)}, @@ -2395,9 +2418,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .libunwind => { - libunwind.buildStaticLib(self) catch |err| { + libunwind.buildStaticLib(comp) catch |err| { // TODO Surface more error details. - try self.setMiscFailure( + try comp.setMiscFailure( .libunwind, "unable to build libunwind: {s}", .{@errorName(err)}, @@ -2405,9 +2428,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .libcxx => { - libcxx.buildLibCXX(self) catch |err| { + libcxx.buildLibCXX(comp) catch |err| { // TODO Surface more error details. - try self.setMiscFailure( + try comp.setMiscFailure( .libcxx, "unable to build libcxx: {s}", .{@errorName(err)}, @@ -2415,9 +2438,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .libcxxabi => { - libcxx.buildLibCXXABI(self) catch |err| { + libcxx.buildLibCXXABI(comp) catch |err| { // TODO Surface more error details. - try self.setMiscFailure( + try comp.setMiscFailure( .libcxxabi, "unable to build libcxxabi: {s}", .{@errorName(err)}, @@ -2425,9 +2448,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .libtsan => { - libtsan.buildTsan(self) catch |err| { + libtsan.buildTsan(comp) catch |err| { // TODO Surface more error details. - try self.setMiscFailure( + try comp.setMiscFailure( .libtsan, "unable to build TSAN library: {s}", .{@errorName(err)}, @@ -2435,9 +2458,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .wasi_libc_crt_file => |crt_file| { - wasi_libc.buildCRTFile(self, crt_file) catch |err| { + wasi_libc.buildCRTFile(comp, crt_file) catch |err| { // TODO Surface more error details. - try self.setMiscFailure( + try comp.setMiscFailure( .wasi_libc_crt_file, "unable to build WASI libc CRT file: {s}", .{@errorName(err)}, @@ -2445,15 +2468,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .compiler_rt_lib => { - self.buildOutputFromZig( + comp.buildOutputFromZig( "compiler_rt.zig", .Lib, - &self.compiler_rt_static_lib, + &comp.compiler_rt_static_lib, .compiler_rt, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - error.SubCompilationFailed => continue, // error reported already - else => try self.setMiscFailure( + error.SubCompilationFailed => return, // error reported already + else => try comp.setMiscFailure( .compiler_rt, "unable to build compiler_rt: {s}", .{@errorName(err)}, @@ -2461,15 +2484,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .compiler_rt_obj => { - self.buildOutputFromZig( + comp.buildOutputFromZig( "compiler_rt.zig", .Obj, - &self.compiler_rt_obj, + &comp.compiler_rt_obj, .compiler_rt, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - error.SubCompilationFailed => continue, // error reported already - else => try self.setMiscFailure( + error.SubCompilationFailed => return, // error reported already + else => try comp.setMiscFailure( .compiler_rt, "unable to build compiler_rt: {s}", .{@errorName(err)}, @@ -2477,15 +2500,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .libssp => { - self.buildOutputFromZig( + comp.buildOutputFromZig( "ssp.zig", .Lib, - &self.libssp_static_lib, + &comp.libssp_static_lib, .libssp, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - error.SubCompilationFailed => continue, // error reported already - else => try self.setMiscFailure( + error.SubCompilationFailed => return, // error reported already + else => try comp.setMiscFailure( .libssp, "unable to build libssp: {s}", .{@errorName(err)}, @@ -2493,15 +2516,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .zig_libc => { - self.buildOutputFromZig( + comp.buildOutputFromZig( "c.zig", .Lib, - &self.libc_static_lib, + &comp.libc_static_lib, .zig_libc, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - error.SubCompilationFailed => continue, // error reported already - else => try self.setMiscFailure( + error.SubCompilationFailed => return, // error reported already + else => try comp.setMiscFailure( .zig_libc, "unable to build zig's multitarget libc: {s}", .{@errorName(err)}, @@ -2512,11 +2535,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor if (!build_options.is_stage1) unreachable; - self.updateStage1Module(main_progress_node) catch |err| { + comp.updateStage1Module(main_progress_node) catch |err| { fatal("unable to build stage1 zig object: {s}", .{@errorName(err)}); }; }, - }; + } } const AstGenSrc = union(enum) { diff --git a/src/Module.zig b/src/Module.zig index de6770d3d7..2c51b0ab69 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3039,10 +3039,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{ decl, decl.name, dep, dep.name, }); - // We don't perform a deletion here, because this Decl or another one - // may end up referencing it before the update is complete. - dep.deletion_flag = true; - try mod.deletion_set.put(mod.gpa, dep, {}); + try mod.markDeclForDeletion(dep); } } decl.dependencies.clearRetainingCapacity(); @@ -3433,21 +3430,29 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { decl.owns_tv = false; var queue_linker_work = false; - if (decl_tv.val.castTag(.variable)) |payload| { - const variable = payload.data; - if (variable.owner_decl == decl) { - decl.owns_tv = true; - queue_linker_work = true; + switch (decl_tv.val.tag()) { + .variable => { + const variable = decl_tv.val.castTag(.variable).?.data; + if (variable.owner_decl == decl) { + decl.owns_tv = true; + queue_linker_work = true; - const copied_init = try variable.init.copy(&decl_arena.allocator); - variable.init = copied_init; - } - } else if (decl_tv.val.castTag(.extern_fn)) |payload| { - const owner_decl = payload.data; - if (decl == owner_decl) { - decl.owns_tv = true; + const copied_init = try variable.init.copy(&decl_arena.allocator); + variable.init = copied_init; + } + }, + .extern_fn => { + const owner_decl = decl_tv.val.castTag(.extern_fn).?.data; + if (decl == owner_decl) { + decl.owns_tv = true; + queue_linker_work = true; + } + }, + .array, .@"struct", .@"union" => { + log.debug("send global const to linker: {*} ({s})", .{ decl, decl.name }); queue_linker_work = true; - } + }, + else => {}, } decl.ty = try decl_tv.ty.copy(&decl_arena.allocator); @@ -3462,6 +3467,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { decl.generation = mod.generation; if (queue_linker_work and decl.ty.hasCodeGenBits()) { + log.debug("queue linker work for {*} ({s})", .{ decl, decl.name }); + try mod.comp.bin_file.allocateDeclIndexes(decl); try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); @@ -3985,6 +3992,7 @@ pub fn clearDecl( decl.analysis = .unreferenced; } +/// This function is exclusively called for anonymous decls. pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void { log.debug("deleteUnusedDecl {*} ({s})", .{ decl, decl.name }); @@ -4019,6 +4027,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void { decl.destroy(mod); } +/// We don't perform a deletion here, because this Decl or another one +/// may end up referencing it before the update is complete. +fn markDeclForDeletion(mod: *Module, decl: *Decl) !void { + decl.deletion_flag = true; + try mod.deletion_set.put(mod.gpa, decl, {}); +} + /// Cancel the creation of an anon decl and delete any references to it. /// If other decls depend on this decl, they must be aborted first. pub fn abortAnonDecl(mod: *Module, decl: *Decl) void { @@ -4369,12 +4384,13 @@ pub fn createAnonymousDeclFromDeclNamed( namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {}); - // TODO: This generates the Decl into the machine code file if it is of a - // type that is non-zero size. We should be able to further improve the - // compiler to omit Decls which are only referenced at compile-time and not runtime. + // The Decl starts off with alive=false and the codegen backend will set alive=true + // if the Decl is referenced by an instruction or another constant. Otherwise, + // the Decl will be garbage collected by the `codegen_decl` task instead of sent + // to the linker. if (typed_value.ty.hasCodeGenBits()) { try mod.comp.bin_file.allocateDeclIndexes(new_decl); - try mod.comp.work_queue.writeItem(.{ .codegen_decl = new_decl }); + try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl }); } return new_decl; diff --git a/src/codegen.zig b/src/codegen.zig index 5f5ee1b549..374a9353e1 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -285,6 +285,13 @@ pub fn generateSymbol( } return Result{ .appended = {} }; }, + .Struct => { + const field_vals = typed_value.val.castTag(.@"struct").?.data; + _ = field_vals; // TODO write the fields for real + const target = bin_file.options.target; + try code.writer().writeByteNTimes(0xaa, typed_value.ty.abiSize(target)); + return Result{ .appended = {} }; + }, else => |t| { return Result{ .fail = try ErrorMsg.create( diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 75e6a1d78e..14b51c3932 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -809,6 +809,11 @@ pub const Context = struct { try self.emitConstant(val, ty); return Result.appended; }, + .Struct => { + // TODO write the fields for real + try self.code.writer().writeByteNTimes(0xaa, ty.abiSize(self.target)); + return Result{ .appended = {} }; + }, else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}), } } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index f3d511916f..08d29fdb30 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -459,3 +459,18 @@ var gdt = [_]GDTEntry{ GDTEntry{ .field = 2 }, }; var global_ptr = &gdt[0]; + +test "global constant is loaded with a runtime-known index" { + const S = struct { + fn doTheTest() !void { + var index: usize = 1; + const ptr = &pieces[index].field; + try expect(ptr.* == 2); + } + const Piece = struct { + field: i32, + }; + const pieces = [_]Piece{ Piece{ .field = 1 }, Piece{ .field = 2 }, Piece{ .field = 3 } }; + }; + try S.doTheTest(); +} diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 3ec49a060a..f5fc282b05 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -90,25 +90,6 @@ fn returnsFive() i32 { return 5; } -const Number = union(enum) { - One: u64, - Two: u8, - Three: f32, -}; - -const number = Number{ .Three = 1.23 }; - -fn returnsFalse() bool { - switch (number) { - Number.One => |x| return x > 1234, - Number.Two => |x| return x == 'a', - Number.Three => |x| return x > 12.34, - } -} -test "switch on const enum with var" { - try expect(!returnsFalse()); -} - test "switch on type" { try expect(trueIfBoolFalseOtherwise(bool)); try expect(!trueIfBoolFalseOtherwise(i32)); diff --git a/test/behavior/switch_stage1.zig b/test/behavior/switch_stage1.zig index 1b85d767d5..6a86eb9494 100644 --- a/test/behavior/switch_stage1.zig +++ b/test/behavior/switch_stage1.zig @@ -3,6 +3,24 @@ const expect = std.testing.expect; const expectError = std.testing.expectError; const expectEqual = std.testing.expectEqual; +const Number = union(enum) { + One: u64, + Two: u8, + Three: f32, +}; + +const number = Number{ .Three = 1.23 }; + +fn returnsFalse() bool { + switch (number) { + Number.One => |x| return x > 1234, + Number.Two => |x| return x == 'a', + Number.Three => |x| return x > 12.34, + } +} +test "switch on const enum with var" { + try expect(!returnsFalse()); +} test "switch all prongs unreachable" { try testAllProngsUnreachable(); comptime try testAllProngsUnreachable(); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index b528dc8730..e296f6bbb8 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -71,34 +71,3 @@ test "0-sized extern union definition" { try expect(U.f == 1); } - -const Value = union(enum) { - Int: u64, - Array: [9]u8, -}; - -const Agg = struct { - val1: Value, - val2: Value, -}; - -const v1 = Value{ .Int = 1234 }; -const v2 = Value{ .Array = [_]u8{3} ** 9 }; - -const err = @as(anyerror!Agg, Agg{ - .val1 = v1, - .val2 = v2, -}); - -const array = [_]Value{ v1, v2, v1, v2 }; - -test "unions embedded in aggregate types" { - switch (array[1]) { - Value.Array => |arr| try expect(arr[4] == 3), - else => unreachable, - } - switch ((err catch unreachable).val1) { - Value.Int => |x| try expect(x == 1234), - else => unreachable, - } -} diff --git a/test/behavior/union_stage1.zig b/test/behavior/union_stage1.zig index 7a2aa96e75..6a68737ecf 100644 --- a/test/behavior/union_stage1.zig +++ b/test/behavior/union_stage1.zig @@ -3,6 +3,37 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const Tag = std.meta.Tag; +const Value = union(enum) { + Int: u64, + Array: [9]u8, +}; + +const Agg = struct { + val1: Value, + val2: Value, +}; + +const v1 = Value{ .Int = 1234 }; +const v2 = Value{ .Array = [_]u8{3} ** 9 }; + +const err = @as(anyerror!Agg, Agg{ + .val1 = v1, + .val2 = v2, +}); + +const array = [_]Value{ v1, v2, v1, v2 }; + +test "unions embedded in aggregate types" { + switch (array[1]) { + Value.Array => |arr| try expect(arr[4] == 3), + else => unreachable, + } + switch ((err catch unreachable).val1) { + Value.Int => |x| try expect(x == 1234), + else => unreachable, + } +} + const Letter = enum { A, B, C }; const Payload = union(Letter) { A: i32,