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 {...@@ -345,12 +345,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
345 },345 },
346346
347 .assign_destructure => {347 .assign_destructure => {
348 const lhs_count = ast.extra_data[datas[node].lhs];348 const full = tree.assignDestructure(node);
349 assert(lhs_count > 1);349 for (full.ast.variables) |variable_node| {
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| {
354 switch (node_tags[lhs_node]) {350 switch (node_tags[lhs_node]) {
355 .global_var_decl,351 .global_var_decl,
356 .local_var_decl,352 .local_var_decl,
...@@ -358,10 +354,10 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {...@@ -358,10 +354,10 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
358 .aligned_var_decl,354 .aligned_var_decl,
359 => try walkLocalVarDecl(w, ast.fullVarDecl(lhs_node).?),355 => try walkLocalVarDecl(w, ast.fullVarDecl(lhs_node).?),
360356
361 else => try walkExpression(w, lhs_node),357 else => try walkExpression(w, variable_node),
362 }358 }
363 }359 }
364 return walkExpression(w, rhs);360 return walkExpression(w, full.ast.assign_expr);
365 },361 },
366362
367 .bit_not,363 .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)...@@ -699,12 +699,9 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
699 },699 },
700700
701 .assign_destructure => {701 .assign_destructure => {
702 const extra_index = node_datas[node].lhs;702 const full = ast.assignDestructure(node);
703 const lhs_count = ast.extra_data[extra_index];703 for (full.ast.variables) |variable_node| try expr(w, scope, parent_decl, variable_node);
704 const lhs_nodes: []const Ast.Node.Index = @ptrCast(ast.extra_data[extra_index + 1 ..][0..lhs_count]);704 _ = try expr(w, scope, parent_decl, full.ast.value_expr);
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);
708 },705 },
709706
710 .bool_not,707 .bool_not,
lib/std/zig/Ast.zig+43
...@@ -1406,6 +1406,16 @@ pub fn alignedVarDecl(tree: Ast, node: Node.Index) full.VarDecl {...@@ -1406,6 +1406,16 @@ pub fn alignedVarDecl(tree: Ast, node: Node.Index) full.VarDecl {
1406 });1406 });
1407}1407}
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
1409pub fn ifSimple(tree: Ast, node: Node.Index) full.If {1419pub fn ifSimple(tree: Ast, node: Node.Index) full.If {
1410 assert(tree.nodes.items(.tag)[node] == .if_simple);1420 assert(tree.nodes.items(.tag)[node] == .if_simple);
1411 const data = tree.nodes.items(.data)[node];1421 const data = tree.nodes.items(.data)[node];
...@@ -2045,6 +2055,28 @@ fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl...@@ -2045,6 +2055,28 @@ fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl
2045 return result;2055 return result;
2046}2056}
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
2048fn fullIfComponents(tree: Ast, info: full.If.Components) full.If {2080fn fullIfComponents(tree: Ast, info: full.If.Components) full.If {
2049 const token_tags = tree.tokens.items(.tag);2081 const token_tags = tree.tokens.items(.tag);
2050 var result: full.If = .{2082 var result: full.If = .{
...@@ -2508,6 +2540,17 @@ pub const full = struct {...@@ -2508,6 +2540,17 @@ pub const full = struct {
2508 }2540 }
2509 };2541 };
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
2511 pub const If = struct {2554 pub const If = struct {
2512 /// Points to the first token after the `|`. Will either be an identifier or2555 /// Points to the first token after the `|`. Will either be an identifier or
2513 /// a `*` (with an identifier immediately after it).2556 /// 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...@@ -3406,45 +3406,36 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro
3406 try emitDbgNode(gz, node);3406 try emitDbgNode(gz, node);
3407 const astgen = gz.astgen;3407 const astgen = gz.astgen;
3408 const tree = astgen.tree;3408 const tree = astgen.tree;
3409 const token_tags = tree.tokens.items(.tag);
3410 const node_datas = tree.nodes.items(.data);
3411 const main_tokens = tree.nodes.items(.main_token);3409 const main_tokens = tree.nodes.items(.main_token);
3412 const node_tags = tree.nodes.items(.tag);3410 const node_tags = tree.nodes.items(.tag);
34133411
3414 const extra_index = node_datas[node].lhs;3412 const full = tree.assignDestructure(node);
3415 const lhs_count = tree.extra_data[extra_index];3413 if (full.comptime_token != null and gz.is_comptime) {
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) {
3423 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});3414 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
3424 }3415 }
34253416
3426 // If this expression is marked comptime, we must wrap the whole thing in a comptime block.3417 // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
3427 var gz_buf: GenZir = undefined;3418 var gz_buf: GenZir = undefined;
3428 const inner_gz = if (declared_comptime) bs: {3419 const inner_gz = if (full.comptime_token) |_| bs: {
3429 gz_buf = gz.makeSubBlock(scope);3420 gz_buf = gz.makeSubBlock(scope);
3430 gz_buf.is_comptime = true;3421 gz_buf.is_comptime = true;
3431 break :bs &gz_buf;3422 break :bs &gz_buf;
3432 } else gz;3423 } 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);3426 const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
3436 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {3427 for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
3437 if (node_tags[lhs_node] == .identifier) {3428 if (node_tags[variable_node] == .identifier) {
3438 // This intentionally does not support `@"_"` syntax.3429 // 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]);
3440 if (mem.eql(u8, ident_name, "_")) {3431 if (mem.eql(u8, ident_name, "_")) {
3441 lhs_rl.* = .discard;3432 variable_rl.* = .discard;
3442 continue;3433 continue;
3443 }3434 }
3444 }3435 }
3445 lhs_rl.* = .{ .typed_ptr = .{3436 variable_rl.* = .{ .typed_ptr = .{
3446 .inst = try lvalExpr(inner_gz, scope, lhs_node),3437 .inst = try lvalExpr(inner_gz, scope, variable_node),
3447 .src_node = lhs_node,3438 .src_node = variable_node,
3448 } };3439 } };
3449 }3440 }
34503441
...@@ -3453,9 +3444,9 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro...@@ -3453,9 +3444,9 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro
3453 .components = rl_components,3444 .components = rl_components,
3454 } } };3445 } } };
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) |_| {
3459 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);3450 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
3460 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);3451 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
3461 try inner_gz.setBlockBody(comptime_block_inst);3452 try inner_gz.setBlockBody(comptime_block_inst);
...@@ -3474,23 +3465,16 @@ fn assignDestructureMaybeDecls(...@@ -3474,23 +3465,16 @@ fn assignDestructureMaybeDecls(
3474 const astgen = gz.astgen;3465 const astgen = gz.astgen;
3475 const tree = astgen.tree;3466 const tree = astgen.tree;
3476 const token_tags = tree.tokens.items(.tag);3467 const token_tags = tree.tokens.items(.tag);
3477 const node_datas = tree.nodes.items(.data);
3478 const main_tokens = tree.nodes.items(.main_token);3468 const main_tokens = tree.nodes.items(.main_token);
3479 const node_tags = tree.nodes.items(.tag);3469 const node_tags = tree.nodes.items(.tag);
34803470
3481 const extra_index = node_datas[node].lhs;3471 const full = tree.assignDestructure(node);
3482 const lhs_count = tree.extra_data[extra_index];3472 if (full.comptime_token != null and gz.is_comptime) {
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) {
3489 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});3473 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
3490 }3474 }
34913475
3492 const is_comptime = declared_comptime or gz.is_comptime;3476 const is_comptime = full.comptime_token != null or gz.is_comptime;
3493 const rhs_is_comptime = tree.nodes.items(.tag)[rhs] == .@"comptime";3477 const value_is_comptime = node_tags[full.ast.value_expr] == .@"comptime";
34943478
3495 // When declaring consts via a destructure, we always use a result pointer.3479 // When declaring consts via a destructure, we always use a result pointer.
3496 // This avoids the need to create tuple types, and is also likely easier to3480 // This avoids the need to create tuple types, and is also likely easier to
...@@ -3499,24 +3483,24 @@ fn assignDestructureMaybeDecls(...@@ -3499,24 +3483,24 @@ fn assignDestructureMaybeDecls(
34993483
3500 // We know this rl information won't live past the evaluation of this3484 // We know this rl information won't live past the evaluation of this
3501 // expression, so it may as well go in the block arena.3485 // 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);3486 const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
3503 var any_non_const_lhs = false;3487 var any_non_const_variables = false;
3504 var any_lvalue_expr = false;3488 var any_lvalue_expr = false;
3505 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {3489 for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
3506 switch (node_tags[lhs_node]) {3490 switch (node_tags[variable_node]) {
3507 .identifier => {3491 .identifier => {
3508 // This intentionally does not support `@"_"` syntax.3492 // 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]);
3510 if (mem.eql(u8, ident_name, "_")) {3494 if (mem.eql(u8, ident_name, "_")) {
3511 any_non_const_lhs = true;3495 any_non_const_variables = true;
3512 lhs_rl.* = .discard;3496 variable_rl.* = .discard;
3513 continue;3497 continue;
3514 }3498 }
3515 },3499 },
3516 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => {3500 .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;
3520 const ident_name_raw = tree.tokenSlice(name_token);3504 const ident_name_raw = tree.tokenSlice(name_token);
3521 if (mem.eql(u8, ident_name_raw, "_")) {3505 if (mem.eql(u8, ident_name_raw, "_")) {
3522 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});3506 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
...@@ -3524,35 +3508,35 @@ fn assignDestructureMaybeDecls(...@@ -3524,35 +3508,35 @@ fn assignDestructureMaybeDecls(
35243508
3525 // We detect shadowing in the second pass over these, while we're creating scopes.3509 // We detect shadowing in the second pass over these, while we're creating scopes.
35263510
3527 if (full.ast.addrspace_node != 0) {3511 if (full_var_decl.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});3512 return astgen.failTok(main_tokens[full_var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
3529 }3513 }
3530 if (full.ast.section_node != 0) {3514 if (full_var_decl.ast.section_node != 0) {
3531 return astgen.failTok(main_tokens[full.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});3515 return astgen.failTok(main_tokens[full_var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
3532 }3516 }
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]) {
3535 .keyword_var => false,3519 .keyword_var => false,
3536 .keyword_const => true,3520 .keyword_const => true,
3537 else => unreachable,3521 else => unreachable,
3538 };3522 };
3539 if (!is_const) any_non_const_lhs = true;3523 if (!is_const) any_non_const_variables = true;
35403524
3541 // We also mark `const`s as comptime if the RHS is definitely comptime-known.3525 // 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)3528 const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node != 0)
3545 try expr(gz, scope, coerced_align_ri, full.ast.align_node)3529 try expr(gz, scope, coerced_align_ri, full_var_decl.ast.align_node)
3546 else3530 else
3547 .none;3531 .none;
35483532
3549 if (full.ast.type_node != 0) {3533 if (full_var_decl.ast.type_node != 0) {
3550 // Typed alloc3534 // 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);
3552 const ptr = if (align_inst == .none) ptr: {3536 const ptr = if (align_inst == .none) ptr: {
3553 const tag: Zir.Inst.Tag = if (is_const)3537 const tag: Zir.Inst.Tag = if (is_const)
3554 .alloc3538 .alloc
3555 else if (this_lhs_comptime)3539 else if (this_variable_comptime)
3556 .alloc_comptime_mut3540 .alloc_comptime_mut
3557 else3541 else
3558 .alloc_mut;3542 .alloc_mut;
...@@ -3562,16 +3546,16 @@ fn assignDestructureMaybeDecls(...@@ -3562,16 +3546,16 @@ fn assignDestructureMaybeDecls(
3562 .type_inst = type_inst,3546 .type_inst = type_inst,
3563 .align_inst = align_inst,3547 .align_inst = align_inst,
3564 .is_const = is_const,3548 .is_const = is_const,
3565 .is_comptime = this_lhs_comptime,3549 .is_comptime = this_variable_comptime,
3566 });3550 });
3567 lhs_rl.* = .{ .typed_ptr = .{ .inst = ptr } };3551 variable_rl.* = .{ .typed_ptr = .{ .inst = ptr } };
3568 } else {3552 } else {
3569 // Inferred alloc3553 // Inferred alloc
3570 const ptr = if (align_inst == .none) ptr: {3554 const ptr = if (align_inst == .none) ptr: {
3571 const tag: Zir.Inst.Tag = if (is_const) tag: {3555 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;
3573 } else tag: {3557 } 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;
3575 };3559 };
3576 break :ptr try gz.addNode(tag, node);3560 break :ptr try gz.addNode(tag, node);
3577 } else try gz.addAllocExtended(.{3561 } else try gz.addAllocExtended(.{
...@@ -3579,48 +3563,48 @@ fn assignDestructureMaybeDecls(...@@ -3579,48 +3563,48 @@ fn assignDestructureMaybeDecls(
3579 .type_inst = .none,3563 .type_inst = .none,
3580 .align_inst = align_inst,3564 .align_inst = align_inst,
3581 .is_const = is_const,3565 .is_const = is_const,
3582 .is_comptime = this_lhs_comptime,3566 .is_comptime = this_variable_comptime,
3583 });3567 });
3584 lhs_rl.* = .{ .inferred_ptr = ptr };3568 variable_rl.* = .{ .inferred_ptr = ptr };
3585 }3569 }
35863570
3587 continue;3571 continue;
3588 },3572 },
3589 else => {},3573 else => {},
3590 }3574 }
3591 // This LHS is just an lvalue expression.3575 // This variable is just an lvalue expression.
3592 // We will fill in its result pointer later, inside a comptime block.3576 // We will fill in its result pointer later, inside a comptime block.
3593 any_non_const_lhs = true;3577 any_non_const_variables = true;
3594 any_lvalue_expr = true;3578 any_lvalue_expr = true;
3595 lhs_rl.* = .{ .typed_ptr = .{3579 variable_rl.* = .{ .typed_ptr = .{
3596 .inst = undefined,3580 .inst = undefined,
3597 .src_node = lhs_node,3581 .src_node = variable_node,
3598 } };3582 } };
3599 }3583 }
36003584
3601 if (declared_comptime and !any_non_const_lhs) {3585 if (full.comptime_token != null and !any_non_const_variables) {
3602 try astgen.appendErrorTok(maybe_comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});3586 try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
3603 }3587 }
36043588
3605 // If this expression is marked comptime, we must wrap it in a comptime block.3589 // If this expression is marked comptime, we must wrap it in a comptime block.
3606 var gz_buf: GenZir = undefined;3590 var gz_buf: GenZir = undefined;
3607 const inner_gz = if (declared_comptime) bs: {3591 const inner_gz = if (full.comptime_token) |_| bs: {
3608 gz_buf = gz.makeSubBlock(scope);3592 gz_buf = gz.makeSubBlock(scope);
3609 gz_buf.is_comptime = true;3593 gz_buf.is_comptime = true;
3610 break :bs &gz_buf;3594 break :bs &gz_buf;
3611 } else gz;3595 } else gz;
3612 defer if (declared_comptime) inner_gz.unstack();3596 defer if (full.comptime_token) |_| inner_gz.unstack();
36133597
3614 if (any_lvalue_expr) {3598 if (any_lvalue_expr) {
3615 // At least one LHS was an lvalue expr. Iterate again in order to3599 // At least one variable was an lvalue expr. Iterate again in order to
3616 // evaluate the lvalues from within the possible block_comptime.3600 // evaluate the lvalues from within the possible block_comptime.
3617 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {3601 for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
3618 if (lhs_rl.* != .typed_ptr) continue;3602 if (variable_rl.* != .typed_ptr) continue;
3619 switch (node_tags[lhs_node]) {3603 switch (node_tags[variable_node]) {
3620 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue,3604 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue,
3621 else => {},3605 else => {},
3622 }3606 }
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);
3624 }3608 }
3625 }3609 }
36263610
...@@ -3629,9 +3613,9 @@ fn assignDestructureMaybeDecls(...@@ -3629,9 +3613,9 @@ fn assignDestructureMaybeDecls(
3629 _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{3613 _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{
3630 .src_node = node,3614 .src_node = node,
3631 .components = rl_components,3615 .components = rl_components,
3632 } } }, rhs, node);3616 } } }, full.ast.value_expr, node);
36333617
3634 if (declared_comptime) {3618 if (full.comptime_token) |_| {
3635 // Finish the block_comptime. Inferred alloc resolution etc will occur3619 // Finish the block_comptime. Inferred alloc resolution etc will occur
3636 // in the parent block.3620 // in the parent block.
3637 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);3621 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
...@@ -3640,37 +3624,37 @@ fn assignDestructureMaybeDecls(...@@ -3640,37 +3624,37 @@ fn assignDestructureMaybeDecls(
3640 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);3624 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
3641 }3625 }
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.
3644 // If there were any inferred allocations, resolve them.3628 // If there were any inferred allocations, resolve them.
3645 // If there were any `const` decls, make the pointer constant.3629 // If there were any `const` decls, make the pointer constant.
3646 var cur_scope = scope;3630 var cur_scope = scope;
3647 for (rl_components, lhs_nodes) |lhs_rl, lhs_node| {3631 for (rl_components, full.ast.variables) |variable_rl, variable_node| {
3648 switch (node_tags[lhs_node]) {3632 switch (node_tags[variable_node]) {
3649 .local_var_decl, .simple_var_decl, .aligned_var_decl => {},3633 .local_var_decl, .simple_var_decl, .aligned_var_decl => {},
3650 else => continue, // We were mutating an existing lvalue - nothing to do3634 else => continue, // We were mutating an existing lvalue - nothing to do
3651 }3635 }
3652 const full = tree.fullVarDecl(lhs_node).?;3636 const full_var_decl = tree.fullVarDecl(variable_node).?;
3653 const raw_ptr = switch (lhs_rl) {3637 const raw_ptr = switch (variable_rl) {
3654 .discard => unreachable,3638 .discard => unreachable,
3655 .typed_ptr => |typed_ptr| typed_ptr.inst,3639 .typed_ptr => |typed_ptr| typed_ptr.inst,
3656 .inferred_ptr => |ptr_inst| ptr_inst,3640 .inferred_ptr => |ptr_inst| ptr_inst,
3657 };3641 };
3658 // If the alloc was inferred, resolve it.3642 // If the alloc was inferred, resolve it.
3659 if (full.ast.type_node == 0) {3643 if (full_var_decl.ast.type_node == 0) {
3660 _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, lhs_node);3644 _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, variable_node);
3661 }3645 }
3662 const is_const = switch (token_tags[full.ast.mut_token]) {3646 const is_const = switch (token_tags[full_var_decl.ast.mut_token]) {
3663 .keyword_var => false,3647 .keyword_var => false,
3664 .keyword_const => true,3648 .keyword_const => true,
3665 else => unreachable,3649 else => unreachable,
3666 };3650 };
3667 // If the alloc was const, make it const.3651 // 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: {
3669 // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc`3653 // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc`
3670 // handles it for us.3654 // handles it for us.
3671 break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node);3655 break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node);
3672 } else raw_ptr;3656 } else raw_ptr;
3673 const name_token = full.ast.mut_token + 1;3657 const name_token = full_var_decl.ast.mut_token + 1;
3674 const ident_name_raw = tree.tokenSlice(name_token);3658 const ident_name_raw = tree.tokenSlice(name_token);
3675 const ident_name = try astgen.identAsString(name_token);3659 const ident_name = try astgen.identAsString(name_token);
3676 try astgen.detectLocalShadowing(3660 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...@@ -204,13 +204,12 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
204 }204 }
205 },205 },
206 .assign_destructure => {206 .assign_destructure => {
207 const lhs_count = tree.extra_data[node_datas[node].lhs];207 const full = tree.assignDestructure(node);
208 const all_lhs = tree.extra_data[node_datas[node].lhs + 1 ..][0..lhs_count];208 for (full.ast.variables) |variable_node| {
209 for (all_lhs) |lhs| {209 _ = try astrl.expr(variable_node, block, ResultInfo.none);
210 _ = try astrl.expr(lhs, block, ResultInfo.none);
211 }210 }
212 // We don't need to gather any meaningful data here, because destructures always use RLS211 // 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);
214 return false;213 return false;
215 },214 },
216 .assign => {215 .assign => {
lib/std/zig/parser_test.zig+19
...@@ -2914,6 +2914,25 @@ test "zig fmt: test declaration" {...@@ -2914,6 +2914,25 @@ test "zig fmt: test declaration" {
2914 );2914 );
2915}2915}
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
2917test "zig fmt: infix operators" {2936test "zig fmt: infix operators" {
2918 try testCanonical(2937 try testCanonical(
2919 \\test {2938 \\test {
lib/std/zig/render.zig+12-18
...@@ -569,39 +569,33 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -569,39 +569,33 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
569 },569 },
570570
571 .assign_destructure => {571 .assign_destructure => {
572 const lhs_count = tree.extra_data[datas[node].lhs];572 const full = tree.assignDestructure(node);
573 assert(lhs_count > 1);573 if (full.comptime_token) |comptime_token| {
574 const lhs_exprs = tree.extra_data[datas[node].lhs + 1 ..][0..lhs_count];574 try renderToken(r, comptime_token, .space);
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);
580 }575 }
581576
582 for (lhs_exprs, 0..) |lhs_node, i| {577 for (full.ast.variables, 0..) |variable_node, i| {
583 const lhs_space: Space = if (i == lhs_exprs.len - 1) .space else .comma_space;578 const variable_space: Space = if (i == full.ast.variables.len - 1) .space else .comma_space;
584 switch (node_tags[lhs_node]) {579 switch (node_tags[variable_node]) {
585 .global_var_decl,580 .global_var_decl,
586 .local_var_decl,581 .local_var_decl,
587 .simple_var_decl,582 .simple_var_decl,
588 .aligned_var_decl,583 .aligned_var_decl,
589 => {584 => {
590 try renderVarDecl(r, tree.fullVarDecl(lhs_node).?, true, lhs_space);585 try renderVarDecl(r, tree.fullVarDecl(variable_node).?, true, variable_space);
591 },586 },
592 else => try renderExpression(r, lhs_node, lhs_space),587 else => try renderExpression(r, variable_node, variable_space),
593 }588 }
594 }589 }
595 const equal_token = main_tokens[node];590 if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {
596 if (tree.tokensOnSameLine(equal_token, equal_token + 1)) {591 try renderToken(r, full.ast.equal_token, .space);
597 try renderToken(r, equal_token, .space);
598 } else {592 } else {
599 ais.pushIndent();593 ais.pushIndent();
600 try renderToken(r, equal_token, .newline);594 try renderToken(r, full.ast.equal_token, .newline);
601 ais.popIndent();595 ais.popIndent();
602 }596 }
603 ais.pushIndentOneShot();597 ais.pushIndentOneShot();
604 return renderExpression(r, rhs, space);598 return renderExpression(r, full.ast.value_expr, space);
605 },599 },
606600
607 .bit_not,601 .bit_not,
test/behavior/destructure.zig+46-12
...@@ -24,21 +24,55 @@ test "simple destructure" {...@@ -24,21 +24,55 @@ test "simple destructure" {
2424
25test "destructure with comptime syntax" {25test "destructure with comptime syntax" {
26 const S = struct {26 const S = struct {
27 fn doTheTest() void {27 fn doTheTest() !void {
28 comptime var x: f32 = undefined;28 {
29 comptime x, const y, var z = .{ 0.5, 123, 456 }; // z is a comptime var29 comptime var x: f32 = undefined;
30 _ = &z;30 comptime x, const y, var z = .{ 0.5, 123, 456 }; // z is a comptime var
3131 _ = &z;
32 comptime assert(@TypeOf(y) == comptime_int);32
33 comptime assert(@TypeOf(z) == comptime_int);33 comptime assert(@TypeOf(y) == comptime_int);
34 comptime assert(x == 0.5);34 comptime assert(@TypeOf(z) == comptime_int);
35 comptime assert(y == 123);35 comptime assert(x == 0.5);
36 comptime assert(z == 456);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 }
37 }71 }
38 };72 };
3973
40 S.doTheTest();74 try S.doTheTest();
41 comptime S.doTheTest();75 try comptime S.doTheTest();
42}76}
4377
44test "destructure from labeled block" {78test "destructure from labeled block" {