diff --git a/lib/compiler/reduce/Walk.zig b/lib/compiler/reduce/Walk.zig index 572243d829..d0145b2d0f 100644 --- a/lib/compiler/reduce/Walk.zig +++ b/lib/compiler/reduce/Walk.zig @@ -345,12 +345,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { }, .assign_destructure => { - const lhs_count = ast.extra_data[datas[node].lhs]; - assert(lhs_count > 1); - const lhs_exprs = ast.extra_data[datas[node].lhs + 1 ..][0..lhs_count]; - const rhs = datas[node].rhs; - - for (lhs_exprs) |lhs_node| { + const full = tree.assignDestructure(node); + for (full.ast.variables) |variable_node| { switch (node_tags[lhs_node]) { .global_var_decl, .local_var_decl, @@ -358,10 +354,10 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { .aligned_var_decl, => try walkLocalVarDecl(w, ast.fullVarDecl(lhs_node).?), - else => try walkExpression(w, lhs_node), + else => try walkExpression(w, variable_node), } } - return walkExpression(w, rhs); + return walkExpression(w, full.ast.assign_expr); }, .bit_not, diff --git a/lib/docs/wasm/Walk.zig b/lib/docs/wasm/Walk.zig index ebf6817b51..486f4c32d2 100644 --- a/lib/docs/wasm/Walk.zig +++ b/lib/docs/wasm/Walk.zig @@ -699,12 +699,9 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index) }, .assign_destructure => { - const extra_index = node_datas[node].lhs; - const lhs_count = ast.extra_data[extra_index]; - const lhs_nodes: []const Ast.Node.Index = @ptrCast(ast.extra_data[extra_index + 1 ..][0..lhs_count]); - const rhs = node_datas[node].rhs; - for (lhs_nodes) |lhs_node| try expr(w, scope, parent_decl, lhs_node); - _ = try expr(w, scope, parent_decl, rhs); + const full = ast.assignDestructure(node); + for (full.ast.variables) |variable_node| try expr(w, scope, parent_decl, variable_node); + _ = try expr(w, scope, parent_decl, full.ast.value_expr); }, .bool_not, diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 6d994a569a..20bdba8cf7 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -1406,6 +1406,16 @@ pub fn alignedVarDecl(tree: Ast, node: Node.Index) full.VarDecl { }); } +pub fn assignDestructure(tree: Ast, node: Node.Index) full.AssignDestructure { + const data = tree.nodes.items(.data)[node]; + const variable_count = tree.extra_data[data.lhs]; + return tree.fullAssignDestructureComponents(.{ + .variables = tree.extra_data[data.lhs + 1 ..][0..variable_count], + .equal_token = tree.nodes.items(.main_token)[node], + .value_expr = data.rhs, + }); +} + pub fn ifSimple(tree: Ast, node: Node.Index) full.If { assert(tree.nodes.items(.tag)[node] == .if_simple); const data = tree.nodes.items(.data)[node]; @@ -2045,6 +2055,28 @@ fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl return result; } +fn fullAssignDestructureComponents(tree: Ast, info: full.AssignDestructure.Components) full.AssignDestructure { + const token_tags = tree.tokens.items(.tag); + const node_tags = tree.nodes.items(.tag); + var result: full.AssignDestructure = .{ + .comptime_token = null, + .ast = info, + }; + const first_variable_token = tree.firstToken(info.variables[0]); + const maybe_comptime_token = switch (node_tags[info.variables[0]]) { + .global_var_decl, + .local_var_decl, + .aligned_var_decl, + .simple_var_decl, + => first_variable_token, + else => first_variable_token - 1, + }; + if (token_tags[maybe_comptime_token] == .keyword_comptime) { + result.comptime_token = maybe_comptime_token; + } + return result; +} + fn fullIfComponents(tree: Ast, info: full.If.Components) full.If { const token_tags = tree.tokens.items(.tag); var result: full.If = .{ @@ -2508,6 +2540,17 @@ pub const full = struct { } }; + pub const AssignDestructure = struct { + comptime_token: ?TokenIndex, + ast: Components, + + pub const Components = struct { + variables: []const Node.Index, + equal_token: TokenIndex, + value_expr: Node.Index, + }; + }; + pub const If = struct { /// Points to the first token after the `|`. Will either be an identifier or /// a `*` (with an identifier immediately after it). diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 54efe22b8d..1bbfc2001e 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -3406,45 +3406,36 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro try emitDbgNode(gz, node); const astgen = gz.astgen; const tree = astgen.tree; - const token_tags = tree.tokens.items(.tag); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); const node_tags = tree.nodes.items(.tag); - const extra_index = node_datas[node].lhs; - const lhs_count = tree.extra_data[extra_index]; - const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]); - const rhs = node_datas[node].rhs; - - const maybe_comptime_token = tree.firstToken(node) - 1; - const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime; - - if (declared_comptime and gz.is_comptime) { + const full = tree.assignDestructure(node); + if (full.comptime_token != null and gz.is_comptime) { return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{}); } // If this expression is marked comptime, we must wrap the whole thing in a comptime block. var gz_buf: GenZir = undefined; - const inner_gz = if (declared_comptime) bs: { + const inner_gz = if (full.comptime_token) |_| bs: { gz_buf = gz.makeSubBlock(scope); gz_buf.is_comptime = true; break :bs &gz_buf; } else gz; - defer if (declared_comptime) inner_gz.unstack(); + defer if (full.comptime_token) |_| inner_gz.unstack(); - const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len); - for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| { - if (node_tags[lhs_node] == .identifier) { + const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len); + for (rl_components, full.ast.variables) |*variable_rl, variable_node| { + if (node_tags[variable_node] == .identifier) { // This intentionally does not support `@"_"` syntax. - const ident_name = tree.tokenSlice(main_tokens[lhs_node]); + const ident_name = tree.tokenSlice(main_tokens[variable_node]); if (mem.eql(u8, ident_name, "_")) { - lhs_rl.* = .discard; + variable_rl.* = .discard; continue; } } - lhs_rl.* = .{ .typed_ptr = .{ - .inst = try lvalExpr(inner_gz, scope, lhs_node), - .src_node = lhs_node, + variable_rl.* = .{ .typed_ptr = .{ + .inst = try lvalExpr(inner_gz, scope, variable_node), + .src_node = variable_node, } }; } @@ -3453,9 +3444,9 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro .components = rl_components, } } }; - _ = try expr(inner_gz, scope, ri, rhs); + _ = try expr(inner_gz, scope, ri, full.ast.value_expr); - if (declared_comptime) { + if (full.comptime_token) |_| { const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value); try inner_gz.setBlockBody(comptime_block_inst); @@ -3474,23 +3465,16 @@ fn assignDestructureMaybeDecls( const astgen = gz.astgen; const tree = astgen.tree; const token_tags = tree.tokens.items(.tag); - const node_datas = tree.nodes.items(.data); const main_tokens = tree.nodes.items(.main_token); const node_tags = tree.nodes.items(.tag); - const extra_index = node_datas[node].lhs; - const lhs_count = tree.extra_data[extra_index]; - const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]); - const rhs = node_datas[node].rhs; - - const maybe_comptime_token = tree.firstToken(node) - 1; - const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime; - if (declared_comptime and gz.is_comptime) { + const full = tree.assignDestructure(node); + if (full.comptime_token != null and gz.is_comptime) { return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{}); } - const is_comptime = declared_comptime or gz.is_comptime; - const rhs_is_comptime = tree.nodes.items(.tag)[rhs] == .@"comptime"; + const is_comptime = full.comptime_token != null or gz.is_comptime; + const value_is_comptime = node_tags[full.ast.value_expr] == .@"comptime"; // When declaring consts via a destructure, we always use a result pointer. // This avoids the need to create tuple types, and is also likely easier to @@ -3499,24 +3483,24 @@ fn assignDestructureMaybeDecls( // We know this rl information won't live past the evaluation of this // expression, so it may as well go in the block arena. - const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len); - var any_non_const_lhs = false; + const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len); + var any_non_const_variables = false; var any_lvalue_expr = false; - for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| { - switch (node_tags[lhs_node]) { + for (rl_components, full.ast.variables) |*variable_rl, variable_node| { + switch (node_tags[variable_node]) { .identifier => { // This intentionally does not support `@"_"` syntax. - const ident_name = tree.tokenSlice(main_tokens[lhs_node]); + const ident_name = tree.tokenSlice(main_tokens[variable_node]); if (mem.eql(u8, ident_name, "_")) { - any_non_const_lhs = true; - lhs_rl.* = .discard; + any_non_const_variables = true; + variable_rl.* = .discard; continue; } }, .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => { - const full = tree.fullVarDecl(lhs_node).?; + const full_var_decl = tree.fullVarDecl(variable_node).?; - const name_token = full.ast.mut_token + 1; + const name_token = full_var_decl.ast.mut_token + 1; const ident_name_raw = tree.tokenSlice(name_token); if (mem.eql(u8, ident_name_raw, "_")) { return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{}); @@ -3524,35 +3508,35 @@ fn assignDestructureMaybeDecls( // We detect shadowing in the second pass over these, while we're creating scopes. - if (full.ast.addrspace_node != 0) { - return astgen.failTok(main_tokens[full.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw}); + if (full_var_decl.ast.addrspace_node != 0) { + return astgen.failTok(main_tokens[full_var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw}); } - if (full.ast.section_node != 0) { - return astgen.failTok(main_tokens[full.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw}); + if (full_var_decl.ast.section_node != 0) { + return astgen.failTok(main_tokens[full_var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw}); } - const is_const = switch (token_tags[full.ast.mut_token]) { + const is_const = switch (token_tags[full_var_decl.ast.mut_token]) { .keyword_var => false, .keyword_const => true, else => unreachable, }; - if (!is_const) any_non_const_lhs = true; + if (!is_const) any_non_const_variables = true; // We also mark `const`s as comptime if the RHS is definitely comptime-known. - const this_lhs_comptime = is_comptime or (is_const and rhs_is_comptime); + const this_variable_comptime = is_comptime or (is_const and value_is_comptime); - const align_inst: Zir.Inst.Ref = if (full.ast.align_node != 0) - try expr(gz, scope, coerced_align_ri, full.ast.align_node) + const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node != 0) + try expr(gz, scope, coerced_align_ri, full_var_decl.ast.align_node) else .none; - if (full.ast.type_node != 0) { + if (full_var_decl.ast.type_node != 0) { // Typed alloc - const type_inst = try typeExpr(gz, scope, full.ast.type_node); + const type_inst = try typeExpr(gz, scope, full_var_decl.ast.type_node); const ptr = if (align_inst == .none) ptr: { const tag: Zir.Inst.Tag = if (is_const) .alloc - else if (this_lhs_comptime) + else if (this_variable_comptime) .alloc_comptime_mut else .alloc_mut; @@ -3562,16 +3546,16 @@ fn assignDestructureMaybeDecls( .type_inst = type_inst, .align_inst = align_inst, .is_const = is_const, - .is_comptime = this_lhs_comptime, + .is_comptime = this_variable_comptime, }); - lhs_rl.* = .{ .typed_ptr = .{ .inst = ptr } }; + variable_rl.* = .{ .typed_ptr = .{ .inst = ptr } }; } else { // Inferred alloc const ptr = if (align_inst == .none) ptr: { const tag: Zir.Inst.Tag = if (is_const) tag: { - break :tag if (this_lhs_comptime) .alloc_inferred_comptime else .alloc_inferred; + break :tag if (this_variable_comptime) .alloc_inferred_comptime else .alloc_inferred; } else tag: { - break :tag if (this_lhs_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut; + break :tag if (this_variable_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut; }; break :ptr try gz.addNode(tag, node); } else try gz.addAllocExtended(.{ @@ -3579,48 +3563,48 @@ fn assignDestructureMaybeDecls( .type_inst = .none, .align_inst = align_inst, .is_const = is_const, - .is_comptime = this_lhs_comptime, + .is_comptime = this_variable_comptime, }); - lhs_rl.* = .{ .inferred_ptr = ptr }; + variable_rl.* = .{ .inferred_ptr = ptr }; } continue; }, else => {}, } - // This LHS is just an lvalue expression. + // This variable is just an lvalue expression. // We will fill in its result pointer later, inside a comptime block. - any_non_const_lhs = true; + any_non_const_variables = true; any_lvalue_expr = true; - lhs_rl.* = .{ .typed_ptr = .{ + variable_rl.* = .{ .typed_ptr = .{ .inst = undefined, - .src_node = lhs_node, + .src_node = variable_node, } }; } - if (declared_comptime and !any_non_const_lhs) { - try astgen.appendErrorTok(maybe_comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); + if (full.comptime_token != null and !any_non_const_variables) { + try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); } // If this expression is marked comptime, we must wrap it in a comptime block. var gz_buf: GenZir = undefined; - const inner_gz = if (declared_comptime) bs: { + const inner_gz = if (full.comptime_token) |_| bs: { gz_buf = gz.makeSubBlock(scope); gz_buf.is_comptime = true; break :bs &gz_buf; } else gz; - defer if (declared_comptime) inner_gz.unstack(); + defer if (full.comptime_token) |_| inner_gz.unstack(); if (any_lvalue_expr) { - // At least one LHS was an lvalue expr. Iterate again in order to + // At least one variable was an lvalue expr. Iterate again in order to // evaluate the lvalues from within the possible block_comptime. - for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| { - if (lhs_rl.* != .typed_ptr) continue; - switch (node_tags[lhs_node]) { + for (rl_components, full.ast.variables) |*variable_rl, variable_node| { + if (variable_rl.* != .typed_ptr) continue; + switch (node_tags[variable_node]) { .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue, else => {}, } - lhs_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, lhs_node); + variable_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, variable_node); } } @@ -3629,9 +3613,9 @@ fn assignDestructureMaybeDecls( _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{ .src_node = node, .components = rl_components, - } } }, rhs, node); + } } }, full.ast.value_expr, node); - if (declared_comptime) { + if (full.comptime_token) |_| { // Finish the block_comptime. Inferred alloc resolution etc will occur // in the parent block. const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); @@ -3640,37 +3624,37 @@ fn assignDestructureMaybeDecls( try gz.instructions.append(gz.astgen.gpa, comptime_block_inst); } - // Now, iterate over the LHS exprs to construct any new scopes. + // Now, iterate over the variable exprs to construct any new scopes. // If there were any inferred allocations, resolve them. // If there were any `const` decls, make the pointer constant. var cur_scope = scope; - for (rl_components, lhs_nodes) |lhs_rl, lhs_node| { - switch (node_tags[lhs_node]) { + for (rl_components, full.ast.variables) |variable_rl, variable_node| { + switch (node_tags[variable_node]) { .local_var_decl, .simple_var_decl, .aligned_var_decl => {}, else => continue, // We were mutating an existing lvalue - nothing to do } - const full = tree.fullVarDecl(lhs_node).?; - const raw_ptr = switch (lhs_rl) { + const full_var_decl = tree.fullVarDecl(variable_node).?; + const raw_ptr = switch (variable_rl) { .discard => unreachable, .typed_ptr => |typed_ptr| typed_ptr.inst, .inferred_ptr => |ptr_inst| ptr_inst, }; // If the alloc was inferred, resolve it. - if (full.ast.type_node == 0) { - _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, lhs_node); + if (full_var_decl.ast.type_node == 0) { + _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, variable_node); } - const is_const = switch (token_tags[full.ast.mut_token]) { + const is_const = switch (token_tags[full_var_decl.ast.mut_token]) { .keyword_var => false, .keyword_const => true, else => unreachable, }; // If the alloc was const, make it const. - const var_ptr = if (is_const and full.ast.type_node != 0) make_const: { + const var_ptr = if (is_const and full_var_decl.ast.type_node != 0) make_const: { // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc` // handles it for us. break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node); } else raw_ptr; - const name_token = full.ast.mut_token + 1; + const name_token = full_var_decl.ast.mut_token + 1; const ident_name_raw = tree.tokenSlice(name_token); const ident_name = try astgen.identAsString(name_token); try astgen.detectLocalShadowing( diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig index a43de68951..7e75b5c148 100644 --- a/lib/std/zig/AstRlAnnotate.zig +++ b/lib/std/zig/AstRlAnnotate.zig @@ -204,13 +204,12 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI } }, .assign_destructure => { - const lhs_count = tree.extra_data[node_datas[node].lhs]; - const all_lhs = tree.extra_data[node_datas[node].lhs + 1 ..][0..lhs_count]; - for (all_lhs) |lhs| { - _ = try astrl.expr(lhs, block, ResultInfo.none); + const full = tree.assignDestructure(node); + for (full.ast.variables) |variable_node| { + _ = try astrl.expr(variable_node, block, ResultInfo.none); } // We don't need to gather any meaningful data here, because destructures always use RLS - _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none); + _ = try astrl.expr(full.ast.value_expr, block, ResultInfo.none); return false; }, .assign => { diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 37d90a68df..38d279461e 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -2914,6 +2914,25 @@ test "zig fmt: test declaration" { ); } +test "zig fmt: destructure" { + try testCanonical( + \\comptime { + \\ var w: u8, var x: u8 = .{ 1, 2 }; + \\ w, var y: u8 = .{ 3, 4 }; + \\ var z: u8, x = .{ 5, 6 }; + \\ y, z = .{ 7, 8 }; + \\} + \\ + \\comptime { + \\ comptime var w, var x = .{ 1, 2 }; + \\ comptime w, var y = .{ 3, 4 }; + \\ comptime var z, x = .{ 5, 6 }; + \\ comptime y, z = .{ 7, 8 }; + \\} + \\ + ); +} + test "zig fmt: infix operators" { try testCanonical( \\test { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index a87f289642..c6a6f3ce71 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -569,39 +569,33 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { }, .assign_destructure => { - const lhs_count = tree.extra_data[datas[node].lhs]; - assert(lhs_count > 1); - const lhs_exprs = tree.extra_data[datas[node].lhs + 1 ..][0..lhs_count]; - const rhs = datas[node].rhs; - - const maybe_comptime_token = tree.firstToken(node) - 1; - if (token_tags[maybe_comptime_token] == .keyword_comptime) { - try renderToken(r, maybe_comptime_token, .space); + const full = tree.assignDestructure(node); + if (full.comptime_token) |comptime_token| { + try renderToken(r, comptime_token, .space); } - for (lhs_exprs, 0..) |lhs_node, i| { - const lhs_space: Space = if (i == lhs_exprs.len - 1) .space else .comma_space; - switch (node_tags[lhs_node]) { + for (full.ast.variables, 0..) |variable_node, i| { + const variable_space: Space = if (i == full.ast.variables.len - 1) .space else .comma_space; + switch (node_tags[variable_node]) { .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl, => { - try renderVarDecl(r, tree.fullVarDecl(lhs_node).?, true, lhs_space); + try renderVarDecl(r, tree.fullVarDecl(variable_node).?, true, variable_space); }, - else => try renderExpression(r, lhs_node, lhs_space), + else => try renderExpression(r, variable_node, variable_space), } } - const equal_token = main_tokens[node]; - if (tree.tokensOnSameLine(equal_token, equal_token + 1)) { - try renderToken(r, equal_token, .space); + if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) { + try renderToken(r, full.ast.equal_token, .space); } else { ais.pushIndent(); - try renderToken(r, equal_token, .newline); + try renderToken(r, full.ast.equal_token, .newline); ais.popIndent(); } ais.pushIndentOneShot(); - return renderExpression(r, rhs, space); + return renderExpression(r, full.ast.value_expr, space); }, .bit_not, diff --git a/test/behavior/destructure.zig b/test/behavior/destructure.zig index 1c249ed8d5..43ddbb7a4d 100644 --- a/test/behavior/destructure.zig +++ b/test/behavior/destructure.zig @@ -24,21 +24,55 @@ test "simple destructure" { test "destructure with comptime syntax" { const S = struct { - fn doTheTest() void { - comptime var x: f32 = undefined; - comptime x, const y, var z = .{ 0.5, 123, 456 }; // z is a comptime var - _ = &z; + fn doTheTest() !void { + { + comptime var x: f32 = undefined; + comptime x, const y, var z = .{ 0.5, 123, 456 }; // z is a comptime var + _ = &z; - comptime assert(@TypeOf(y) == comptime_int); - comptime assert(@TypeOf(z) == comptime_int); - comptime assert(x == 0.5); - comptime assert(y == 123); - comptime assert(z == 456); + comptime assert(@TypeOf(y) == comptime_int); + comptime assert(@TypeOf(z) == comptime_int); + comptime assert(x == 0.5); + comptime assert(y == 123); + comptime assert(z == 456); + } + { + var w: u8, var x: u8 = .{ 1, 2 }; + w, var y: u8 = .{ 3, 4 }; + var z: u8, x = .{ 5, 6 }; + y, z = .{ 7, 8 }; + { + w += 1; + x -= 2; + y *= 3; + z /= 4; + } + try expect(w == 4); + try expect(x == 4); + try expect(y == 21); + try expect(z == 2); + } + { + comptime var w, var x = .{ 1, 2 }; + comptime w, var y = .{ 3, 4 }; + comptime var z, x = .{ 5, 6 }; + comptime y, z = .{ 7, 8 }; + comptime { + w += 1; + x -= 2; + y *= 3; + z /= 4; + } + comptime assert(w == 4); + comptime assert(x == 4); + comptime assert(y == 21); + comptime assert(z == 2); + } } }; - S.doTheTest(); - comptime S.doTheTest(); + try S.doTheTest(); + try comptime S.doTheTest(); } test "destructure from labeled block" {