authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-09-15 00:00:11+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-15 11:42:08-07:00
logf366d9f8793fc297e581321fbbd6242089f07440
treed6254129a206accbdc10e615689e658f80e05c58
parentded628e4fae41e0816c30b5c93f56b44f0e23a41

compiler: start using destructure syntax


2 files changed, 74 insertions(+), 108 deletions(-)

src/AstGen.zig+28-46
......@@ -1392,14 +1392,8 @@ fn arrayInitExpr(
13921392
13931393 assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
13941394
1395 const types: struct {
1396 array: Zir.Inst.Ref,
1397 elem: Zir.Inst.Ref,
1398 } = inst: {
1399 if (array_init.ast.type_expr == 0) break :inst .{
1400 .array = .none,
1401 .elem = .none,
1402 };
1395 const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: {
1396 if (array_init.ast.type_expr == 0) break :inst .{ .none, .none };
14031397
14041398 infer: {
14051399 const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer;
......@@ -1414,10 +1408,7 @@ fn arrayInitExpr(
14141408 .lhs = len_inst,
14151409 .rhs = elem_type,
14161410 });
1417 break :inst .{
1418 .array = array_type_inst,
1419 .elem = elem_type,
1420 };
1411 break :inst .{ array_type_inst, elem_type };
14211412 } else {
14221413 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
14231414 const array_type_inst = try gz.addPlNode(
......@@ -1429,10 +1420,7 @@ fn arrayInitExpr(
14291420 .sentinel = sentinel,
14301421 },
14311422 );
1432 break :inst .{
1433 .array = array_type_inst,
1434 .elem = elem_type,
1435 };
1423 break :inst .{ array_type_inst, elem_type };
14361424 }
14371425 }
14381426 }
......@@ -1441,29 +1429,26 @@ fn arrayInitExpr(
14411429 .ty = array_type_inst,
14421430 .init_count = @intCast(array_init.ast.elements.len),
14431431 });
1444 break :inst .{
1445 .array = array_type_inst,
1446 .elem = .none,
1447 };
1432 break :inst .{ array_type_inst, .none };
14481433 };
14491434
14501435 switch (ri.rl) {
14511436 .discard => {
1452 if (types.elem != .none) {
1453 const elem_ri: ResultInfo = .{ .rl = .{ .ty = types.elem } };
1437 if (elem_ty != .none) {
1438 const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } };
14541439 for (array_init.ast.elements) |elem_init| {
14551440 _ = try expr(gz, scope, elem_ri, elem_init);
14561441 }
1457 } else if (types.array != .none) {
1442 } else if (array_ty != .none) {
14581443 for (array_init.ast.elements, 0..) |elem_init, i| {
1459 const elem_ty = try gz.add(.{
1444 const this_elem_ty = try gz.add(.{
14601445 .tag = .elem_type_index,
14611446 .data = .{ .bin = .{
1462 .lhs = types.array,
1447 .lhs = array_ty,
14631448 .rhs = @enumFromInt(i),
14641449 } },
14651450 });
1466 _ = try expr(gz, scope, .{ .rl = .{ .ty = elem_ty } }, elem_init);
1451 _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init);
14671452 }
14681453 } else {
14691454 for (array_init.ast.elements) |elem_init| {
......@@ -1473,15 +1458,15 @@ fn arrayInitExpr(
14731458 return Zir.Inst.Ref.void_value;
14741459 },
14751460 .ref => {
1476 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init_ref else .array_init_anon_ref;
1477 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
1461 const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init_ref else .array_init_anon_ref;
1462 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, tag);
14781463 },
14791464 .none => {
1480 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
1481 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
1465 const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init else .array_init_anon;
1466 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, tag);
14821467 },
14831468 .ty, .coerced_ty => |ty_inst| {
1484 const arr_ty = if (types.array != .none) types.array else blk: {
1469 const arr_ty = if (array_ty != .none) array_ty else blk: {
14851470 const arr_ty = try gz.addUnNode(.opt_eu_base_ty, ty_inst, node);
14861471 _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
14871472 .ty = arr_ty,
......@@ -1489,29 +1474,29 @@ fn arrayInitExpr(
14891474 });
14901475 break :blk arr_ty;
14911476 };
1492 const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, arr_ty, types.elem, .array_init);
1477 const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, arr_ty, elem_ty, .array_init);
14931478 return rvalue(gz, ri, result, node);
14941479 },
14951480 .ptr => |ptr_res| {
1496 return arrayInitExprRlPtr(gz, scope, node, ptr_res.inst, array_init.ast.elements, types.array);
1481 return arrayInitExprRlPtr(gz, scope, node, ptr_res.inst, array_init.ast.elements, array_ty);
14971482 },
14981483 .inferred_ptr => |ptr_inst| {
1499 if (types.array == .none) {
1484 if (array_ty == .none) {
15001485 // We treat this case differently so that we don't get a crash when
15011486 // analyzing array_base_ptr against an alloc_inferred_mut.
15021487 // See corresponding logic in structInitExpr.
15031488 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
15041489 return rvalue(gz, ri, result, node);
15051490 } else {
1506 return arrayInitExprRlPtr(gz, scope, node, ptr_inst, array_init.ast.elements, types.array);
1491 return arrayInitExprRlPtr(gz, scope, node, ptr_inst, array_init.ast.elements, array_ty);
15071492 }
15081493 },
15091494 .destructure => |destructure| {
1510 if (types.array != .none) {
1495 if (array_ty != .none) {
15111496 // We have a specific type, so there may be things like default
15121497 // field values messing with us. Do this as a standard typed
15131498 // init followed by an rvalue destructure.
1514 const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, .array_init);
1499 const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, .array_init);
15151500 return rvalue(gz, ri, result, node);
15161501 }
15171502 // Untyped init - destructure directly into result pointers
......@@ -3177,10 +3162,7 @@ fn varDecl(
31773162 .keyword_var => {
31783163 const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
31793164 var resolve_inferred_alloc: Zir.Inst.Ref = .none;
3180 const var_data: struct {
3181 result_info: ResultInfo,
3182 alloc: Zir.Inst.Ref,
3183 } = if (var_decl.ast.type_node != 0) a: {
3165 const alloc: Zir.Inst.Ref, const result_info: ResultInfo = if (var_decl.ast.type_node != 0) a: {
31843166 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
31853167 const alloc = alloc: {
31863168 if (align_inst == .none) {
......@@ -3199,7 +3181,7 @@ fn varDecl(
31993181 });
32003182 }
32013183 };
3202 break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
3184 break :a .{ alloc, .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
32033185 } else a: {
32043186 const alloc = alloc: {
32053187 if (align_inst == .none) {
......@@ -3219,24 +3201,24 @@ fn varDecl(
32193201 }
32203202 };
32213203 resolve_inferred_alloc = alloc;
3222 break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .inferred_ptr = alloc } } };
3204 break :a .{ alloc, .{ .rl = .{ .inferred_ptr = alloc } } };
32233205 };
32243206 const prev_anon_name_strategy = gz.anon_name_strategy;
32253207 gz.anon_name_strategy = .dbg_var;
3226 _ = try reachableExprComptime(gz, scope, var_data.result_info, var_decl.ast.init_node, node, is_comptime);
3208 _ = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, is_comptime);
32273209 gz.anon_name_strategy = prev_anon_name_strategy;
32283210 if (resolve_inferred_alloc != .none) {
32293211 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
32303212 }
32313213
3232 try gz.addDbgVar(.dbg_var_ptr, ident_name, var_data.alloc);
3214 try gz.addDbgVar(.dbg_var_ptr, ident_name, alloc);
32333215
32343216 const sub_scope = try block_arena.create(Scope.LocalPtr);
32353217 sub_scope.* = .{
32363218 .parent = scope,
32373219 .gen_zir = gz,
32383220 .name = ident_name,
3239 .ptr = var_data.alloc,
3221 .ptr = alloc,
32403222 .token_src = name_token,
32413223 .maybe_comptime = is_comptime,
32423224 .id_cat = .@"local variable",
src/Sema.zig+46-62
......@@ -11092,17 +11092,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1109211092 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
1109311093 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
1109411094
11095 const raw_operand: struct { val: Air.Inst.Ref, ptr: Air.Inst.Ref } = blk: {
11095 const raw_operand_val: Air.Inst.Ref, const raw_operand_ptr: Air.Inst.Ref = blk: {
1109611096 const maybe_ptr = try sema.resolveInst(extra.data.operand);
1109711097 if (operand_is_ref) {
1109811098 const val = try sema.analyzeLoad(block, src, maybe_ptr, operand_src);
11099 break :blk .{ .val = val, .ptr = maybe_ptr };
11099 break :blk .{ val, maybe_ptr };
1110011100 } else {
11101 break :blk .{ .val = maybe_ptr, .ptr = undefined };
11101 break :blk .{ maybe_ptr, undefined };
1110211102 }
1110311103 };
1110411104
11105 const operand = try sema.switchCond(block, operand_src, raw_operand.val);
11105 const operand = try sema.switchCond(block, operand_src, raw_operand_val);
1110611106
1110711107 // AstGen guarantees that the instruction immediately preceding
1110811108 // switch_block(_ref) is a dbg_stmt
......@@ -11160,7 +11160,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1116011160 },
1116111161 };
1116211162
11163 const maybe_union_ty = sema.typeOf(raw_operand.val);
11163 const maybe_union_ty = sema.typeOf(raw_operand_val);
1116411164 const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union;
1116511165
1116611166 // Duplicate checking variables later also used for `inline else`.
......@@ -11711,8 +11711,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1171111711 const spa: SwitchProngAnalysis = .{
1171211712 .sema = sema,
1171311713 .parent_block = block,
11714 .operand = raw_operand.val,
11715 .operand_ptr = raw_operand.ptr,
11714 .operand = raw_operand_val,
11715 .operand_ptr = raw_operand_ptr,
1171611716 .cond = operand,
1171711717 .else_error_ty = else_error_ty,
1171811718 .switch_block_inst = inst,
......@@ -15500,11 +15500,7 @@ fn analyzeArithmetic(
1550015500
1550115501 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
1550215502 const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs);
15503 const rs: struct {
15504 src: LazySrcLoc,
15505 air_tag: Air.Inst.Tag,
15506 air_tag_safe: Air.Inst.Tag,
15507 } = rs: {
15503 const runtime_src: LazySrcLoc, const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag = rs: {
1550815504 switch (zir_tag) {
1550915505 .add, .add_unsafe => {
1551015506 // For integers:intAddSat
......@@ -15551,8 +15547,8 @@ fn analyzeArithmetic(
1555115547 } else {
1555215548 return Air.internedToRef((try Value.floatAdd(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern());
1555315549 }
15554 } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .add_safe };
15555 } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .add_safe };
15550 } else break :rs .{ rhs_src, air_tag, .add_safe };
15551 } else break :rs .{ lhs_src, air_tag, .add_safe };
1555615552 },
1555715553 .addwrap => {
1555815554 // Integers only; floats are checked above.
......@@ -15572,8 +15568,8 @@ fn analyzeArithmetic(
1557215568 }
1557315569 if (maybe_lhs_val) |lhs_val| {
1557415570 return Air.internedToRef((try sema.numberAddWrapScalar(lhs_val, rhs_val, resolved_type)).toIntern());
15575 } else break :rs .{ .src = lhs_src, .air_tag = .add_wrap, .air_tag_safe = .add_wrap };
15576 } else break :rs .{ .src = rhs_src, .air_tag = .add_wrap, .air_tag_safe = .add_wrap };
15571 } else break :rs .{ lhs_src, .add_wrap, .add_wrap };
15572 } else break :rs .{ rhs_src, .add_wrap, .add_wrap };
1557715573 },
1557815574 .add_sat => {
1557915575 // Integers only; floats are checked above.
......@@ -15599,14 +15595,14 @@ fn analyzeArithmetic(
1559915595
1560015596 return Air.internedToRef(val.toIntern());
1560115597 } else break :rs .{
15602 .src = lhs_src,
15603 .air_tag = .add_sat,
15604 .air_tag_safe = .add_sat,
15598 lhs_src,
15599 .add_sat,
15600 .add_sat,
1560515601 };
1560615602 } else break :rs .{
15607 .src = rhs_src,
15608 .air_tag = .add_sat,
15609 .air_tag_safe = .add_sat,
15603 rhs_src,
15604 .add_sat,
15605 .add_sat,
1561015606 };
1561115607 },
1561215608 .sub => {
......@@ -15649,8 +15645,8 @@ fn analyzeArithmetic(
1564915645 } else {
1565015646 return Air.internedToRef((try Value.floatSub(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern());
1565115647 }
15652 } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .sub_safe };
15653 } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .sub_safe };
15648 } else break :rs .{ rhs_src, air_tag, .sub_safe };
15649 } else break :rs .{ lhs_src, air_tag, .sub_safe };
1565415650 },
1565515651 .subwrap => {
1565615652 // Integers only; floats are checked above.
......@@ -15670,8 +15666,8 @@ fn analyzeArithmetic(
1567015666 }
1567115667 if (maybe_rhs_val) |rhs_val| {
1567215668 return Air.internedToRef((try sema.numberSubWrapScalar(lhs_val, rhs_val, resolved_type)).toIntern());
15673 } else break :rs .{ .src = rhs_src, .air_tag = .sub_wrap, .air_tag_safe = .sub_wrap };
15674 } else break :rs .{ .src = lhs_src, .air_tag = .sub_wrap, .air_tag_safe = .sub_wrap };
15669 } else break :rs .{ rhs_src, .sub_wrap, .sub_wrap };
15670 } else break :rs .{ lhs_src, .sub_wrap, .sub_wrap };
1567515671 },
1567615672 .sub_sat => {
1567715673 // Integers only; floats are checked above.
......@@ -15696,8 +15692,8 @@ fn analyzeArithmetic(
1569615692 try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod);
1569715693
1569815694 return Air.internedToRef(val.toIntern());
15699 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat, .air_tag_safe = .sub_sat };
15700 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat, .air_tag_safe = .sub_sat };
15695 } else break :rs .{ rhs_src, .sub_sat, .sub_sat };
15696 } else break :rs .{ lhs_src, .sub_sat, .sub_sat };
1570115697 },
1570215698 .mul => {
1570315699 // For integers:
......@@ -15789,8 +15785,8 @@ fn analyzeArithmetic(
1578915785 } else {
1579015786 return Air.internedToRef((try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, mod)).toIntern());
1579115787 }
15792 } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .mul_safe };
15793 } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .mul_safe };
15788 } else break :rs .{ lhs_src, air_tag, .mul_safe };
15789 } else break :rs .{ rhs_src, air_tag, .mul_safe };
1579415790 },
1579515791 .mulwrap => {
1579615792 // Integers only; floats are handled above.
......@@ -15834,8 +15830,8 @@ fn analyzeArithmetic(
1583415830 return mod.undefRef(resolved_type);
1583515831 }
1583615832 return Air.internedToRef((try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, mod)).toIntern());
15837 } else break :rs .{ .src = lhs_src, .air_tag = .mul_wrap, .air_tag_safe = .mul_wrap };
15838 } else break :rs .{ .src = rhs_src, .air_tag = .mul_wrap, .air_tag_safe = .mul_wrap };
15833 } else break :rs .{ lhs_src, .mul_wrap, .mul_wrap };
15834 } else break :rs .{ rhs_src, .mul_wrap, .mul_wrap };
1583915835 },
1584015836 .mul_sat => {
1584115837 // Integers only; floats are checked above.
......@@ -15885,20 +15881,20 @@ fn analyzeArithmetic(
1588515881 try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, mod);
1588615882
1588715883 return Air.internedToRef(val.toIntern());
15888 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat, .air_tag_safe = .mul_sat };
15889 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat, .air_tag_safe = .mul_sat };
15884 } else break :rs .{ lhs_src, .mul_sat, .mul_sat };
15885 } else break :rs .{ rhs_src, .mul_sat, .mul_sat };
1589015886 },
1589115887 else => unreachable,
1589215888 }
1589315889 };
1589415890
15895 try sema.requireRuntimeBlock(block, src, rs.src);
15891 try sema.requireRuntimeBlock(block, src, runtime_src);
1589615892 if (block.wantSafety() and want_safety and scalar_tag == .Int) {
1589715893 if (mod.backendSupportsFeature(.safety_checked_instructions)) {
1589815894 _ = try sema.preparePanicId(block, .integer_overflow);
15899 return block.addBinOp(rs.air_tag_safe, casted_lhs, casted_rhs);
15895 return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs);
1590015896 } else {
15901 const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) {
15897 const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) {
1590215898 .add => .add_with_overflow,
1590315899 .sub => .sub_with_overflow,
1590415900 .mul => .mul_with_overflow,
......@@ -15935,7 +15931,7 @@ fn analyzeArithmetic(
1593515931 }
1593615932 }
1593715933 }
15938 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
15934 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
1593915935}
1594015936
1594115937fn analyzePtrArithmetic(
......@@ -32345,16 +32341,10 @@ fn compareIntsOnlyPossibleResult(
3234532341
3234632342 // For any other comparison, we need to know if the LHS value is
3234732343 // equal to the maximum or minimum possible value of the RHS type.
32348 const edge: struct { min: bool, max: bool } = edge: {
32349 if (is_zero and rhs_info.signedness == .unsigned) break :edge .{
32350 .min = true,
32351 .max = false,
32352 };
32344 const is_min, const is_max = edge: {
32345 if (is_zero and rhs_info.signedness == .unsigned) break :edge .{ true, false };
3235332346
32354 if (req_bits != rhs_info.bits) break :edge .{
32355 .min = false,
32356 .max = false,
32357 };
32347 if (req_bits != rhs_info.bits) break :edge .{ false, false };
3235832348
3235932349 const ty = try mod.intType(
3236032350 if (is_negative) .signed else .unsigned,
......@@ -32363,24 +32353,18 @@ fn compareIntsOnlyPossibleResult(
3236332353 const pop_count = lhs_val.popCount(ty, mod);
3236432354
3236532355 if (is_negative) {
32366 break :edge .{
32367 .min = pop_count == 1,
32368 .max = false,
32369 };
32356 break :edge .{ pop_count == 1, false };
3237032357 } else {
32371 break :edge .{
32372 .min = false,
32373 .max = pop_count == req_bits - sign_adj,
32374 };
32358 break :edge .{ false, pop_count == req_bits - sign_adj };
3237532359 }
3237632360 };
3237732361
3237832362 assert(fits);
3237932363 return switch (op) {
32380 .lt => if (edge.max) false else null,
32381 .lte => if (edge.min) true else null,
32382 .gt => if (edge.min) false else null,
32383 .gte => if (edge.max) true else null,
32364 .lt => if (is_max) false else null,
32365 .lte => if (is_min) true else null,
32366 .gt => if (is_min) false else null,
32367 .gte => if (is_max) true else null,
3238432368 .eq, .neq => unreachable,
3238532369 };
3238632370}
......@@ -32617,7 +32601,7 @@ const PeerResolveStrategy = enum {
3261732601 either,
3261832602 };
3261932603
32620 const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) {
32604 const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) {
3262132605 .unknown => .{ .all_s1, s1 },
3262232606 .error_set => switch (s1) {
3262332607 .error_set => .{ .either, .error_set },
......@@ -32685,7 +32669,7 @@ const PeerResolveStrategy = enum {
3268532669 .exact => .{ .all_s0, .exact },
3268632670 };
3268732671
32688 switch (res[0]) {
32672 switch (reason_method) {
3268932673 .all_s0 => {
3269032674 if (!s0_is_a) {
3269132675 reason_peer.* = b_peer_idx;
......@@ -32702,7 +32686,7 @@ const PeerResolveStrategy = enum {
3270232686 },
3270332687 }
3270432688
32705 return res[1];
32689 return strat;
3270632690 }
3270732691
3270832692 fn select(ty: Type, mod: *Module) PeerResolveStrategy {