| author | |
| committer | |
| log | 4931b8dc93ee4a99a415dffab03d400e95d1a90a |
| tree | 8586196d647638d70dd7c3c3e657f9adeabdb39c |
| parent | 3f586781b6c8d698468cc58ab64797097323c253 |
13 files changed, 197 insertions(+), 23 deletions(-)
src/Air.zig+5-1| ... | ... | @@ -501,6 +501,10 @@ pub const Inst = struct { |
| 501 | 501 | /// Uses the `un_op` field. |
| 502 | 502 | tag_name, |
| 503 | 503 | |
| 504 | /// Given an error value, return the error name. Result type is always `[:0] const u8`. | |
| 505 | /// Uses the `un_op` field. | |
| 506 | error_name, | |
| 507 | ||
| 504 | 508 | pub fn fromCmpOp(op: std.math.CompareOperator) Tag { |
| 505 | 509 | return switch (op) { |
| 506 | 510 | .lt => .cmp_lt, |
| ... | ... | @@ -816,7 +820,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 816 | 820 | |
| 817 | 821 | .bool_to_int => return Type.initTag(.u1), |
| 818 | 822 | |
| 819 | .tag_name => return Type.initTag(.const_slice_u8_sentinel_0), | |
| 823 | .tag_name, .error_name => return Type.initTag(.const_slice_u8_sentinel_0), | |
| 820 | 824 | |
| 821 | 825 | .call => { |
| 822 | 826 | const callee_ty = air.typeOf(datas[inst].pl_op.operand); |
src/Liveness.zig+1| ... | ... | @@ -334,6 +334,7 @@ fn analyzeInst( |
| 334 | 334 | .ret, |
| 335 | 335 | .ret_load, |
| 336 | 336 | .tag_name, |
| 337 | .error_name, | |
| 337 | 338 | => { |
| 338 | 339 | const operand = inst_datas[inst].un_op; |
| 339 | 340 | return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); |
src/Sema.zig+12-1| ... | ... | @@ -10478,7 +10478,18 @@ fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 10478 | 10478 | fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 10479 | 10479 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 10480 | 10480 | const src = inst_data.src(); |
| 10481 | return sema.fail(block, src, "TODO: Sema.zirErrorName", .{}); | |
| 10481 | _ = src; | |
| 10482 | const operand = sema.resolveInst(inst_data.operand); | |
| 10483 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 10484 | ||
| 10485 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { | |
| 10486 | const bytes = val.castTag(.@"error").?.data.name; | |
| 10487 | return sema.addStrLit(block, bytes); | |
| 10488 | } | |
| 10489 | ||
| 10490 | // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass | |
| 10491 | // might be able to resolve the result at compile time. | |
| 10492 | return block.addUnOp(.error_name, operand); | |
| 10482 | 10493 | } |
| 10483 | 10494 | |
| 10484 | 10495 | fn zirUnaryMath(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
src/arch/aarch64/CodeGen.zig+11| ... | ... | @@ -592,6 +592,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 592 | 592 | .ctz => try self.airCtz(inst), |
| 593 | 593 | .popcount => try self.airPopcount(inst), |
| 594 | 594 | .tag_name => try self.airTagName(inst), |
| 595 | .error_name => try self.airErrorName(inst), | |
| 595 | 596 | |
| 596 | 597 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 597 | 598 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | ... | @@ -2557,6 +2558,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 2557 | 2558 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2558 | 2559 | } |
| 2559 | 2560 | |
| 2561 | fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { | |
| 2562 | const un_op = self.air.instructions.items(.data)[inst].un_op; | |
| 2563 | const operand = try self.resolveInst(un_op); | |
| 2564 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { | |
| 2565 | _ = operand; | |
| 2566 | return self.fail("TODO implement airErrorName for aarch64", .{}); | |
| 2567 | }; | |
| 2568 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | |
| 2569 | } | |
| 2570 | ||
| 2560 | 2571 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 2561 | 2572 | // First section of indexes correspond to a set number of constant values. |
| 2562 | 2573 | const ref_int = @enumToInt(inst); |
src/arch/arm/CodeGen.zig+11| ... | ... | @@ -590,6 +590,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 590 | 590 | .ctz => try self.airCtz(inst), |
| 591 | 591 | .popcount => try self.airPopcount(inst), |
| 592 | 592 | .tag_name => try self.airTagName(inst), |
| 593 | .error_name => try self.airErrorName(inst), | |
| 593 | 594 | |
| 594 | 595 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 595 | 596 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | ... | @@ -3682,6 +3683,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 3682 | 3683 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3683 | 3684 | } |
| 3684 | 3685 | |
| 3686 | fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { | |
| 3687 | const un_op = self.air.instructions.items(.data)[inst].un_op; | |
| 3688 | const operand = try self.resolveInst(un_op); | |
| 3689 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { | |
| 3690 | _ = operand; | |
| 3691 | return self.fail("TODO implement airErrorName for arm", .{}); | |
| 3692 | }; | |
| 3693 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | |
| 3694 | } | |
| 3695 | ||
| 3685 | 3696 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 3686 | 3697 | // First section of indexes correspond to a set number of constant values. |
| 3687 | 3698 | const ref_int = @enumToInt(inst); |
src/arch/riscv64/CodeGen.zig+11| ... | ... | @@ -571,6 +571,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 571 | 571 | .ctz => try self.airCtz(inst), |
| 572 | 572 | .popcount => try self.airPopcount(inst), |
| 573 | 573 | .tag_name => try self.airTagName(inst), |
| 574 | .error_name => try self.airErrorName(inst), | |
| 574 | 575 | |
| 575 | 576 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 576 | 577 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | ... | @@ -2056,6 +2057,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 2056 | 2057 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2057 | 2058 | } |
| 2058 | 2059 | |
| 2060 | fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { | |
| 2061 | const un_op = self.air.instructions.items(.data)[inst].un_op; | |
| 2062 | const operand = try self.resolveInst(un_op); | |
| 2063 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { | |
| 2064 | _ = operand; | |
| 2065 | return self.fail("TODO implement airErrorName for riscv64", .{}); | |
| 2066 | }; | |
| 2067 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | |
| 2068 | } | |
| 2069 | ||
| 2059 | 2070 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 2060 | 2071 | // First section of indexes correspond to a set number of constant values. |
| 2061 | 2072 | const ref_int = @enumToInt(inst); |
src/arch/x86_64/CodeGen.zig+11| ... | ... | @@ -635,6 +635,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 635 | 635 | .ctz => try self.airCtz(inst), |
| 636 | 636 | .popcount => try self.airPopcount(inst), |
| 637 | 637 | .tag_name => try self.airTagName(inst), |
| 638 | .error_name, => try self.airErrorName(inst), | |
| 638 | 639 | |
| 639 | 640 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 640 | 641 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | ... | @@ -3649,6 +3650,16 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 3649 | 3650 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3650 | 3651 | } |
| 3651 | 3652 | |
| 3653 | fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { | |
| 3654 | const un_op = self.air.instructions.items(.data)[inst].un_op; | |
| 3655 | const operand = try self.resolveInst(un_op); | |
| 3656 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { | |
| 3657 | _ = operand; | |
| 3658 | return self.fail("TODO implement airErrorName for x86_64", .{}); | |
| 3659 | }; | |
| 3660 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | |
| 3661 | } | |
| 3662 | ||
| 3652 | 3663 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 3653 | 3664 | // First section of indexes correspond to a set number of constant values. |
| 3654 | 3665 | const ref_int = @enumToInt(inst); |
src/codegen/c.zig+17| ... | ... | @@ -1244,6 +1244,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1244 | 1244 | .ctz => try airBuiltinCall(f, inst, "ctz"), |
| 1245 | 1245 | .popcount => try airBuiltinCall(f, inst, "popcount"), |
| 1246 | 1246 | .tag_name => try airTagName(f, inst), |
| 1247 | .error_name => try airErrorName(f, inst), | |
| 1247 | 1248 | |
| 1248 | 1249 | .int_to_float, |
| 1249 | 1250 | .float_to_int, |
| ... | ... | @@ -2998,6 +2999,22 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2998 | 2999 | //return local; |
| 2999 | 3000 | } |
| 3000 | 3001 | |
| 3002 | fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 3003 | if (f.liveness.isUnused(inst)) return CValue.none; | |
| 3004 | ||
| 3005 | const un_op = f.air.instructions.items(.data)[inst].un_op; | |
| 3006 | const writer = f.object.writer(); | |
| 3007 | const inst_ty = f.air.typeOfIndex(inst); | |
| 3008 | const operand = try f.resolveInst(un_op); | |
| 3009 | const local = try f.allocLocal(inst_ty, .Const); | |
| 3010 | ||
| 3011 | try writer.writeAll(" = "); | |
| 3012 | ||
| 3013 | _ = operand; | |
| 3014 | _ = local; | |
| 3015 | return f.fail("TODO: C backend: implement airErrorName", .{}); | |
| 3016 | } | |
| 3017 | ||
| 3001 | 3018 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { |
| 3002 | 3019 | return switch (order) { |
| 3003 | 3020 | .Unordered => "memory_order_relaxed", |
src/codegen/llvm.zig+92| ... | ... | @@ -181,6 +181,9 @@ pub const Object = struct { |
| 181 | 181 | /// The backing memory for `type_map`. Periodically garbage collected after flush(). |
| 182 | 182 | /// The code for doing the periodical GC is not yet implemented. |
| 183 | 183 | type_map_arena: std.heap.ArenaAllocator, |
| 184 | /// The LLVM global table which holds the names corresponding to Zig errors. Note that the values | |
| 185 | /// are not added until flushModule, when all errors in the compilation are known. | |
| 186 | error_name_table: ?*const llvm.Value, | |
| 184 | 187 | |
| 185 | 188 | pub const TypeMap = std.HashMapUnmanaged( |
| 186 | 189 | Type, |
| ... | ... | @@ -269,6 +272,7 @@ pub const Object = struct { |
| 269 | 272 | .decl_map = .{}, |
| 270 | 273 | .type_map = .{}, |
| 271 | 274 | .type_map_arena = std.heap.ArenaAllocator.init(gpa), |
| 275 | .error_name_table = null, | |
| 272 | 276 | }; |
| 273 | 277 | } |
| 274 | 278 | |
| ... | ... | @@ -298,7 +302,60 @@ pub const Object = struct { |
| 298 | 302 | return slice.ptr; |
| 299 | 303 | } |
| 300 | 304 | |
| 305 | fn genErrorNameTable(self: *Object, comp: *Compilation) !void { | |
| 306 | // If self.error_name_table is null, there was no instruction that actually referenced the error table. | |
| 307 | const error_name_table_ptr_global = self.error_name_table orelse return; | |
| 308 | ||
| 309 | const mod = comp.bin_file.options.module.?; | |
| 310 | const target = mod.getTarget(); | |
| 311 | ||
| 312 | const llvm_ptr_ty = self.context.intType(8).pointerType(0); // TODO: Address space | |
| 313 | const llvm_usize_ty = self.context.intType(target.cpu.arch.ptrBitWidth()); | |
| 314 | const type_fields = [_]*const llvm.Type{ | |
| 315 | llvm_ptr_ty, | |
| 316 | llvm_usize_ty, | |
| 317 | }; | |
| 318 | const llvm_slice_ty = self.context.structType(&type_fields, type_fields.len, .False); | |
| 319 | const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); | |
| 320 | const slice_alignment = slice_ty.abiAlignment(target); | |
| 321 | ||
| 322 | const error_name_list = mod.error_name_list.items; | |
| 323 | const llvm_errors = try comp.gpa.alloc(*const llvm.Value, error_name_list.len); | |
| 324 | defer comp.gpa.free(llvm_errors); | |
| 325 | ||
| 326 | llvm_errors[0] = llvm_slice_ty.getUndef(); | |
| 327 | for (llvm_errors[1..]) |*llvm_error, i| { | |
| 328 | const name = error_name_list[1..][i]; | |
| 329 | const str_init = self.context.constString(name.ptr, @intCast(c_uint, name.len), .False); | |
| 330 | const str_global = self.llvm_module.addGlobal(str_init.typeOf(), ""); | |
| 331 | str_global.setInitializer(str_init); | |
| 332 | str_global.setLinkage(.Private); | |
| 333 | str_global.setGlobalConstant(.True); | |
| 334 | str_global.setUnnamedAddr(.True); | |
| 335 | str_global.setAlignment(1); | |
| 336 | ||
| 337 | const slice_fields = [_]*const llvm.Value{ | |
| 338 | str_global.constBitCast(llvm_ptr_ty), | |
| 339 | llvm_usize_ty.constInt(name.len, .False), | |
| 340 | }; | |
| 341 | llvm_error.* = llvm_slice_ty.constNamedStruct(&slice_fields, slice_fields.len); | |
| 342 | } | |
| 343 | ||
| 344 | const error_name_table_init = llvm_slice_ty.constArray(llvm_errors.ptr, @intCast(c_uint, error_name_list.len)); | |
| 345 | ||
| 346 | const error_name_table_global = self.llvm_module.addGlobal(error_name_table_init.typeOf(), ""); | |
| 347 | error_name_table_global.setInitializer(error_name_table_init); | |
| 348 | error_name_table_global.setLinkage(.Private); | |
| 349 | error_name_table_global.setGlobalConstant(.True); | |
| 350 | error_name_table_global.setUnnamedAddr(.True); | |
| 351 | error_name_table_global.setAlignment(slice_alignment); // TODO: Dont hardcode | |
| 352 | ||
| 353 | const error_name_table_ptr = error_name_table_global.constBitCast(llvm_slice_ty.pointerType(0)); // TODO: Address space | |
| 354 | error_name_table_ptr_global.setInitializer(error_name_table_ptr); | |
| 355 | } | |
| 356 | ||
| 301 | 357 | pub fn flushModule(self: *Object, comp: *Compilation) !void { |
| 358 | try self.genErrorNameTable(comp); | |
| 302 | 359 | if (comp.verbose_llvm_ir) { |
| 303 | 360 | self.llvm_module.dump(); |
| 304 | 361 | } |
| ... | ... | @@ -2031,6 +2088,7 @@ pub const FuncGen = struct { |
| 2031 | 2088 | .ctz => try self.airClzCtz(inst, "cttz"), |
| 2032 | 2089 | .popcount => try self.airPopCount(inst, "ctpop"), |
| 2033 | 2090 | .tag_name => try self.airTagName(inst), |
| 2091 | .error_name => try self.airErrorName(inst), | |
| 2034 | 2092 | |
| 2035 | 2093 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 2036 | 2094 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | ... | @@ -4279,6 +4337,40 @@ pub const FuncGen = struct { |
| 4279 | 4337 | return fn_val; |
| 4280 | 4338 | } |
| 4281 | 4339 | |
| 4340 | fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 4341 | if (self.liveness.isUnused(inst)) return null; | |
| 4342 | ||
| 4343 | const un_op = self.air.instructions.items(.data)[inst].un_op; | |
| 4344 | const operand = try self.resolveInst(un_op); | |
| 4345 | ||
| 4346 | const error_name_table_ptr = try self.getErrorNameTable(); | |
| 4347 | const error_name_table = self.builder.buildLoad(error_name_table_ptr, ""); | |
| 4348 | const indices = [_]*const llvm.Value{operand}; | |
| 4349 | const error_name_ptr = self.builder.buildInBoundsGEP(error_name_table, &indices, indices.len, ""); | |
| 4350 | return self.builder.buildLoad(error_name_ptr, ""); | |
| 4351 | } | |
| 4352 | ||
| 4353 | fn getErrorNameTable(self: *FuncGen) !*const llvm.Value { | |
| 4354 | if (self.dg.object.error_name_table) |table| { | |
| 4355 | return table; | |
| 4356 | } | |
| 4357 | ||
| 4358 | const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); | |
| 4359 | const slice_alignment = slice_ty.abiAlignment(self.dg.module.getTarget()); | |
| 4360 | const llvm_slice_ty = try self.dg.llvmType(slice_ty); | |
| 4361 | const llvm_slice_ptr_ty = llvm_slice_ty.pointerType(0); // TODO: Address space | |
| 4362 | ||
| 4363 | const error_name_table_global = self.dg.object.llvm_module.addGlobal(llvm_slice_ptr_ty, "__zig_err_name_table"); | |
| 4364 | error_name_table_global.setInitializer(llvm_slice_ptr_ty.getUndef()); | |
| 4365 | error_name_table_global.setLinkage(.Private); | |
| 4366 | error_name_table_global.setGlobalConstant(.True); | |
| 4367 | error_name_table_global.setUnnamedAddr(.True); | |
| 4368 | error_name_table_global.setAlignment(slice_alignment); | |
| 4369 | ||
| 4370 | self.dg.object.error_name_table = error_name_table_global; | |
| 4371 | return error_name_table_global; | |
| 4372 | } | |
| 4373 | ||
| 4282 | 4374 | /// Assumes the optional is not pointer-like and payload has bits. |
| 4283 | 4375 | fn optIsNonNull(self: *FuncGen, opt_handle: *const llvm.Value, is_by_ref: bool) *const llvm.Value { |
| 4284 | 4376 | if (is_by_ref) { |
src/print_air.zig+1| ... | ... | @@ -156,6 +156,7 @@ const Writer = struct { |
| 156 | 156 | .ret, |
| 157 | 157 | .ret_load, |
| 158 | 158 | .tag_name, |
| 159 | .error_name, | |
| 159 | 160 | => try w.writeUnOp(s, inst), |
| 160 | 161 | |
| 161 | 162 | .breakpoint, |
test/behavior.zig+1| ... | ... | @@ -90,6 +90,7 @@ test { |
| 90 | 90 | _ = @import("behavior/bugs/9584.zig"); |
| 91 | 91 | _ = @import("behavior/cast_llvm.zig"); |
| 92 | 92 | _ = @import("behavior/enum_llvm.zig"); |
| 93 | _ = @import("behavior/error_llvm.zig"); | |
| 93 | 94 | _ = @import("behavior/eval.zig"); |
| 94 | 95 | _ = @import("behavior/floatop.zig"); |
| 95 | 96 | _ = @import("behavior/fn.zig"); |
test/behavior/error_llvm.zig created+24| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const mem = std.mem; | |
| 4 | ||
| 5 | fn gimmeItBroke() anyerror { | |
| 6 | return error.ItBroke; | |
| 7 | } | |
| 8 | ||
| 9 | test "@errorName" { | |
| 10 | try expect(mem.eql(u8, @errorName(error.AnError), "AnError")); | |
| 11 | try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); | |
| 12 | try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke")); | |
| 13 | } | |
| 14 | ||
| 15 | test "@errorName sentinel length matches slice length" { | |
| 16 | const name = testBuiltinErrorName(error.FooBar); | |
| 17 | const length: usize = 6; | |
| 18 | try expect(length == std.mem.indexOfSentinel(u8, 0, name.ptr)); | |
| 19 | try expect(length == name.len); | |
| 20 | } | |
| 21 | ||
| 22 | pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 { | |
| 23 | return @errorName(err); | |
| 24 | } |
test/behavior/error_stage1.zig-21| ... | ... | @@ -4,27 +4,6 @@ const expectError = std.testing.expectError; |
| 4 | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | |
| 7 | fn gimmeItBroke() anyerror { | |
| 8 | return error.ItBroke; | |
| 9 | } | |
| 10 | ||
| 11 | test "@errorName" { | |
| 12 | try expect(mem.eql(u8, @errorName(error.AnError), "AnError")); | |
| 13 | try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); | |
| 14 | try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke")); | |
| 15 | } | |
| 16 | ||
| 17 | test "@errorName sentinel length matches slice length" { | |
| 18 | const name = testBuiltinErrorName(error.FooBar); | |
| 19 | const length: usize = 6; | |
| 20 | try expectEqual(length, std.mem.indexOfSentinel(u8, 0, name.ptr)); | |
| 21 | try expectEqual(length, name.len); | |
| 22 | } | |
| 23 | ||
| 24 | pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 { | |
| 25 | return @errorName(err); | |
| 26 | } | |
| 27 | ||
| 28 | 7 | test "error union type " { |
| 29 | 8 | try testErrorUnionType(); |
| 30 | 9 | comptime try testErrorUnionType(); |