| author | |
| committer | |
| log | 6926e6e705b7d1c0a69ec20b6b0e1ea280f036d1 |
| tree | bccebf1e310cc17a63318f1906f996143fc2c0e7 |
| parent | 87d5db057b53fc643ac0c316653a46ada54b3c91 |
| parent | df10e998ee4a935f49943fb5c0ef134f336c6ee3 |
10 files changed, 208 insertions(+), 27 deletions(-)
lib/std/fmt.zig+7| ... | @@ -544,6 +544,13 @@ pub fn formatType( | ... | @@ -544,6 +544,13 @@ pub fn formatType( |
| 544 | return formatText(value, actual_fmt, options, writer); | 544 | return formatText(value, actual_fmt, options, writer); |
| 545 | } | 545 | } |
| 546 | } | 546 | } |
| 547 | if (comptime std.meta.trait.isZigString(info.child)) { | ||
| 548 | for (value) |item, i| { | ||
| 549 | if (i != 0) try formatText(", ", actual_fmt, options, writer); | ||
| 550 | try formatText(item, actual_fmt, options, writer); | ||
| 551 | } | ||
| 552 | return; | ||
| 553 | } | ||
| 547 | @compileError("Unknown format string: '" ++ actual_fmt ++ "' for type '" ++ @typeName(T) ++ "'"); | 554 | @compileError("Unknown format string: '" ++ actual_fmt ++ "' for type '" ++ @typeName(T) ++ "'"); |
| 548 | }, | 555 | }, |
| 549 | .Enum, .Union, .Struct => { | 556 | .Enum, .Union, .Struct => { |
lib/std/rand.zig+30| ... | @@ -47,6 +47,19 @@ pub const Random = struct { | ... | @@ -47,6 +47,19 @@ pub const Random = struct { |
| 47 | return r.int(u1) != 0; | 47 | return r.int(u1) != 0; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | /// Returns a random value from an enum, evenly distributed. | ||
| 51 | pub fn enumValue(r: *Random, comptime EnumType: type) EnumType { | ||
| 52 | if (comptime !std.meta.trait.is(.Enum)(EnumType)) { | ||
| 53 | @compileError("Random.enumValue requires an enum type, not a " ++ @typeName(EnumType)); | ||
| 54 | } | ||
| 55 | |||
| 56 | // We won't use int -> enum casting because enum elements can have | ||
| 57 | // arbitrary values. Instead we'll randomly pick one of the type's values. | ||
| 58 | const values = std.enums.values(EnumType); | ||
| 59 | const index = r.uintLessThan(usize, values.len); | ||
| 60 | return values[index]; | ||
| 61 | } | ||
| 62 | |||
| 50 | /// Returns a random int `i` such that `0 <= i <= maxInt(T)`. | 63 | /// Returns a random int `i` such that `0 <= i <= maxInt(T)`. |
| 51 | /// `i` is evenly distributed. | 64 | /// `i` is evenly distributed. |
| 52 | pub fn int(r: *Random, comptime T: type) T { | 65 | pub fn int(r: *Random, comptime T: type) T { |
| ... | @@ -377,6 +390,23 @@ fn testRandomBoolean() !void { | ... | @@ -377,6 +390,23 @@ fn testRandomBoolean() !void { |
| 377 | try expect(r.random.boolean() == true); | 390 | try expect(r.random.boolean() == true); |
| 378 | } | 391 | } |
| 379 | 392 | ||
| 393 | test "Random enum" { | ||
| 394 | try testRandomEnumValue(); | ||
| 395 | comptime try testRandomEnumValue(); | ||
| 396 | } | ||
| 397 | fn testRandomEnumValue() !void { | ||
| 398 | const TestEnum = enum { | ||
| 399 | First, | ||
| 400 | Second, | ||
| 401 | Third, | ||
| 402 | }; | ||
| 403 | var r = SequentialPrng.init(); | ||
| 404 | r.next_value = 0; | ||
| 405 | try expect(r.random.enumValue(TestEnum) == TestEnum.First); | ||
| 406 | try expect(r.random.enumValue(TestEnum) == TestEnum.First); | ||
| 407 | try expect(r.random.enumValue(TestEnum) == TestEnum.First); | ||
| 408 | } | ||
| 409 | |||
| 380 | test "Random intLessThan" { | 410 | test "Random intLessThan" { |
| 381 | @setEvalBranchQuota(10000); | 411 | @setEvalBranchQuota(10000); |
| 382 | try testRandomIntLessThan(); | 412 | try testRandomIntLessThan(); |
src/Sema.zig+1-1| ... | @@ -8323,7 +8323,7 @@ fn coerceNum( | ... | @@ -8323,7 +8323,7 @@ fn coerceNum( |
| 8323 | return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{}); | 8323 | return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{}); |
| 8324 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | 8324 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { |
| 8325 | if (!val.intFitsInType(dest_type, target)) { | 8325 | if (!val.intFitsInType(dest_type, target)) { |
| 8326 | return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ inst_ty, val }); | 8326 | return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val }); |
| 8327 | } | 8327 | } |
| 8328 | return try sema.addConstant(dest_type, val); | 8328 | return try sema.addConstant(dest_type, val); |
| 8329 | } | 8329 | } |
src/codegen.zig+2| ... | @@ -1247,6 +1247,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1247,6 +1247,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1247 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1247 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1248 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | 1248 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
| 1249 | .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and), | 1249 | .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and), |
| 1250 | .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs), | ||
| 1250 | else => return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}), | 1251 | else => return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}), |
| 1251 | }; | 1252 | }; |
| 1252 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1253 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| ... | @@ -1256,6 +1257,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1256,6 +1257,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1256 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1257 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1257 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | 1258 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
| 1258 | .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or), | 1259 | .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or), |
| 1260 | .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs), | ||
| 1259 | else => return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}), | 1261 | else => return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}), |
| 1260 | }; | 1262 | }; |
| 1261 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1263 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
src/link/MachO.zig+23-14| ... | @@ -54,6 +54,11 @@ d_sym: ?DebugSymbols = null, | ... | @@ -54,6 +54,11 @@ d_sym: ?DebugSymbols = null, |
| 54 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. | 54 | /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. |
| 55 | page_size: u16, | 55 | page_size: u16, |
| 56 | 56 | ||
| 57 | /// TODO Should we figure out embedding code signatures for other Apple platforms as part of the linker? | ||
| 58 | /// Or should this be a separate tool? | ||
| 59 | /// https://github.com/ziglang/zig/issues/9567 | ||
| 60 | requires_adhoc_codesig: bool, | ||
| 61 | |||
| 57 | /// We commit 0x1000 = 4096 bytes of space to the header and | 62 | /// We commit 0x1000 = 4096 bytes of space to the header and |
| 58 | /// the table of load commands. This should be plenty for any | 63 | /// the table of load commands. This should be plenty for any |
| 59 | /// potential future extensions. | 64 | /// potential future extensions. |
| ... | @@ -391,6 +396,13 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -391,6 +396,13 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 391 | 396 | ||
| 392 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { | 397 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 393 | const self = try gpa.create(MachO); | 398 | const self = try gpa.create(MachO); |
| 399 | const cpu_arch = options.target.cpu.arch; | ||
| 400 | const os_tag = options.target.os.tag; | ||
| 401 | const abi = options.target.abi; | ||
| 402 | const page_size: u16 = if (cpu_arch == .aarch64) 0x4000 else 0x1000; | ||
| 403 | // Adhoc code signature is required when targeting aarch64-macos either directly or indirectly via the simulator | ||
| 404 | // ABI such as aarch64-ios-simulator, etc. | ||
| 405 | const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator); | ||
| 394 | 406 | ||
| 395 | self.* = .{ | 407 | self.* = .{ |
| 396 | .base = .{ | 408 | .base = .{ |
| ... | @@ -399,7 +411,8 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { | ... | @@ -399,7 +411,8 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { |
| 399 | .allocator = gpa, | 411 | .allocator = gpa, |
| 400 | .file = null, | 412 | .file = null, |
| 401 | }, | 413 | }, |
| 402 | .page_size = if (options.target.cpu.arch == .aarch64) 0x4000 else 0x1000, | 414 | .page_size = page_size, |
| 415 | .requires_adhoc_codesig = requires_adhoc_codesig, | ||
| 403 | }; | 416 | }; |
| 404 | 417 | ||
| 405 | return self; | 418 | return self; |
| ... | @@ -433,7 +446,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -433,7 +446,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 433 | defer tracy.end(); | 446 | defer tracy.end(); |
| 434 | 447 | ||
| 435 | const output_mode = self.base.options.output_mode; | 448 | const output_mode = self.base.options.output_mode; |
| 436 | const target = self.base.options.target; | ||
| 437 | 449 | ||
| 438 | switch (output_mode) { | 450 | switch (output_mode) { |
| 439 | .Exe => { | 451 | .Exe => { |
| ... | @@ -459,7 +471,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -459,7 +471,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 459 | try ds.flushModule(self.base.allocator, self.base.options); | 471 | try ds.flushModule(self.base.allocator, self.base.options); |
| 460 | } | 472 | } |
| 461 | 473 | ||
| 462 | if (target.cpu.arch == .aarch64) { | 474 | if (self.requires_adhoc_codesig) { |
| 463 | // Preallocate space for the code signature. | 475 | // Preallocate space for the code signature. |
| 464 | // We need to do this at this stage so that we have the load commands with proper values | 476 | // We need to do this at this stage so that we have the load commands with proper values |
| 465 | // written out to the file. | 477 | // written out to the file. |
| ... | @@ -492,11 +504,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -492,11 +504,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 492 | assert(!self.strtab_dirty); | 504 | assert(!self.strtab_dirty); |
| 493 | assert(!self.strtab_needs_relocation); | 505 | assert(!self.strtab_needs_relocation); |
| 494 | 506 | ||
| 495 | if (target.cpu.arch == .aarch64) { | 507 | if (self.requires_adhoc_codesig) { |
| 496 | switch (output_mode) { | 508 | try self.writeCodeSignature(); // code signing always comes last |
| 497 | .Exe, .Lib => try self.writeCodeSignature(), // code signing always comes last | ||
| 498 | else => {}, | ||
| 499 | } | ||
| 500 | } | 509 | } |
| 501 | } | 510 | } |
| 502 | 511 | ||
| ... | @@ -2841,7 +2850,7 @@ fn addDataInCodeLC(self: *MachO) !void { | ... | @@ -2841,7 +2850,7 @@ fn addDataInCodeLC(self: *MachO) !void { |
| 2841 | } | 2850 | } |
| 2842 | 2851 | ||
| 2843 | fn addCodeSignatureLC(self: *MachO) !void { | 2852 | fn addCodeSignatureLC(self: *MachO) !void { |
| 2844 | if (self.code_signature_cmd_index == null and self.base.options.target.cpu.arch == .aarch64) { | 2853 | if (self.code_signature_cmd_index == null and self.requires_adhoc_codesig) { |
| 2845 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | 2854 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 2846 | try self.load_commands.append(self.base.allocator, .{ | 2855 | try self.load_commands.append(self.base.allocator, .{ |
| 2847 | .LinkeditData = .{ | 2856 | .LinkeditData = .{ |
| ... | @@ -2935,14 +2944,14 @@ fn flushZld(self: *MachO) !void { | ... | @@ -2935,14 +2944,14 @@ fn flushZld(self: *MachO) !void { |
| 2935 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); | 2944 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); |
| 2936 | } | 2945 | } |
| 2937 | 2946 | ||
| 2938 | if (self.base.options.target.cpu.arch == .aarch64) { | 2947 | if (self.requires_adhoc_codesig) { |
| 2939 | try self.writeCodeSignaturePadding(); | 2948 | try self.writeCodeSignaturePadding(); |
| 2940 | } | 2949 | } |
| 2941 | 2950 | ||
| 2942 | try self.writeLoadCommands(); | 2951 | try self.writeLoadCommands(); |
| 2943 | try self.writeHeader(); | 2952 | try self.writeHeader(); |
| 2944 | 2953 | ||
| 2945 | if (self.base.options.target.cpu.arch == .aarch64) { | 2954 | if (self.requires_adhoc_codesig) { |
| 2946 | try self.writeCodeSignature(); | 2955 | try self.writeCodeSignature(); |
| 2947 | } | 2956 | } |
| 2948 | } | 2957 | } |
| ... | @@ -4454,7 +4463,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -4454,7 +4463,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 4454 | try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd }); | 4463 | try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd }); |
| 4455 | self.load_commands_dirty = true; | 4464 | self.load_commands_dirty = true; |
| 4456 | } | 4465 | } |
| 4457 | if (self.code_signature_cmd_index == null) { | 4466 | if (self.code_signature_cmd_index == null and self.requires_adhoc_codesig) { |
| 4458 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | 4467 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 4459 | try self.load_commands.append(self.base.allocator, .{ | 4468 | try self.load_commands.append(self.base.allocator, .{ |
| 4460 | .LinkeditData = .{ | 4469 | .LinkeditData = .{ |
| ... | @@ -5719,8 +5728,8 @@ fn writeStringTableZld(self: *MachO) !void { | ... | @@ -5719,8 +5728,8 @@ fn writeStringTableZld(self: *MachO) !void { |
| 5719 | 5728 | ||
| 5720 | try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); | 5729 | try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); |
| 5721 | 5730 | ||
| 5722 | if (symtab.strsize > self.strtab.items.len and self.base.options.target.cpu.arch == .x86_64) { | 5731 | if (symtab.strsize > self.strtab.items.len) { |
| 5723 | // This is the last section, so we need to pad it out. | 5732 | // This is potentially the last section, so we need to pad it out. |
| 5724 | try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); | 5733 | try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); |
| 5725 | } | 5734 | } |
| 5726 | } | 5735 | } |
src/stage1/codegen.cpp+8-5| ... | @@ -3831,10 +3831,14 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, Stage1Air *executable, | ... | @@ -3831,10 +3831,14 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, Stage1Air *executable, |
| 3831 | LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); | 3831 | LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); |
| 3832 | LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); | 3832 | LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); |
| 3833 | 3833 | ||
| 3834 | LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); | ||
| 3835 | LLVMValueRef mask = LLVMConstAllOnes(LLVMIntType(size_in_bits)); | ||
| 3836 | mask = LLVMConstZExt(mask, LLVMTypeOf(containing_int)); | ||
| 3837 | LLVMValueRef masked_value = LLVMBuildAnd(g->builder, shifted_value, mask, ""); | ||
| 3838 | |||
| 3834 | if (handle_is_ptr(g, child_type)) { | 3839 | if (handle_is_ptr(g, child_type)) { |
| 3835 | LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); | 3840 | LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); |
| 3836 | LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); | 3841 | LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, masked_value, same_size_int, ""); |
| 3837 | LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); | ||
| 3838 | LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, result_loc, | 3842 | LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, result_loc, |
| 3839 | LLVMPointerType(same_size_int, 0), ""); | 3843 | LLVMPointerType(same_size_int, 0), ""); |
| 3840 | LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr); | 3844 | LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr); |
| ... | @@ -3842,12 +3846,11 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, Stage1Air *executable, | ... | @@ -3842,12 +3846,11 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, Stage1Air *executable, |
| 3842 | } | 3846 | } |
| 3843 | 3847 | ||
| 3844 | if (child_type->id == ZigTypeIdFloat) { | 3848 | if (child_type->id == ZigTypeIdFloat) { |
| 3845 | LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); | 3849 | LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, masked_value, same_size_int, ""); |
| 3846 | LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); | ||
| 3847 | return LLVMBuildBitCast(g->builder, truncated_int, get_llvm_type(g, child_type), ""); | 3850 | return LLVMBuildBitCast(g->builder, truncated_int, get_llvm_type(g, child_type), ""); |
| 3848 | } | 3851 | } |
| 3849 | 3852 | ||
| 3850 | return LLVMBuildTrunc(g->builder, shifted_value, get_llvm_type(g, child_type), ""); | 3853 | return LLVMBuildTrunc(g->builder, masked_value, get_llvm_type(g, child_type), ""); |
| 3851 | } | 3854 | } |
| 3852 | 3855 | ||
| 3853 | static bool value_is_all_undef_array(CodeGen *g, ZigValue *const_val, size_t len) { | 3856 | static bool value_is_all_undef_array(CodeGen *g, ZigValue *const_val, size_t len) { |
src/type.zig+16-7| ... | @@ -534,15 +534,24 @@ pub const Type = extern union { | ... | @@ -534,15 +534,24 @@ pub const Type = extern union { |
| 534 | return a_data.error_set.eql(b_data.error_set) and a_data.payload.eql(b_data.payload); | 534 | return a_data.error_set.eql(b_data.error_set) and a_data.payload.eql(b_data.payload); |
| 535 | }, | 535 | }, |
| 536 | .ErrorSet => { | 536 | .ErrorSet => { |
| 537 | const a_is_anyerror = a.tag() == .anyerror; | 537 | if (a.tag() == .anyerror and b.tag() == .anyerror) { |
| 538 | const b_is_anyerror = b.tag() == .anyerror; | 538 | return true; |
| 539 | } | ||
| 539 | 540 | ||
| 540 | if (a_is_anyerror and b_is_anyerror) return true; | 541 | if (a.tag() == .error_set and b.tag() == .error_set) { |
| 541 | if (a_is_anyerror or b_is_anyerror) return false; | 542 | return a.castTag(.error_set).?.data.owner_decl == b.castTag(.error_set).?.data.owner_decl; |
| 543 | } | ||
| 542 | 544 | ||
| 543 | std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ | 545 | if (a.tag() == .error_set_inferred and b.tag() == .error_set_inferred) { |
| 544 | a.tag(), b.tag(), | 546 | return a.castTag(.error_set_inferred).?.data.func == b.castTag(.error_set_inferred).?.data.func; |
| 545 | }); | 547 | } |
| 548 | |||
| 549 | if (a.tag() == .error_set_single and b.tag() == .error_set_single) { | ||
| 550 | const a_data = a.castTag(.error_set_single).?.data; | ||
| 551 | const b_data = b.castTag(.error_set_single).?.data; | ||
| 552 | return std.mem.eql(u8, a_data, b_data); | ||
| 553 | } | ||
| 554 | return false; | ||
| 546 | }, | 555 | }, |
| 547 | .Opaque, | 556 | .Opaque, |
| 548 | .Float, | 557 | .Float, |
test/behavior.zig+1| ... | @@ -71,6 +71,7 @@ test { | ... | @@ -71,6 +71,7 @@ test { |
| 71 | _ = @import("behavior/bugs/7047.zig"); | 71 | _ = @import("behavior/bugs/7047.zig"); |
| 72 | _ = @import("behavior/bugs/7003.zig"); | 72 | _ = @import("behavior/bugs/7003.zig"); |
| 73 | _ = @import("behavior/bugs/7250.zig"); | 73 | _ = @import("behavior/bugs/7250.zig"); |
| 74 | _ = @import("behavior/bugs/9584.zig"); | ||
| 74 | _ = @import("behavior/bugs/394.zig"); | 75 | _ = @import("behavior/bugs/394.zig"); |
| 75 | _ = @import("behavior/bugs/421.zig"); | 76 | _ = @import("behavior/bugs/421.zig"); |
| 76 | _ = @import("behavior/bugs/529.zig"); | 77 | _ = @import("behavior/bugs/529.zig"); |
test/behavior/bugs/9584.zig created+60| ... | @@ -0,0 +1,60 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | const A = packed struct { | ||
| 4 | a: bool, | ||
| 5 | b: bool, | ||
| 6 | c: bool, | ||
| 7 | d: bool, | ||
| 8 | |||
| 9 | e: bool, | ||
| 10 | f: bool, | ||
| 11 | g: bool, | ||
| 12 | h: bool, | ||
| 13 | }; | ||
| 14 | |||
| 15 | const X = union { | ||
| 16 | x: A, | ||
| 17 | y: u64, | ||
| 18 | }; | ||
| 19 | |||
| 20 | pub fn a( | ||
| 21 | x0: i32, | ||
| 22 | x1: i32, | ||
| 23 | x2: i32, | ||
| 24 | x3: i32, | ||
| 25 | x4: i32, | ||
| 26 | flag_a: bool, | ||
| 27 | flag_b: bool, | ||
| 28 | ) !void { | ||
| 29 | _ = x0; | ||
| 30 | _ = x1; | ||
| 31 | _ = x2; | ||
| 32 | _ = x3; | ||
| 33 | _ = x4; | ||
| 34 | _ = flag_a; | ||
| 35 | // With this bug present, `flag_b` would actually contain the value 17. | ||
| 36 | // Note: this bug only presents itself on debug mode. | ||
| 37 | try std.testing.expect(@ptrCast(*const u8, &flag_b).* == 1); | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn b(x: *X) !void { | ||
| 41 | try a(0, 1, 2, 3, 4, x.x.a, x.x.b); | ||
| 42 | } | ||
| 43 | |||
| 44 | test "bug 9584" { | ||
| 45 | var flags = A{ | ||
| 46 | .a = false, | ||
| 47 | .b = true, | ||
| 48 | .c = false, | ||
| 49 | .d = false, | ||
| 50 | |||
| 51 | .e = false, | ||
| 52 | .f = true, | ||
| 53 | .g = false, | ||
| 54 | .h = false, | ||
| 55 | }; | ||
| 56 | var x = X{ | ||
| 57 | .x = flags, | ||
| 58 | }; | ||
| 59 | try b(&x); | ||
| 60 | } | ||
test/cases.zig+60| ... | @@ -1535,6 +1535,48 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1535,6 +1535,48 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1535 | \\} | 1535 | \\} |
| 1536 | , ""); | 1536 | , ""); |
| 1537 | } | 1537 | } |
| 1538 | { | ||
| 1539 | var case = ctx.exe("runtime bitwise and", linux_x64); | ||
| 1540 | |||
| 1541 | case.addCompareOutput( | ||
| 1542 | \\pub fn main() void { | ||
| 1543 | \\ var i: u32 = 10; | ||
| 1544 | \\ var j: u32 = 11; | ||
| 1545 | \\ assert(i & 1 == 0); | ||
| 1546 | \\ assert(j & 1 == 1); | ||
| 1547 | \\ var m1: u32 = 0b1111; | ||
| 1548 | \\ var m2: u32 = 0b0000; | ||
| 1549 | \\ assert(m1 & 0b1010 == 0b1010); | ||
| 1550 | \\ assert(m2 & 0b1010 == 0b0000); | ||
| 1551 | \\} | ||
| 1552 | \\fn assert(b: bool) void { | ||
| 1553 | \\ if (!b) unreachable; | ||
| 1554 | \\} | ||
| 1555 | , | ||
| 1556 | "", | ||
| 1557 | ); | ||
| 1558 | } | ||
| 1559 | { | ||
| 1560 | var case = ctx.exe("runtime bitwise or", linux_x64); | ||
| 1561 | |||
| 1562 | case.addCompareOutput( | ||
| 1563 | \\pub fn main() void { | ||
| 1564 | \\ var i: u32 = 10; | ||
| 1565 | \\ var j: u32 = 11; | ||
| 1566 | \\ assert(i | 1 == 11); | ||
| 1567 | \\ assert(j | 1 == 11); | ||
| 1568 | \\ var m1: u32 = 0b1111; | ||
| 1569 | \\ var m2: u32 = 0b0000; | ||
| 1570 | \\ assert(m1 | 0b1010 == 0b1111); | ||
| 1571 | \\ assert(m2 | 0b1010 == 0b1010); | ||
| 1572 | \\} | ||
| 1573 | \\fn assert(b: bool) void { | ||
| 1574 | \\ if (!b) unreachable; | ||
| 1575 | \\} | ||
| 1576 | , | ||
| 1577 | "", | ||
| 1578 | ); | ||
| 1579 | } | ||
| 1538 | { | 1580 | { |
| 1539 | var case = ctx.exe("merge error sets", linux_x64); | 1581 | var case = ctx.exe("merge error sets", linux_x64); |
| 1540 | 1582 | ||
| ... | @@ -1567,6 +1609,24 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1567,6 +1609,24 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1567 | ":2:20: note: '||' merges error sets; 'or' performs boolean OR", | 1609 | ":2:20: note: '||' merges error sets; 'or' performs boolean OR", |
| 1568 | }); | 1610 | }); |
| 1569 | } | 1611 | } |
| 1612 | { | ||
| 1613 | var case = ctx.exe("error set equality", linux_x64); | ||
| 1614 | |||
| 1615 | case.addCompareOutput( | ||
| 1616 | \\pub fn main() void { | ||
| 1617 | \\ assert(@TypeOf(error.Foo) == @TypeOf(error.Foo)); | ||
| 1618 | \\ assert(@TypeOf(error.Bar) != @TypeOf(error.Foo)); | ||
| 1619 | \\ assert(anyerror == anyerror); | ||
| 1620 | \\ assert(error{Foo} != error{Foo}); | ||
| 1621 | \\ // TODO put inferred error sets here when @typeInfo works | ||
| 1622 | \\} | ||
| 1623 | \\fn assert(b: bool) void { | ||
| 1624 | \\ if (!b) unreachable; | ||
| 1625 | \\} | ||
| 1626 | , | ||
| 1627 | "", | ||
| 1628 | ); | ||
| 1629 | } | ||
| 1570 | { | 1630 | { |
| 1571 | var case = ctx.exe("inline assembly", linux_x64); | 1631 | var case = ctx.exe("inline assembly", linux_x64); |
| 1572 | 1632 |