authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-24 17:53:04-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-24 17:53:04-07:00
log1f16b07d6fe43f96287b6cca8e8b58996199481f
tree5d16f515eea5c7a72466e352ce63e3de0a7ed53d
parentc711c788f0a840f45d0d7423efe2f946b47caafb

stage2: treat `error{}!void` as a zero-bit type


4 files changed, 183 insertions(+), 90 deletions(-)

src/Sema.zig+23-1
...@@ -23320,7 +23320,6 @@ pub fn typeHasOnePossibleValue(...@@ -23320,7 +23320,6 @@ pub fn typeHasOnePossibleValue(
23320 .optional_single_const_pointer,23320 .optional_single_const_pointer,
23321 .enum_literal,23321 .enum_literal,
23322 .anyerror_void_error_union,23322 .anyerror_void_error_union,
23323 .error_union,
23324 .error_set_inferred,23323 .error_set_inferred,
23325 .@"opaque",23324 .@"opaque",
23326 .var_args_param,23325 .var_args_param,
...@@ -23360,6 +23359,29 @@ pub fn typeHasOnePossibleValue(...@@ -23360,6 +23359,29 @@ pub fn typeHasOnePossibleValue(
23360 }23359 }
23361 },23360 },
2336223361
23362 .error_union => {
23363 const error_ty = ty.errorUnionSet();
23364 switch (error_ty.errorSetCardinality()) {
23365 .zero => {
23366 const payload_ty = ty.errorUnionPayload();
23367 if (try typeHasOnePossibleValue(sema, block, src, payload_ty)) |payload_val| {
23368 return try Value.Tag.eu_payload.create(sema.arena, payload_val);
23369 } else {
23370 return null;
23371 }
23372 },
23373 .one => {
23374 if (ty.errorUnionPayload().isNoReturn()) {
23375 const error_val = (try typeHasOnePossibleValue(sema, block, src, error_ty)).?;
23376 return error_val;
23377 } else {
23378 return null;
23379 }
23380 },
23381 .many => return null,
23382 }
23383 },
23384
23363 .error_set_single => {23385 .error_set_single => {
23364 const name = ty.castTag(.error_set_single).?.data;23386 const name = ty.castTag(.error_set_single).?.data;
23365 return try Value.Tag.@"error".create(sema.arena, .{ .name = name });23387 return try Value.Tag.@"error".create(sema.arena, .{ .name = name });
src/codegen/llvm.zig+78-73
...@@ -1470,16 +1470,25 @@ pub const Object = struct {...@@ -1470,16 +1470,25 @@ pub const Object = struct {
1470 return full_di_ty;1470 return full_di_ty;
1471 },1471 },
1472 .ErrorUnion => {1472 .ErrorUnion => {
1473 const err_set_ty = ty.errorUnionSet();
1474 const payload_ty = ty.errorUnionPayload();1473 const payload_ty = ty.errorUnionPayload();
1475 if (err_set_ty.errorSetCardinality() == .zero) {1474 switch (ty.errorUnionSet().errorSetCardinality()) {
1476 const payload_di_ty = try o.lowerDebugType(payload_ty, .full);1475 .zero => {
1477 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.1476 const payload_di_ty = try o.lowerDebugType(payload_ty, .full);
1478 try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module });1477 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
1479 return payload_di_ty;1478 try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module });
1479 return payload_di_ty;
1480 },
1481 .one => {
1482 if (payload_ty.isNoReturn()) {
1483 const di_type = dib.createBasicType("void", 0, DW.ATE.signed);
1484 gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type);
1485 return di_type;
1486 }
1487 },
1488 .many => {},
1480 }1489 }
1481 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {1490 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
1482 const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full);1491 const err_set_di_ty = try o.lowerDebugType(Type.anyerror, .full);
1483 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.1492 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
1484 try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .mod = o.module });1493 try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .mod = o.module });
1485 return err_set_di_ty;1494 return err_set_di_ty;
...@@ -1502,56 +1511,51 @@ pub const Object = struct {...@@ -1502,56 +1511,51 @@ pub const Object = struct {
1502 break :blk fwd_decl;1511 break :blk fwd_decl;
1503 };1512 };
15041513
1505 const err_set_size = err_set_ty.abiSize(target);1514 const error_size = Type.anyerror.abiSize(target);
1506 const err_set_align = err_set_ty.abiAlignment(target);1515 const error_align = Type.anyerror.abiAlignment(target);
1507 const payload_size = payload_ty.abiSize(target);1516 const payload_size = payload_ty.abiSize(target);
1508 const payload_align = payload_ty.abiAlignment(target);1517 const payload_align = payload_ty.abiAlignment(target);
15091518
1510 var offset: u64 = 0;1519 var error_index: u32 = undefined;
1511 offset += err_set_size;1520 var payload_index: u32 = undefined;
1512 offset = std.mem.alignForwardGeneric(u64, offset, payload_align);1521 var error_offset: u64 = undefined;
1513 const payload_offset = offset;1522 var payload_offset: u64 = undefined;
15141523 if (error_align > payload_align) {
1515 var len: u8 = 2;1524 error_index = 0;
1516 var fields: [3]*llvm.DIType = .{1525 payload_index = 1;
1517 dib.createMemberType(1526 error_offset = 0;
1518 fwd_decl.toScope(),1527 payload_offset = std.mem.alignForwardGeneric(u64, error_size, payload_align);
1519 "tag",1528 } else {
1520 di_file,1529 payload_index = 0;
1521 line,1530 error_index = 1;
1522 err_set_size * 8, // size in bits1531 payload_offset = 0;
1523 err_set_align * 8, // align in bits1532 error_offset = std.mem.alignForwardGeneric(u64, payload_size, error_align);
1524 0, // offset in bits
1525 0, // flags
1526 try o.lowerDebugType(err_set_ty, .full),
1527 ),
1528 dib.createMemberType(
1529 fwd_decl.toScope(),
1530 "value",
1531 di_file,
1532 line,
1533 payload_size * 8, // size in bits
1534 payload_align * 8, // align in bits
1535 payload_offset * 8, // offset in bits
1536 0, // flags
1537 try o.lowerDebugType(payload_ty, .full),
1538 ),
1539 undefined,
1540 };
1541
1542 const error_size = Type.anyerror.abiSize(target);
1543 if (payload_align > error_size) {
1544 fields[2] = fields[1];
1545 const pad_len = @intCast(u32, payload_align - error_size);
1546 fields[1] = dib.createArrayType(
1547 pad_len * 8,
1548 8,
1549 try o.lowerDebugType(Type.u8, .full),
1550 @intCast(c_int, pad_len),
1551 );
1552 len += 1;
1553 }1533 }
15541534
1535 var fields: [2]*llvm.DIType = undefined;
1536 fields[error_index] = dib.createMemberType(
1537 fwd_decl.toScope(),
1538 "tag",
1539 di_file,
1540 line,
1541 error_size * 8, // size in bits
1542 error_align * 8, // align in bits
1543 error_offset * 8, // offset in bits
1544 0, // flags
1545 try o.lowerDebugType(Type.anyerror, .full),
1546 );
1547 fields[payload_index] = dib.createMemberType(
1548 fwd_decl.toScope(),
1549 "value",
1550 di_file,
1551 line,
1552 payload_size * 8, // size in bits
1553 payload_align * 8, // align in bits
1554 payload_offset * 8, // offset in bits
1555 0, // flags
1556 try o.lowerDebugType(payload_ty, .full),
1557 );
1558
1555 const full_di_ty = dib.createStructType(1559 const full_di_ty = dib.createStructType(
1556 compile_unit_scope,1560 compile_unit_scope,
1557 name.ptr,1561 name.ptr,
...@@ -1562,7 +1566,7 @@ pub const Object = struct {...@@ -1562,7 +1566,7 @@ pub const Object = struct {
1562 0, // flags1566 0, // flags
1563 null, // derived from1567 null, // derived from
1564 &fields,1568 &fields,
1565 len,1569 fields.len,
1566 0, // run time lang1570 0, // run time lang
1567 null, // vtable holder1571 null, // vtable holder
1568 "", // unique id1572 "", // unique id
...@@ -2455,18 +2459,23 @@ pub const DeclGen = struct {...@@ -2455,18 +2459,23 @@ pub const DeclGen = struct {
2455 return dg.context.structType(&fields, fields.len, .False);2459 return dg.context.structType(&fields, fields.len, .False);
2456 },2460 },
2457 .ErrorUnion => {2461 .ErrorUnion => {
2458 const error_type = t.errorUnionSet();2462 const payload_ty = t.errorUnionPayload();
2459 const payload_type = t.errorUnionPayload();2463 switch (t.errorUnionSet().errorSetCardinality()) {
2460 if (error_type.errorSetCardinality() == .zero) {2464 .zero => return dg.lowerType(payload_ty),
2461 return dg.lowerType(payload_type);2465 .one => {
2466 if (payload_ty.isNoReturn()) {
2467 return dg.context.voidType();
2468 }
2469 },
2470 .many => {},
2462 }2471 }
2463 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {2472 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
2464 return try dg.lowerType(Type.anyerror);2473 return try dg.lowerType(Type.anyerror);
2465 }2474 }
2466 const llvm_error_type = try dg.lowerType(error_type);2475 const llvm_error_type = try dg.lowerType(Type.anyerror);
2467 const llvm_payload_type = try dg.lowerType(payload_type);2476 const llvm_payload_type = try dg.lowerType(payload_ty);
24682477
2469 const payload_align = payload_type.abiAlignment(target);2478 const payload_align = payload_ty.abiAlignment(target);
2470 const error_align = Type.anyerror.abiAlignment(target);2479 const error_align = Type.anyerror.abiAlignment(target);
2471 if (error_align > payload_align) {2480 if (error_align > payload_align) {
2472 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };2481 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };
...@@ -2476,9 +2485,7 @@ pub const DeclGen = struct {...@@ -2476,9 +2485,7 @@ pub const DeclGen = struct {
2476 return dg.context.structType(&fields, fields.len, .False);2485 return dg.context.structType(&fields, fields.len, .False);
2477 }2486 }
2478 },2487 },
2479 .ErrorSet => {2488 .ErrorSet => return dg.context.intType(16),
2480 return dg.context.intType(16);
2481 },
2482 .Struct => {2489 .Struct => {
2483 const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module });2490 const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module });
2484 if (gop.found_existing) return gop.value_ptr.*;2491 if (gop.found_existing) return gop.value_ptr.*;
...@@ -3095,7 +3102,7 @@ pub const DeclGen = struct {...@@ -3095,7 +3102,7 @@ pub const DeclGen = struct {
3095 return dg.resolveLlvmFunction(fn_decl_index);3102 return dg.resolveLlvmFunction(fn_decl_index);
3096 },3103 },
3097 .ErrorSet => {3104 .ErrorSet => {
3098 const llvm_ty = try dg.lowerType(tv.ty);3105 const llvm_ty = try dg.lowerType(Type.anyerror);
3099 switch (tv.val.tag()) {3106 switch (tv.val.tag()) {
3100 .@"error" => {3107 .@"error" => {
3101 const err_name = tv.val.castTag(.@"error").?.data.name;3108 const err_name = tv.val.castTag(.@"error").?.data.name;
...@@ -3109,9 +3116,8 @@ pub const DeclGen = struct {...@@ -3109,9 +3116,8 @@ pub const DeclGen = struct {
3109 }3116 }
3110 },3117 },
3111 .ErrorUnion => {3118 .ErrorUnion => {
3112 const error_type = tv.ty.errorUnionSet();
3113 const payload_type = tv.ty.errorUnionPayload();3119 const payload_type = tv.ty.errorUnionPayload();
3114 if (error_type.errorSetCardinality() == .zero) {3120 if (tv.ty.errorUnionSet().errorSetCardinality() == .zero) {
3115 const payload_val = tv.val.castTag(.eu_payload).?.data;3121 const payload_val = tv.val.castTag(.eu_payload).?.data;
3116 return dg.lowerValue(.{ .ty = payload_type, .val = payload_val });3122 return dg.lowerValue(.{ .ty = payload_type, .val = payload_val });
3117 }3123 }
...@@ -3120,13 +3126,13 @@ pub const DeclGen = struct {...@@ -3120,13 +3126,13 @@ pub const DeclGen = struct {
3120 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {3126 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
3121 // We use the error type directly as the type.3127 // We use the error type directly as the type.
3122 const err_val = if (!is_pl) tv.val else Value.initTag(.zero);3128 const err_val = if (!is_pl) tv.val else Value.initTag(.zero);
3123 return dg.lowerValue(.{ .ty = error_type, .val = err_val });3129 return dg.lowerValue(.{ .ty = Type.anyerror, .val = err_val });
3124 }3130 }
31253131
3126 const payload_align = payload_type.abiAlignment(target);3132 const payload_align = payload_type.abiAlignment(target);
3127 const error_align = Type.anyerror.abiAlignment(target);3133 const error_align = Type.anyerror.abiAlignment(target);
3128 const llvm_error_value = try dg.lowerValue(.{3134 const llvm_error_value = try dg.lowerValue(.{
3129 .ty = error_type,3135 .ty = Type.anyerror,
3130 .val = if (is_pl) Value.initTag(.zero) else tv.val,3136 .val = if (is_pl) Value.initTag(.zero) else tv.val,
3131 });3137 });
3132 const llvm_payload_value = try dg.lowerValue(.{3138 const llvm_payload_value = try dg.lowerValue(.{
...@@ -5656,13 +5662,12 @@ pub const FuncGen = struct {...@@ -5656,13 +5662,12 @@ pub const FuncGen = struct {
5656 const operand = try self.resolveInst(ty_op.operand);5662 const operand = try self.resolveInst(ty_op.operand);
5657 const error_union_ty = self.air.typeOf(ty_op.operand).childType();5663 const error_union_ty = self.air.typeOf(ty_op.operand).childType();
56585664
5659 const error_ty = error_union_ty.errorUnionSet();5665 if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
5660 if (error_ty.errorSetCardinality() == .zero) {
5661 // TODO: write undefined bytes through the pointer here5666 // TODO: write undefined bytes through the pointer here
5662 return operand;5667 return operand;
5663 }5668 }
5664 const payload_ty = error_union_ty.errorUnionPayload();5669 const payload_ty = error_union_ty.errorUnionPayload();
5665 const non_error_val = try self.dg.lowerValue(.{ .ty = error_ty, .val = Value.zero });5670 const non_error_val = try self.dg.lowerValue(.{ .ty = Type.anyerror, .val = Value.zero });
5666 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {5671 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
5667 _ = self.builder.buildStore(non_error_val, operand);5672 _ = self.builder.buildStore(non_error_val, operand);
5668 return operand;5673 return operand;
...@@ -6715,9 +6720,9 @@ pub const FuncGen = struct {...@@ -6715,9 +6720,9 @@ pub const FuncGen = struct {
6715 if (self.liveness.isUnused(inst)) return null;6720 if (self.liveness.isUnused(inst)) return null;
67166721
6717 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6722 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6718 const operand = try self.resolveInst(ty_op.operand);
6719 const operand_ty = self.air.typeOf(ty_op.operand);6723 const operand_ty = self.air.typeOf(ty_op.operand);
6720 const inst_ty = self.air.typeOfIndex(inst);6724 const inst_ty = self.air.typeOfIndex(inst);
6725 const operand = try self.resolveInst(ty_op.operand);
6721 const operand_is_ref = isByRef(operand_ty);6726 const operand_is_ref = isByRef(operand_ty);
6722 const result_is_ref = isByRef(inst_ty);6727 const result_is_ref = isByRef(inst_ty);
6723 const llvm_dest_ty = try self.dg.lowerType(inst_ty);6728 const llvm_dest_ty = try self.dg.lowerType(inst_ty);
src/type.zig+52-13
...@@ -2416,14 +2416,18 @@ pub const Type = extern union {...@@ -2416,14 +2416,18 @@ pub const Type = extern union {
2416 // This code needs to be kept in sync with the equivalent switch prong2416 // This code needs to be kept in sync with the equivalent switch prong
2417 // in abiSizeAdvanced.2417 // in abiSizeAdvanced.
2418 const data = ty.castTag(.error_union).?.data;2418 const data = ty.castTag(.error_union).?.data;
2419 if (data.error_set.errorSetCardinality() == .zero) {2419 switch (data.error_set.errorSetCardinality()) {
2420 return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit);2420 .zero => return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit),
2421 } else if (ignore_comptime_only) {2421 .one => return !data.payload.isNoReturn(),
2422 return true;2422 .many => {
2423 } else if (sema_kit) |sk| {2423 if (ignore_comptime_only) {
2424 return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty));2424 return true;
2425 } else {2425 } else if (sema_kit) |sk| {
2426 return !comptimeOnly(ty);2426 return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty));
2427 } else {
2428 return !comptimeOnly(ty);
2429 }
2430 },
2427 }2431 }
2428 },2432 },
24292433
...@@ -2970,8 +2974,14 @@ pub const Type = extern union {...@@ -2970,8 +2974,14 @@ pub const Type = extern union {
2970 // This code needs to be kept in sync with the equivalent switch prong2974 // This code needs to be kept in sync with the equivalent switch prong
2971 // in abiSizeAdvanced.2975 // in abiSizeAdvanced.
2972 const data = ty.castTag(.error_union).?.data;2976 const data = ty.castTag(.error_union).?.data;
2973 if (data.error_set.errorSetCardinality() == .zero) {2977 switch (data.error_set.errorSetCardinality()) {
2974 return abiAlignmentAdvanced(data.payload, target, strat);2978 .zero => return abiAlignmentAdvanced(data.payload, target, strat),
2979 .one => {
2980 if (data.payload.isNoReturn()) {
2981 return AbiAlignmentAdvanced{ .scalar = 0 };
2982 }
2983 },
2984 .many => {},
2975 }2985 }
2976 const code_align = abiAlignment(Type.anyerror, target);2986 const code_align = abiAlignment(Type.anyerror, target);
2977 switch (strat) {2987 switch (strat) {
...@@ -3440,8 +3450,14 @@ pub const Type = extern union {...@@ -3440,8 +3450,14 @@ pub const Type = extern union {
3440 // 1 bit of data which is whether or not the value is an error.3450 // 1 bit of data which is whether or not the value is an error.
3441 // Zig still uses the error code encoding at runtime, even when only 1 bit3451 // Zig still uses the error code encoding at runtime, even when only 1 bit
3442 // would suffice. This prevents coercions from needing to branch.3452 // would suffice. This prevents coercions from needing to branch.
3443 if (data.error_set.errorSetCardinality() == .zero) {3453 switch (data.error_set.errorSetCardinality()) {
3444 return abiSizeAdvanced(data.payload, target, strat);3454 .zero => return abiSizeAdvanced(data.payload, target, strat),
3455 .one => {
3456 if (data.payload.isNoReturn()) {
3457 return AbiSizeAdvanced{ .scalar = 0 };
3458 }
3459 },
3460 .many => {},
3445 }3461 }
3446 const code_size = abiSize(Type.anyerror, target);3462 const code_size = abiSize(Type.anyerror, target);
3447 if (!data.payload.hasRuntimeBits()) {3463 if (!data.payload.hasRuntimeBits()) {
...@@ -4843,7 +4859,6 @@ pub const Type = extern union {...@@ -4843,7 +4859,6 @@ pub const Type = extern union {
4843 .optional_single_const_pointer,4859 .optional_single_const_pointer,
4844 .enum_literal,4860 .enum_literal,
4845 .anyerror_void_error_union,4861 .anyerror_void_error_union,
4846 .error_union,
4847 .error_set_inferred,4862 .error_set_inferred,
4848 .@"opaque",4863 .@"opaque",
4849 .var_args_param,4864 .var_args_param,
...@@ -4883,6 +4898,30 @@ pub const Type = extern union {...@@ -4883,6 +4898,30 @@ pub const Type = extern union {
4883 }4898 }
4884 },4899 },
48854900
4901 .error_union => {
4902 const error_ty = ty.errorUnionSet();
4903 switch (error_ty.errorSetCardinality()) {
4904 .zero => {
4905 const payload_ty = ty.errorUnionPayload();
4906 if (onePossibleValue(payload_ty)) |payload_val| {
4907 _ = payload_val;
4908 return Value.initTag(.the_only_possible_value);
4909 } else {
4910 return null;
4911 }
4912 },
4913 .one => {
4914 if (ty.errorUnionPayload().isNoReturn()) {
4915 const error_val = onePossibleValue(error_ty).?;
4916 return error_val;
4917 } else {
4918 return null;
4919 }
4920 },
4921 .many => return null,
4922 }
4923 },
4924
4886 .error_set_single => return Value.initTag(.the_only_possible_value),4925 .error_set_single => return Value.initTag(.the_only_possible_value),
4887 .error_set => {4926 .error_set => {
4888 const err_set_obj = ty.castTag(.error_set).?.data;4927 const err_set_obj = ty.castTag(.error_set).?.data;
test/behavior/error.zig+30-3
...@@ -479,11 +479,38 @@ test "optional error set with only one error is the same size as bool" {...@@ -479,11 +479,38 @@ test "optional error set with only one error is the same size as bool" {
479test "optional empty error set" {479test "optional empty error set" {
480 if (builtin.zig_backend == .stage1) return error.SkipZigTest;480 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
481481
482 const T = ?error{};482 comptime try expect(@sizeOf(error{}!void) == @sizeOf(void));
483 var t: T = undefined;483 comptime try expect(@alignOf(error{}!void) == @alignOf(void));
484 if (t != null) {484
485 var x: ?error{} = undefined;
486 if (x != null) {
487 @compileError("test failed");
488 }
489}
490
491test "empty error set plus zero-bit payload" {
492 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
493 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
494 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
495
496 comptime try expect(@sizeOf(error{}!void) == @sizeOf(void));
497 comptime try expect(@alignOf(error{}!void) == @alignOf(void));
498
499 var x: error{}!void = undefined;
500 if (x) |payload| {
501 if (payload != {}) {
502 @compileError("test failed");
503 }
504 } else |_| {
485 @compileError("test failed");505 @compileError("test failed");
486 }506 }
507 const S = struct {
508 fn empty() error{}!void {}
509 fn inferred() !void {
510 return empty();
511 }
512 };
513 try S.inferred();
487}514}
488515
489test "nested catch" {516test "nested catch" {