Commit 85b0a4a8fd

Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
2022-03-07 17:26:00
stage2: new zir array_init_sent for sentinel-terminated array inits
This uses a new ZIR inst `array_init_sent` (and a ref equivalent) to represent array init expressions that terminate in a a sentinel value. The sentienl value is the last value in the `MultiOp` payload. This makes it a bit more awkward to deal with (lots of "len - 1") but makes it so that the payload matches the fact that sentinels appear at the end of arrays. However, this is not a hill I want to die on so if we want to change it to index 0, I'm happy to do so. This makes the following work properly: try expect(@TypeOf([_:0]u8{}) == [0:0]u8);
1 parent c9fac41
src/AstGen.zig
@@ -1259,10 +1259,12 @@ fn arrayInitExpr(
     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,
         };
 
         infer: {
@@ -1282,6 +1284,7 @@ fn arrayInitExpr(
                     break :inst .{
                         .array = array_type_inst,
                         .elem = elem_type,
+                        .sentinel = .none,
                     };
                 } else {
                     const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel);
@@ -1297,6 +1300,7 @@ fn arrayInitExpr(
                     break :inst .{
                         .array = array_type_inst,
                         .elem = elem_type,
+                        .sentinel = sentinel,
                     };
                 }
             }
@@ -1307,6 +1311,7 @@ fn arrayInitExpr(
         break :inst .{
             .array = array_type_inst,
             .elem = elem_type,
+            .sentinel = .none,
         };
     };
 
@@ -1319,25 +1324,25 @@ fn arrayInitExpr(
         },
         .ref => {
             if (types.array != .none) {
-                return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.array, .array_init_ref);
+                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);
             }
         },
         .none => {
             if (types.array != .none) {
-                return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.array, .array_init);
+                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);
             }
         },
         .ty, .coerced_ty => |ty_inst| {
             if (types.array != .none) {
-                const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.array, .array_init);
+                const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, false);
                 return rvalue(gz, rl, result, node);
             } else {
                 const elem_type = try gz.addUnNode(.elem_type, ty_inst, node);
-                return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, elem_type, types.array, .array_init);
+                return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, elem_type, types.sentinel, false);
             }
         },
         .ptr => |ptr_inst| {
@@ -1387,18 +1392,32 @@ fn arrayInitExprRlTy(
     node: Ast.Node.Index,
     elements: []const Ast.Node.Index,
     elem_ty_inst: Zir.Inst.Ref,
-    array_ty: Zir.Inst.Ref,
-    tag: Zir.Inst.Tag,
+    sentinel: Zir.Inst.Ref,
+    ref: bool,
 ) 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 payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
-        .operands_len = @intCast(u32, elements.len + 1),
+        .operands_len = @intCast(u32, info.len),
     });
-    var extra_index = try reserveExtra(astgen, elements.len + 1);
-
-    astgen.extra.items[extra_index] = @enumToInt(array_ty);
-    extra_index += 1;
+    var extra_index = try reserveExtra(astgen, info.len);
 
     const elem_rl: ResultLoc = .{ .ty = elem_ty_inst };
     for (elements) |elem_init| {
@@ -1406,7 +1425,13 @@ fn arrayInitExprRlTy(
         astgen.extra.items[extra_index] = @enumToInt(elem_ref);
         extra_index += 1;
     }
-    return try gz.addPlNodePayloadIndex(tag, node, payload_index);
+
+    if (sentinel != .none) {
+        astgen.extra.items[extra_index] = @enumToInt(sentinel);
+        extra_index += 1;
+    }
+
+    return try gz.addPlNodePayloadIndex(info.tag, node, payload_index);
 }
 
 fn arrayInitExprRlPtr(
@@ -2221,8 +2246,10 @@ 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
@@ -269,6 +269,10 @@ 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),
@@ -2022,6 +2026,26 @@ const Writer = struct {
         try self.writeSrc(stream, inst_data.src());
     }
 
+    fn writeArrayInitSent(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.MultiOp, inst_data.payload_index);
+        const args = self.code.refSlice(extra.end, extra.data.operands_len);
+        const sent = args[args.len - 1];
+        const elems = args[0 .. args.len - 1];
+
+        try self.writeInstRef(stream, sent);
+        try stream.writeAll(", ");
+
+        try stream.writeAll(".{");
+        for (elems) |elem, i| {
+            if (i != 0) try stream.writeAll(", ");
+            try self.writeInstRef(stream, elem);
+        }
+        try stream.writeAll("}) ");
+        try self.writeSrc(stream, inst_data.src());
+    }
+
     fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
         const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
         const safety_str = if (inst_data.safety) "safe" else "unsafe";
src/Sema.zig
@@ -712,8 +712,10 @@ 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),
-            .array_init_ref               => try sema.zirArrayInit(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_anon              => try sema.zirArrayInitAnon(block, inst, false),
             .array_init_anon_ref          => try sema.zirArrayInitAnon(block, inst, true),
             .union_init                   => try sema.zirUnionInit(block, inst),
@@ -11782,6 +11784,7 @@ 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;
@@ -11796,23 +11799,28 @@ fn zirArrayInit(
 
     for (args) |arg, i| resolved_args[i] = sema.resolveInst(arg);
 
-    const elem_ty = sema.typeOf(resolved_args[1]);
-    const array_ty = switch (resolved_args[0]) {
-        .none => try Type.Tag.array.create(sema.arena, .{
-            .len = resolved_args.len,
-            .elem_type = elem_ty,
-        }),
+    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,
+            });
+        }
 
-        else => |ref| blk: {
-            assert(sema.typeOf(ref).zigTypeTag() == .Type);
-            var buffer: Value.ToTypeBuffer = undefined;
-            const val = try sema.resolveConstValue(block, src, ref);
-            const ty = val.toType(&buffer);
-            break :blk try ty.copy(sema.arena);
-        },
+        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,
+        });
     };
 
-    const elems = resolved_args[1..];
+    const elems = if (!is_sent)
+        resolved_args
+    else
+        resolved_args[0 .. resolved_args.len - 1];
 
     const opt_runtime_src: ?LazySrcLoc = for (elems) |arg| {
         const arg_src = src; // TODO better source location
src/Zir.zig
@@ -710,12 +710,20 @@ 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,
@@ -1119,8 +1127,10 @@ 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,
@@ -1377,8 +1387,10 @@ 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,