Ast: fix comptime destructure
A preceding `comptime` keyword was being ignored if the first destructure variable was an expression.
This commit is contained in:
committed by
Andrew Kelley
parent
d981549d65
commit
c11b6adf13
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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" {
|
||||
|
||||
Reference in New Issue
Block a user