| author | |
| committer | |
| log | 01546e68cd0d82ef78498a10649e6bc2937680da |
| tree | 19eb970a346b362eac55f7e73eb7ee3bfbf90cd7 |
| parent | 3a4a7d2ca378862dd6b31678a143315a9e306f8c |
| signature |
11 files changed, 127 insertions(+), 114 deletions(-)
doc/langref.html.in+1-1| ... | @@ -2595,7 +2595,7 @@ or | ... | @@ -2595,7 +2595,7 @@ or |
| 2595 | 2595 | ||
| 2596 | {#header_close#} | 2596 | {#header_close#} |
| 2597 | 2597 | ||
| 2598 | {#header_open|Switching on errors#} | 2598 | {#header_open|Switching on Errors#} |
| 2599 | <p> | 2599 | <p> |
| 2600 | When switching on errors, some special cases are allowed to simplify generic programming patterns: | 2600 | When switching on errors, some special cases are allowed to simplify generic programming patterns: |
| 2601 | </p> | 2601 | </p> |
doc/langref/test_switch_on_errors.zig+4-1| ... | @@ -12,7 +12,10 @@ test "unreachable else prong" { | ... | @@ -12,7 +12,10 @@ test "unreachable else prong" { |
| 12 | switch (openFile0()) { | 12 | switch (openFile0()) { |
| 13 | error.AccessDenied, error.FileNotFound => |e| return e, | 13 | error.AccessDenied, error.FileNotFound => |e| return e, |
| 14 | error.OutOfMemory => {}, | 14 | error.OutOfMemory => {}, |
| 15 | else => unreachable, // technically unreachable, but will still compile! | 15 | // 'openFile0' cannot return any more errors, so an 'else' prong would be |
| 16 | // statically known to be unreachable. Nonetheless, in this case, adding | ||
| 17 | // one does not raise an "unreachable else prong" compile error: | ||
| 18 | else => unreachable, | ||
| 16 | } | 19 | } |
| 17 | 20 | ||
| 18 | // Allowed unreachable else prongs are: | 21 | // Allowed unreachable else prongs are: |
doc/langref/test_tagged_union.zig+4-1| ... | @@ -20,7 +20,10 @@ test "switch on tagged union" { | ... | @@ -20,7 +20,10 @@ test "switch on tagged union" { |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | switch (c) { | 22 | switch (c) { |
| 23 | .ok => |_, tag| try expect(tag == .ok), | 23 | .ok => |_, tag| { |
| 24 | // Because we're in the '.ok' prong, 'tag' is compile-time known to be '.ok': | ||
| 25 | comptime std.debug.assert(tag == .ok); | ||
| 26 | }, | ||
| 24 | .not_ok => unreachable, | 27 | .not_ok => unreachable, |
| 25 | } | 28 | } |
| 26 | } | 29 | } |
src/Air/Liveness.zig+1-4| ... | @@ -176,10 +176,7 @@ pub fn analyze(zcu: *Zcu, air: Air, intern_pool: *InternPool) Allocator.Error!Li | ... | @@ -176,10 +176,7 @@ pub fn analyze(zcu: *Zcu, air: Air, intern_pool: *InternPool) Allocator.Error!Li |
| 176 | data.old_extra = a.extra; | 176 | data.old_extra = a.extra; |
| 177 | a.extra = .{}; | 177 | a.extra = .{}; |
| 178 | try analyzeBody(&a, .main_analysis, &data, main_body); | 178 | try analyzeBody(&a, .main_analysis, &data, main_body); |
| 179 | if (std.debug.runtime_safety and data.live_set.count() != 0) { | 179 | assert(data.live_set.count() == 0); |
| 180 | log.debug("instructions still in live set after analysis: {f}", .{fmtInstSet(&data.live_set)}); | ||
| 181 | @panic("liveness analysis failed"); | ||
| 182 | } | ||
| 183 | } | 180 | } |
| 184 | 181 | ||
| 185 | return .{ | 182 | return .{ |
src/Sema.zig+41-27| ... | @@ -12591,6 +12591,7 @@ fn resolveSwitchProng( | ... | @@ -12591,6 +12591,7 @@ fn resolveSwitchProng( |
| 12591 | sema.typeOf(operand.simple.by_val), | 12591 | sema.typeOf(operand.simple.by_val), |
| 12592 | capture_src, | 12592 | capture_src, |
| 12593 | inline_case_capture, | 12593 | inline_case_capture, |
| 12594 | kind, | ||
| 12594 | ); | 12595 | ); |
| 12595 | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); | 12596 | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); |
| 12596 | break :inst tag_inst; | 12597 | break :inst tag_inst; |
| ... | @@ -12723,6 +12724,7 @@ fn analyzeSwitchProng( | ... | @@ -12723,6 +12724,7 @@ fn analyzeSwitchProng( |
| 12723 | operand_ty, | 12724 | operand_ty, |
| 12724 | capture_src, | 12725 | capture_src, |
| 12725 | inline_case_capture, | 12726 | inline_case_capture, |
| 12727 | kind, | ||
| 12726 | ); | 12728 | ); |
| 12727 | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); | 12729 | sema.inst_map.putAssumeCapacity(tag_inst, tag_ref); |
| 12728 | break :inst tag_inst; | 12730 | break :inst tag_inst; |
| ... | @@ -12743,6 +12745,7 @@ fn analyzeSwitchTagCapture( | ... | @@ -12743,6 +12745,7 @@ fn analyzeSwitchTagCapture( |
| 12743 | operand_ty: Type, | 12745 | operand_ty: Type, |
| 12744 | capture_src: LazySrcLoc, | 12746 | capture_src: LazySrcLoc, |
| 12745 | inline_case_capture: Air.Inst.Ref, | 12747 | inline_case_capture: Air.Inst.Ref, |
| 12748 | kind: SwitchProngKind, | ||
| 12746 | ) CompileError!Air.Inst.Ref { | 12749 | ) CompileError!Air.Inst.Ref { |
| 12747 | const pt = sema.pt; | 12750 | const pt = sema.pt; |
| 12748 | const zcu = pt.zcu; | 12751 | const zcu = pt.zcu; |
| ... | @@ -12760,6 +12763,10 @@ fn analyzeSwitchTagCapture( | ... | @@ -12760,6 +12763,10 @@ fn analyzeSwitchTagCapture( |
| 12760 | if (inline_case_capture != .none) { | 12763 | if (inline_case_capture != .none) { |
| 12761 | return inline_case_capture; // this already is the tag, it's what we're switching on! | 12764 | return inline_case_capture; // this already is the tag, it's what we're switching on! |
| 12762 | } | 12765 | } |
| 12766 | switch (kind) { | ||
| 12767 | .has_ranges, .special => {}, | ||
| 12768 | .item_refs => |refs| if (refs.len == 1) return refs[0], | ||
| 12769 | } | ||
| 12763 | const tag_ty = operand_ty.unionTagType(zcu).?; | 12770 | const tag_ty = operand_ty.unionTagType(zcu).?; |
| 12764 | return sema.unionToTag(case_block, tag_ty, operand_val, tag_capture_src); | 12771 | return sema.unionToTag(case_block, tag_ty, operand_val, tag_capture_src); |
| 12765 | } | 12772 | } |
| ... | @@ -13150,10 +13157,12 @@ fn analyzeSwitchPayloadCapture( | ... | @@ -13150,10 +13157,12 @@ fn analyzeSwitchPayloadCapture( |
| 13150 | return sema.bitCast(case_block, error_ty, operand_val, operand_src, null); | 13157 | return sema.bitCast(case_block, error_ty, operand_val, operand_src, null); |
| 13151 | }, | 13158 | }, |
| 13152 | else => { | 13159 | else => { |
| 13153 | // In this case the capture value is just the passed-through value | 13160 | // In this case the capture value is just the passed-through value of the |
| 13154 | // of the switch condition. | 13161 | // switch condition. It is comptime-known if there is only one item. |
| 13155 | if (capture_by_ref) { | 13162 | if (capture_by_ref) { |
| 13156 | return operand_ptr; | 13163 | return operand_ptr; |
| 13164 | } else if (case_vals.len == 1) { | ||
| 13165 | return case_vals[0]; | ||
| 13157 | } else { | 13166 | } else { |
| 13158 | return operand_val; | 13167 | return operand_val; |
| 13159 | } | 13168 | } |
| ... | @@ -31361,17 +31370,19 @@ fn resolvePtrIsNonErrVal( | ... | @@ -31361,17 +31370,19 @@ fn resolvePtrIsNonErrVal( |
| 31361 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); | 31370 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); |
| 31362 | const child_ty = ptr_ty.childType(zcu); | 31371 | const child_ty = ptr_ty.childType(zcu); |
| 31363 | 31372 | ||
| 31364 | const child_tag = child_ty.zigTypeTag(zcu); | 31373 | if (try sema.resolveIsNonErrFromType(block, src, child_ty)) |res| { |
| 31365 | if (child_tag != .error_set and child_tag != .error_union) return .true; | 31374 | return res; |
| 31366 | if (child_tag == .error_set) return .false; | 31375 | } |
| 31367 | assert(child_tag == .error_union); | 31376 | assert(child_ty.zigTypeTag(zcu) == .error_union); |
| 31368 | 31377 | ||
| 31369 | if (try sema.resolveValue(operand)) |ptr_val| { | 31378 | if (try sema.resolveValue(operand)) |eu_ptr_val| { |
| 31370 | if (ptr_val.isUndef(zcu)) return .undef_bool; | 31379 | if (eu_ptr_val.isUndef(zcu)) return .undef_bool; |
| 31371 | if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |val| { | 31380 | if (try sema.pointerDeref(block, src, eu_ptr_val, ptr_ty)) |err_union| { |
| 31372 | return try sema.resolveIsNonErrVal(block, src, .fromValue(val)); | 31381 | if (err_union.isUndef(zcu)) return .undef_bool; |
| 31382 | return .makeBool(err_union.getErrorName(zcu) == .none); | ||
| 31373 | } | 31383 | } |
| 31374 | } | 31384 | } |
| 31385 | |||
| 31375 | return null; | 31386 | return null; |
| 31376 | } | 31387 | } |
| 31377 | 31388 | ||
| ... | @@ -31380,11 +31391,30 @@ fn resolveIsNonErrVal( | ... | @@ -31380,11 +31391,30 @@ fn resolveIsNonErrVal( |
| 31380 | block: *Block, | 31391 | block: *Block, |
| 31381 | src: LazySrcLoc, | 31392 | src: LazySrcLoc, |
| 31382 | operand: Air.Inst.Ref, | 31393 | operand: Air.Inst.Ref, |
| 31394 | ) CompileError!?Value { | ||
| 31395 | const zcu = sema.pt.zcu; | ||
| 31396 | if (try sema.resolveIsNonErrFromType(block, src, sema.typeOf(operand))) |res| { | ||
| 31397 | return res; | ||
| 31398 | } | ||
| 31399 | assert(sema.typeOf(operand).zigTypeTag(zcu) == .error_union); | ||
| 31400 | |||
| 31401 | if (try sema.resolveValue(operand)) |err_union| { | ||
| 31402 | if (err_union.isUndef(zcu)) return .undef_bool; | ||
| 31403 | return .makeBool(err_union.getErrorName(zcu) == .none); | ||
| 31404 | } | ||
| 31405 | |||
| 31406 | return null; | ||
| 31407 | } | ||
| 31408 | |||
| 31409 | fn resolveIsNonErrFromType( | ||
| 31410 | sema: *Sema, | ||
| 31411 | block: *Block, | ||
| 31412 | src: LazySrcLoc, | ||
| 31413 | operand_ty: Type, | ||
| 31383 | ) CompileError!?Value { | 31414 | ) CompileError!?Value { |
| 31384 | const pt = sema.pt; | 31415 | const pt = sema.pt; |
| 31385 | const zcu = pt.zcu; | 31416 | const zcu = pt.zcu; |
| 31386 | const ip = &zcu.intern_pool; | 31417 | const ip = &zcu.intern_pool; |
| 31387 | const operand_ty = sema.typeOf(operand); | ||
| 31388 | const ot = operand_ty.zigTypeTag(zcu); | 31418 | const ot = operand_ty.zigTypeTag(zcu); |
| 31389 | if (ot != .error_set and ot != .error_union) return .true; | 31419 | if (ot != .error_set and ot != .error_union) return .true; |
| 31390 | if (ot == .error_set) return .false; | 31420 | if (ot == .error_set) return .false; |
| ... | @@ -31395,15 +31425,6 @@ fn resolveIsNonErrVal( | ... | @@ -31395,15 +31425,6 @@ fn resolveIsNonErrVal( |
| 31395 | return .false; | 31425 | return .false; |
| 31396 | } | 31426 | } |
| 31397 | 31427 | ||
| 31398 | if (operand == .undef) { | ||
| 31399 | return .undef_bool; | ||
| 31400 | } else if (@intFromEnum(operand) < InternPool.static_len) { | ||
| 31401 | // None of the ref tags can be errors. | ||
| 31402 | return .true; | ||
| 31403 | } | ||
| 31404 | |||
| 31405 | const maybe_operand_val = try sema.resolveValue(operand); | ||
| 31406 | |||
| 31407 | // exception if the error union error set is known to be empty, | 31428 | // exception if the error union error set is known to be empty, |
| 31408 | // we allow the comparison but always make it comptime-known. | 31429 | // we allow the comparison but always make it comptime-known. |
| 31409 | const set_ty = ip.errorUnionSet(operand_ty.toIntern()); | 31430 | const set_ty = ip.errorUnionSet(operand_ty.toIntern()); |
| ... | @@ -31419,9 +31440,6 @@ fn resolveIsNonErrVal( | ... | @@ -31419,9 +31440,6 @@ fn resolveIsNonErrVal( |
| 31419 | else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, | 31440 | else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, |
| 31420 | } | 31441 | } |
| 31421 | 31442 | ||
| 31422 | if (maybe_operand_val != null) break :blk; | ||
| 31423 | |||
| 31424 | // Try to avoid resolving inferred error set if possible. | ||
| 31425 | if (ies.errors.count() != 0) return null; | 31443 | if (ies.errors.count() != 0) return null; |
| 31426 | switch (ies.resolved) { | 31444 | switch (ies.resolved) { |
| 31427 | .anyerror_type => return null, | 31445 | .anyerror_type => return null, |
| ... | @@ -31450,7 +31468,6 @@ fn resolveIsNonErrVal( | ... | @@ -31450,7 +31468,6 @@ fn resolveIsNonErrVal( |
| 31450 | .none => {}, | 31468 | .none => {}, |
| 31451 | else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, | 31469 | else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk, |
| 31452 | } | 31470 | } |
| 31453 | if (maybe_operand_val != null) break :blk; | ||
| 31454 | if (sema.fn_ret_ty_ies) |ies| { | 31471 | if (sema.fn_ret_ty_ies) |ies| { |
| 31455 | if (ies.func == func_index) { | 31472 | if (ies.func == func_index) { |
| 31456 | // Try to avoid resolving inferred error set if possible. | 31473 | // Try to avoid resolving inferred error set if possible. |
| ... | @@ -31479,9 +31496,6 @@ fn resolveIsNonErrVal( | ... | @@ -31479,9 +31496,6 @@ fn resolveIsNonErrVal( |
| 31479 | }, | 31496 | }, |
| 31480 | } | 31497 | } |
| 31481 | 31498 | ||
| 31482 | if (maybe_operand_val) |err_union| { | ||
| 31483 | return if (err_union.isUndef(zcu)) .undef_bool else if (err_union.getErrorName(zcu) == .none) .true else .false; | ||
| 31484 | } | ||
| 31485 | return null; | 31499 | return null; |
| 31486 | } | 31500 | } |
| 31487 | 31501 |
src/Zcu.zig+1-10| ... | @@ -2221,16 +2221,7 @@ pub const SrcLoc = struct { | ... | @@ -2221,16 +2221,7 @@ pub const SrcLoc = struct { |
| 2221 | continue; | 2221 | continue; |
| 2222 | } | 2222 | } |
| 2223 | return tree.nodeToSpan(item_node); | 2223 | return tree.nodeToSpan(item_node); |
| 2224 | } else { | 2224 | } else unreachable; |
| 2225 | for (case.ast.values) |item_node| { | ||
| 2226 | const item_span = tree.nodeToSpan(item_node); | ||
| 2227 | std.debug.print("{s}\n", .{tree.source[item_span.start..item_span.end]}); | ||
| 2228 | } | ||
| 2229 | std.debug.print("want_case_idx={any}\n", .{want_case_idx}); | ||
| 2230 | std.debug.print("want_item_idx={any}\n", .{want_item_idx}); | ||
| 2231 | unreachable; | ||
| 2232 | } | ||
| 2233 | // } else unreachable; | ||
| 2234 | }, | 2225 | }, |
| 2235 | .range => { | 2226 | .range => { |
| 2236 | var range_i: u32 = 0; | 2227 | var range_i: u32 = 0; |
test/behavior/for.zig+1-1| ... | @@ -525,7 +525,7 @@ test "for loop 0 length range" { | ... | @@ -525,7 +525,7 @@ test "for loop 0 length range" { |
| 525 | } | 525 | } |
| 526 | } | 526 | } |
| 527 | 527 | ||
| 528 | test "labeled break from else prong" { | 528 | test "labeled break from else" { |
| 529 | const S = struct { | 529 | const S = struct { |
| 530 | fn doTheTest(x: u32) !void { | 530 | fn doTheTest(x: u32) !void { |
| 531 | var y: u32 = 0; | 531 | var y: u32 = 0; |
test/behavior/switch.zig+55-56| ... | @@ -1128,20 +1128,17 @@ test "decl literals as switch cases" { | ... | @@ -1128,20 +1128,17 @@ test "decl literals as switch cases" { |
| 1128 | 1128 | ||
| 1129 | const foo: @This() = @enumFromInt(0xa); | 1129 | const foo: @This() = @enumFromInt(0xa); |
| 1130 | 1130 | ||
| 1131 | fn doTheTest() !void { | 1131 | fn doTheTest(e: @This()) !void { |
| 1132 | var e: @This() = .foo; | 1132 | switch (e) { |
| 1133 | _ = &e; | 1133 | .bar => return error.TestFailed, |
| 1134 | const ok = switch (e) { | 1134 | .foo => {}, |
| 1135 | .bar => false, | 1135 | else => return error.TestFailed, |
| 1136 | .foo => true, | 1136 | } |
| 1137 | else => false, | ||
| 1138 | }; | ||
| 1139 | try expect(ok); | ||
| 1140 | } | 1137 | } |
| 1141 | }; | 1138 | }; |
| 1142 | 1139 | ||
| 1143 | try E.doTheTest(); | 1140 | try E.doTheTest(.foo); |
| 1144 | try comptime E.doTheTest(); | 1141 | try comptime E.doTheTest(.foo); |
| 1145 | } | 1142 | } |
| 1146 | 1143 | ||
| 1147 | // TODO audit after #15909 and/or #19855 are decided/implemented | 1144 | // TODO audit after #15909 and/or #19855 are decided/implemented |
| ... | @@ -1152,32 +1149,30 @@ test "switch with uninstantiable union fields" { | ... | @@ -1152,32 +1149,30 @@ test "switch with uninstantiable union fields" { |
| 1152 | b: noreturn, | 1149 | b: noreturn, |
| 1153 | c: error{}, | 1150 | c: error{}, |
| 1154 | 1151 | ||
| 1155 | fn doTheTest() !void { | 1152 | fn doTheTest(u: @This()) void { |
| 1156 | var u: @This() = .ok; | 1153 | switch (u) { |
| 1157 | _ = &u; | 1154 | .ok => {}, |
| 1158 | try expect(switch (u) { | ||
| 1159 | .ok => true, | ||
| 1160 | .a => comptime unreachable, | 1155 | .a => comptime unreachable, |
| 1161 | .b => comptime unreachable, | 1156 | .b => comptime unreachable, |
| 1162 | .c => comptime unreachable, | 1157 | .c => comptime unreachable, |
| 1163 | }); | 1158 | } |
| 1164 | try expect(switch (u) { | 1159 | switch (u) { |
| 1165 | .ok => true, | 1160 | .ok => {}, |
| 1166 | .a, .b, .c => comptime unreachable, | 1161 | .a, .b, .c => comptime unreachable, |
| 1167 | }); | 1162 | } |
| 1168 | try expect(switch (u) { | 1163 | switch (u) { |
| 1169 | .ok => true, | 1164 | .ok => {}, |
| 1170 | else => comptime unreachable, | 1165 | else => comptime unreachable, |
| 1171 | }); | 1166 | } |
| 1172 | try expect(switch (u) { | 1167 | switch (u) { |
| 1173 | .a => comptime unreachable, | 1168 | .a => comptime unreachable, |
| 1174 | .ok, .b, .c => true, | 1169 | .ok, .b, .c => {}, |
| 1175 | }); | 1170 | } |
| 1176 | } | 1171 | } |
| 1177 | }; | 1172 | }; |
| 1178 | 1173 | ||
| 1179 | try U.doTheTest(); | 1174 | U.doTheTest(.ok); |
| 1180 | try comptime U.doTheTest(); | 1175 | comptime U.doTheTest(.ok); |
| 1181 | } | 1176 | } |
| 1182 | 1177 | ||
| 1183 | test "switch with tag capture" { | 1178 | test "switch with tag capture" { |
| ... | @@ -1196,8 +1191,8 @@ test "switch with tag capture" { | ... | @@ -1196,8 +1191,8 @@ test "switch with tag capture" { |
| 1196 | fn doTheSwitch(u: @This()) !void { | 1191 | fn doTheSwitch(u: @This()) !void { |
| 1197 | switch (u) { | 1192 | switch (u) { |
| 1198 | .a => |nothing, tag| { | 1193 | .a => |nothing, tag| { |
| 1199 | try expect(nothing == {}); | 1194 | comptime assert(nothing == {}); |
| 1200 | try expect(tag == .a); | 1195 | comptime assert(tag == .a); |
| 1201 | try expect(@intFromEnum(tag) == @intFromEnum(@This().a)); | 1196 | try expect(@intFromEnum(tag) == @intFromEnum(@This().a)); |
| 1202 | }, | 1197 | }, |
| 1203 | .b, .d => |_, tag| { | 1198 | .b, .d => |_, tag| { |
| ... | @@ -1216,13 +1211,13 @@ test "switch with tag capture" { | ... | @@ -1216,13 +1211,13 @@ test "switch with tag capture" { |
| 1216 | } | 1211 | } |
| 1217 | switch (u) { | 1212 | switch (u) { |
| 1218 | inline .a, .b, .c => |payload, tag| { | 1213 | inline .a, .b, .c => |payload, tag| { |
| 1219 | if (@TypeOf(payload) == void) try expect(tag == .a); | 1214 | if (@TypeOf(payload) == void) comptime assert(tag == .a); |
| 1220 | if (@TypeOf(payload) == i32) try expect(tag == .b); | 1215 | if (@TypeOf(payload) == i32) comptime assert(tag == .b); |
| 1221 | if (@TypeOf(payload) == u8) try expect(tag == .c); | 1216 | if (@TypeOf(payload) == u8) comptime assert(tag == .c); |
| 1222 | }, | 1217 | }, |
| 1223 | inline else => |payload, tag| { | 1218 | inline else => |payload, tag| { |
| 1224 | if (@TypeOf(payload) == i32) try expect(tag == .d); | 1219 | if (@TypeOf(payload) == i32) comptime assert(tag == .d); |
| 1225 | try expect(tag != .e); | 1220 | comptime assert(tag != .e); |
| 1226 | }, | 1221 | }, |
| 1227 | } | 1222 | } |
| 1228 | } | 1223 | } |
| ... | @@ -1232,7 +1227,7 @@ test "switch with tag capture" { | ... | @@ -1232,7 +1227,7 @@ test "switch with tag capture" { |
| 1232 | try comptime U.doTheTest(); | 1227 | try comptime U.doTheTest(); |
| 1233 | } | 1228 | } |
| 1234 | 1229 | ||
| 1235 | test "switch with advanced prong items" { | 1230 | test "switch with complex item expressions" { |
| 1236 | const S = struct { | 1231 | const S = struct { |
| 1237 | fn doTheTest() !void { | 1232 | fn doTheTest() !void { |
| 1238 | try doTheSwitch(2000, 20); | 1233 | try doTheSwitch(2000, 20); |
| ... | @@ -1278,19 +1273,11 @@ test "switch with advanced prong items" { | ... | @@ -1278,19 +1273,11 @@ test "switch with advanced prong items" { |
| 1278 | } | 1273 | } |
| 1279 | 1274 | ||
| 1280 | test "switch evaluation order" { | 1275 | test "switch evaluation order" { |
| 1281 | const eval = comptime eval: { | 1276 | const eu: anyerror!u32 = 0; |
| 1282 | var eval = false; | 1277 | _ = eu catch |err| switch (err) { |
| 1283 | const eu: anyerror!u32 = 0; | 1278 | if (true) @compileError("unreachable") => unreachable, |
| 1284 | _ = eu catch |err| switch (err) { | 1279 | else => unreachable, |
| 1285 | blk: { | ||
| 1286 | eval = true; | ||
| 1287 | break :blk error.MyError; | ||
| 1288 | } => {}, | ||
| 1289 | else => unreachable, | ||
| 1290 | }; | ||
| 1291 | break :eval eval; | ||
| 1292 | }; | 1280 | }; |
| 1293 | try comptime expect(!eval); | ||
| 1294 | } | 1281 | } |
| 1295 | 1282 | ||
| 1296 | test "switch resolves lazy values correctly" { | 1283 | test "switch resolves lazy values correctly" { |
| ... | @@ -1298,13 +1285,25 @@ test "switch resolves lazy values correctly" { | ... | @@ -1298,13 +1285,25 @@ test "switch resolves lazy values correctly" { |
| 1298 | a: u16, | 1285 | a: u16, |
| 1299 | b: i16, | 1286 | b: i16, |
| 1300 | }; | 1287 | }; |
| 1301 | const ok1 = switch (@sizeOf(S)) { | 1288 | switch (@sizeOf(S)) { |
| 1302 | 4 => true, | 1289 | 4 => {}, |
| 1303 | else => false, | 1290 | else => comptime unreachable, |
| 1304 | }; | 1291 | } |
| 1305 | const ok2 = switch (@sizeOf(S)) { | 1292 | } |
| 1306 | 4 => true, | 1293 | |
| 1307 | else => false, | 1294 | test "single-item prong in switch on enum has comptime-known capture" { |
| 1295 | const E = enum { | ||
| 1296 | a, | ||
| 1297 | b, | ||
| 1298 | c, | ||
| 1299 | fn doTheTest(e: @This()) !void { | ||
| 1300 | switch (e) { | ||
| 1301 | .a => |tag| comptime assert(tag == .a), | ||
| 1302 | .b => return error.TestFailed, | ||
| 1303 | .c => return error.TestFailed, | ||
| 1304 | } | ||
| 1305 | } | ||
| 1308 | }; | 1306 | }; |
| 1309 | try comptime expect(ok1 == ok2); | 1307 | try E.doTheTest(.a); |
| 1308 | try comptime E.doTheTest(.a); | ||
| 1310 | } | 1309 | } |
test/behavior/switch_loop.zig+9-8| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | const assert = std.debug.assert; | ||
| 3 | const expect = std.testing.expect; | 4 | const expect = std.testing.expect; |
| 4 | 5 | ||
| 5 | test "simple switch loop" { | 6 | test "simple switch loop" { |
| ... | @@ -340,7 +341,7 @@ test "switch loop with single catch-all prong" { | ... | @@ -340,7 +341,7 @@ test "switch loop with single catch-all prong" { |
| 340 | continue :label .{ .b = 456 }; | 341 | continue :label .{ .b = 456 }; |
| 341 | }, | 342 | }, |
| 342 | }; | 343 | }; |
| 343 | try expect(ok); | 344 | comptime assert(ok); |
| 344 | } | 345 | } |
| 345 | }; | 346 | }; |
| 346 | try S.doTheTest(); | 347 | try S.doTheTest(); |
| ... | @@ -411,8 +412,8 @@ test "switch loop with tag capture" { | ... | @@ -411,8 +412,8 @@ test "switch loop with tag capture" { |
| 411 | fn doTheSwitch(u: @This()) !void { | 412 | fn doTheSwitch(u: @This()) !void { |
| 412 | const ok1 = label: switch (u) { | 413 | const ok1 = label: switch (u) { |
| 413 | .a => |nothing, tag| { | 414 | .a => |nothing, tag| { |
| 414 | try expect(nothing == {}); | 415 | comptime assert(nothing == {}); |
| 415 | try expect(tag == .a); | 416 | comptime assert(tag == .a); |
| 416 | try expect(@intFromEnum(tag) == @intFromEnum(@This().a)); | 417 | try expect(@intFromEnum(tag) == @intFromEnum(@This().a)); |
| 417 | continue :label .{ .d = 456 }; | 418 | continue :label .{ .d = 456 }; |
| 418 | }, | 419 | }, |
| ... | @@ -438,21 +439,21 @@ test "switch loop with tag capture" { | ... | @@ -438,21 +439,21 @@ test "switch loop with tag capture" { |
| 438 | const ok2 = label: switch (u) { | 439 | const ok2 = label: switch (u) { |
| 439 | inline .a, .b, .c => |payload, tag| { | 440 | inline .a, .b, .c => |payload, tag| { |
| 440 | if (@TypeOf(payload) == void) { | 441 | if (@TypeOf(payload) == void) { |
| 441 | try expect(tag == .a); | 442 | comptime assert(tag == .a); |
| 442 | continue :label .{ .b = 456 }; | 443 | continue :label .{ .b = 456 }; |
| 443 | } | 444 | } |
| 444 | if (@TypeOf(payload) == i32) { | 445 | if (@TypeOf(payload) == i32) { |
| 445 | try expect(tag == .b); | 446 | comptime assert(tag == .b); |
| 446 | continue :label .{ .d = payload }; | 447 | continue :label .{ .d = payload }; |
| 447 | } | 448 | } |
| 448 | if (@TypeOf(payload) == u8) { | 449 | if (@TypeOf(payload) == u8) { |
| 449 | try expect(tag == .c); | 450 | comptime assert(tag == .c); |
| 450 | continue :label .{ .d = payload }; | 451 | continue :label .{ .d = payload }; |
| 451 | } | 452 | } |
| 452 | }, | 453 | }, |
| 453 | inline else => |payload, tag| { | 454 | inline else => |payload, tag| { |
| 454 | if (@TypeOf(payload) == i32) try expect(tag == .d); | 455 | if (@TypeOf(payload) == i32) comptime assert(tag == .d); |
| 455 | try expect(tag != .e); | 456 | comptime assert(tag != .e); |
| 456 | if (payload == 0) break :label false; | 457 | if (payload == 0) break :label false; |
| 457 | break :label true; | 458 | break :label true; |
| 458 | }, | 459 | }, |
test/behavior/switch_on_captured_error.zig+6-1| ... | @@ -17,7 +17,9 @@ test "switch on error union catch capture" { | ... | @@ -17,7 +17,9 @@ test "switch on error union catch capture" { |
| 17 | try testElse(); | 17 | try testElse(); |
| 18 | try testCapture(); | 18 | try testCapture(); |
| 19 | try testInline(); | 19 | try testInline(); |
| 20 | try testEmptyErrSet(); | ||
| 20 | try testUnreachableElseProng(); | 21 | try testUnreachableElseProng(); |
| 22 | try testErrNotInSet(); | ||
| 21 | try testAddressOf(); | 23 | try testAddressOf(); |
| 22 | } | 24 | } |
| 23 | 25 | ||
| ... | @@ -384,8 +386,11 @@ test "switch on error union if else capture" { | ... | @@ -384,8 +386,11 @@ test "switch on error union if else capture" { |
| 384 | try testCapturePtr(); | 386 | try testCapturePtr(); |
| 385 | try testInline(); | 387 | try testInline(); |
| 386 | try testInlinePtr(); | 388 | try testInlinePtr(); |
| 389 | try testEmptyErrSet(); | ||
| 390 | try testEmptyErrSetPtr(); | ||
| 387 | try testUnreachableElseProng(); | 391 | try testUnreachableElseProng(); |
| 388 | try testUnreachableElseProngPtr(); | 392 | try testUnreachableElseProngPtr(); |
| 393 | try testErrNotInSet(); | ||
| 389 | try testAddressOf(); | 394 | try testAddressOf(); |
| 390 | } | 395 | } |
| 391 | 396 | ||
| ... | @@ -835,7 +840,7 @@ test "switch on error union if else capture" { | ... | @@ -835,7 +840,7 @@ test "switch on error union if else capture" { |
| 835 | var a: error{}!u64 = 0; | 840 | var a: error{}!u64 = 0; |
| 836 | _ = &a; | 841 | _ = &a; |
| 837 | const b = if (a) |*x| x.* else |err| switch (err) { | 842 | const b = if (a) |*x| x.* else |err| switch (err) { |
| 838 | error.undefined => @compileError("unreachable"), | 843 | undefined => @compileError("unreachable"), |
| 839 | }; | 844 | }; |
| 840 | try expectEqual(@as(u64, 0), b); | 845 | try expectEqual(@as(u64, 0), b); |
| 841 | } | 846 | } |
test/behavior/while.zig+4-4| ... | @@ -400,12 +400,12 @@ test "breaking from a loop in an if statement" { | ... | @@ -400,12 +400,12 @@ test "breaking from a loop in an if statement" { |
| 400 | _ = opt; | 400 | _ = opt; |
| 401 | } | 401 | } |
| 402 | 402 | ||
| 403 | test "labeled break from else prong" { | 403 | test "labeled break from else" { |
| 404 | const S = struct { | 404 | const S = struct { |
| 405 | fn doTheTest(x: u32) !void { | 405 | fn doTheTest(x: u32) !void { |
| 406 | var y: u32 = 0; | 406 | const arr: []const u32 = &.{ 1, 3, 10 }; |
| 407 | const ok = label: while (y < x) : (y += 1) { | 407 | const ok = label: for (arr) |y| { |
| 408 | if (y == 10) break :label false; | 408 | if (y == x) break :label false; |
| 409 | } else { | 409 | } else { |
| 410 | break :label true; | 410 | break :label true; |
| 411 | }; | 411 | }; |