Commit c7b778992e

Veikka Tuominen <git@vexu.eu>
2022-05-28 17:38:35
AstGen: improve generated Zir for array init exprs
1 parent ee651c3
src/AstGen.zig
@@ -1273,16 +1273,8 @@ fn arrayInitExpr(
 
     assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
 
-    const types: struct {
-        array: Zir.Inst.Ref,
-        elem: Zir.Inst.Ref,
-        sentinel: Zir.Inst.Ref,
-    } = inst: {
-        if (array_init.ast.type_expr == 0) break :inst .{
-            .array = .none,
-            .elem = .none,
-            .sentinel = .none,
-        };
+    const array_ty: Zir.Inst.Ref = inst: {
+        if (array_init.ast.type_expr == 0) break :inst .none;
 
         infer: {
             const array_type: Ast.full.ArrayType = switch (node_tags[array_init.ast.type_expr]) {
@@ -1297,15 +1289,10 @@ fn arrayInitExpr(
                 const len_inst = try gz.addInt(array_init.ast.elements.len);
                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
                 if (array_type.ast.sentinel == 0) {
-                    const array_type_inst = try gz.addBin(.array_type, len_inst, elem_type);
-                    break :inst .{
-                        .array = array_type_inst,
-                        .elem = elem_type,
-                        .sentinel = .none,
-                    };
+                    break :inst try gz.addBin(.array_type, len_inst, elem_type);
                 } else {
                     const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel);
-                    const array_type_inst = try gz.addPlNode(
+                    break :inst try gz.addPlNode(
                         .array_type_sentinel,
                         array_init.ast.type_expr,
                         Zir.Inst.ArrayTypeSentinel{
@@ -1314,76 +1301,57 @@ fn arrayInitExpr(
                             .sentinel = sentinel,
                         },
                     );
-                    break :inst .{
-                        .array = array_type_inst,
-                        .elem = elem_type,
-                        .sentinel = sentinel,
-                    };
                 }
             }
         }
         const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
         _ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, node);
-        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,
-            .sentinel = .none,
-        };
+        break :inst array_type_inst;
     };
 
     switch (rl) {
         .discard => {
+            // TODO elements should still be coerced if type is provided
             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, node, array_init.ast.elements, types.elem, types.sentinel, true);
-            } else {
-                return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon_ref);
-            }
+            const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init_ref else .array_init_anon_ref;
+            return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, tag);
         },
         .none => {
-            if (types.array != .none) {
-                return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, false);
-            } else {
-                return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
-            }
+            const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init else .array_init_anon;
+            return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, tag);
         },
         .ty, .coerced_ty => {
-            if (types.array != .none) {
-                const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, false);
-                return rvalue(gz, rl, result, node);
-            } else {
-                const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
-                return rvalue(gz, rl, result, node);
-            }
+            const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init else .array_init_anon;
+            const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, tag);
+            return rvalue(gz, rl, result, node);
         },
         .ptr => |ptr_inst| {
-            return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array);
+            return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, array_ty);
         },
         .inferred_ptr => |ptr_inst| {
-            if (types.array == .none) {
+            if (array_ty == .none) {
                 // We treat this case differently so that we don't get a crash when
                 // analyzing array_base_ptr against an alloc_inferred_mut.
                 // See corresponding logic in structInitExpr.
                 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
                 return rvalue(gz, rl, result, node);
             } else {
-                return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array);
+                return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, array_ty);
             }
         },
         .block_ptr => |block_gz| {
             // This condition is here for the same reason as the above condition in `inferred_ptr`.
             // See corresponding logic in structInitExpr.
-            if (types.array == .none and astgen.isInferred(block_gz.rl_ptr)) {
+            if (array_ty == .none and astgen.isInferred(block_gz.rl_ptr)) {
                 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
                 return rvalue(gz, rl, result, node);
             }
-            return arrayInitExprRlPtr(gz, scope, rl, node, block_gz.rl_ptr, array_init.ast.elements, types.array);
+            return arrayInitExprRlPtr(gz, scope, rl, node, block_gz.rl_ptr, array_init.ast.elements, array_ty);
         },
     }
 }
@@ -1410,52 +1378,33 @@ fn arrayInitExprRlNone(
     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
 }
 
-fn arrayInitExprRlTy(
+fn arrayInitExprInner(
     gz: *GenZir,
     scope: *Scope,
     node: Ast.Node.Index,
     elements: []const Ast.Node.Index,
-    elem_ty_inst: Zir.Inst.Ref,
-    sentinel: Zir.Inst.Ref,
-    ref: bool,
+    array_ty_inst: Zir.Inst.Ref,
+    tag: Zir.Inst.Tag,
 ) InnerError!Zir.Inst.Ref {
     const astgen = gz.astgen;
 
-    const info: struct {
-        len: usize,
-        tag: Zir.Inst.Tag,
-    } = blk: {
-        if (sentinel != .none) {
-            break :blk .{
-                .len = elements.len + 1,
-                .tag = if (ref) .array_init_sent_ref else .array_init_sent,
-            };
-        } else {
-            break :blk .{
-                .len = elements.len,
-                .tag = if (ref) .array_init_ref else .array_init,
-            };
-        }
-    };
-
+    const len = elements.len + @boolToInt(array_ty_inst != .none);
     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
-        .operands_len = @intCast(u32, info.len),
+        .operands_len = @intCast(u32, len),
     });
-    var extra_index = try reserveExtra(astgen, info.len);
-
-    const elem_rl: ResultLoc = .{ .ty = elem_ty_inst };
-    for (elements) |elem_init| {
-        const elem_ref = try expr(gz, scope, elem_rl, elem_init);
-        astgen.extra.items[extra_index] = @enumToInt(elem_ref);
+    var extra_index = try reserveExtra(astgen, len);
+    if (array_ty_inst != .none) {
+        astgen.extra.items[extra_index] = @enumToInt(array_ty_inst);
         extra_index += 1;
     }
 
-    if (sentinel != .none) {
-        astgen.extra.items[extra_index] = @enumToInt(sentinel);
+    for (elements) |elem_init| {
+        const elem_ref = try expr(gz, scope, .none, elem_init);
+        astgen.extra.items[extra_index] = @enumToInt(elem_ref);
         extra_index += 1;
     }
 
-    return try gz.addPlNodePayloadIndex(info.tag, node, payload_index);
+    return try gz.addPlNodePayloadIndex(tag, node, payload_index);
 }
 
 fn arrayInitExprRlPtr(
@@ -2244,7 +2193,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
             .array_type,
             .array_type_sentinel,
             .vector_type,
-            .elem_type,
             .indexable_ptr_len,
             .anyframe_type,
             .as,
@@ -2347,10 +2295,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
             .struct_init_anon_ref,
             .array_init,
             .array_init_anon,
-            .array_init_sent,
             .array_init_ref,
             .array_init_anon_ref,
-            .array_init_sent_ref,
             .union_init,
             .field_type,
             .field_type_ref,
src/print_zir.zig
@@ -226,7 +226,6 @@ const Writer = struct {
             .pop_count,
             .byte_swap,
             .bit_reverse,
-            .elem_type,
             .@"resume",
             .@"await",
             .switch_cond,
@@ -268,10 +267,6 @@ const Writer = struct {
             .array_init_anon_ref,
             => try self.writeArrayInit(stream, inst),
 
-            .array_init_sent,
-            .array_init_sent_ref,
-            => try self.writeArrayInitSent(stream, inst),
-
             .slice_start => try self.writeSliceStart(stream, inst),
             .slice_end => try self.writeSliceEnd(stream, inst),
             .slice_sentinel => try self.writeSliceSentinel(stream, inst),
@@ -2085,8 +2080,9 @@ const Writer = struct {
         const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
         const args = self.code.refSlice(extra.end, extra.data.operands_len);
 
-        try stream.writeAll(".{");
-        for (args) |arg, i| {
+        try self.writeInstRef(stream, args[0]);
+        try stream.writeAll("{");
+        for (args[1..]) |arg, i| {
             if (i != 0) try stream.writeAll(", ");
             try self.writeInstRef(stream, arg);
         }
src/Sema.zig
@@ -726,7 +726,6 @@ 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_type                    => try sema.zirElemType(block, inst),
             .enum_literal                 => try sema.zirEnumLiteral(block, inst),
             .enum_to_int                  => try sema.zirEnumToInt(block, inst),
             .int_to_enum                  => try sema.zirIntToEnum(block, inst),
@@ -798,10 +797,8 @@ fn analyzeBodyInner(
             .struct_init_ref              => try sema.zirStructInit(block, inst, true),
             .struct_init_anon             => try sema.zirStructInitAnon(block, inst, false),
             .struct_init_anon_ref         => try sema.zirStructInitAnon(block, inst, true),
-            .array_init                   => try sema.zirArrayInit(block, inst, false, false),
-            .array_init_sent              => try sema.zirArrayInit(block, inst, false, true),
-            .array_init_ref               => try sema.zirArrayInit(block, inst, true, false),
-            .array_init_sent_ref          => try sema.zirArrayInit(block, inst, true, true),
+            .array_init                   => try sema.zirArrayInit(block, inst, false),
+            .array_init_ref               => try sema.zirArrayInit(block, inst, true),
             .array_init_anon              => try sema.zirArrayInitAnon(block, inst, false),
             .array_init_anon_ref          => try sema.zirArrayInitAnon(block, inst, true),
             .union_init                   => try sema.zirUnionInit(block, inst),
@@ -13436,7 +13433,6 @@ fn zirArrayInit(
     block: *Block,
     inst: Zir.Inst.Index,
     is_ref: bool,
-    is_sent: bool,
 ) CompileError!Air.Inst.Ref {
     const gpa = sema.gpa;
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
@@ -13444,30 +13440,23 @@ fn zirArrayInit(
 
     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
     const args = sema.code.refSlice(extra.end, extra.data.operands_len);
-    assert(args.len != 0);
+    assert(args.len >= 2); // array_ty + at least one element
 
-    const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len);
-    defer gpa.free(resolved_args);
-
-    for (args) |arg, i| resolved_args[i] = try sema.resolveInst(arg);
+    const array_ty = try sema.resolveType(block, src, args[0]);
+    const sentinel_val = array_ty.sentinel();
 
-    const elem_ty = sema.typeOf(resolved_args[0]);
-    const array_ty = blk: {
-        if (!is_sent) {
-            break :blk try Type.Tag.array.create(sema.arena, .{
-                .len = resolved_args.len,
-                .elem_type = elem_ty,
-            });
-        }
+    const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @boolToInt(sentinel_val != null));
+    defer gpa.free(resolved_args);
+    const elem_ty = array_ty.elemType2();
+    for (args[1..]) |arg, i| {
+        const resolved_arg = try sema.resolveInst(arg);
+        const arg_src = src; // TODO better source location
+        resolved_args[i] = try sema.coerce(block, elem_ty, resolved_arg, arg_src);
+    }
 
-        const sentinel_ref = resolved_args[resolved_args.len - 1];
-        const val = try sema.resolveConstValue(block, src, sentinel_ref);
-        break :blk try Type.Tag.array_sentinel.create(sema.arena, .{
-            .len = resolved_args.len - 1,
-            .sentinel = val,
-            .elem_type = elem_ty,
-        });
-    };
+    if (sentinel_val) |some| {
+        resolved_args[resolved_args.len - 1] = try sema.addConstant(elem_ty, some);
+    }
 
     const opt_runtime_src: ?LazySrcLoc = for (resolved_args) |arg| {
         const arg_src = src; // TODO better source location
@@ -13488,7 +13477,7 @@ fn zirArrayInit(
     };
 
     try sema.requireRuntimeBlock(block, runtime_src);
-    try sema.queueFullTypeResolution(elem_ty);
+    try sema.queueFullTypeResolution(array_ty);
 
     if (is_ref) {
         const target = sema.mod.getTarget();
src/Zir.zig
@@ -221,9 +221,6 @@ pub const Inst = struct {
         /// Uses the `pl_node` union field with `Bin` payload.
         /// lhs is length, rhs is element type.
         vector_type,
-        /// 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.
@@ -737,20 +734,12 @@ pub const Inst = struct {
         /// Array initialization syntax.
         /// Uses the `pl_node` field. Payload is `MultiOp`.
         array_init,
-        /// Array initialization with sentinel.
-        /// Uses the `pl_node` field. Payload is `MultiOp`.
-        /// Final op in MultiOp is the sentinel.
-        array_init_sent,
         /// 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,
-        /// Array initialization with sentinel.
-        /// Uses the `pl_node` field. Payload is `MultiOp`.
-        /// Final op in MultiOp is the sentinel.
-        array_init_sent_ref,
         /// Anonymous array initialization syntax, make the result a pointer.
         /// Uses the `pl_node` field. Payload is `MultiOp`.
         array_init_anon_ref,
@@ -1019,7 +1008,6 @@ pub const Inst = struct {
                 .array_type,
                 .array_type_sentinel,
                 .vector_type,
-                .elem_type,
                 .indexable_ptr_len,
                 .anyframe_type,
                 .as,
@@ -1153,10 +1141,8 @@ pub const Inst = struct {
                 .struct_init_anon,
                 .struct_init_anon_ref,
                 .array_init,
-                .array_init_sent,
                 .array_init_anon,
                 .array_init_ref,
-                .array_init_sent_ref,
                 .array_init_anon_ref,
                 .union_init,
                 .field_type,
@@ -1314,7 +1300,6 @@ pub const Inst = struct {
                 .array_type,
                 .array_type_sentinel,
                 .vector_type,
-                .elem_type,
                 .indexable_ptr_len,
                 .anyframe_type,
                 .as,
@@ -1426,10 +1411,8 @@ pub const Inst = struct {
                 .struct_init_anon,
                 .struct_init_anon_ref,
                 .array_init,
-                .array_init_sent,
                 .array_init_anon,
                 .array_init_ref,
-                .array_init_sent_ref,
                 .array_init_anon_ref,
                 .union_init,
                 .field_type,
@@ -1554,7 +1537,6 @@ pub const Inst = struct {
                 .array_type = .bin,
                 .array_type_sentinel = .pl_node,
                 .vector_type = .pl_node,
-                .elem_type = .un_node,
                 .indexable_ptr_len = .un_node,
                 .anyframe_type = .un_node,
                 .as = .bin,
@@ -1688,10 +1670,8 @@ pub const Inst = struct {
                 .struct_init_anon = .pl_node,
                 .struct_init_anon_ref = .pl_node,
                 .array_init = .pl_node,
-                .array_init_sent = .pl_node,
                 .array_init_anon = .pl_node,
                 .array_init_ref = .pl_node,
-                .array_init_sent_ref = .pl_node,
                 .array_init_anon_ref = .pl_node,
                 .union_init = .pl_node,
                 .type_info = .un_node,
test/behavior/basic.zig
@@ -942,3 +942,10 @@ test "comptime int in switch in catch is casted to correct inferred type" {
     };
     _ = b;
 }
+
+test "vector initialized with array init syntax has proper type" {
+    comptime {
+        const actual = -@Vector(4, i32){ 1, 2, 3, 4 };
+        try std.testing.expectEqual(@Vector(4, i32){ -1, -2, -3, -4 }, actual);
+    }
+}
test/behavior/tuple.zig
@@ -194,8 +194,6 @@ test "tuple as the result from a labeled block" {
 }
 
 test "initializing tuple with explicit type" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
-
     const T = @TypeOf(.{ @as(i32, 0), @as(u32, 0) });
     var a = T{ 0, 0 };
     _ = a;