Ast: fix comptime destructure

A preceding `comptime` keyword was being ignored if the first
destructure variable was an expression.
This commit is contained in:
Jacob Young authored and Andrew Kelley committed 2024-03-17 15:23:16 -07:00
1 parent d981549d65
commit c11b6adf13
8 files changed
+200 -134

No files matched your search

+4 -8
View File
@@ -345,12 +345,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
}, },
.assign_destructure => { .assign_destructure => {
const lhs_count = ast.extra_data[datas[node].lhs]; const full = tree.assignDestructure(node);
assert(lhs_count > 1); for (full.ast.variables) |variable_node| {
const lhs_exprs = ast.extra_data[datas[node].lhs + 1 ..][0..lhs_count];
const rhs = datas[node].rhs;
for (lhs_exprs) |lhs_node| {
switch (node_tags[lhs_node]) { switch (node_tags[lhs_node]) {
.global_var_decl, .global_var_decl,
.local_var_decl, .local_var_decl,
@@ -358,10 +354,10 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
.aligned_var_decl, .aligned_var_decl,
=> try walkLocalVarDecl(w, ast.fullVarDecl(lhs_node).?), => 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, .bit_not,
+3 -6
View File
@@ -699,12 +699,9 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
}, },
.assign_destructure => { .assign_destructure => {
const extra_index = node_datas[node].lhs; const full = ast.assignDestructure(node);
const lhs_count = ast.extra_data[extra_index]; for (full.ast.variables) |variable_node| try expr(w, scope, parent_decl, variable_node);
const lhs_nodes: []const Ast.Node.Index = @ptrCast(ast.extra_data[extra_index + 1 ..][0..lhs_count]); _ = try expr(w, scope, parent_decl, full.ast.value_expr);
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);
}, },
.bool_not, .bool_not,
+43
View File
@@ -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 { pub fn ifSimple(tree: Ast, node: Node.Index) full.If {
assert(tree.nodes.items(.tag)[node] == .if_simple); assert(tree.nodes.items(.tag)[node] == .if_simple);
const data = tree.nodes.items(.data)[node]; const data = tree.nodes.items(.data)[node];
@@ -2045,6 +2055,28 @@ fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl
return result; 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 { fn fullIfComponents(tree: Ast, info: full.If.Components) full.If {
const token_tags = tree.tokens.items(.tag); const token_tags = tree.tokens.items(.tag);
var result: full.If = .{ 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 { pub const If = struct {
/// Points to the first token after the `|`. Will either be an identifier or /// Points to the first token after the `|`. Will either be an identifier or
/// a `*` (with an identifier immediately after it). /// a `*` (with an identifier immediately after it).
+70 -86
View File
@@ -3406,45 +3406,36 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro
try emitDbgNode(gz, node); try emitDbgNode(gz, node);
const astgen = gz.astgen; const astgen = gz.astgen;
const tree = astgen.tree; 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 main_tokens = tree.nodes.items(.main_token);
const node_tags = tree.nodes.items(.tag); const node_tags = tree.nodes.items(.tag);
const extra_index = node_datas[node].lhs; const full = tree.assignDestructure(node);
const lhs_count = tree.extra_data[extra_index]; if (full.comptime_token != null and gz.is_comptime) {
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) {
return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{}); 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. // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
var gz_buf: GenZir = undefined; 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 = gz.makeSubBlock(scope);
gz_buf.is_comptime = true; gz_buf.is_comptime = true;
break :bs &gz_buf; break :bs &gz_buf;
} else gz; } 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); const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| { for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
if (node_tags[lhs_node] == .identifier) { if (node_tags[variable_node] == .identifier) {
// This intentionally does not support `@"_"` syntax. // 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, "_")) { if (mem.eql(u8, ident_name, "_")) {
lhs_rl.* = .discard; variable_rl.* = .discard;
continue; continue;
} }
} }
lhs_rl.* = .{ .typed_ptr = .{ variable_rl.* = .{ .typed_ptr = .{
.inst = try lvalExpr(inner_gz, scope, lhs_node), .inst = try lvalExpr(inner_gz, scope, variable_node),
.src_node = lhs_node, .src_node = variable_node,
} }; } };
} }
@@ -3453,9 +3444,9 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro
.components = rl_components, .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); const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
_ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value); _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
try inner_gz.setBlockBody(comptime_block_inst); try inner_gz.setBlockBody(comptime_block_inst);
@@ -3474,23 +3465,16 @@ fn assignDestructureMaybeDecls(
const astgen = gz.astgen; const astgen = gz.astgen;
const tree = astgen.tree; const tree = astgen.tree;
const token_tags = tree.tokens.items(.tag); const token_tags = tree.tokens.items(.tag);
const node_datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token); const main_tokens = tree.nodes.items(.main_token);
const node_tags = tree.nodes.items(.tag); const node_tags = tree.nodes.items(.tag);
const extra_index = node_datas[node].lhs; const full = tree.assignDestructure(node);
const lhs_count = tree.extra_data[extra_index]; if (full.comptime_token != null and gz.is_comptime) {
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) {
return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{}); return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
} }
const is_comptime = declared_comptime or gz.is_comptime; const is_comptime = full.comptime_token != null or gz.is_comptime;
const rhs_is_comptime = tree.nodes.items(.tag)[rhs] == .@"comptime"; const value_is_comptime = node_tags[full.ast.value_expr] == .@"comptime";
// When declaring consts via a destructure, we always use a result pointer. // 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 // 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 // We know this rl information won't live past the evaluation of this
// expression, so it may as well go in the block arena. // expression, so it may as well go in the block arena.
const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len); const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
var any_non_const_lhs = false; var any_non_const_variables = false;
var any_lvalue_expr = false; var any_lvalue_expr = false;
for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| { for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
switch (node_tags[lhs_node]) { switch (node_tags[variable_node]) {
.identifier => { .identifier => {
// This intentionally does not support `@"_"` syntax. // 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, "_")) { if (mem.eql(u8, ident_name, "_")) {
any_non_const_lhs = true; any_non_const_variables = true;
lhs_rl.* = .discard; variable_rl.* = .discard;
continue; continue;
} }
}, },
.global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => { .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); const ident_name_raw = tree.tokenSlice(name_token);
if (mem.eql(u8, ident_name_raw, "_")) { if (mem.eql(u8, ident_name_raw, "_")) {
return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{}); 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. // We detect shadowing in the second pass over these, while we're creating scopes.
if (full.ast.addrspace_node != 0) { if (full_var_decl.ast.addrspace_node != 0) {
return astgen.failTok(main_tokens[full.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw}); 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) { if (full_var_decl.ast.section_node != 0) {
return astgen.failTok(main_tokens[full.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw}); 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_var => false,
.keyword_const => true, .keyword_const => true,
else => unreachable, 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. // 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) const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node != 0)
try expr(gz, scope, coerced_align_ri, full.ast.align_node) try expr(gz, scope, coerced_align_ri, full_var_decl.ast.align_node)
else else
.none; .none;
if (full.ast.type_node != 0) { if (full_var_decl.ast.type_node != 0) {
// Typed alloc // 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 ptr = if (align_inst == .none) ptr: {
const tag: Zir.Inst.Tag = if (is_const) const tag: Zir.Inst.Tag = if (is_const)
.alloc .alloc
else if (this_lhs_comptime) else if (this_variable_comptime)
.alloc_comptime_mut .alloc_comptime_mut
else else
.alloc_mut; .alloc_mut;
@@ -3562,16 +3546,16 @@ fn assignDestructureMaybeDecls(
.type_inst = type_inst, .type_inst = type_inst,
.align_inst = align_inst, .align_inst = align_inst,
.is_const = is_const, .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 { } else {
// Inferred alloc // Inferred alloc
const ptr = if (align_inst == .none) ptr: { const ptr = if (align_inst == .none) ptr: {
const tag: Zir.Inst.Tag = if (is_const) tag: { 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: { } 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); break :ptr try gz.addNode(tag, node);
} else try gz.addAllocExtended(.{ } else try gz.addAllocExtended(.{
@@ -3579,48 +3563,48 @@ fn assignDestructureMaybeDecls(
.type_inst = .none, .type_inst = .none,
.align_inst = align_inst, .align_inst = align_inst,
.is_const = is_const, .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; continue;
}, },
else => {}, 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. // 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; any_lvalue_expr = true;
lhs_rl.* = .{ .typed_ptr = .{ variable_rl.* = .{ .typed_ptr = .{
.inst = undefined, .inst = undefined,
.src_node = lhs_node, .src_node = variable_node,
} }; } };
} }
if (declared_comptime and !any_non_const_lhs) { if (full.comptime_token != null and !any_non_const_variables) {
try astgen.appendErrorTok(maybe_comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); 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. // If this expression is marked comptime, we must wrap it in a comptime block.
var gz_buf: GenZir = undefined; 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 = gz.makeSubBlock(scope);
gz_buf.is_comptime = true; gz_buf.is_comptime = true;
break :bs &gz_buf; break :bs &gz_buf;
} else gz; } else gz;
defer if (declared_comptime) inner_gz.unstack(); defer if (full.comptime_token) |_| inner_gz.unstack();
if (any_lvalue_expr) { 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. // evaluate the lvalues from within the possible block_comptime.
for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| { for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
if (lhs_rl.* != .typed_ptr) continue; if (variable_rl.* != .typed_ptr) continue;
switch (node_tags[lhs_node]) { switch (node_tags[variable_node]) {
.global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue, .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue,
else => {}, 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 = .{ _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{
.src_node = node, .src_node = node,
.components = rl_components, .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 // Finish the block_comptime. Inferred alloc resolution etc will occur
// in the parent block. // in the parent block.
const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); 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); 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 inferred allocations, resolve them.
// If there were any `const` decls, make the pointer constant. // If there were any `const` decls, make the pointer constant.
var cur_scope = scope; var cur_scope = scope;
for (rl_components, lhs_nodes) |lhs_rl, lhs_node| { for (rl_components, full.ast.variables) |variable_rl, variable_node| {
switch (node_tags[lhs_node]) { switch (node_tags[variable_node]) {
.local_var_decl, .simple_var_decl, .aligned_var_decl => {}, .local_var_decl, .simple_var_decl, .aligned_var_decl => {},
else => continue, // We were mutating an existing lvalue - nothing to do else => continue, // We were mutating an existing lvalue - nothing to do
} }
const full = tree.fullVarDecl(lhs_node).?; const full_var_decl = tree.fullVarDecl(variable_node).?;
const raw_ptr = switch (lhs_rl) { const raw_ptr = switch (variable_rl) {
.discard => unreachable, .discard => unreachable,
.typed_ptr => |typed_ptr| typed_ptr.inst, .typed_ptr => |typed_ptr| typed_ptr.inst,
.inferred_ptr => |ptr_inst| ptr_inst, .inferred_ptr => |ptr_inst| ptr_inst,
}; };
// If the alloc was inferred, resolve it. // If the alloc was inferred, resolve it.
if (full.ast.type_node == 0) { if (full_var_decl.ast.type_node == 0) {
_ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, lhs_node); _ = 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_var => false,
.keyword_const => true, .keyword_const => true,
else => unreachable, else => unreachable,
}; };
// If the alloc was const, make it const. // 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` // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc`
// handles it for us. // handles it for us.
break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node); break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node);
} else raw_ptr; } 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_raw = tree.tokenSlice(name_token);
const ident_name = try astgen.identAsString(name_token); const ident_name = try astgen.identAsString(name_token);
try astgen.detectLocalShadowing( try astgen.detectLocalShadowing(
+4 -5
View File
@@ -204,13 +204,12 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
} }
}, },
.assign_destructure => { .assign_destructure => {
const lhs_count = tree.extra_data[node_datas[node].lhs]; const full = tree.assignDestructure(node);
const all_lhs = tree.extra_data[node_datas[node].lhs + 1 ..][0..lhs_count]; for (full.ast.variables) |variable_node| {
for (all_lhs) |lhs| { _ = try astrl.expr(variable_node, block, ResultInfo.none);
_ = try astrl.expr(lhs, block, ResultInfo.none);
} }
// We don't need to gather any meaningful data here, because destructures always use RLS // 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; return false;
}, },
.assign => { .assign => {
+19
View File
@@ -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" { test "zig fmt: infix operators" {
try testCanonical( try testCanonical(
\\test { \\test {
+12 -18
View File
@@ -569,39 +569,33 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
}, },
.assign_destructure => { .assign_destructure => {
const lhs_count = tree.extra_data[datas[node].lhs]; const full = tree.assignDestructure(node);
assert(lhs_count > 1); if (full.comptime_token) |comptime_token| {
const lhs_exprs = tree.extra_data[datas[node].lhs + 1 ..][0..lhs_count]; try renderToken(r, comptime_token, .space);
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);
} }
for (lhs_exprs, 0..) |lhs_node, i| { for (full.ast.variables, 0..) |variable_node, i| {
const lhs_space: Space = if (i == lhs_exprs.len - 1) .space else .comma_space; const variable_space: Space = if (i == full.ast.variables.len - 1) .space else .comma_space;
switch (node_tags[lhs_node]) { switch (node_tags[variable_node]) {
.global_var_decl, .global_var_decl,
.local_var_decl, .local_var_decl,
.simple_var_decl, .simple_var_decl,
.aligned_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(full.ast.equal_token, full.ast.equal_token + 1)) {
if (tree.tokensOnSameLine(equal_token, equal_token + 1)) { try renderToken(r, full.ast.equal_token, .space);
try renderToken(r, equal_token, .space);
} else { } else {
ais.pushIndent(); ais.pushIndent();
try renderToken(r, equal_token, .newline); try renderToken(r, full.ast.equal_token, .newline);
ais.popIndent(); ais.popIndent();
} }
ais.pushIndentOneShot(); ais.pushIndentOneShot();
return renderExpression(r, rhs, space); return renderExpression(r, full.ast.value_expr, space);
}, },
.bit_not, .bit_not,
+45 -11
View File
@@ -24,21 +24,55 @@ test "simple destructure" {
test "destructure with comptime syntax" { test "destructure with comptime syntax" {
const S = struct { const S = struct {
fn doTheTest() void { fn doTheTest() !void {
comptime var x: f32 = undefined; {
comptime x, const y, var z = .{ 0.5, 123, 456 }; // z is a comptime var comptime var x: f32 = undefined;
_ = &z; 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(y) == comptime_int);
comptime assert(@TypeOf(z) == comptime_int); comptime assert(@TypeOf(z) == comptime_int);
comptime assert(x == 0.5); comptime assert(x == 0.5);
comptime assert(y == 123); comptime assert(y == 123);
comptime assert(z == 456); 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(); try S.doTheTest();
comptime S.doTheTest(); try comptime S.doTheTest();
} }
test "destructure from labeled block" { test "destructure from labeled block" {