Commit e315120b79

Andrew Kelley <andrew@ziglang.org>
2021-04-20 08:23:24
AstGen: implement array initialization expressions
1 parent 693dbee
Changed files (5)
lib/std/zig/render.zig
@@ -1590,7 +1590,6 @@ fn renderStructInit(
     return renderToken(ais, tree, rbrace, space);
 }
 
-// TODO: handle comments between elements
 fn renderArrayInit(
     gpa: *Allocator,
     ais: *Ais,
src/AstGen.zig
@@ -822,15 +822,20 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn
         .@"errdefer" => return astgen.failNode(node, "TODO implement astgen.expr for .errdefer", .{}),
         .@"try" => return astgen.failNode(node, "TODO implement astgen.expr for .Try", .{}),
 
-        .array_init_one,
-        .array_init_one_comma,
-        .array_init_dot_two,
-        .array_init_dot_two_comma,
+        .array_init_one, .array_init_one_comma => {
+            var elements: [1]ast.Node.Index = undefined;
+            return arrayInitExpr(gz, scope, rl, node, tree.arrayInitOne(&elements, node));
+        },
+        .array_init_dot_two, .array_init_dot_two_comma => {
+            var elements: [2]ast.Node.Index = undefined;
+            return arrayInitExpr(gz, scope, rl, node, tree.arrayInitDotTwo(&elements, node));
+        },
         .array_init_dot,
         .array_init_dot_comma,
+        => return arrayInitExpr(gz, scope, rl, node, tree.arrayInitDot(node)),
         .array_init,
         .array_init_comma,
-        => return astgen.failNode(node, "TODO implement astgen.expr for array literals", .{}),
+        => return arrayInitExpr(gz, scope, rl, node, tree.arrayInit(node)),
 
         .struct_init_one, .struct_init_one_comma => {
             var fields: [1]ast.Node.Index = undefined;
@@ -856,6 +861,182 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn
     }
 }
 
+pub fn arrayInitExpr(
+    gz: *GenZir,
+    scope: *Scope,
+    rl: ResultLoc,
+    node: ast.Node.Index,
+    array_init: ast.full.ArrayInit,
+) InnerError!Zir.Inst.Ref {
+    const astgen = gz.astgen;
+    const tree = &astgen.file.tree;
+    const gpa = astgen.gpa;
+    const node_tags = tree.nodes.items(.tag);
+    const main_tokens = tree.nodes.items(.main_token);
+
+    assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
+
+    const types: struct {
+        array: Zir.Inst.Ref,
+        elem: Zir.Inst.Ref,
+    } = inst: {
+        if (array_init.ast.type_expr == 0) break :inst .{
+            .array = .none,
+            .elem = .none,
+        };
+
+        infer: {
+            const array_type: ast.full.ArrayType = switch (node_tags[array_init.ast.type_expr]) {
+                .array_type => tree.arrayType(array_init.ast.type_expr),
+                .array_type_sentinel => tree.arrayTypeSentinel(array_init.ast.type_expr),
+                else => break :infer,
+            };
+            // This intentionally does not support `@"_"` syntax.
+            if (node_tags[array_type.ast.elem_count] == .identifier and
+                mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"))
+            {
+                const tag: Zir.Inst.Tag = switch (node_tags[array_init.ast.type_expr]) {
+                    .array_type => .array_type,
+                    .array_type_sentinel => .array_type_sentinel,
+                    else => unreachable,
+                };
+                const len_inst = try gz.addInt(array_init.ast.elements.len);
+                const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
+                const array_type_inst = try gz.addBin(tag, len_inst, elem_type);
+                break :inst .{
+                    .array = array_type_inst,
+                    .elem = elem_type,
+                };
+            }
+        }
+        const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
+        const elem_type = try gz.addUnNode(.elem_type, array_type_inst, array_init.ast.type_expr);
+        break :inst .{
+            .array = array_type_inst,
+            .elem = elem_type,
+        };
+    };
+
+    switch (rl) {
+        .discard => {
+            for (array_init.ast.elements) |elem_init| {
+                _ = try expr(gz, scope, .discard, elem_init);
+            }
+            return Zir.Inst.Ref.void_value;
+        },
+        .ref => {
+            if (types.array != .none) {
+                return arrayInitExprRlTy(gz, scope, rl, node, array_init.ast.elements, types.array, types.elem, .array_init_ref);
+            } else {
+                return arrayInitExprRlNone(gz, scope, rl, node, array_init.ast.elements, .array_init_anon_ref);
+            }
+        },
+        .none, .none_or_ref => {
+            if (types.array != .none) {
+                return arrayInitExprRlTy(gz, scope, rl, node, array_init.ast.elements, types.array, types.elem, .array_init);
+            } else {
+                return arrayInitExprRlNone(gz, scope, rl, node, array_init.ast.elements, .array_init_anon);
+            }
+        },
+        .ty => |ty_inst| {
+            if (types.array != .none) {
+                const result = try arrayInitExprRlTy(gz, scope, rl, node, array_init.ast.elements, types.array, types.elem, .array_init);
+                return rvalue(gz, scope, rl, result, node);
+            } else {
+                const elem_type = try gz.addUnNode(.elem_type, ty_inst, node);
+                return arrayInitExprRlTy(gz, scope, rl, node, array_init.ast.elements, ty_inst, elem_type, .array_init);
+            }
+        },
+        .ptr, .inferred_ptr => |ptr_inst| {
+            return arrayInitExprRlPtr(gz, scope, rl, node, array_init.ast.elements, ptr_inst);
+        },
+        .block_ptr => |block_gz| {
+            return arrayInitExprRlPtr(gz, scope, rl, node, array_init.ast.elements, block_gz.rl_ptr);
+        },
+    }
+}
+
+pub fn arrayInitExprRlNone(
+    gz: *GenZir,
+    scope: *Scope,
+    rl: ResultLoc,
+    node: ast.Node.Index,
+    elements: []const ast.Node.Index,
+    tag: Zir.Inst.Tag,
+) InnerError!Zir.Inst.Ref {
+    const astgen = gz.astgen;
+    const gpa = astgen.gpa;
+    const elem_list = try gpa.alloc(Zir.Inst.Ref, elements.len);
+    defer gpa.free(elem_list);
+
+    for (elements) |elem_init, i| {
+        elem_list[i] = try expr(gz, scope, .none, elem_init);
+    }
+    const init_inst = try gz.addPlNode(tag, node, Zir.Inst.MultiOp{
+        .operands_len = @intCast(u32, elem_list.len),
+    });
+    try astgen.appendRefs(elem_list);
+    return init_inst;
+}
+
+pub fn arrayInitExprRlTy(
+    gz: *GenZir,
+    scope: *Scope,
+    rl: ResultLoc,
+    node: ast.Node.Index,
+    elements: []const ast.Node.Index,
+    array_ty_inst: Zir.Inst.Ref,
+    elem_ty_inst: Zir.Inst.Ref,
+    tag: Zir.Inst.Tag,
+) InnerError!Zir.Inst.Ref {
+    const astgen = gz.astgen;
+    const gpa = astgen.gpa;
+
+    const elem_list = try gpa.alloc(Zir.Inst.Ref, elements.len);
+    defer gpa.free(elem_list);
+
+    const elem_rl: ResultLoc = .{ .ty = elem_ty_inst };
+
+    for (elements) |elem_init, i| {
+        elem_list[i] = try expr(gz, scope, elem_rl, elem_init);
+    }
+    const init_inst = try gz.addPlNode(tag, node, Zir.Inst.MultiOp{
+        .operands_len = @intCast(u32, elem_list.len),
+    });
+    try astgen.appendRefs(elem_list);
+    return init_inst;
+}
+
+pub fn arrayInitExprRlPtr(
+    gz: *GenZir,
+    scope: *Scope,
+    rl: ResultLoc,
+    node: ast.Node.Index,
+    elements: []const ast.Node.Index,
+    result_ptr: Zir.Inst.Ref,
+) InnerError!Zir.Inst.Ref {
+    const astgen = gz.astgen;
+    const gpa = astgen.gpa;
+
+    const elem_ptr_list = try gpa.alloc(Zir.Inst.Index, elements.len);
+    defer gpa.free(elem_ptr_list);
+
+    for (elements) |elem_init, i| {
+        const index_inst = try gz.addInt(i);
+        const elem_ptr = try gz.addPlNode(.elem_ptr_node, elem_init, Zir.Inst.Bin{
+            .lhs = result_ptr,
+            .rhs = index_inst,
+        });
+        elem_ptr_list[i] = gz.refToIndex(elem_ptr).?;
+        _ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);
+    }
+    _ = try gz.addPlNode(.validate_array_init_ptr, node, Zir.Inst.Block{
+        .body_len = @intCast(u32, elem_ptr_list.len),
+    });
+    try astgen.extra.appendSlice(gpa, elem_ptr_list);
+    return .void_value;
+}
+
 pub fn structInitExpr(
     gz: *GenZir,
     scope: *Scope,
@@ -911,7 +1092,14 @@ pub fn structInitExpr(
             return init_inst;
         },
         .ref => unreachable, // struct literal not valid as l-value
-        .ty => |ty_inst| return structInitExprRlTy(gz, scope, rl, node, struct_init, ty_inst),
+        .ty => |ty_inst| {
+            if (struct_init.ast.type_expr == 0) {
+                return structInitExprRlTy(gz, scope, rl, node, struct_init, ty_inst);
+            }
+            const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
+            const result = try structInitExprRlTy(gz, scope, rl, node, struct_init, inner_ty_inst);
+            return rvalue(gz, scope, rl, result, node);
+        },
         .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst),
         .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr),
     }
@@ -942,11 +1130,11 @@ pub fn structInitExprRlPtr(
         field_ptr_list[i] = gz.refToIndex(field_ptr).?;
         _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init);
     }
-    const validate_inst = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{
+    _ = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{
         .body_len = @intCast(u32, field_ptr_list.len),
     });
     try astgen.extra.appendSlice(gpa, field_ptr_list);
-    return validate_inst;
+    return .void_value;
 }
 
 pub fn structInitExprRlTy(
@@ -1336,6 +1524,7 @@ fn blockExprStmts(
                         .array_mul,
                         .array_type,
                         .array_type_sentinel,
+                        .elem_type,
                         .indexable_ptr_len,
                         .as,
                         .as_node,
@@ -1393,8 +1582,6 @@ fn blockExprStmts(
                         .param_type,
                         .ptrtoint,
                         .ref,
-                        .ret_ptr,
-                        .ret_type,
                         .shl,
                         .shr,
                         .str,
@@ -1453,6 +1640,10 @@ fn blockExprStmts(
                         .struct_init_empty,
                         .struct_init,
                         .struct_init_anon,
+                        .array_init,
+                        .array_init_anon,
+                        .array_init_ref,
+                        .array_init_anon_ref,
                         .union_init_ptr,
                         .field_type,
                         .field_type_ref,
@@ -1469,18 +1660,12 @@ fn blockExprStmts(
                         .type_info,
                         .size_of,
                         .bit_size_of,
-                        .this,
-                        .ret_addr,
-                        .builtin_src,
                         .add_with_overflow,
                         .sub_with_overflow,
                         .mul_with_overflow,
                         .shl_with_overflow,
                         .log2_int_type,
                         .typeof_log2_int_type,
-                        .error_return_trace,
-                        .frame,
-                        .frame_address,
                         .ptr_to_int,
                         .align_of,
                         .bool_to_int,
@@ -1575,6 +1760,7 @@ fn blockExprStmts(
                         .repeat,
                         .repeat_inline,
                         .validate_struct_init_ptr,
+                        .validate_array_init_ptr,
                         .panic,
                         .set_align_stack,
                         .set_cold,
@@ -2572,13 +2758,17 @@ fn structDeclInner(
 
         field_index += 1;
     }
-    if (field_index != 0) {
+    {
         const empty_slot_count = 16 - (field_index % 16);
-        cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
+        if (empty_slot_count < 16) {
+            cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
+        }
     }
-    if (wip_decls.decl_index != 0) {
+    {
         const empty_slot_count = 16 - (wip_decls.decl_index % 16);
-        wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
+        if (empty_slot_count < 16) {
+            wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
+        }
     }
 
     const decl_inst = try gz.addBlock(tag, node);
@@ -4609,9 +4799,9 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref
     const operand_node = node_datas[node].lhs;
     const operand: Zir.Inst.Ref = if (operand_node != 0) operand: {
         const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{
-            .ptr = try gz.addNode(.ret_ptr, node),
+            .ptr = try gz.addNodeExtended(.ret_ptr, node),
         } else .{
-            .ty = try gz.addNode(.ret_type, node),
+            .ty = try gz.addNodeExtended(.ret_type, node),
         };
         break :operand try expr(gz, scope, rl, operand_node);
     } else .void_value;
@@ -5203,12 +5393,12 @@ fn builtinCall(
         .breakpoint => return simpleNoOpVoid(gz, scope, rl, node, .breakpoint),
         .fence      => return simpleNoOpVoid(gz, scope, rl, node, .fence),
 
-        .This               => return rvalue(gz, scope, rl, try gz.addNode(.this,               node), node),
-        .return_address     => return rvalue(gz, scope, rl, try gz.addNode(.ret_addr,           node), node),
-        .src                => return rvalue(gz, scope, rl, try gz.addNode(.builtin_src,        node), node),
-        .error_return_trace => return rvalue(gz, scope, rl, try gz.addNode(.error_return_trace, node), node),
-        .frame              => return rvalue(gz, scope, rl, try gz.addNode(.frame,              node), node),
-        .frame_address      => return rvalue(gz, scope, rl, try gz.addNode(.frame_address,      node), node),
+        .This               => return rvalue(gz, scope, rl, try gz.addNodeExtended(.this,               node), node),
+        .return_address     => return rvalue(gz, scope, rl, try gz.addNodeExtended(.ret_addr,           node), node),
+        .src                => return rvalue(gz, scope, rl, try gz.addNodeExtended(.builtin_src,        node), node),
+        .error_return_trace => return rvalue(gz, scope, rl, try gz.addNodeExtended(.error_return_trace, node), node),
+        .frame              => return rvalue(gz, scope, rl, try gz.addNodeExtended(.frame,              node), node),
+        .frame_address      => return rvalue(gz, scope, rl, try gz.addNodeExtended(.frame_address,      node), node),
 
         .type_info   => return simpleUnOpType(gz, scope, rl, node, params[0], .type_info),
         .size_of     => return simpleUnOpType(gz, scope, rl, node, params[0], .size_of),
src/Module.zig
@@ -1622,6 +1622,22 @@ pub const Scope = struct {
             });
         }
 
+        pub fn addNodeExtended(
+            gz: *GenZir,
+            opcode: Zir.Inst.Extended,
+            /// Absolute node index. This function does the conversion to offset from Decl.
+            src_node: ast.Node.Index,
+        ) !Zir.Inst.Ref {
+            return gz.add(.{
+                .tag = .extended,
+                .data = .{ .extended = .{
+                    .opcode = opcode,
+                    .small = undefined,
+                    .operand = @bitCast(u32, gz.nodeIndexToRelative(src_node)),
+                } },
+            });
+        }
+
         /// Asserts that `str` is 8 or fewer bytes.
         pub fn addSmallStr(
             gz: *GenZir,
@@ -2583,6 +2599,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
         return error.AnalysisFail;
     }
 
+    log.debug("AstGen success: {s}", .{file.sub_file_path});
     file.status = .success;
 }
 
src/Sema.zig
@@ -175,6 +175,7 @@ pub fn analyzeBody(
             .elem_ptr_node                => try sema.zirElemPtrNode(block, inst),
             .elem_val                     => try sema.zirElemVal(block, inst),
             .elem_val_node                => try sema.zirElemValNode(block, inst),
+            .elem_type                    => try sema.zirElemType(block, inst),
             .enum_literal                 => try sema.zirEnumLiteral(block, inst),
             .enum_literal_small           => try sema.zirEnumLiteralSmall(block, inst),
             .enum_to_int                  => try sema.zirEnumToInt(block, inst),
@@ -223,8 +224,6 @@ pub fn analyzeBody(
             .ptr_type_simple              => try sema.zirPtrTypeSimple(block, inst),
             .ptrtoint                     => try sema.zirPtrtoint(block, inst),
             .ref                          => try sema.zirRef(block, inst),
-            .ret_ptr                      => try sema.zirRetPtr(block, inst),
-            .ret_type                     => try sema.zirRetType(block, inst),
             .shl                          => try sema.zirShl(block, inst),
             .shr                          => try sema.zirShr(block, inst),
             .slice_end                    => try sema.zirSliceEnd(block, inst),
@@ -252,9 +251,6 @@ pub fn analyzeBody(
             .type_info                    => try sema.zirTypeInfo(block, inst),
             .size_of                      => try sema.zirSizeOf(block, inst),
             .bit_size_of                  => try sema.zirBitSizeOf(block, inst),
-            .this                         => try sema.zirThis(block, inst),
-            .ret_addr                     => try sema.zirRetAddr(block, inst),
-            .builtin_src                  => try sema.zirBuiltinSrc(block, inst),
             .typeof                       => try sema.zirTypeof(block, inst),
             .typeof_elem                  => try sema.zirTypeofElem(block, inst),
             .typeof_peer                  => try sema.zirTypeofPeer(block, inst),
@@ -264,12 +260,13 @@ pub fn analyzeBody(
             .struct_init_empty            => try sema.zirStructInitEmpty(block, inst),
             .struct_init                  => try sema.zirStructInit(block, inst),
             .struct_init_anon             => try sema.zirStructInitAnon(block, inst),
+            .array_init                   => try sema.zirArrayInit(block, inst, false),
+            .array_init_anon              => try sema.zirArrayInitAnon(block, inst, false),
+            .array_init_ref               => try sema.zirArrayInit(block, inst, true),
+            .array_init_anon_ref          => try sema.zirArrayInitAnon(block, inst, true),
             .union_init_ptr               => try sema.zirUnionInitPtr(block, inst),
             .field_type                   => try sema.zirFieldType(block, inst),
             .field_type_ref               => try sema.zirFieldTypeRef(block, inst),
-            .error_return_trace           => try sema.zirErrorReturnTrace(block, inst),
-            .frame                        => try sema.zirFrame(block, inst),
-            .frame_address                => try sema.zirFrameAddress(block, inst),
             .ptr_to_int                   => try sema.zirPtrToInt(block, inst),
             .align_of                     => try sema.zirAlignOf(block, inst),
             .bool_to_int                  => try sema.zirBoolToInt(block, inst),
@@ -435,6 +432,10 @@ pub fn analyzeBody(
                 try sema.zirValidateStructInitPtr(block, inst);
                 continue;
             },
+            .validate_array_init_ptr => {
+                try sema.zirValidateArrayInitPtr(block, inst);
+                continue;
+            },
             .@"export" => {
                 try sema.zirExport(block, inst);
                 continue;
@@ -499,6 +500,28 @@ pub fn analyzeBody(
     }
 }
 
+fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
+    const extended = sema.code.instructions.items(.data)[inst].extended;
+    switch (extended.opcode) {
+        // zig fmt: off
+        .func               => return sema.zirFuncExtended(    block, extended),
+        .ret_ptr            => return sema.zirRetPtr(          block, extended),
+        .ret_type           => return sema.zirRetType(         block, extended),
+        .this               => return sema.zirThis(            block, extended),
+        .ret_addr           => return sema.zirRetAddr(         block, extended),
+        .builtin_src        => return sema.zirBuiltinSrc(      block, extended),
+        .error_return_trace => return sema.zirErrorReturnTrace(block, extended),
+        .frame              => return sema.zirFrame(           block, extended),
+        .frame_address      => return sema.zirFrameAddress(    block, extended),
+        .c_undef            => return sema.zirCUndef(          block, extended),
+        .c_include          => return sema.zirCInclude(        block, extended),
+        .c_define           => return sema.zirCDefine(         block, extended),
+        .wasm_memory_size   => return sema.zirWasmMemorySize(  block, extended),
+        .wasm_memory_grow   => return sema.zirWasmMemoryGrow(  block, extended),
+        // zig fmt: on
+    }
+}
+
 /// TODO when we rework TZIR memory layout, this function will no longer have a possible error.
 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) error{OutOfMemory}!*ir.Inst {
     var i: usize = @enumToInt(zir_ref);
@@ -990,11 +1013,15 @@ fn zirErrorSetDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
     return sema.mod.fail(&block.base, sema.src, "TODO implement zirErrorSetDecl", .{});
 }
 
-fn zirRetPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
+fn zirRetPtr(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const src: LazySrcLoc = .unneeded;
+    const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
     try sema.requireFunctionBlock(block, src);
     const fn_ty = sema.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
     const ret_type = fn_ty.fnReturnType();
@@ -1011,11 +1038,15 @@ fn zirRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*In
     return sema.analyzeRef(block, inst_data.src(), operand);
 }
 
-fn zirRetType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
+fn zirRetType(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const src: LazySrcLoc = .unneeded;
+    const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
     try sema.requireFunctionBlock(block, src);
     const fn_ty = sema.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
     const ret_type = fn_ty.fnReturnType();
@@ -1247,6 +1278,12 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
     }
 }
 
+fn zirValidateArrayInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src = inst_data.src();
+    return sema.mod.fail(&block.base, src, "TODO implement Sema.zirValidateArrayInitPtr", .{});
+}
+
 fn failWithBadFieldAccess(
     sema: *Sema,
     block: *Scope.Block,
@@ -2064,6 +2101,14 @@ fn zirOptionalTypeFromPtrElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.I
     return sema.mod.constType(sema.arena, inst_data.src(), opt_ty);
 }
 
+fn zirElemType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
+    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const src = inst_data.src();
+    const array_type = try sema.resolveType(block, src, inst_data.operand);
+    const elem_type = array_type.elemType();
+    return sema.mod.constType(sema.arena, src, elem_type);
+}
+
 fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
@@ -4512,21 +4557,30 @@ fn zirBitSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
     return sema.mod.constIntUnsigned(sema.arena, src, Type.initTag(.comptime_int), bit_size);
 }
 
-fn zirThis(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
-    const src_node = sema.code.instructions.items(.data)[inst].node;
-    const src: LazySrcLoc = .{ .node_offset = src_node };
+fn zirThis(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
+    const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
     return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirThis", .{});
 }
 
-fn zirRetAddr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
-    const src_node = sema.code.instructions.items(.data)[inst].node;
-    const src: LazySrcLoc = .{ .node_offset = src_node };
+fn zirRetAddr(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
+    const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
     return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirRetAddr", .{});
 }
 
-fn zirBuiltinSrc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
-    const src_node = sema.code.instructions.items(.data)[inst].node;
-    const src: LazySrcLoc = .{ .node_offset = src_node };
+fn zirBuiltinSrc(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
+    const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
     return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirBuiltinSrc", .{});
 }
 
@@ -4983,6 +5037,18 @@ fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inn
     return sema.mod.fail(&block.base, src, "TODO: Sema.zirStructInitAnon", .{});
 }
 
+fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) InnerError!*Inst {
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src = inst_data.src();
+    return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInit", .{});
+}
+
+fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) InnerError!*Inst {
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src = inst_data.src();
+    return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInitAnon", .{});
+}
+
 fn zirFieldTypeRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const src = inst_data.src();
@@ -4995,21 +5061,30 @@ fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
     return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldType", .{});
 }
 
-fn zirErrorReturnTrace(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
-    const src = inst_data.src();
+fn zirErrorReturnTrace(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
+    const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
     return sema.mod.fail(&block.base, src, "TODO: Sema.zirErrorReturnTrace", .{});
 }
 
-fn zirFrame(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
-    const src = inst_data.src();
+fn zirFrame(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
+    const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
     return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrame", .{});
 }
 
-fn zirFrameAddress(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
-    const src = inst_data.src();
+fn zirFrameAddress(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
+    const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
     return sema.mod.fail(&block.base, src, "TODO: Sema.zirFrameAddress", .{});
 }
 
@@ -5295,24 +5370,9 @@ fn zirBuiltinAsyncCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) I
     return sema.mod.fail(&block.base, src, "TODO: Sema.zirBuiltinAsyncCall", .{});
 }
 
-fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
-    const extended = sema.code.instructions.items(.data)[inst].extended;
-    switch (extended.opcode) {
-        // zig fmt: off
-        .func               => return sema.zirFuncExtended(  block, inst, extended),
-        .c_undef            => return sema.zirCUndef(        block, inst, extended),
-        .c_include          => return sema.zirCInclude(      block, inst, extended),
-        .c_define           => return sema.zirCDefine(       block, inst, extended),
-        .wasm_memory_size   => return sema.zirWasmMemorySize(block, inst, extended),
-        .wasm_memory_grow   => return sema.zirWasmMemoryGrow(block, inst, extended),
-        // zig fmt: on
-    }
-}
-
 fn zirFuncExtended(
     sema: *Sema,
     block: *Scope.Block,
-    inst: Zir.Inst.Index,
     extended: Zir.Inst.Extended.InstData,
 ) InnerError!*Inst {
     const tracy = trace(@src());
@@ -5364,7 +5424,6 @@ fn zirFuncExtended(
 fn zirCUndef(
     sema: *Sema,
     block: *Scope.Block,
-    inst: Zir.Inst.Index,
     extended: Zir.Inst.Extended.InstData,
 ) InnerError!*Inst {
     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
@@ -5375,7 +5434,6 @@ fn zirCUndef(
 fn zirCInclude(
     sema: *Sema,
     block: *Scope.Block,
-    inst: Zir.Inst.Index,
     extended: Zir.Inst.Extended.InstData,
 ) InnerError!*Inst {
     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
@@ -5386,7 +5444,6 @@ fn zirCInclude(
 fn zirCDefine(
     sema: *Sema,
     block: *Scope.Block,
-    inst: Zir.Inst.Index,
     extended: Zir.Inst.Extended.InstData,
 ) InnerError!*Inst {
     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
@@ -5397,7 +5454,6 @@ fn zirCDefine(
 fn zirWasmMemorySize(
     sema: *Sema,
     block: *Scope.Block,
-    inst: Zir.Inst.Index,
     extended: Zir.Inst.Extended.InstData,
 ) InnerError!*Inst {
     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
@@ -5408,7 +5464,6 @@ fn zirWasmMemorySize(
 fn zirWasmMemoryGrow(
     sema: *Sema,
     block: *Scope.Block,
-    inst: Zir.Inst.Index,
     extended: Zir.Inst.Extended.InstData,
 ) InnerError!*Inst {
     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
src/Zir.zig
@@ -169,6 +169,9 @@ pub const Inst = struct {
         /// `[N:S]T` syntax. No source location provided.
         /// Uses the `array_type_sentinel` field.
         array_type_sentinel,
+        /// Given an array type, returns the element type.
+        /// Uses the `un_node` union field.
+        elem_type,
         /// Given a pointer to an indexable object, returns the len property. This is
         /// used by for loops. This instruction also emits a for-loop specific compile
         /// error if the indexable object is not indexable.
@@ -457,12 +460,6 @@ pub const Inst = struct {
         /// instruction.
         /// Uses the `un_tok` union field.
         ref,
-        /// Obtains a pointer to the return value.
-        /// Uses the `node` union field.
-        ret_ptr,
-        /// Obtains the return type of the in-scope function.
-        /// Uses the `node` union field.
-        ret_type,
         /// Sends control flow back to the function's callee.
         /// Includes an operand as the return value.
         /// Includes an AST node source location.
@@ -674,6 +671,13 @@ pub const Inst = struct {
         /// because it must use one of them to find out the struct type.
         /// Uses the `pl_node` field. Payload is `Block`.
         validate_struct_init_ptr,
+        /// Given a set of `elem_ptr_node` instructions, assumes they are all part of an
+        /// array initialization expression, and emits a compile error if the number of
+        /// elements does not match the array type.
+        /// This instruction asserts that there is at least one elem_ptr_node instruction,
+        /// because it must use one of them to find out the array type.
+        /// Uses the `pl_node` field. Payload is `Block`.
+        validate_array_init_ptr,
         /// A struct literal with a specified type, with no fields.
         /// Uses the `un_node` field.
         struct_init_empty,
@@ -690,6 +694,18 @@ pub const Inst = struct {
         /// Struct initialization without a type.
         /// Uses the `pl_node` field. Payload is `StructInitAnon`.
         struct_init_anon,
+        /// Array initialization syntax.
+        /// Uses the `pl_node` field. Payload is `MultiOp`.
+        array_init,
+        /// Anonymous array initialization syntax.
+        /// Uses the `pl_node` field. Payload is `MultiOp`.
+        array_init_anon,
+        /// Array initialization syntax, make the result a pointer.
+        /// Uses the `pl_node` field. Payload is `MultiOp`.
+        array_init_ref,
+        /// Anonymous array initialization syntax, make the result a pointer.
+        /// Uses the `pl_node` field. Payload is `MultiOp`.
+        array_init_anon_ref,
         /// Given a pointer to a union and a comptime known field name, activates that field
         /// and returns a pointer to it.
         /// Uses the `pl_node` field. Payload is `UnionInitPtr`.
@@ -700,14 +716,8 @@ pub const Inst = struct {
         size_of,
         /// Implements the `@bitSizeOf` builtin. Uses `un_node`.
         bit_size_of,
-        /// Implements the `@This` builtin. Uses `node`.
-        this,
-        /// Implements the `@fence` builtin. Uses `un_node`.
+        /// Implements the `@fence` builtin. Uses `node`.
         fence,
-        /// Implements the `@returnAddress` builtin. Uses `un_node`.
-        ret_addr,
-        /// Implements the `@src` builtin. Uses `un_node`.
-        builtin_src,
         /// Implements the `@addWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
         add_with_overflow,
         /// Implements the `@subWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
@@ -717,16 +727,6 @@ pub const Inst = struct {
         /// Implements the `@shlWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
         shl_with_overflow,
 
-        /// Implements the `@errorReturnTrace` builtin.
-        /// Uses the `un_node` field.
-        error_return_trace,
-        /// Implements the `@frame` builtin.
-        /// Uses the `un_node` field.
-        frame,
-        /// Implements the `@frameAddress` builtin.
-        /// Uses the `un_node` field.
-        frame_address,
-
         /// Implement builtin `@ptrToInt`. Uses `un_node`.
         ptr_to_int,
         /// Implement builtin `@errToInt`. Uses `un_node`.
@@ -951,6 +951,7 @@ pub const Inst = struct {
                 .array_mul,
                 .array_type,
                 .array_type_sentinel,
+                .elem_type,
                 .indexable_ptr_len,
                 .as,
                 .as_node,
@@ -970,6 +971,7 @@ pub const Inst = struct {
                 .bool_and,
                 .bool_or,
                 .breakpoint,
+                .fence,
                 .call,
                 .call_chkused,
                 .call_compile_time,
@@ -1026,8 +1028,6 @@ pub const Inst = struct {
                 .param_type,
                 .ptrtoint,
                 .ref,
-                .ret_ptr,
-                .ret_type,
                 .shl,
                 .shr,
                 .store,
@@ -1094,9 +1094,14 @@ pub const Inst = struct {
                 .switch_block_ref_under,
                 .switch_block_ref_under_multi,
                 .validate_struct_init_ptr,
+                .validate_array_init_ptr,
                 .struct_init_empty,
                 .struct_init,
                 .struct_init_anon,
+                .array_init,
+                .array_init_anon,
+                .array_init_ref,
+                .array_init_anon_ref,
                 .union_init_ptr,
                 .field_type,
                 .field_type_ref,
@@ -1105,17 +1110,10 @@ pub const Inst = struct {
                 .type_info,
                 .size_of,
                 .bit_size_of,
-                .this,
-                .fence,
-                .ret_addr,
-                .builtin_src,
                 .add_with_overflow,
                 .sub_with_overflow,
                 .mul_with_overflow,
                 .shl_with_overflow,
-                .error_return_trace,
-                .frame,
-                .frame_address,
                 .ptr_to_int,
                 .align_of,
                 .bool_to_int,
@@ -1211,6 +1209,30 @@ pub const Inst = struct {
         /// `operand` is payload index to `ExtendedFunc`.
         /// `small` is `ExtendedFunc.Small`.
         func,
+        /// Obtains a pointer to the return value.
+        /// `operand` is `src_node: i32`.
+        ret_ptr,
+        /// Obtains the return type of the in-scope function.
+        /// `operand` is `src_node: i32`.
+        ret_type,
+        /// Implements the `@This` builtin.
+        /// `operand` is `src_node: i32`.
+        this,
+        /// Implements the `@returnAddress` builtin.
+        /// `operand` is `src_node: i32`.
+        ret_addr,
+        /// Implements the `@src` builtin.
+        /// `operand` is `src_node: i32`.
+        builtin_src,
+        /// Implements the `@errorReturnTrace` builtin.
+        /// `operand` is `src_node: i32`.
+        error_return_trace,
+        /// Implements the `@frame` builtin.
+        /// `operand` is `src_node: i32`.
+        frame,
+        /// Implements the `@frameAddress` builtin.
+        /// `operand` is `src_node: i32`.
+        frame_address,
         /// `operand` is payload index to `UnNode`.
         c_undef,
         /// `operand` is payload index to `UnNode`.
@@ -2281,6 +2303,7 @@ const Writer = struct {
             .pop_count,
             .byte_swap,
             .bit_reverse,
+            .elem_type,
             => try self.writeUnNode(stream, inst),
 
             .ref,
@@ -2317,6 +2340,10 @@ const Writer = struct {
             .union_decl,
             .struct_init,
             .struct_init_anon,
+            .array_init,
+            .array_init_anon,
+            .array_init_ref,
+            .array_init_anon_ref,
             .union_init_ptr,
             .field_type,
             .field_type_ref,
@@ -2409,6 +2436,7 @@ const Writer = struct {
             .block_inline_var,
             .loop,
             .validate_struct_init_ptr,
+            .validate_array_init_ptr,
             .c_import,
             => try self.writePlNodeBlock(stream, inst),
 
@@ -2450,21 +2478,13 @@ const Writer = struct {
             .as_node => try self.writeAs(stream, inst),
 
             .breakpoint,
+            .fence,
             .opaque_decl,
             .dbg_stmt_node,
-            .ret_ptr,
-            .ret_type,
             .repeat,
             .repeat_inline,
             .alloc_inferred,
             .alloc_inferred_mut,
-            .this,
-            .fence,
-            .ret_addr,
-            .builtin_src,
-            .error_return_trace,
-            .frame,
-            .frame_address,
             => try self.writeNode(stream, inst),
 
             .error_value,