diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2023-08-31 14:30:58 +0100 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2023-09-15 11:33:53 -0700 |
| commit | 88f5315ddfc6eaf3e28433504ec046fb3252db7c (patch) | |
| tree | 5cd6e8e16b285d136a1fbaa98d12739aeab34feb /src/Sema.zig | |
| parent | 50ef10eb4963167225f7153dc5165292dbac0046 (diff) | |
| download | zig-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.zig | 54 |
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(); |
