aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2023-08-31 14:30:58 +0100
committerAndrew Kelley <andrew@ziglang.org>2023-09-15 11:33:53 -0700
commit88f5315ddfc6eaf3e28433504ec046fb3252db7c (patch)
tree5cd6e8e16b285d136a1fbaa98d12739aeab34feb /src/Sema.zig
parent50ef10eb4963167225f7153dc5165292dbac0046 (diff)
downloadzig-88f5315ddfc6eaf3e28433504ec046fb3252db7c.tar.gz
zig-88f5315ddfc6eaf3e28433504ec046fb3252db7c.zip
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: #498
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig54
1 files changed, 54 insertions, 0 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 3fb425ffa7..7542b80f37 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -1018,6 +1018,7 @@ fn analyzeBodyInner(
.elem_ptr_imm => try sema.zirElemPtrImm(block, inst),
.elem_val => try sema.zirElemVal(block, inst),
.elem_val_node => try sema.zirElemValNode(block, inst),
+ .elem_val_imm => try sema.zirElemValImm(block, inst),
.elem_type_index => try sema.zirElemTypeIndex(block, inst),
.elem_type => try sema.zirElemType(block, inst),
.indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
@@ -1379,6 +1380,11 @@ fn analyzeBodyInner(
i += 1;
continue;
},
+ .validate_destructure => {
+ try sema.zirValidateDestructure(block, inst);
+ i += 1;
+ continue;
+ },
.@"export" => {
try sema.zirExport(block, inst);
i += 1;
@@ -5178,6 +5184,43 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
}
}
+fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
+ const mod = sema.mod;
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data;
+ const src = inst_data.src();
+ const destructure_src = LazySrcLoc.nodeOffset(extra.destructure_node);
+ const operand = try sema.resolveInst(extra.operand);
+ const operand_ty = sema.typeOf(operand);
+
+ const can_destructure = switch (operand_ty.zigTypeTag(mod)) {
+ .Array => true,
+ .Struct => operand_ty.isTuple(mod),
+ else => false,
+ };
+
+ if (!can_destructure) {
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(block, src, "type '{}' cannot be destructured", .{operand_ty.fmt(mod)});
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(block, destructure_src, msg, "result destructured here", .{});
+ break :msg msg;
+ });
+ }
+
+ if (operand_ty.arrayLen(mod) != extra.expect_len) {
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(block, src, "expected {} elements for destructure, found {}", .{
+ extra.expect_len,
+ operand_ty.arrayLen(mod),
+ });
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(block, destructure_src, msg, "result destructured here", .{});
+ break :msg msg;
+ });
+ }
+}
+
fn failWithBadMemberAccess(
sema: *Sema,
block: *Block,
@@ -10304,6 +10347,17 @@ fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
return sema.elemVal(block, src, array, elem_index, elem_index_src, true);
}
+fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const mod = sema.mod;
+ const inst_data = sema.code.instructions.items(.data)[inst].elem_val_imm;
+ const array = try sema.resolveInst(inst_data.operand);
+ const elem_index = try mod.intRef(Type.usize, inst_data.idx);
+ return sema.elemVal(block, .unneeded, array, elem_index, .unneeded, false);
+}
+
fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();