authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-03-17 02:13:19+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-17 15:23:16-07:00
logc11b6adf13fe5c765ec480af5bad6338e6982a9d
tree6873326a7b500659ea540472bae1255a3f3d33de
parentd981549d65849591749d8d9db12ddf2bf7361399

Ast: fix comptime destructure

A preceding `comptime` keyword was being ignored if the first destructure variable was an expression.

8 files changed, 201 insertions(+), 135 deletions(-)

lib/compiler/reduce/Walk.zig+4-8
......@@ -345,12 +345,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
345345 },
346346
347347 .assign_destructure => {
348 const lhs_count = ast.extra_data[datas[node].lhs];
349 assert(lhs_count > 1);
350 const lhs_exprs = ast.extra_data[datas[node].lhs + 1 ..][0..lhs_count];
351 const rhs = datas[node].rhs;
352
353 for (lhs_exprs) |lhs_node| {
348 const full = tree.assignDestructure(node);
349 for (full.ast.variables) |variable_node| {
354350 switch (node_tags[lhs_node]) {
355351 .global_var_decl,
356352 .local_var_decl,
......@@ -358,10 +354,10 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
358354 .aligned_var_decl,
359355 => try walkLocalVarDecl(w, ast.fullVarDecl(lhs_node).?),
360356
361 else => try walkExpression(w, lhs_node),
357 else => try walkExpression(w, variable_node),
362358 }
363359 }
364 return walkExpression(w, rhs);
360 return walkExpression(w, full.ast.assign_expr);
365361 },
366362
367363 .bit_not,
lib/docs/wasm/Walk.zig+3-6
......@@ -699,12 +699,9 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
699699 },
700700
701701 .assign_destructure => {
702 const extra_index = node_datas[node].lhs;
703 const lhs_count = ast.extra_data[extra_index];
704 const lhs_nodes: []const Ast.Node.Index = @ptrCast(ast.extra_data[extra_index + 1 ..][0..lhs_count]);
705 const rhs = node_datas[node].rhs;
706 for (lhs_nodes) |lhs_node| try expr(w, scope, parent_decl, lhs_node);
707 _ = try expr(w, scope, parent_decl, rhs);
702 const full = ast.assignDestructure(node);
703 for (full.ast.variables) |variable_node| try expr(w, scope, parent_decl, variable_node);
704 _ = try expr(w, scope, parent_decl, full.ast.value_expr);
708705 },
709706
710707 .bool_not,
lib/std/zig/Ast.zig+43
......@@ -1406,6 +1406,16 @@ pub fn alignedVarDecl(tree: Ast, node: Node.Index) full.VarDecl {
14061406 });
14071407}
14081408
1409pub fn assignDestructure(tree: Ast, node: Node.Index) full.AssignDestructure {
1410 const data = tree.nodes.items(.data)[node];
1411 const variable_count = tree.extra_data[data.lhs];
1412 return tree.fullAssignDestructureComponents(.{
1413 .variables = tree.extra_data[data.lhs + 1 ..][0..variable_count],
1414 .equal_token = tree.nodes.items(.main_token)[node],
1415 .value_expr = data.rhs,
1416 });
1417}
1418
14091419pub fn ifSimple(tree: Ast, node: Node.Index) full.If {
14101420 assert(tree.nodes.items(.tag)[node] == .if_simple);
14111421 const data = tree.nodes.items(.data)[node];
......@@ -2045,6 +2055,28 @@ fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl
20452055 return result;
20462056}
20472057
2058fn fullAssignDestructureComponents(tree: Ast, info: full.AssignDestructure.Components) full.AssignDestructure {
2059 const token_tags = tree.tokens.items(.tag);
2060 const node_tags = tree.nodes.items(.tag);
2061 var result: full.AssignDestructure = .{
2062 .comptime_token = null,
2063 .ast = info,
2064 };
2065 const first_variable_token = tree.firstToken(info.variables[0]);
2066 const maybe_comptime_token = switch (node_tags[info.variables[0]]) {
2067 .global_var_decl,
2068 .local_var_decl,
2069 .aligned_var_decl,
2070 .simple_var_decl,
2071 => first_variable_token,
2072 else => first_variable_token - 1,
2073 };
2074 if (token_tags[maybe_comptime_token] == .keyword_comptime) {
2075 result.comptime_token = maybe_comptime_token;
2076 }
2077 return result;
2078}
2079
20482080fn fullIfComponents(tree: Ast, info: full.If.Components) full.If {
20492081 const token_tags = tree.tokens.items(.tag);
20502082 var result: full.If = .{
......@@ -2508,6 +2540,17 @@ pub const full = struct {
25082540 }
25092541 };
25102542
2543 pub const AssignDestructure = struct {
2544 comptime_token: ?TokenIndex,
2545 ast: Components,
2546
2547 pub const Components = struct {
2548 variables: []const Node.Index,
2549 equal_token: TokenIndex,
2550 value_expr: Node.Index,
2551 };
2552 };
2553
25112554 pub const If = struct {
25122555 /// Points to the first token after the `|`. Will either be an identifier or
25132556 /// a `*` (with an identifier immediately after it).
lib/std/zig/AstGen.zig+70-86
......@@ -3406,45 +3406,36 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro
34063406 try emitDbgNode(gz, node);
34073407 const astgen = gz.astgen;
34083408 const tree = astgen.tree;
3409 const token_tags = tree.tokens.items(.tag);
3410 const node_datas = tree.nodes.items(.data);
34113409 const main_tokens = tree.nodes.items(.main_token);
34123410 const node_tags = tree.nodes.items(.tag);
34133411
3414 const extra_index = node_datas[node].lhs;
3415 const lhs_count = tree.extra_data[extra_index];
3416 const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]);
3417 const rhs = node_datas[node].rhs;
3418
3419 const maybe_comptime_token = tree.firstToken(node) - 1;
3420 const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime;
3421
3422 if (declared_comptime and gz.is_comptime) {
3412 const full = tree.assignDestructure(node);
3413 if (full.comptime_token != null and gz.is_comptime) {
34233414 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
34243415 }
34253416
34263417 // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
34273418 var gz_buf: GenZir = undefined;
3428 const inner_gz = if (declared_comptime) bs: {
3419 const inner_gz = if (full.comptime_token) |_| bs: {
34293420 gz_buf = gz.makeSubBlock(scope);
34303421 gz_buf.is_comptime = true;
34313422 break :bs &gz_buf;
34323423 } else gz;
3433 defer if (declared_comptime) inner_gz.unstack();
3424 defer if (full.comptime_token) |_| inner_gz.unstack();
34343425
3435 const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len);
3436 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {
3437 if (node_tags[lhs_node] == .identifier) {
3426 const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
3427 for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
3428 if (node_tags[variable_node] == .identifier) {
34383429 // This intentionally does not support `@"_"` syntax.
3439 const ident_name = tree.tokenSlice(main_tokens[lhs_node]);
3430 const ident_name = tree.tokenSlice(main_tokens[variable_node]);
34403431 if (mem.eql(u8, ident_name, "_")) {
3441 lhs_rl.* = .discard;
3432 variable_rl.* = .discard;
34423433 continue;
34433434 }
34443435 }
3445 lhs_rl.* = .{ .typed_ptr = .{
3446 .inst = try lvalExpr(inner_gz, scope, lhs_node),
3447 .src_node = lhs_node,
3436 variable_rl.* = .{ .typed_ptr = .{
3437 .inst = try lvalExpr(inner_gz, scope, variable_node),
3438 .src_node = variable_node,
34483439 } };
34493440 }
34503441
......@@ -3453,9 +3444,9 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro
34533444 .components = rl_components,
34543445 } } };
34553446
3456 _ = try expr(inner_gz, scope, ri, rhs);
3447 _ = try expr(inner_gz, scope, ri, full.ast.value_expr);
34573448
3458 if (declared_comptime) {
3449 if (full.comptime_token) |_| {
34593450 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
34603451 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
34613452 try inner_gz.setBlockBody(comptime_block_inst);
......@@ -3474,23 +3465,16 @@ fn assignDestructureMaybeDecls(
34743465 const astgen = gz.astgen;
34753466 const tree = astgen.tree;
34763467 const token_tags = tree.tokens.items(.tag);
3477 const node_datas = tree.nodes.items(.data);
34783468 const main_tokens = tree.nodes.items(.main_token);
34793469 const node_tags = tree.nodes.items(.tag);
34803470
3481 const extra_index = node_datas[node].lhs;
3482 const lhs_count = tree.extra_data[extra_index];
3483 const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]);
3484 const rhs = node_datas[node].rhs;
3485
3486 const maybe_comptime_token = tree.firstToken(node) - 1;
3487 const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime;
3488 if (declared_comptime and gz.is_comptime) {
3471 const full = tree.assignDestructure(node);
3472 if (full.comptime_token != null and gz.is_comptime) {
34893473 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
34903474 }
34913475
3492 const is_comptime = declared_comptime or gz.is_comptime;
3493 const rhs_is_comptime = tree.nodes.items(.tag)[rhs] == .@"comptime";
3476 const is_comptime = full.comptime_token != null or gz.is_comptime;
3477 const value_is_comptime = node_tags[full.ast.value_expr] == .@"comptime";
34943478
34953479 // When declaring consts via a destructure, we always use a result pointer.
34963480 // This avoids the need to create tuple types, and is also likely easier to
......@@ -3499,24 +3483,24 @@ fn assignDestructureMaybeDecls(
34993483
35003484 // We know this rl information won't live past the evaluation of this
35013485 // expression, so it may as well go in the block arena.
3502 const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len);
3503 var any_non_const_lhs = false;
3486 const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
3487 var any_non_const_variables = false;
35043488 var any_lvalue_expr = false;
3505 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {
3506 switch (node_tags[lhs_node]) {
3489 for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
3490 switch (node_tags[variable_node]) {
35073491 .identifier => {
35083492 // This intentionally does not support `@"_"` syntax.
3509 const ident_name = tree.tokenSlice(main_tokens[lhs_node]);
3493 const ident_name = tree.tokenSlice(main_tokens[variable_node]);
35103494 if (mem.eql(u8, ident_name, "_")) {
3511 any_non_const_lhs = true;
3512 lhs_rl.* = .discard;
3495 any_non_const_variables = true;
3496 variable_rl.* = .discard;
35133497 continue;
35143498 }
35153499 },
35163500 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => {
3517 const full = tree.fullVarDecl(lhs_node).?;
3501 const full_var_decl = tree.fullVarDecl(variable_node).?;
35183502
3519 const name_token = full.ast.mut_token + 1;
3503 const name_token = full_var_decl.ast.mut_token + 1;
35203504 const ident_name_raw = tree.tokenSlice(name_token);
35213505 if (mem.eql(u8, ident_name_raw, "_")) {
35223506 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
......@@ -3524,35 +3508,35 @@ fn assignDestructureMaybeDecls(
35243508
35253509 // We detect shadowing in the second pass over these, while we're creating scopes.
35263510
3527 if (full.ast.addrspace_node != 0) {
3528 return astgen.failTok(main_tokens[full.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
3511 if (full_var_decl.ast.addrspace_node != 0) {
3512 return astgen.failTok(main_tokens[full_var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
35293513 }
3530 if (full.ast.section_node != 0) {
3531 return astgen.failTok(main_tokens[full.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
3514 if (full_var_decl.ast.section_node != 0) {
3515 return astgen.failTok(main_tokens[full_var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
35323516 }
35333517
3534 const is_const = switch (token_tags[full.ast.mut_token]) {
3518 const is_const = switch (token_tags[full_var_decl.ast.mut_token]) {
35353519 .keyword_var => false,
35363520 .keyword_const => true,
35373521 else => unreachable,
35383522 };
3539 if (!is_const) any_non_const_lhs = true;
3523 if (!is_const) any_non_const_variables = true;
35403524
35413525 // We also mark `const`s as comptime if the RHS is definitely comptime-known.
3542 const this_lhs_comptime = is_comptime or (is_const and rhs_is_comptime);
3526 const this_variable_comptime = is_comptime or (is_const and value_is_comptime);
35433527
3544 const align_inst: Zir.Inst.Ref = if (full.ast.align_node != 0)
3545 try expr(gz, scope, coerced_align_ri, full.ast.align_node)
3528 const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node != 0)
3529 try expr(gz, scope, coerced_align_ri, full_var_decl.ast.align_node)
35463530 else
35473531 .none;
35483532
3549 if (full.ast.type_node != 0) {
3533 if (full_var_decl.ast.type_node != 0) {
35503534 // Typed alloc
3551 const type_inst = try typeExpr(gz, scope, full.ast.type_node);
3535 const type_inst = try typeExpr(gz, scope, full_var_decl.ast.type_node);
35523536 const ptr = if (align_inst == .none) ptr: {
35533537 const tag: Zir.Inst.Tag = if (is_const)
35543538 .alloc
3555 else if (this_lhs_comptime)
3539 else if (this_variable_comptime)
35563540 .alloc_comptime_mut
35573541 else
35583542 .alloc_mut;
......@@ -3562,16 +3546,16 @@ fn assignDestructureMaybeDecls(
35623546 .type_inst = type_inst,
35633547 .align_inst = align_inst,
35643548 .is_const = is_const,
3565 .is_comptime = this_lhs_comptime,
3549 .is_comptime = this_variable_comptime,
35663550 });
3567 lhs_rl.* = .{ .typed_ptr = .{ .inst = ptr } };
3551 variable_rl.* = .{ .typed_ptr = .{ .inst = ptr } };
35683552 } else {
35693553 // Inferred alloc
35703554 const ptr = if (align_inst == .none) ptr: {
35713555 const tag: Zir.Inst.Tag = if (is_const) tag: {
3572 break :tag if (this_lhs_comptime) .alloc_inferred_comptime else .alloc_inferred;
3556 break :tag if (this_variable_comptime) .alloc_inferred_comptime else .alloc_inferred;
35733557 } else tag: {
3574 break :tag if (this_lhs_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut;
3558 break :tag if (this_variable_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut;
35753559 };
35763560 break :ptr try gz.addNode(tag, node);
35773561 } else try gz.addAllocExtended(.{
......@@ -3579,48 +3563,48 @@ fn assignDestructureMaybeDecls(
35793563 .type_inst = .none,
35803564 .align_inst = align_inst,
35813565 .is_const = is_const,
3582 .is_comptime = this_lhs_comptime,
3566 .is_comptime = this_variable_comptime,
35833567 });
3584 lhs_rl.* = .{ .inferred_ptr = ptr };
3568 variable_rl.* = .{ .inferred_ptr = ptr };
35853569 }
35863570
35873571 continue;
35883572 },
35893573 else => {},
35903574 }
3591 // This LHS is just an lvalue expression.
3575 // This variable is just an lvalue expression.
35923576 // We will fill in its result pointer later, inside a comptime block.
3593 any_non_const_lhs = true;
3577 any_non_const_variables = true;
35943578 any_lvalue_expr = true;
3595 lhs_rl.* = .{ .typed_ptr = .{
3579 variable_rl.* = .{ .typed_ptr = .{
35963580 .inst = undefined,
3597 .src_node = lhs_node,
3581 .src_node = variable_node,
35983582 } };
35993583 }
36003584
3601 if (declared_comptime and !any_non_const_lhs) {
3602 try astgen.appendErrorTok(maybe_comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
3585 if (full.comptime_token != null and !any_non_const_variables) {
3586 try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
36033587 }
36043588
36053589 // If this expression is marked comptime, we must wrap it in a comptime block.
36063590 var gz_buf: GenZir = undefined;
3607 const inner_gz = if (declared_comptime) bs: {
3591 const inner_gz = if (full.comptime_token) |_| bs: {
36083592 gz_buf = gz.makeSubBlock(scope);
36093593 gz_buf.is_comptime = true;
36103594 break :bs &gz_buf;
36113595 } else gz;
3612 defer if (declared_comptime) inner_gz.unstack();
3596 defer if (full.comptime_token) |_| inner_gz.unstack();
36133597
36143598 if (any_lvalue_expr) {
3615 // At least one LHS was an lvalue expr. Iterate again in order to
3599 // At least one variable was an lvalue expr. Iterate again in order to
36163600 // evaluate the lvalues from within the possible block_comptime.
3617 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {
3618 if (lhs_rl.* != .typed_ptr) continue;
3619 switch (node_tags[lhs_node]) {
3601 for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
3602 if (variable_rl.* != .typed_ptr) continue;
3603 switch (node_tags[variable_node]) {
36203604 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue,
36213605 else => {},
36223606 }
3623 lhs_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, lhs_node);
3607 variable_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, variable_node);
36243608 }
36253609 }
36263610
......@@ -3629,9 +3613,9 @@ fn assignDestructureMaybeDecls(
36293613 _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{
36303614 .src_node = node,
36313615 .components = rl_components,
3632 } } }, rhs, node);
3616 } } }, full.ast.value_expr, node);
36333617
3634 if (declared_comptime) {
3618 if (full.comptime_token) |_| {
36353619 // Finish the block_comptime. Inferred alloc resolution etc will occur
36363620 // in the parent block.
36373621 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
......@@ -3640,37 +3624,37 @@ fn assignDestructureMaybeDecls(
36403624 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
36413625 }
36423626
3643 // Now, iterate over the LHS exprs to construct any new scopes.
3627 // Now, iterate over the variable exprs to construct any new scopes.
36443628 // If there were any inferred allocations, resolve them.
36453629 // If there were any `const` decls, make the pointer constant.
36463630 var cur_scope = scope;
3647 for (rl_components, lhs_nodes) |lhs_rl, lhs_node| {
3648 switch (node_tags[lhs_node]) {
3631 for (rl_components, full.ast.variables) |variable_rl, variable_node| {
3632 switch (node_tags[variable_node]) {
36493633 .local_var_decl, .simple_var_decl, .aligned_var_decl => {},
36503634 else => continue, // We were mutating an existing lvalue - nothing to do
36513635 }
3652 const full = tree.fullVarDecl(lhs_node).?;
3653 const raw_ptr = switch (lhs_rl) {
3636 const full_var_decl = tree.fullVarDecl(variable_node).?;
3637 const raw_ptr = switch (variable_rl) {
36543638 .discard => unreachable,
36553639 .typed_ptr => |typed_ptr| typed_ptr.inst,
36563640 .inferred_ptr => |ptr_inst| ptr_inst,
36573641 };
36583642 // If the alloc was inferred, resolve it.
3659 if (full.ast.type_node == 0) {
3660 _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, lhs_node);
3643 if (full_var_decl.ast.type_node == 0) {
3644 _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, variable_node);
36613645 }
3662 const is_const = switch (token_tags[full.ast.mut_token]) {
3646 const is_const = switch (token_tags[full_var_decl.ast.mut_token]) {
36633647 .keyword_var => false,
36643648 .keyword_const => true,
36653649 else => unreachable,
36663650 };
36673651 // If the alloc was const, make it const.
3668 const var_ptr = if (is_const and full.ast.type_node != 0) make_const: {
3652 const var_ptr = if (is_const and full_var_decl.ast.type_node != 0) make_const: {
36693653 // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc`
36703654 // handles it for us.
36713655 break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node);
36723656 } else raw_ptr;
3673 const name_token = full.ast.mut_token + 1;
3657 const name_token = full_var_decl.ast.mut_token + 1;
36743658 const ident_name_raw = tree.tokenSlice(name_token);
36753659 const ident_name = try astgen.identAsString(name_token);
36763660 try astgen.detectLocalShadowing(
lib/std/zig/AstRlAnnotate.zig+4-5
......@@ -204,13 +204,12 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
204204 }
205205 },
206206 .assign_destructure => {
207 const lhs_count = tree.extra_data[node_datas[node].lhs];
208 const all_lhs = tree.extra_data[node_datas[node].lhs + 1 ..][0..lhs_count];
209 for (all_lhs) |lhs| {
210 _ = try astrl.expr(lhs, block, ResultInfo.none);
207 const full = tree.assignDestructure(node);
208 for (full.ast.variables) |variable_node| {
209 _ = try astrl.expr(variable_node, block, ResultInfo.none);
211210 }
212211 // We don't need to gather any meaningful data here, because destructures always use RLS
213 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
212 _ = try astrl.expr(full.ast.value_expr, block, ResultInfo.none);
214213 return false;
215214 },
216215 .assign => {
lib/std/zig/parser_test.zig+19
......@@ -2914,6 +2914,25 @@ test "zig fmt: test declaration" {
29142914 );
29152915}
29162916
2917test "zig fmt: destructure" {
2918 try testCanonical(
2919 \\comptime {
2920 \\ var w: u8, var x: u8 = .{ 1, 2 };
2921 \\ w, var y: u8 = .{ 3, 4 };
2922 \\ var z: u8, x = .{ 5, 6 };
2923 \\ y, z = .{ 7, 8 };
2924 \\}
2925 \\
2926 \\comptime {
2927 \\ comptime var w, var x = .{ 1, 2 };
2928 \\ comptime w, var y = .{ 3, 4 };
2929 \\ comptime var z, x = .{ 5, 6 };
2930 \\ comptime y, z = .{ 7, 8 };
2931 \\}
2932 \\
2933 );
2934}
2935
29172936test "zig fmt: infix operators" {
29182937 try testCanonical(
29192938 \\test {
lib/std/zig/render.zig+12-18
......@@ -569,39 +569,33 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
569569 },
570570
571571 .assign_destructure => {
572 const lhs_count = tree.extra_data[datas[node].lhs];
573 assert(lhs_count > 1);
574 const lhs_exprs = tree.extra_data[datas[node].lhs + 1 ..][0..lhs_count];
575 const rhs = datas[node].rhs;
576
577 const maybe_comptime_token = tree.firstToken(node) - 1;
578 if (token_tags[maybe_comptime_token] == .keyword_comptime) {
579 try renderToken(r, maybe_comptime_token, .space);
572 const full = tree.assignDestructure(node);
573 if (full.comptime_token) |comptime_token| {
574 try renderToken(r, comptime_token, .space);
580575 }
581576
582 for (lhs_exprs, 0..) |lhs_node, i| {
583 const lhs_space: Space = if (i == lhs_exprs.len - 1) .space else .comma_space;
584 switch (node_tags[lhs_node]) {
577 for (full.ast.variables, 0..) |variable_node, i| {
578 const variable_space: Space = if (i == full.ast.variables.len - 1) .space else .comma_space;
579 switch (node_tags[variable_node]) {
585580 .global_var_decl,
586581 .local_var_decl,
587582 .simple_var_decl,
588583 .aligned_var_decl,
589584 => {
590 try renderVarDecl(r, tree.fullVarDecl(lhs_node).?, true, lhs_space);
585 try renderVarDecl(r, tree.fullVarDecl(variable_node).?, true, variable_space);
591586 },
592 else => try renderExpression(r, lhs_node, lhs_space),
587 else => try renderExpression(r, variable_node, variable_space),
593588 }
594589 }
595 const equal_token = main_tokens[node];
596 if (tree.tokensOnSameLine(equal_token, equal_token + 1)) {
597 try renderToken(r, equal_token, .space);
590 if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {
591 try renderToken(r, full.ast.equal_token, .space);
598592 } else {
599593 ais.pushIndent();
600 try renderToken(r, equal_token, .newline);
594 try renderToken(r, full.ast.equal_token, .newline);
601595 ais.popIndent();
602596 }
603597 ais.pushIndentOneShot();
604 return renderExpression(r, rhs, space);
598 return renderExpression(r, full.ast.value_expr, space);
605599 },
606600
607601 .bit_not,
test/behavior/destructure.zig+46-12
......@@ -24,21 +24,55 @@ test "simple destructure" {
2424
2525test "destructure with comptime syntax" {
2626 const S = struct {
27 fn doTheTest() void {
28 comptime var x: f32 = undefined;
29 comptime x, const y, var z = .{ 0.5, 123, 456 }; // z is a comptime var
30 _ = &z;
31
32 comptime assert(@TypeOf(y) == comptime_int);
33 comptime assert(@TypeOf(z) == comptime_int);
34 comptime assert(x == 0.5);
35 comptime assert(y == 123);
36 comptime assert(z == 456);
27 fn doTheTest() !void {
28 {
29 comptime var x: f32 = undefined;
30 comptime x, const y, var z = .{ 0.5, 123, 456 }; // z is a comptime var
31 _ = &z;
32
33 comptime assert(@TypeOf(y) == comptime_int);
34 comptime assert(@TypeOf(z) == comptime_int);
35 comptime assert(x == 0.5);
36 comptime assert(y == 123);
37 comptime assert(z == 456);
38 }
39 {
40 var w: u8, var x: u8 = .{ 1, 2 };
41 w, var y: u8 = .{ 3, 4 };
42 var z: u8, x = .{ 5, 6 };
43 y, z = .{ 7, 8 };
44 {
45 w += 1;
46 x -= 2;
47 y *= 3;
48 z /= 4;
49 }
50 try expect(w == 4);
51 try expect(x == 4);
52 try expect(y == 21);
53 try expect(z == 2);
54 }
55 {
56 comptime var w, var x = .{ 1, 2 };
57 comptime w, var y = .{ 3, 4 };
58 comptime var z, x = .{ 5, 6 };
59 comptime y, z = .{ 7, 8 };
60 comptime {
61 w += 1;
62 x -= 2;
63 y *= 3;
64 z /= 4;
65 }
66 comptime assert(w == 4);
67 comptime assert(x == 4);
68 comptime assert(y == 21);
69 comptime assert(z == 2);
70 }
3771 }
3872 };
3973
40 S.doTheTest();
41 comptime S.doTheTest();
74 try S.doTheTest();
75 try comptime S.doTheTest();
4276}
4377
4478test "destructure from labeled block" {