| author | |
| committer | |
| log | 293d6bdc73c5fe01b07ebe3d09c9a78613fed093 |
| tree | ff2ab85b168aa731fcdf1ddb530ed70d1c285245 |
| parent | 841add6890d001d315591dc20f7d464c264d88bb |
4 files changed, 106 insertions(+), 143 deletions(-)
src/AstGen.zig+71-88| ... | @@ -88,7 +88,6 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { | ... | @@ -88,7 +88,6 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { |
| 88 | Zir.Inst.BuiltinCall.Flags => @bitCast(u32, @field(extra, field.name)), | 88 | Zir.Inst.BuiltinCall.Flags => @bitCast(u32, @field(extra, field.name)), |
| 89 | Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)), | 89 | Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)), |
| 90 | Zir.Inst.FuncFancy.Bits => @bitCast(u32, @field(extra, field.name)), | 90 | Zir.Inst.FuncFancy.Bits => @bitCast(u32, @field(extra, field.name)), |
| 91 | Zir.Inst.ElemPtrImm.Bits => @bitCast(u32, @field(extra, field.name)), | ||
| 92 | else => @compileError("bad field type"), | 91 | else => @compileError("bad field type"), |
| 93 | }; | 92 | }; |
| 94 | i += 1; | 93 | i += 1; |
| ... | @@ -1566,9 +1565,7 @@ fn arrayInitExprRlPtrInner( | ... | @@ -1566,9 +1565,7 @@ fn arrayInitExprRlPtrInner( |
| 1566 | for (elements) |elem_init, i| { | 1565 | for (elements) |elem_init, i| { |
| 1567 | const elem_ptr = try gz.addPlNode(.elem_ptr_imm, elem_init, Zir.Inst.ElemPtrImm{ | 1566 | const elem_ptr = try gz.addPlNode(.elem_ptr_imm, elem_init, Zir.Inst.ElemPtrImm{ |
| 1568 | .ptr = result_ptr, | 1567 | .ptr = result_ptr, |
| 1569 | .bits = .{ | 1568 | .index = @intCast(u32, i), |
| 1570 | .index = @intCast(u31, i), | ||
| 1571 | }, | ||
| 1572 | }); | 1569 | }); |
| 1573 | astgen.extra.items[extra_index] = refToIndex(elem_ptr).?; | 1570 | astgen.extra.items[extra_index] = refToIndex(elem_ptr).?; |
| 1574 | extra_index += 1; | 1571 | extra_index += 1; |
| ... | @@ -2601,6 +2598,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2601,6 +2598,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2601 | .field_base_ptr, | 2598 | .field_base_ptr, |
| 2602 | .ret_ptr, | 2599 | .ret_ptr, |
| 2603 | .ret_type, | 2600 | .ret_type, |
| 2601 | .for_len, | ||
| 2604 | .@"try", | 2602 | .@"try", |
| 2605 | .try_ptr, | 2603 | .try_ptr, |
| 2606 | //.try_inline, | 2604 | //.try_inline, |
| ... | @@ -2669,7 +2667,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2669,7 +2667,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2669 | .validate_deref, | 2667 | .validate_deref, |
| 2670 | .save_err_ret_index, | 2668 | .save_err_ret_index, |
| 2671 | .restore_err_ret_index, | 2669 | .restore_err_ret_index, |
| 2672 | .for_check_lens, | ||
| 2673 | => break :b true, | 2670 | => break :b true, |
| 2674 | 2671 | ||
| 2675 | .@"defer" => unreachable, | 2672 | .@"defer" => unreachable, |
| ... | @@ -6305,23 +6302,26 @@ fn forExpr( | ... | @@ -6305,23 +6302,26 @@ fn forExpr( |
| 6305 | const node_data = tree.nodes.items(.data); | 6302 | const node_data = tree.nodes.items(.data); |
| 6306 | const gpa = astgen.gpa; | 6303 | const gpa = astgen.gpa; |
| 6307 | 6304 | ||
| 6308 | const allocs = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len); | 6305 | // For counters, this is the start value; for indexables, this is the base |
| 6309 | defer gpa.free(allocs); | 6306 | // pointer that can be used with elem_ptr and similar instructions. |
| 6307 | // Special value `none` means that this is a counter and its start value is | ||
| 6308 | // zero, indicating that the main index counter can be used directly. | ||
| 6309 | const indexables = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len); | ||
| 6310 | defer gpa.free(indexables); | ||
| 6310 | // elements of this array can be `none`, indicating no length check. | 6311 | // elements of this array can be `none`, indicating no length check. |
| 6311 | const lens = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len); | 6312 | const lens = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len); |
| 6312 | defer gpa.free(lens); | 6313 | defer gpa.free(lens); |
| 6313 | 6314 | ||
| 6314 | const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc_mut; | 6315 | // We will use a single zero-based counter no matter how many indexables there are. |
| 6316 | const index_ptr = blk: { | ||
| 6317 | const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc; | ||
| 6318 | const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node); | ||
| 6319 | // initialize to zero | ||
| 6320 | _ = try parent_gz.addBin(.store, index_ptr, .zero_usize); | ||
| 6321 | break :blk index_ptr; | ||
| 6322 | }; | ||
| 6315 | 6323 | ||
| 6316 | // Tracks the index of allocs/lens that has a length to be checked and is | 6324 | var any_len_checks = false; |
| 6317 | // used for the end value. | ||
| 6318 | // If this is null, there are no len checks. | ||
| 6319 | var end_input_index: ?u32 = null; | ||
| 6320 | // This is a value to use to find out if the for loop has reached the end | ||
| 6321 | // yet. It prefers to use a counter since the end value is provided directly, | ||
| 6322 | // and otherwise falls back to adding ptr+len of a slice to compute end. | ||
| 6323 | // Corresponds to end_input_index and will be .none in case that value is null. | ||
| 6324 | var cond_end_val: Zir.Inst.Ref = .none; | ||
| 6325 | 6325 | ||
| 6326 | { | 6326 | { |
| 6327 | var capture_token = for_full.payload_token; | 6327 | var capture_token = for_full.payload_token; |
| ... | @@ -6341,10 +6341,8 @@ fn forExpr( | ... | @@ -6341,10 +6341,8 @@ fn forExpr( |
| 6341 | if (capture_is_ref) { | 6341 | if (capture_is_ref) { |
| 6342 | return astgen.failTok(ident_tok, "cannot capture reference to range", .{}); | 6342 | return astgen.failTok(ident_tok, "cannot capture reference to range", .{}); |
| 6343 | } | 6343 | } |
| 6344 | const counter_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node); | ||
| 6345 | const start_node = node_data[input].lhs; | 6344 | const start_node = node_data[input].lhs; |
| 6346 | const start_val = try expr(parent_gz, scope, .{ .rl = .none }, start_node); | 6345 | const start_val = try expr(parent_gz, scope, .{ .rl = .none }, start_node); |
| 6347 | _ = try parent_gz.addBin(.store, counter_ptr, start_val); | ||
| 6348 | 6346 | ||
| 6349 | const end_node = node_data[input].rhs; | 6347 | const end_node = node_data[input].rhs; |
| 6350 | const end_val = if (end_node != 0) | 6348 | const end_val = if (end_node != 0) |
| ... | @@ -6352,7 +6350,8 @@ fn forExpr( | ... | @@ -6352,7 +6350,8 @@ fn forExpr( |
| 6352 | else | 6350 | else |
| 6353 | .none; | 6351 | .none; |
| 6354 | 6352 | ||
| 6355 | const range_len = if (end_val == .none or nodeIsTriviallyZero(tree, start_node)) | 6353 | const start_is_zero = nodeIsTriviallyZero(tree, start_node); |
| 6354 | const range_len = if (end_val == .none or start_is_zero) | ||
| 6356 | end_val | 6355 | end_val |
| 6357 | else | 6356 | else |
| 6358 | try parent_gz.addPlNode(.sub, input, Zir.Inst.Bin{ | 6357 | try parent_gz.addPlNode(.sub, input, Zir.Inst.Bin{ |
| ... | @@ -6360,61 +6359,33 @@ fn forExpr( | ... | @@ -6360,61 +6359,33 @@ fn forExpr( |
| 6360 | .rhs = start_val, | 6359 | .rhs = start_val, |
| 6361 | }); | 6360 | }); |
| 6362 | 6361 | ||
| 6363 | if (range_len != .none and cond_end_val == .none) { | 6362 | any_len_checks = any_len_checks or range_len != .none; |
| 6364 | end_input_index = i; | 6363 | indexables[i] = if (start_is_zero) .none else start_val; |
| 6365 | cond_end_val = end_val; | ||
| 6366 | } | ||
| 6367 | |||
| 6368 | allocs[i] = counter_ptr; | ||
| 6369 | lens[i] = range_len; | 6364 | lens[i] = range_len; |
| 6370 | } else { | 6365 | } else { |
| 6371 | const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input); | 6366 | const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input); |
| 6372 | // This instruction has nice compile errors so we put it before the other ones | 6367 | const indexable_len = try parent_gz.addUnNode(.indexable_ptr_len, indexable, input); |
| 6373 | // even though it is not needed until later in the block. | ||
| 6374 | const ptr_len = try parent_gz.addUnNode(.indexable_ptr_len, indexable, input); | ||
| 6375 | const base_ptr = try parent_gz.addPlNode(.elem_ptr_imm, input, Zir.Inst.ElemPtrImm{ | ||
| 6376 | .ptr = indexable, | ||
| 6377 | .bits = .{ | ||
| 6378 | .index = 0, | ||
| 6379 | .manyptr = true, | ||
| 6380 | }, | ||
| 6381 | }); | ||
| 6382 | const alloc_ty_inst = try parent_gz.addUnNode(.typeof, base_ptr, node); | ||
| 6383 | const alloc = try parent_gz.addUnNode(alloc_tag, alloc_ty_inst, node); | ||
| 6384 | _ = try parent_gz.addBin(.store, alloc, base_ptr); | ||
| 6385 | |||
| 6386 | if (end_input_index == null) { | ||
| 6387 | end_input_index = i; | ||
| 6388 | assert(cond_end_val == .none); | ||
| 6389 | } | ||
| 6390 | 6368 | ||
| 6391 | allocs[i] = alloc; | 6369 | any_len_checks = true; |
| 6392 | lens[i] = ptr_len; | 6370 | indexables[i] = indexable; |
| 6371 | lens[i] = indexable_len; | ||
| 6393 | } | 6372 | } |
| 6394 | } | 6373 | } |
| 6395 | } | 6374 | } |
| 6396 | 6375 | ||
| 6397 | // In case there are no counters which already have an end computed, we | ||
| 6398 | // compute an end from base pointer plus length. | ||
| 6399 | if (end_input_index) |i| { | ||
| 6400 | if (cond_end_val == .none) { | ||
| 6401 | cond_end_val = try parent_gz.addPlNode(.add, for_full.ast.inputs[i], Zir.Inst.Bin{ | ||
| 6402 | .lhs = allocs[i], | ||
| 6403 | .rhs = lens[i], | ||
| 6404 | }); | ||
| 6405 | } | ||
| 6406 | } | ||
| 6407 | |||
| 6408 | // We use a dedicated ZIR instruction to assert the lengths to assist with | 6376 | // We use a dedicated ZIR instruction to assert the lengths to assist with |
| 6409 | // nicer error reporting as well as fewer ZIR bytes emitted. | 6377 | // nicer error reporting as well as fewer ZIR bytes emitted. |
| 6410 | if (end_input_index != null) { | 6378 | const len: Zir.Inst.Ref = len: { |
| 6379 | if (!any_len_checks) break :len .none; | ||
| 6380 | |||
| 6411 | const lens_len = @intCast(u32, lens.len); | 6381 | const lens_len = @intCast(u32, lens.len); |
| 6412 | try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).Struct.fields.len + lens_len); | 6382 | try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).Struct.fields.len + lens_len); |
| 6413 | _ = try parent_gz.addPlNode(.for_check_lens, node, Zir.Inst.MultiOp{ | 6383 | const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{ |
| 6414 | .operands_len = lens_len, | 6384 | .operands_len = lens_len, |
| 6415 | }); | 6385 | }); |
| 6416 | appendRefsAssumeCapacity(astgen, lens); | 6386 | appendRefsAssumeCapacity(astgen, lens); |
| 6417 | } | 6387 | break :len len; |
| 6388 | }; | ||
| 6418 | 6389 | ||
| 6419 | const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; | 6390 | const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop; |
| 6420 | const loop_block = try parent_gz.makeBlockInst(loop_tag, node); | 6391 | const loop_block = try parent_gz.makeBlockInst(loop_tag, node); |
| ... | @@ -6429,22 +6400,14 @@ fn forExpr( | ... | @@ -6429,22 +6400,14 @@ fn forExpr( |
| 6429 | var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); | 6400 | var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); |
| 6430 | defer cond_scope.unstack(); | 6401 | defer cond_scope.unstack(); |
| 6431 | 6402 | ||
| 6432 | // Load all the iterables. | ||
| 6433 | const loaded_ptrs = try gpa.alloc(Zir.Inst.Ref, allocs.len); | ||
| 6434 | defer gpa.free(loaded_ptrs); | ||
| 6435 | for (allocs) |alloc, i| { | ||
| 6436 | loaded_ptrs[i] = try cond_scope.addUnNode(.load, alloc, for_full.ast.inputs[i]); | ||
| 6437 | } | ||
| 6438 | |||
| 6439 | // Check the condition. | 6403 | // Check the condition. |
| 6440 | const input_index = end_input_index orelse { | 6404 | if (!any_len_checks) { |
| 6441 | return astgen.failNode(node, "TODO: handle infinite for loop", .{}); | 6405 | return astgen.failNode(node, "TODO: handle infinite for loop", .{}); |
| 6442 | }; | 6406 | } |
| 6443 | assert(cond_end_val != .none); | 6407 | const index = try cond_scope.addUnNode(.load, index_ptr, node); |
| 6444 | 6408 | const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{ | |
| 6445 | const cond = try cond_scope.addPlNode(.cmp_neq, for_full.ast.inputs[input_index], Zir.Inst.Bin{ | 6409 | .lhs = index, |
| 6446 | .lhs = loaded_ptrs[input_index], | 6410 | .rhs = len, |
| 6447 | .rhs = cond_end_val, | ||
| 6448 | }); | 6411 | }); |
| 6449 | 6412 | ||
| 6450 | const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; | 6413 | const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr; |
| ... | @@ -6455,14 +6418,12 @@ fn forExpr( | ... | @@ -6455,14 +6418,12 @@ fn forExpr( |
| 6455 | // cond_block unstacked now, can add new instructions to loop_scope | 6418 | // cond_block unstacked now, can add new instructions to loop_scope |
| 6456 | try loop_scope.instructions.append(gpa, cond_block); | 6419 | try loop_scope.instructions.append(gpa, cond_block); |
| 6457 | 6420 | ||
| 6458 | // Increment the loop variables. | 6421 | // Increment the index variable. |
| 6459 | for (allocs) |alloc, i| { | 6422 | const index_plus_one = try loop_scope.addPlNode(.add, node, Zir.Inst.Bin{ |
| 6460 | const incremented = try loop_scope.addPlNode(.add, node, Zir.Inst.Bin{ | 6423 | .lhs = index, |
| 6461 | .lhs = loaded_ptrs[i], | 6424 | .rhs = .one_usize, |
| 6462 | .rhs = .one_usize, | 6425 | }); |
| 6463 | }); | 6426 | _ = try loop_scope.addBin(.store, index_ptr, index_plus_one); |
| 6464 | _ = try loop_scope.addBin(.store, alloc, incremented); | ||
| 6465 | } | ||
| 6466 | const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; | 6427 | const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; |
| 6467 | _ = try loop_scope.addNode(repeat_tag, node); | 6428 | _ = try loop_scope.addNode(repeat_tag, node); |
| 6468 | 6429 | ||
| ... | @@ -6500,21 +6461,43 @@ fn forExpr( | ... | @@ -6500,21 +6461,43 @@ fn forExpr( |
| 6500 | const name_str_index = try astgen.identAsString(ident_tok); | 6461 | const name_str_index = try astgen.identAsString(ident_tok); |
| 6501 | try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture); | 6462 | try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture); |
| 6502 | 6463 | ||
| 6503 | const loaded = if (capture_is_ref) | 6464 | const capture_inst = inst: { |
| 6504 | loaded_ptrs[i] | 6465 | const is_counter = node_tags[input] == .for_range; |
| 6505 | else | 6466 | |
| 6506 | try then_scope.addUnNode(.load, loaded_ptrs[i], input); | 6467 | if (indexables[i] == .none) { |
| 6468 | // Special case: the main index can be used directly. | ||
| 6469 | assert(is_counter); | ||
| 6470 | assert(!capture_is_ref); | ||
| 6471 | break :inst index; | ||
| 6472 | } | ||
| 6473 | |||
| 6474 | // For counters, we add the index variable to the start value; for | ||
| 6475 | // indexables, we use it as an element index. This is so similar | ||
| 6476 | // that they can share the same code paths, branching only on the | ||
| 6477 | // ZIR tag. | ||
| 6478 | const switch_cond = (@as(u2, @boolToInt(capture_is_ref)) << 1) | @boolToInt(is_counter); | ||
| 6479 | const tag: Zir.Inst.Tag = switch (switch_cond) { | ||
| 6480 | 0b00 => .elem_val, | ||
| 6481 | 0b01 => .add, | ||
| 6482 | 0b10 => .elem_ptr, | ||
| 6483 | 0b11 => unreachable, // compile error emitted already | ||
| 6484 | }; | ||
| 6485 | break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{ | ||
| 6486 | .lhs = indexables[i], | ||
| 6487 | .rhs = index, | ||
| 6488 | }); | ||
| 6489 | }; | ||
| 6507 | 6490 | ||
| 6508 | capture_scopes[i] = .{ | 6491 | capture_scopes[i] = .{ |
| 6509 | .parent = capture_sub_scope, | 6492 | .parent = capture_sub_scope, |
| 6510 | .gen_zir = &then_scope, | 6493 | .gen_zir = &then_scope, |
| 6511 | .name = name_str_index, | 6494 | .name = name_str_index, |
| 6512 | .inst = loaded, | 6495 | .inst = capture_inst, |
| 6513 | .token_src = ident_tok, | 6496 | .token_src = ident_tok, |
| 6514 | .id_cat = .capture, | 6497 | .id_cat = .capture, |
| 6515 | }; | 6498 | }; |
| 6516 | 6499 | ||
| 6517 | try then_scope.addDbgVar(.dbg_var_val, name_str_index, loaded); | 6500 | try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst); |
| 6518 | capture_sub_scope = &capture_scopes[i].base; | 6501 | capture_sub_scope = &capture_scopes[i].base; |
| 6519 | } | 6502 | } |
| 6520 | 6503 |
src/Sema.zig+26-37| ... | @@ -1035,6 +1035,7 @@ fn analyzeBodyInner( | ... | @@ -1035,6 +1035,7 @@ fn analyzeBodyInner( |
| 1035 | .@"await" => try sema.zirAwait(block, inst), | 1035 | .@"await" => try sema.zirAwait(block, inst), |
| 1036 | .array_base_ptr => try sema.zirArrayBasePtr(block, inst), | 1036 | .array_base_ptr => try sema.zirArrayBasePtr(block, inst), |
| 1037 | .field_base_ptr => try sema.zirFieldBasePtr(block, inst), | 1037 | .field_base_ptr => try sema.zirFieldBasePtr(block, inst), |
| 1038 | .for_len => try sema.zirForLen(block, inst), | ||
| 1038 | 1039 | ||
| 1039 | .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), | 1040 | .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), |
| 1040 | .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), | 1041 | .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), |
| ... | @@ -1386,11 +1387,6 @@ fn analyzeBodyInner( | ... | @@ -1386,11 +1387,6 @@ fn analyzeBodyInner( |
| 1386 | i += 1; | 1387 | i += 1; |
| 1387 | continue; | 1388 | continue; |
| 1388 | }, | 1389 | }, |
| 1389 | .for_check_lens => { | ||
| 1390 | try sema.zirForCheckLens(block, inst); | ||
| 1391 | i += 1; | ||
| 1392 | continue; | ||
| 1393 | }, | ||
| 1394 | 1390 | ||
| 1395 | // Special case instructions to handle comptime control flow. | 1391 | // Special case instructions to handle comptime control flow. |
| 1396 | .@"break" => { | 1392 | .@"break" => { |
| ... | @@ -3924,6 +3920,16 @@ fn zirFieldBasePtr( | ... | @@ -3924,6 +3920,16 @@ fn zirFieldBasePtr( |
| 3924 | return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType()); | 3920 | return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType()); |
| 3925 | } | 3921 | } |
| 3926 | 3922 | ||
| 3923 | fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | ||
| 3924 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 3925 | const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); | ||
| 3926 | const args = sema.code.refSlice(extra.end, extra.data.operands_len); | ||
| 3927 | const src = inst_data.src(); | ||
| 3928 | |||
| 3929 | _ = args; | ||
| 3930 | return sema.fail(block, src, "TODO implement zirForCheckLens", .{}); | ||
| 3931 | } | ||
| 3932 | |||
| 3927 | fn validateArrayInitTy( | 3933 | fn validateArrayInitTy( |
| 3928 | sema: *Sema, | 3934 | sema: *Sema, |
| 3929 | block: *Block, | 3935 | block: *Block, |
| ... | @@ -9649,7 +9655,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -9649,7 +9655,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 9649 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | 9655 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9650 | const array_ptr = try sema.resolveInst(extra.lhs); | 9656 | const array_ptr = try sema.resolveInst(extra.lhs); |
| 9651 | const elem_index = try sema.resolveInst(extra.rhs); | 9657 | const elem_index = try sema.resolveInst(extra.rhs); |
| 9652 | return sema.elemPtr(block, src, array_ptr, elem_index, src, false, .One); | 9658 | return sema.elemPtr(block, src, array_ptr, elem_index, src, false); |
| 9653 | } | 9659 | } |
| 9654 | 9660 | ||
| 9655 | fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9661 | fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -9662,7 +9668,7 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -9662,7 +9668,7 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 9662 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | 9668 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9663 | const array_ptr = try sema.resolveInst(extra.lhs); | 9669 | const array_ptr = try sema.resolveInst(extra.lhs); |
| 9664 | const elem_index = try sema.resolveInst(extra.rhs); | 9670 | const elem_index = try sema.resolveInst(extra.rhs); |
| 9665 | return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, .One); | 9671 | return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false); |
| 9666 | } | 9672 | } |
| 9667 | 9673 | ||
| 9668 | fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9674 | fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -9673,9 +9679,8 @@ fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! | ... | @@ -9673,9 +9679,8 @@ fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 9673 | const src = inst_data.src(); | 9679 | const src = inst_data.src(); |
| 9674 | const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; | 9680 | const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; |
| 9675 | const array_ptr = try sema.resolveInst(extra.ptr); | 9681 | const array_ptr = try sema.resolveInst(extra.ptr); |
| 9676 | const elem_index = try sema.addIntUnsigned(Type.usize, extra.bits.index); | 9682 | const elem_index = try sema.addIntUnsigned(Type.usize, extra.index); |
| 9677 | const size: std.builtin.Type.Pointer.Size = if (extra.bits.manyptr) .Many else .One; | 9683 | return sema.elemPtr(block, src, array_ptr, elem_index, src, true); |
| 9678 | return sema.elemPtr(block, src, array_ptr, elem_index, src, true, size); | ||
| 9679 | } | 9684 | } |
| 9680 | 9685 | ||
| 9681 | fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9686 | fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -17102,16 +17107,6 @@ fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) | ... | @@ -17102,16 +17107,6 @@ fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) |
| 17102 | return sema.popErrorReturnTrace(start_block, src, operand, saved_index); | 17107 | return sema.popErrorReturnTrace(start_block, src, operand, saved_index); |
| 17103 | } | 17108 | } |
| 17104 | 17109 | ||
| 17105 | fn zirForCheckLens(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | ||
| 17106 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 17107 | const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); | ||
| 17108 | const args = sema.code.refSlice(extra.end, extra.data.operands_len); | ||
| 17109 | const src = inst_data.src(); | ||
| 17110 | |||
| 17111 | _ = args; | ||
| 17112 | return sema.fail(block, src, "TODO implement zirForCheckLens", .{}); | ||
| 17113 | } | ||
| 17114 | |||
| 17115 | fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { | 17110 | fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { |
| 17116 | assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion); | 17111 | assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion); |
| 17117 | 17112 | ||
| ... | @@ -22906,7 +22901,7 @@ fn panicSentinelMismatch( | ... | @@ -22906,7 +22901,7 @@ fn panicSentinelMismatch( |
| 22906 | const actual_sentinel = if (ptr_ty.isSlice()) | 22901 | const actual_sentinel = if (ptr_ty.isSlice()) |
| 22907 | try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index) | 22902 | try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index) |
| 22908 | else blk: { | 22903 | else blk: { |
| 22909 | const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null, .One); | 22904 | const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null); |
| 22910 | const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty); | 22905 | const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty); |
| 22911 | break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr); | 22906 | break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr); |
| 22912 | }; | 22907 | }; |
| ... | @@ -24073,7 +24068,6 @@ fn elemPtr( | ... | @@ -24073,7 +24068,6 @@ fn elemPtr( |
| 24073 | elem_index: Air.Inst.Ref, | 24068 | elem_index: Air.Inst.Ref, |
| 24074 | elem_index_src: LazySrcLoc, | 24069 | elem_index_src: LazySrcLoc, |
| 24075 | init: bool, | 24070 | init: bool, |
| 24076 | size: std.builtin.Type.Pointer.Size, | ||
| 24077 | ) CompileError!Air.Inst.Ref { | 24071 | ) CompileError!Air.Inst.Ref { |
| 24078 | const indexable_ptr_src = src; // TODO better source location | 24072 | const indexable_ptr_src = src; // TODO better source location |
| 24079 | const indexable_ptr_ty = sema.typeOf(indexable_ptr); | 24073 | const indexable_ptr_ty = sema.typeOf(indexable_ptr); |
| ... | @@ -24100,12 +24094,13 @@ fn elemPtr( | ... | @@ -24100,12 +24094,13 @@ fn elemPtr( |
| 24100 | const index_val = maybe_index_val orelse break :rs elem_index_src; | 24094 | const index_val = maybe_index_val orelse break :rs elem_index_src; |
| 24101 | const index = @intCast(usize, index_val.toUnsignedInt(target)); | 24095 | const index = @intCast(usize, index_val.toUnsignedInt(target)); |
| 24102 | const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod); | 24096 | const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod); |
| 24103 | const elem_ptr_ty = try sema.elemPtrType(indexable_ty, index, size); | 24097 | const result_ty = try sema.elemPtrType(indexable_ty, index); |
| 24104 | return sema.addConstant(elem_ptr_ty, elem_ptr); | 24098 | return sema.addConstant(result_ty, elem_ptr); |
| 24105 | }; | 24099 | }; |
| 24106 | const elem_ptr_ty = try sema.elemPtrType(indexable_ty, null, size); | 24100 | const result_ty = try sema.elemPtrType(indexable_ty, null); |
| 24101 | |||
| 24107 | try sema.requireRuntimeBlock(block, src, runtime_src); | 24102 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 24108 | return block.addPtrElemPtr(indexable, elem_index, elem_ptr_ty); | 24103 | return block.addPtrElemPtr(indexable, elem_index, result_ty); |
| 24109 | }, | 24104 | }, |
| 24110 | .One => { | 24105 | .One => { |
| 24111 | assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable | 24106 | assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable |
| ... | @@ -24167,7 +24162,7 @@ fn elemVal( | ... | @@ -24167,7 +24162,7 @@ fn elemVal( |
| 24167 | }, | 24162 | }, |
| 24168 | .One => { | 24163 | .One => { |
| 24169 | assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable | 24164 | assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable |
| 24170 | const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, .One); | 24165 | const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false); |
| 24171 | return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); | 24166 | return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); |
| 24172 | }, | 24167 | }, |
| 24173 | }, | 24168 | }, |
| ... | @@ -24405,7 +24400,7 @@ fn elemPtrArray( | ... | @@ -24405,7 +24400,7 @@ fn elemPtrArray( |
| 24405 | break :o index; | 24400 | break :o index; |
| 24406 | } else null; | 24401 | } else null; |
| 24407 | 24402 | ||
| 24408 | const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset, .One); | 24403 | const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset); |
| 24409 | 24404 | ||
| 24410 | if (maybe_undef_array_ptr_val) |array_ptr_val| { | 24405 | if (maybe_undef_array_ptr_val) |array_ptr_val| { |
| 24411 | if (array_ptr_val.isUndef()) { | 24406 | if (array_ptr_val.isUndef()) { |
| ... | @@ -24510,7 +24505,7 @@ fn elemPtrSlice( | ... | @@ -24510,7 +24505,7 @@ fn elemPtrSlice( |
| 24510 | break :o index; | 24505 | break :o index; |
| 24511 | } else null; | 24506 | } else null; |
| 24512 | 24507 | ||
| 24513 | const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset, .One); | 24508 | const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset); |
| 24514 | 24509 | ||
| 24515 | if (maybe_undef_slice_val) |slice_val| { | 24510 | if (maybe_undef_slice_val) |slice_val| { |
| 24516 | if (slice_val.isUndef()) { | 24511 | if (slice_val.isUndef()) { |
| ... | @@ -26240,7 +26235,7 @@ fn storePtr2( | ... | @@ -26240,7 +26235,7 @@ fn storePtr2( |
| 26240 | const elem_src = operand_src; // TODO better source location | 26235 | const elem_src = operand_src; // TODO better source location |
| 26241 | const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i); | 26236 | const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i); |
| 26242 | const elem_index = try sema.addIntUnsigned(Type.usize, i); | 26237 | const elem_index = try sema.addIntUnsigned(Type.usize, i); |
| 26243 | const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, .One); | 26238 | const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false); |
| 26244 | try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); | 26239 | try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); |
| 26245 | } | 26240 | } |
| 26246 | return; | 26241 | return; |
| ... | @@ -33277,12 +33272,7 @@ fn compareVector( | ... | @@ -33277,12 +33272,7 @@ fn compareVector( |
| 33277 | /// For []T, returns *T | 33272 | /// For []T, returns *T |
| 33278 | /// Handles const-ness and address spaces in particular. | 33273 | /// Handles const-ness and address spaces in particular. |
| 33279 | /// This code is duplicated in `analyzePtrArithmetic`. | 33274 | /// This code is duplicated in `analyzePtrArithmetic`. |
| 33280 | fn elemPtrType( | 33275 | fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { |
| 33281 | sema: *Sema, | ||
| 33282 | ptr_ty: Type, | ||
| 33283 | offset: ?usize, | ||
| 33284 | size: std.builtin.Type.Pointer.Size, | ||
| 33285 | ) !Type { | ||
| 33286 | const ptr_info = ptr_ty.ptrInfo().data; | 33276 | const ptr_info = ptr_ty.ptrInfo().data; |
| 33287 | const elem_ty = ptr_ty.elemType2(); | 33277 | const elem_ty = ptr_ty.elemType2(); |
| 33288 | const allow_zero = ptr_info.@"allowzero" and (offset orelse 0) == 0; | 33278 | const allow_zero = ptr_info.@"allowzero" and (offset orelse 0) == 0; |
| ... | @@ -33327,7 +33317,6 @@ fn elemPtrType( | ... | @@ -33327,7 +33317,6 @@ fn elemPtrType( |
| 33327 | break :a new_align; | 33317 | break :a new_align; |
| 33328 | }; | 33318 | }; |
| 33329 | return try Type.ptr(sema.arena, sema.mod, .{ | 33319 | return try Type.ptr(sema.arena, sema.mod, .{ |
| 33330 | .size = size, | ||
| 33331 | .pointee_type = elem_ty, | 33320 | .pointee_type = elem_ty, |
| 33332 | .mutable = ptr_info.mutable, | 33321 | .mutable = ptr_info.mutable, |
| 33333 | .@"addrspace" = ptr_info.@"addrspace", | 33322 | .@"addrspace" = ptr_info.@"addrspace", |
src/Zir.zig+7-14| ... | @@ -79,7 +79,6 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en | ... | @@ -79,7 +79,6 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en |
| 79 | Inst.BuiltinCall.Flags => @bitCast(Inst.BuiltinCall.Flags, code.extra[i]), | 79 | Inst.BuiltinCall.Flags => @bitCast(Inst.BuiltinCall.Flags, code.extra[i]), |
| 80 | Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]), | 80 | Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]), |
| 81 | Inst.FuncFancy.Bits => @bitCast(Inst.FuncFancy.Bits, code.extra[i]), | 81 | Inst.FuncFancy.Bits => @bitCast(Inst.FuncFancy.Bits, code.extra[i]), |
| 82 | Inst.ElemPtrImm.Bits => @bitCast(Inst.ElemPtrImm.Bits, code.extra[i]), | ||
| 83 | else => @compileError("bad field type"), | 82 | else => @compileError("bad field type"), |
| 84 | }; | 83 | }; |
| 85 | i += 1; | 84 | i += 1; |
| ... | @@ -501,14 +500,14 @@ pub const Inst = struct { | ... | @@ -501,14 +500,14 @@ pub const Inst = struct { |
| 501 | /// Uses the `node` field. | 500 | /// Uses the `node` field. |
| 502 | repeat_inline, | 501 | repeat_inline, |
| 503 | /// Asserts that all the lengths provided match. Used to build a for loop. | 502 | /// Asserts that all the lengths provided match. Used to build a for loop. |
| 504 | /// Return value is always void. | 503 | /// Return value is the length as a usize. |
| 505 | /// Uses the `pl_node` field with payload `MultiOp`. | 504 | /// Uses the `pl_node` field with payload `MultiOp`. |
| 506 | /// There is exactly one item corresponding to each AST node inside the for | 505 | /// There is exactly one item corresponding to each AST node inside the for |
| 507 | /// loop condition. Each item may be `none`, indicating an unbounded range. | 506 | /// loop condition. Any item may be `none`, indicating an unbounded range. |
| 508 | /// Illegal behaviors: | 507 | /// Illegal behaviors: |
| 509 | /// * If all lengths are unbounded ranges (always a compile error). | 508 | /// * If all lengths are unbounded ranges (always a compile error). |
| 510 | /// * If any two lengths do not match each other. | 509 | /// * If any two lengths do not match each other. |
| 511 | for_check_lens, | 510 | for_len, |
| 512 | /// Merge two error sets into one, `E1 || E2`. | 511 | /// Merge two error sets into one, `E1 || E2`. |
| 513 | /// Uses the `pl_node` field with payload `Bin`. | 512 | /// Uses the `pl_node` field with payload `Bin`. |
| 514 | merge_error_sets, | 513 | merge_error_sets, |
| ... | @@ -1254,7 +1253,7 @@ pub const Inst = struct { | ... | @@ -1254,7 +1253,7 @@ pub const Inst = struct { |
| 1254 | .defer_err_code, | 1253 | .defer_err_code, |
| 1255 | .save_err_ret_index, | 1254 | .save_err_ret_index, |
| 1256 | .restore_err_ret_index, | 1255 | .restore_err_ret_index, |
| 1257 | .for_check_lens, | 1256 | .for_len, |
| 1258 | => false, | 1257 | => false, |
| 1259 | 1258 | ||
| 1260 | .@"break", | 1259 | .@"break", |
| ... | @@ -1322,7 +1321,6 @@ pub const Inst = struct { | ... | @@ -1322,7 +1321,6 @@ pub const Inst = struct { |
| 1322 | .memcpy, | 1321 | .memcpy, |
| 1323 | .memset, | 1322 | .memset, |
| 1324 | .check_comptime_control_flow, | 1323 | .check_comptime_control_flow, |
| 1325 | .for_check_lens, | ||
| 1326 | .@"defer", | 1324 | .@"defer", |
| 1327 | .defer_err_code, | 1325 | .defer_err_code, |
| 1328 | .restore_err_ret_index, | 1326 | .restore_err_ret_index, |
| ... | @@ -1547,6 +1545,7 @@ pub const Inst = struct { | ... | @@ -1547,6 +1545,7 @@ pub const Inst = struct { |
| 1547 | .repeat_inline, | 1545 | .repeat_inline, |
| 1548 | .panic, | 1546 | .panic, |
| 1549 | .panic_comptime, | 1547 | .panic_comptime, |
| 1548 | .for_len, | ||
| 1550 | .@"try", | 1549 | .@"try", |
| 1551 | .try_ptr, | 1550 | .try_ptr, |
| 1552 | //.try_inline, | 1551 | //.try_inline, |
| ... | @@ -1602,7 +1601,7 @@ pub const Inst = struct { | ... | @@ -1602,7 +1601,7 @@ pub const Inst = struct { |
| 1602 | .@"break" = .@"break", | 1601 | .@"break" = .@"break", |
| 1603 | .break_inline = .@"break", | 1602 | .break_inline = .@"break", |
| 1604 | .check_comptime_control_flow = .un_node, | 1603 | .check_comptime_control_flow = .un_node, |
| 1605 | .for_check_lens = .pl_node, | 1604 | .for_len = .pl_node, |
| 1606 | .call = .pl_node, | 1605 | .call = .pl_node, |
| 1607 | .cmp_lt = .pl_node, | 1606 | .cmp_lt = .pl_node, |
| 1608 | .cmp_lte = .pl_node, | 1607 | .cmp_lte = .pl_node, |
| ... | @@ -2975,13 +2974,7 @@ pub const Inst = struct { | ... | @@ -2975,13 +2974,7 @@ pub const Inst = struct { |
| 2975 | 2974 | ||
| 2976 | pub const ElemPtrImm = struct { | 2975 | pub const ElemPtrImm = struct { |
| 2977 | ptr: Ref, | 2976 | ptr: Ref, |
| 2978 | bits: Bits, | 2977 | index: u32, |
| 2979 | |||
| 2980 | pub const Bits = packed struct(u32) { | ||
| 2981 | index: u31, | ||
| 2982 | /// Controls whether the type returned is `*T` or `[*]T`. | ||
| 2983 | manyptr: bool = false, | ||
| 2984 | }; | ||
| 2985 | }; | 2978 | }; |
| 2986 | 2979 | ||
| 2987 | /// 0. multi_cases_len: u32 // If has_multi_cases is set. | 2980 | /// 0. multi_cases_len: u32 // If has_multi_cases is set. |
src/print_zir.zig+2-4| ... | @@ -355,7 +355,7 @@ const Writer = struct { | ... | @@ -355,7 +355,7 @@ const Writer = struct { |
| 355 | .array_type, | 355 | .array_type, |
| 356 | => try self.writePlNodeBin(stream, inst), | 356 | => try self.writePlNodeBin(stream, inst), |
| 357 | 357 | ||
| 358 | .for_check_lens => try self.writePlNodeMultiOp(stream, inst), | 358 | .for_len => try self.writePlNodeMultiOp(stream, inst), |
| 359 | 359 | ||
| 360 | .elem_ptr_imm => try self.writeElemPtrImm(stream, inst), | 360 | .elem_ptr_imm => try self.writeElemPtrImm(stream, inst), |
| 361 | 361 | ||
| ... | @@ -888,9 +888,7 @@ const Writer = struct { | ... | @@ -888,9 +888,7 @@ const Writer = struct { |
| 888 | const extra = self.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; | 888 | const extra = self.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; |
| 889 | 889 | ||
| 890 | try self.writeInstRef(stream, extra.ptr); | 890 | try self.writeInstRef(stream, extra.ptr); |
| 891 | try stream.print(", {d}", .{extra.bits.index}); | 891 | try stream.print(", {d}) ", .{extra.index}); |
| 892 | try self.writeFlag(stream, ", manyptr", extra.bits.manyptr); | ||
| 893 | try stream.writeAll(") "); | ||
| 894 | try self.writeSrc(stream, inst_data.src()); | 892 | try self.writeSrc(stream, inst_data.src()); |
| 895 | } | 893 | } |
| 896 | 894 |