Commit 76335bc7ba

Andrew Kelley <andrew@ziglang.org>
2021-10-08 00:27:05
stage2: implement array literal with explicit type
New ZIR instruction: elem_ptr_imm This saves some memory for array literals since the element indexes are communicated as immediate values rather than as references to other ZIR instructions.
1 parent 601ac82
src/AstGen.zig
@@ -1352,15 +1352,14 @@ fn arrayInitExprRlPtr(
     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,
+        const elem_ptr = try gz.addPlNode(.elem_ptr_imm, elem_init, Zir.Inst.ElemPtrImm{
+            .ptr = result_ptr,
+            .index = @intCast(u32, i),
         });
         elem_ptr_list[i] = refToIndex(elem_ptr).?;
         _ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);
     }
-    _ = try gz.addPlNode(.validate_array_init_ptr, node, Zir.Inst.Block{
+    _ = try gz.addPlNode(.validate_array_init, node, Zir.Inst.Block{
         .body_len = @intCast(u32, elem_ptr_list.len),
     });
     try astgen.extra.appendSlice(gpa, elem_ptr_list);
@@ -1539,7 +1538,7 @@ fn structInitExprRlPtrInner(
         field_ptr_list[i] = refToIndex(field_ptr).?;
         _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init);
     }
-    _ = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{
+    _ = try gz.addPlNode(.validate_struct_init, node, Zir.Inst.Block{
         .body_len = @intCast(u32, field_ptr_list.len),
     });
     try astgen.extra.appendSlice(gpa, field_ptr_list);
@@ -2040,6 +2039,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
             .elem_ptr,
             .elem_val,
             .elem_ptr_node,
+            .elem_ptr_imm,
             .elem_val_node,
             .field_ptr,
             .field_val,
@@ -2246,8 +2246,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
             .store_to_block_ptr,
             .store_to_inferred_ptr,
             .resolve_inferred_alloc,
-            .validate_struct_init_ptr,
-            .validate_array_init_ptr,
+            .validate_struct_init,
+            .validate_array_init,
             .set_align_stack,
             .set_cold,
             .set_float_mode,
src/print_zir.zig
@@ -355,6 +355,8 @@ const Writer = struct {
             .elem_val_node,
             => try self.writePlNodeBin(stream, inst),
 
+            .elem_ptr_imm => try self.writeElemPtrImm(stream, inst),
+
             .@"export" => try self.writePlNodeExport(stream, inst),
             .export_value => try self.writePlNodeExportValue(stream, inst),
 
@@ -364,8 +366,8 @@ const Writer = struct {
             .block_inline,
             .suspend_block,
             .loop,
-            .validate_struct_init_ptr,
-            .validate_array_init_ptr,
+            .validate_struct_init,
+            .validate_array_init,
             .c_import,
             => try self.writePlNodeBlock(stream, inst),
 
@@ -809,6 +811,15 @@ const Writer = struct {
         try self.writeSrc(stream, inst_data.src());
     }
 
+    fn writeElemPtrImm(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
+        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const extra = self.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
+
+        try self.writeInstRef(stream, extra.ptr);
+        try stream.print(", {d}) ", .{extra.index});
+        try self.writeSrc(stream, inst_data.src());
+    }
+
     fn writePlNodeExport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
         const inst_data = self.code.instructions.items(.data)[inst].pl_node;
         const extra = self.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
src/Sema.zig
@@ -479,6 +479,7 @@ pub fn analyzeBody(
             .load                         => try sema.zirLoad(block, inst),
             .elem_ptr                     => try sema.zirElemPtr(block, inst),
             .elem_ptr_node                => try sema.zirElemPtrNode(block, inst),
+            .elem_ptr_imm                 => try sema.zirElemPtrImm(block, inst),
             .elem_val                     => try sema.zirElemVal(block, inst),
             .elem_val_node                => try sema.zirElemValNode(block, inst),
             .elem_type                    => try sema.zirElemType(block, inst),
@@ -741,13 +742,13 @@ pub fn analyzeBody(
                 i += 1;
                 continue;
             },
-            .validate_struct_init_ptr => {
-                try sema.zirValidateStructInitPtr(block, inst);
+            .validate_struct_init => {
+                try sema.zirValidateStructInit(block, inst);
                 i += 1;
                 continue;
             },
-            .validate_array_init_ptr => {
-                try sema.zirValidateArrayInitPtr(block, inst);
+            .validate_array_init => {
+                try sema.zirValidateArrayInit(block, inst);
                 i += 1;
                 continue;
             },
@@ -2106,7 +2107,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
     }
 }
 
-fn zirValidateStructInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
+fn zirValidateStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -2117,15 +2118,15 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Co
     const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
     const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
     const object_ptr = sema.resolveInst(field_ptr_extra.lhs);
-    const agg_ty = sema.typeOf(object_ptr).elemType();
+    const agg_ty = sema.typeOf(object_ptr).childType();
     switch (agg_ty.zigTypeTag()) {
-        .Struct => return sema.validateStructInitPtr(
+        .Struct => return sema.validateStructInit(
             block,
             agg_ty.castTag(.@"struct").?.data,
             init_src,
             instrs,
         ),
-        .Union => return sema.validateUnionInitPtr(
+        .Union => return sema.validateUnionInit(
             block,
             agg_ty.cast(Type.Payload.Union).?.data,
             init_src,
@@ -2136,7 +2137,7 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Co
     }
 }
 
-fn validateUnionInitPtr(
+fn validateUnionInit(
     sema: *Sema,
     block: *Block,
     union_obj: *Module.Union,
@@ -2175,7 +2176,7 @@ fn validateUnionInitPtr(
     _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
 }
 
-fn validateStructInitPtr(
+fn validateStructInit(
     sema: *Sema,
     block: *Block,
     struct_obj: *Module.Struct,
@@ -2239,10 +2240,22 @@ fn validateStructInitPtr(
     }
 }
 
-fn zirValidateArrayInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
-    const src = inst_data.src();
-    return sema.fail(block, src, "TODO implement Sema.zirValidateArrayInitPtr", .{});
+fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
+    const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
+    const init_src = validate_inst.src();
+    const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
+    const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
+    const elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
+    const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, elem_ptr_data.payload_index).data;
+    const array_ptr = sema.resolveInst(elem_ptr_extra.ptr);
+    const array_ty = sema.typeOf(array_ptr).childType();
+    const array_len = array_ty.arrayLen();
+
+    if (instrs.len != array_len) {
+        return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
+            array_len, instrs.len,
+        });
+    }
 }
 
 fn failWithBadFieldAccess(
@@ -5169,6 +5182,18 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
 }
 
+fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+    const tracy = trace(@src());
+    defer tracy.end();
+
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src = inst_data.src();
+    const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
+    const array_ptr = sema.resolveInst(extra.ptr);
+    const elem_index = try sema.addIntUnsigned(Type.usize, extra.index);
+    return sema.elemPtr(block, src, array_ptr, elem_index, src);
+}
+
 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const tracy = trace(@src());
     defer tracy.end();
src/Zir.zig
@@ -344,6 +344,11 @@ pub const Inst = struct {
         /// Same as `elem_ptr` except also stores a source location node.
         /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
         elem_ptr_node,
+        /// Same as `elem_ptr_node` except the index is stored immediately rather than
+        /// as a reference to another ZIR instruction.
+        /// Uses the `pl_node` union field. AST node is an element inside array initialization
+        /// syntax. Payload is `ElemPtrImm`.
+        elem_ptr_imm,
         /// Given an array, slice, or pointer, returns the element at the provided index.
         /// Uses the `bin` union field. Source location is implied to be the same
         /// as the previous instruction.
@@ -675,14 +680,14 @@ pub const Inst = struct {
         /// This instruction asserts that there is at least one field_ptr instruction,
         /// 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
+        validate_struct_init,
+        /// Given a set of `elem_ptr_imm` 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,
+        /// This instruction asserts that there is at least one `elem_ptr_imm` 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,
+        validate_array_init,
         /// A struct literal with a specified type, with no fields.
         /// Uses the `un_node` field.
         struct_init_empty,
@@ -1023,6 +1028,7 @@ pub const Inst = struct {
                 .elem_ptr,
                 .elem_val,
                 .elem_ptr_node,
+                .elem_ptr_imm,
                 .elem_val_node,
                 .ensure_result_used,
                 .ensure_result_non_error,
@@ -1114,8 +1120,8 @@ pub const Inst = struct {
                 .switch_block_ref_else_multi,
                 .switch_block_ref_under,
                 .switch_block_ref_under_multi,
-                .validate_struct_init_ptr,
-                .validate_array_init_ptr,
+                .validate_struct_init,
+                .validate_array_init,
                 .struct_init_empty,
                 .struct_init,
                 .struct_init_ref,
@@ -1291,6 +1297,7 @@ pub const Inst = struct {
                 .div = .pl_node,
                 .elem_ptr = .bin,
                 .elem_ptr_node = .pl_node,
+                .elem_ptr_imm = .pl_node,
                 .elem_val = .bin,
                 .elem_val_node = .pl_node,
                 .ensure_result_used = .un_node,
@@ -1377,8 +1384,8 @@ pub const Inst = struct {
                 .switch_capture_multi_ref = .switch_capture,
                 .switch_capture_else = .switch_capture,
                 .switch_capture_else_ref = .switch_capture,
-                .validate_struct_init_ptr = .pl_node,
-                .validate_array_init_ptr = .pl_node,
+                .validate_struct_init = .pl_node,
+                .validate_array_init = .pl_node,
                 .struct_init_empty = .un_node,
                 .field_type = .pl_node,
                 .field_type_ref = .pl_node,
@@ -2459,6 +2466,11 @@ pub const Inst = struct {
         operand: Ref,
     };
 
+    pub const ElemPtrImm = struct {
+        ptr: Ref,
+        index: u32,
+    };
+
     /// This form is supported when there are no ranges, and exactly 1 item per block.
     /// Depending on zir tag and len fields, extra fields trail
     /// this one in the extra array.
test/behavior/array.zig
@@ -33,8 +33,13 @@ test "array init with mult" {
     var i: [8]u8 = [2]u8{ a, 'b' } ** 4;
     try expect(std.mem.eql(u8, &i, "abababab"));
 
-    // this should cause a Value.repeated to be emitted in AIR.
-    // TODO: find a way to test that this is actually getting emmited
     var j: [4]u8 = [1]u8{'a'} ** 4;
     try expect(std.mem.eql(u8, &j, "aaaa"));
 }
+
+test "array literal with explicit type" {
+    const hex_mult: [4]u16 = .{ 4096, 256, 16, 1 };
+
+    try expect(hex_mult.len == 4);
+    try expect(hex_mult[1] == 256);
+}
test/behavior/array_stage1.zig
@@ -4,6 +4,13 @@ const mem = std.mem;
 const expect = testing.expect;
 const expectEqual = testing.expectEqual;
 
+test "array literal with inferred length" {
+    const hex_mult = [_]u16{ 4096, 256, 16, 1 };
+
+    try expect(hex_mult.len == 4);
+    try expect(hex_mult[1] == 256);
+}
+
 test "array with sentinels" {
     const S = struct {
         fn doTheTest(is_ct: bool) !void {
@@ -39,13 +46,6 @@ test "void arrays" {
     try expect(array.len == 4);
 }
 
-test "array literal" {
-    const hex_mult = [_]u16{ 4096, 256, 16, 1 };
-
-    try expect(hex_mult.len == 4);
-    try expect(hex_mult[1] == 256);
-}
-
 test "array dot len const expr" {
     try expect(comptime x: {
         break :x some_array.len == 4;