diff --git a/doc/docgen.zig b/doc/docgen.zig index dfda54567f..e2da1fe6cc 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -689,7 +689,10 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 { fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void { var code_progress_index: usize = 0; - const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, zig_exe)); + var env_map = try os.getEnvMap(allocator); + try env_map.set("ZIG_DEBUG_COLOR", "1"); + + const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, &env_map, zig_exe)); for (toc.nodes) |node| { switch (node) { @@ -778,12 +781,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try build_args.append("c"); try out.print(" --library c"); } - _ = exec(allocator, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile"); + _ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile"); const run_args = [][]const u8{tmp_bin_file_name}; const result = if (expected_outcome == ExpectedOutcome.Fail) blk: { - const result = try os.ChildProcess.exec(allocator, run_args, null, null, max_doc_file_size); + const result = try os.ChildProcess.exec(allocator, run_args, null, &env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code == 0) { @@ -799,7 +802,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } break :blk result; } else blk: { - break :blk exec(allocator, run_args) catch return parseError(tokenizer, code.source_token, "example crashed"); + break :blk exec(allocator, &env_map, run_args) catch return parseError(tokenizer, code.source_token, "example crashed"); }; const escaped_stderr = try escapeHtml(allocator, result.stderr); @@ -845,7 +848,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var "msvc", }); } - const result = exec(allocator, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed"); + const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed"); const escaped_stderr = try escapeHtml(allocator, result.stderr); const escaped_stdout = try escapeHtml(allocator, result.stdout); try out.print("\n{}{}\n", escaped_stderr, escaped_stdout); @@ -877,7 +880,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try out.print(" --release-small"); }, } - const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size); + const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, &env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code == 0) { @@ -923,7 +926,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var builtin.Mode.ReleaseSmall => try test_args.append("--release-small"), } - const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size); + const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, &env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code == 0) { @@ -1000,7 +1003,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } if (maybe_error_match) |error_match| { - const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, null, max_doc_file_size); + const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, &env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code == 0) { @@ -1032,7 +1035,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try out.print("\n"); } } else { - _ = exec(allocator, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile"); + _ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile"); } if (!code.is_inline) { try out.print("\n"); @@ -1045,8 +1048,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } } -fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult { - const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size); +fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u8) !os.ChildProcess.ExecResult { + const result = try os.ChildProcess.exec(allocator, args, null, env_map, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { if (exit_code != 0) { @@ -1070,8 +1073,8 @@ fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex return result; } -fn getBuiltinCode(allocator: *mem.Allocator, zig_exe: []const u8) ![]const u8 { - const result = try exec(allocator, []const []const u8{ +fn getBuiltinCode(allocator: *mem.Allocator, env_map: *std.BufMap, zig_exe: []const u8) ![]const u8 { + const result = try exec(allocator, env_map, []const []const u8{ zig_exe, "builtin", }); diff --git a/doc/langref.html.in b/doc/langref.html.in index 8eaffb64ad..16e9023f26 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6649,12 +6649,60 @@ pub fn main() void { {#header_close#} {#header_open|Invalid Error Set Cast#} -

TODO

+

At compile-time:

+ {#code_begin|test_err|error.B not a member of error set 'Set2'#} +const Set1 = error{ + A, + B, +}; +const Set2 = error{ + A, + C, +}; +comptime { + _ = @errSetCast(Set2, Set1.B); +} + {#code_end#} +

At runtime:

+ {#code_begin|exe_err#} +const Set1 = error{ + A, + B, +}; +const Set2 = error{ + A, + C, +}; +pub fn main() void { + _ = foo(Set1.B); +} +fn foo(set1: Set1) Set2 { + return @errSetCast(Set2, set1); +} + {#code_end#} {#header_close#} {#header_open|Incorrect Pointer Alignment#} -

TODO

- +

At compile-time:

+ {#code_begin|test_err|pointer address 0x1 is not aligned to 4 bytes#} +comptime { + const ptr = @intToPtr(*i32, 0x1); + const aligned = @alignCast(4, ptr); +} + {#code_end#} +

At runtime:

+ {#code_begin|exe_err#} +pub fn main() !void { + var array align(4) = []u32{ 0x11111111, 0x11111111 }; + const bytes = @sliceToBytes(array[0..]); + if (foo(bytes) != 0x11111111) return error.Wrong; +} +fn foo(bytes: []u8) u32 { + const slice4 = bytes[1..5]; + const int_slice = @bytesToSlice(u32, @alignCast(4, slice4)); + return int_slice[0]; +} + {#code_end#} {#header_close#} {#header_open|Wrong Union Field Access#}

TODO

diff --git a/src/ir.cpp b/src/ir.cpp index 3fc8306339..2dc6ddad2c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19370,6 +19370,15 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 if (!val) return ira->codegen->invalid_instruction; + if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0) + { + ir_add_error(ira, target, + buf_sprintf("pointer address 0x%" ZIG_PRI_x64 " is not aligned to %" PRIu32 " bytes", + val->data.x_ptr.data.hard_coded_addr.addr, align_bytes)); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type); copy_const_val(&result->value, val, false); result->value.type = result_type; @@ -19796,6 +19805,12 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr return ira->codegen->builtin_types.entry_invalid; } + if (!type_has_bits(target->value.type)) { + ir_add_error(ira, target, + buf_sprintf("pointer to size 0 type has no address")); + return ira->codegen->builtin_types.entry_invalid; + } + if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) diff --git a/std/build.zig b/std/build.zig index 99de9b5197..24fa85383a 100644 --- a/std/build.zig +++ b/std/build.zig @@ -814,6 +814,7 @@ pub const LibExeObjStep = struct { out_h_filename: []const u8, assembly_files: ArrayList([]const u8), packages: ArrayList(Pkg), + build_options_contents: std.Buffer, // C only stuff source_files: ArrayList([]const u8), @@ -905,6 +906,7 @@ pub const LibExeObjStep = struct { .lib_paths = ArrayList([]const u8).init(builder.allocator), .object_src = undefined, .disable_libc = true, + .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, }; self.computeOutFileNames(); return self; @@ -945,6 +947,7 @@ pub const LibExeObjStep = struct { .out_h_filename = undefined, .assembly_files = undefined, .packages = undefined, + .build_options_contents = undefined, }; self.computeOutFileNames(); return self; @@ -1096,6 +1099,12 @@ pub const LibExeObjStep = struct { self.include_dirs.append(self.builder.cache_root) catch unreachable; } + pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void { + assert(self.is_zig); + const out = &std.io.BufferOutStream.init(&self.build_options_contents).stream; + out.print("pub const {} = {};\n", name, value) catch unreachable; + } + pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void { self.include_dirs.append(path) catch unreachable; } @@ -1155,6 +1164,15 @@ pub const LibExeObjStep = struct { zig_args.append(builder.pathFromRoot(root_src)) catch unreachable; } + if (self.build_options_contents.len() > 0) { + const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name)); + try std.io.writeFile(builder.allocator, build_options_file, self.build_options_contents.toSliceConst()); + try zig_args.append("--pkg-begin"); + try zig_args.append("build_options"); + try zig_args.append(builder.pathFromRoot(build_options_file)); + try zig_args.append("--pkg-end"); + } + for (self.object_files.toSliceConst()) |object_file| { zig_args.append("--object") catch unreachable; zig_args.append(builder.pathFromRoot(object_file)) catch unreachable; diff --git a/std/debug/index.zig b/std/debug/index.zig index a5e1c313f0..3070e0b40b 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -10,6 +10,7 @@ const ArrayList = std.ArrayList; const builtin = @import("builtin"); pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; +pub const failing_allocator = FailingAllocator.init(global_allocator, 0); pub const runtime_safety = switch (builtin.mode) { builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true, @@ -49,6 +50,12 @@ pub fn getSelfDebugInfo() !*ElfStackTrace { } } +fn wantTtyColor() bool { + var bytes: [128]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); +} + /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. pub fn dumpCurrentStackTrace(start_addr: ?usize) void { const stderr = getStderrStream() catch return; @@ -56,7 +63,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; return; }; - writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty(), start_addr) catch |err| { + writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, wantTtyColor(), start_addr) catch |err| { stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; return; }; @@ -69,7 +76,7 @@ pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; return; }; - writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty()) catch |err| { + writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| { stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; return; }; @@ -161,7 +168,7 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; }) { const return_address = stack_trace.instruction_addresses[frame_index]; - try printSourceAtAddress(debug_info, out_stream, return_address); + try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); } } @@ -194,13 +201,11 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_ } }, } - try printSourceAtAddress(debug_info, out_stream, return_address); + try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); } } -fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize) !void { - const ptr_hex = "0x{x}"; - +fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void { switch (builtin.os) { builtin.Os.windows => return error.UnsupportedDebugInfo, builtin.Os.macosx => { @@ -214,36 +219,58 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us .address = address, }; const symbol = debug_info.symbol_table.search(address) orelse &unknown; - try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address); + try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ "0x{x}" ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address); }, else => { const compile_unit = findCompileUnit(debug_info, address) catch { - try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", address); + if (tty_color) { + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n ???\n\n", address); + } else { + try out_stream.print("???:?:?: 0x{x} in ??? (???)\n ???\n\n", address); + } return; }; const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { defer line_info.deinit(); - try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", line_info.file_name, line_info.line, line_info.column, address, compile_unit_name); - if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { - if (line_info.column == 0) { - try out_stream.write("\n"); - } else { - { - var col_i: usize = 1; - while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); + if (tty_color) { + try out_stream.print( + WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n", + line_info.file_name, + line_info.line, + line_info.column, + address, + compile_unit_name, + ); + if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { + if (line_info.column == 0) { + try out_stream.write("\n"); + } else { + { + var col_i: usize = 1; + while (col_i < line_info.column) : (col_i += 1) { + try out_stream.writeByte(' '); + } } + try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); } - try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); + } else |err| switch (err) { + error.EndOfFile => {}, + else => return err, } - } else |err| switch (err) { - error.EndOfFile => {}, - else => return err, + } else { + try out_stream.print( + "{}:{}:{}: 0x{x} in ??? ({})\n", + line_info.file_name, + line_info.line, + line_info.column, + address, + compile_unit_name, + ); } } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { - try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); + try out_stream.print("0x{x} in ??? ({})\n", address, compile_unit_name); }, else => return err, } diff --git a/std/hash_map.zig b/std/hash_map.zig index 3bd03d4f28..cebd5272c0 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -259,14 +259,14 @@ test "basic hash map usage" { var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator); defer map.deinit(); - assert((map.put(1, 11) catch unreachable) == null); - assert((map.put(2, 22) catch unreachable) == null); - assert((map.put(3, 33) catch unreachable) == null); - assert((map.put(4, 44) catch unreachable) == null); - assert((map.put(5, 55) catch unreachable) == null); + assert((try map.put(1, 11)) == null); + assert((try map.put(2, 22)) == null); + assert((try map.put(3, 33)) == null); + assert((try map.put(4, 44)) == null); + assert((try map.put(5, 55)) == null); - assert((map.put(5, 66) catch unreachable).? == 55); - assert((map.put(5, 55) catch unreachable).? == 66); + assert((try map.put(5, 66)).? == 55); + assert((try map.put(5, 55)).? == 66); assert(map.contains(2)); assert(map.get(2).?.value == 22); @@ -282,9 +282,9 @@ test "iterator hash map" { var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator); defer reset_map.deinit(); - assert((reset_map.put(1, 11) catch unreachable) == null); - assert((reset_map.put(2, 22) catch unreachable) == null); - assert((reset_map.put(3, 33) catch unreachable) == null); + assert((try reset_map.put(1, 11)) == null); + assert((try reset_map.put(2, 22)) == null); + assert((try reset_map.put(3, 33)) == null); var keys = []i32{ 1, diff --git a/std/os/index.zig b/std/os/index.zig index 896d6b3df8..79b2d2ff53 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -553,8 +553,13 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { return null; } +pub const GetEnvVarOwnedError = error{ + OutOfMemory, + EnvironmentVariableNotFound, +}; + /// Caller must free returned memory. -pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 { +pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { if (is_windows) { const key_with_null = try cstr.addNullByte(allocator, key); defer allocator.free(key_with_null); @@ -563,14 +568,17 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 { errdefer allocator.free(buf); while (true) { - const windows_buf_len = try math.cast(windows.DWORD, buf.len); + const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; const result = windows.GetEnvironmentVariableA(key_with_null.ptr, buf.ptr, windows_buf_len); if (result == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound, - else => unexpectedErrorWindows(err), + else => { + _ = unexpectedErrorWindows(err); + return error.EnvironmentVariableNotFound; + }, }; } diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index e4f04df6d0..2f073b3e98 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -122,10 +122,13 @@ pub fn main() !void { return usageAndErr(&builder, true, try stderr_stream); builder.make(targets.toSliceConst()) catch |err| { - if (err == error.InvalidStepName) { - return usageAndErr(&builder, true, try stderr_stream); + switch (err) { + error.InvalidStepName => { + return usageAndErr(&builder, true, try stderr_stream); + }, + error.UncleanExit => os.exit(1), + else => return err, } - return err; }; } diff --git a/std/zig/bench.zig b/std/zig/bench.zig index 59392889a6..630f6b2233 100644 --- a/std/zig/bench.zig +++ b/std/zig/bench.zig @@ -19,20 +19,18 @@ pub fn main() !void { } const end = timer.read(); memory_used /= iterations; - const elapsed_s = f64(end - start) / std.os.time.ns_per_s; - const bytes_per_sec = f64(source.len * iterations) / elapsed_s; + const elapsed_s = @intToFloat(f64, end - start) / std.os.time.ns_per_s; + const bytes_per_sec = @intToFloat(f64, source.len * iterations) / elapsed_s; const mb_per_sec = bytes_per_sec / (1024 * 1024); var stdout_file = try std.io.getStdOut(); - const stdout = *std.io.FileOutStream.init(*stdout_file).stream; - try stdout.print("{.3} MB/s, {} KB used \n", mb_per_sec, memory_used / 1024); + const stdout = &std.io.FileOutStream.init(&stdout_file).stream; + try stdout.print("{.3} MiB/s, {} KiB used \n", mb_per_sec, memory_used / 1024); } fn testOnce() usize { var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var allocator = *fixed_buf_alloc.allocator; - var tokenizer = Tokenizer.init(source); - var parser = Parser.init(*tokenizer, allocator, "(memory buffer)"); - _ = parser.parse() catch @panic("parse failure"); + var allocator = &fixed_buf_alloc.allocator; + _ = std.zig.parse(allocator, source) catch @panic("parse failure"); return fixed_buf_alloc.end_index; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9071f0ad7e..1b76c01564 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,25 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "bad @alignCast at comptime", + \\comptime { + \\ const ptr = @intToPtr(*i32, 0x1); + \\ const aligned = @alignCast(4, ptr); + \\} + , + ".tmp_source.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes", + ); + + cases.add( + "@ptrToInt on *void", + \\export fn entry() bool { + \\ return @ptrToInt(&{}) == @ptrToInt(&{}); + \\} + , + ".tmp_source.zig:2:23: error: pointer to size 0 type has no address", + ); + cases.add( "@popCount - non-integer", \\export fn entry(x: f32) u32 {