Skip to content

Commit 9475af7

Browse files
committed
compiler: implement destructuring syntax
This change implements the following syntax into the compiler: ```zig const x: u32, var y, foo.bar = .{ 1, 2, 3 }; ``` A destructure expression may only appear within a block (i.e. not at comtainer scope). The LHS consists of a sequence of comma-separated var decls and/or lvalue expressions. The RHS is a normal expression. A new result location type, `destructure`, is used, which contains result pointers for each component of the destructure. This means that when the RHS is a more complicated expression, peer type resolution is not used: each result value is individually destructured and written to the result pointers. RLS is always used for destructure expressions, meaning every `const` on the LHS of such an expression creates a true stack allocation. Aside from anonymous array literals, Sema is capable of destructuring the following types: * Tuples * Arrays * Vectors A destructure may be prefixed with the `comptime` keyword, in which case the entire destructure is evaluated at comptime: this means all `var`s in the LHS are `comptime var`s, every lvalue expression is evaluated at comptime, and the RHS is evaluated at comptime. If every LHS is a `const`, this is not allowed: as with single declarations, the user should instead mark the RHS as `comptime`. There are a few subtleties in the grammar changes here. For one thing, if every LHS is an lvalue expression (rather than a var decl), a destructure is considered an expression. This makes, for instance, `if (cond) x, y = .{ 1, 2 };` valid Zig code. A destructure is allowed in almost every context where a standard assignment expression is permitted. The exception is `switch` prongs, which cannot be destructures as the comma is ambiguous with the end of the prong. A follow-up commit will begin utilizing this syntax in the Zig compiler. Resolves: ziglang#498
1 parent b684341 commit 9475af7

14 files changed

+1066
-105
lines changed

lib/std/zig/Ast.zig

+28
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
241241
token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(),
242242
});
243243
},
244+
.expected_expr_or_var_decl => {
245+
return stream.print("expected expression or var decl, found '{s}'", .{
246+
token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(),
247+
});
248+
},
244249
.expected_fn => {
245250
return stream.print("expected function, found '{s}'", .{
246251
token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(),
@@ -584,6 +589,13 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
584589
.error_union,
585590
=> n = datas[n].lhs,
586591

592+
.assign_destructure => {
593+
const extra_idx = datas[n].lhs;
594+
const lhs_len = tree.extra_data[extra_idx];
595+
assert(lhs_len > 0);
596+
n = tree.extra_data[extra_idx + 1];
597+
},
598+
587599
.fn_decl,
588600
.fn_proto_simple,
589601
.fn_proto_multi,
@@ -816,6 +828,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
816828
.assign_add_sat,
817829
.assign_sub_sat,
818830
.assign,
831+
.assign_destructure,
819832
.merge_error_sets,
820833
.mul,
821834
.div,
@@ -2846,6 +2859,7 @@ pub const Error = struct {
28462859
expected_container_members,
28472860
expected_expr,
28482861
expected_expr_or_assignment,
2862+
expected_expr_or_var_decl,
28492863
expected_fn,
28502864
expected_inlinable,
28512865
expected_labelable,
@@ -3006,6 +3020,20 @@ pub const Node = struct {
30063020
assign_sub_sat,
30073021
/// `lhs = rhs`. main_token is op.
30083022
assign,
3023+
/// `a, b, ... = rhs`. main_token is op. lhs is index into `extra_data`
3024+
/// of an lhs elem count followed by an array of that many `Node.Index`,
3025+
/// with each node having one of the following types:
3026+
/// * `global_var_decl`
3027+
/// * `local_var_decl`
3028+
/// * `simple_var_decl`
3029+
/// * `aligned_var_decl`
3030+
/// * Any expression node
3031+
/// The first 3 types correspond to a `var` or `const` lhs node (note
3032+
/// that their `rhs` is always 0). An expression node corresponds to a
3033+
/// standard assignment LHS (which must be evaluated as an lvalue).
3034+
/// There may be a preceding `comptime` token, which does not create a
3035+
/// corresponding `comptime` node so must be manually detected.
3036+
assign_destructure,
30093037
/// `lhs || rhs`. main_token is the `||`.
30103038
merge_error_sets,
30113039
/// `lhs * rhs`. main_token is the `*`.

0 commit comments

Comments
 (0)