Commit 841add6890

Andrew Kelley <andrew@ziglang.org>
2023-02-17 21:44:35
AstGen: finish multi-object for loops
This strategy uses pointer arithmetic to iterate through the loop. This has a problem, however, which is tuples. AstGen does not know whether a given indexable is a tuple or can be iterated based on contiguous memory. Tuples unlike other indexables cannot be represented as a many-item pointer that is incremented as the loop counter. So, after this commit, I will modify AstGen back closer to how @vexu had it before, using a counter and array element access.
1 parent faa44e2
src/AstGen.zig
@@ -88,6 +88,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
             Zir.Inst.BuiltinCall.Flags => @bitCast(u32, @field(extra, field.name)),
             Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)),
             Zir.Inst.FuncFancy.Bits => @bitCast(u32, @field(extra, field.name)),
+            Zir.Inst.ElemPtrImm.Bits => @bitCast(u32, @field(extra, field.name)),
             else => @compileError("bad field type"),
         };
         i += 1;
@@ -1565,7 +1566,9 @@ fn arrayInitExprRlPtrInner(
     for (elements) |elem_init, i| {
         const elem_ptr = try gz.addPlNode(.elem_ptr_imm, elem_init, Zir.Inst.ElemPtrImm{
             .ptr = result_ptr,
-            .index = @intCast(u32, i),
+            .bits = .{
+                .index = @intCast(u31, i),
+            },
         });
         astgen.extra.items[extra_index] = refToIndex(elem_ptr).?;
         extra_index += 1;
@@ -6308,7 +6311,7 @@ fn forExpr(
     const lens = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
     defer gpa.free(lens);
 
-    const counter_alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
+    const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc_mut;
 
     // Tracks the index of allocs/lens that has a length to be checked and is
     // used for the end value.
@@ -6321,23 +6324,24 @@ fn forExpr(
     var cond_end_val: Zir.Inst.Ref = .none;
 
     {
-        var payload = for_full.payload_token;
+        var capture_token = for_full.payload_token;
         for (for_full.ast.inputs) |input, i_usize| {
             const i = @intCast(u32, i_usize);
-            const payload_is_ref = token_tags[payload] == .asterisk;
-            const ident_tok = payload + @boolToInt(payload_is_ref);
+            const capture_is_ref = token_tags[capture_token] == .asterisk;
+            const ident_tok = capture_token + @boolToInt(capture_is_ref);
 
-            if (mem.eql(u8, tree.tokenSlice(ident_tok), "_") and payload_is_ref) {
-                return astgen.failTok(payload, "pointer modifier invalid on discard", .{});
+            if (mem.eql(u8, tree.tokenSlice(ident_tok), "_") and capture_is_ref) {
+                return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{});
             }
-            payload = ident_tok + @as(u32, 2);
+            // Skip over the comma, and on to the next capture (or the ending pipe character).
+            capture_token = ident_tok + 2;
 
             try emitDbgNode(parent_gz, input);
             if (node_tags[input] == .for_range) {
-                if (payload_is_ref) {
+                if (capture_is_ref) {
                     return astgen.failTok(ident_tok, "cannot capture reference to range", .{});
                 }
-                const counter_ptr = try parent_gz.addUnNode(counter_alloc_tag, .usize_type, node);
+                const counter_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
                 const start_node = node_data[input].lhs;
                 const start_val = try expr(parent_gz, scope, .{ .rl = .none }, start_node);
                 _ = try parent_gz.addBin(.store, counter_ptr, start_val);
@@ -6364,20 +6368,28 @@ fn forExpr(
                 allocs[i] = counter_ptr;
                 lens[i] = range_len;
             } else {
-                const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
-                const indexable = try expr(parent_gz, scope, cond_ri, input);
+                const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input);
+                // This instruction has nice compile errors so we put it before the other ones
+                // even though it is not needed until later in the block.
+                const ptr_len = try parent_gz.addUnNode(.indexable_ptr_len, indexable, input);
                 const base_ptr = try parent_gz.addPlNode(.elem_ptr_imm, input, Zir.Inst.ElemPtrImm{
                     .ptr = indexable,
-                    .index = 0,
+                    .bits = .{
+                        .index = 0,
+                        .manyptr = true,
+                    },
                 });
+                const alloc_ty_inst = try parent_gz.addUnNode(.typeof, base_ptr, node);
+                const alloc = try parent_gz.addUnNode(alloc_tag, alloc_ty_inst, node);
+                _ = try parent_gz.addBin(.store, alloc, base_ptr);
 
                 if (end_input_index == null) {
                     end_input_index = i;
                     assert(cond_end_val == .none);
                 }
 
-                allocs[i] = base_ptr;
-                lens[i] = try parent_gz.addUnNode(.indexable_ptr_len, indexable, input);
+                allocs[i] = alloc;
+                lens[i] = ptr_len;
             }
         }
     }
@@ -6467,62 +6479,47 @@ fn forExpr(
     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
     defer then_scope.unstack();
 
-    const then_sub_scope = &then_scope.base;
-
-    // try then_scope.addDbgBlockBegin();
-    // var payload_val_scope: Scope.LocalVal = undefined;
-    // var index_scope: Scope.LocalPtr = undefined;
-    // const then_sub_scope = blk: {
-    //     const payload_token = for_full.payload_token.?;
-    //     const ident = if (token_tags[payload_token] == .asterisk)
-    //         payload_token + 1
-    //     else
-    //         payload_token;
-    //     const is_ptr = ident != payload_token;
-    //     const value_name = tree.tokenSlice(ident);
-    //     var payload_sub_scope: *Scope = undefined;
-    //     if (!mem.eql(u8, value_name, "_")) {
-    //         const name_str_index = try astgen.identAsString(ident);
-    //         const tag: Zir.Inst.Tag = if (is_ptr) .elem_ptr else .elem_val;
-    //         const payload_inst = try then_scope.addPlNode(tag, for_full.ast.cond_expr, Zir.Inst.Bin{
-    //             .lhs = array_ptr,
-    //             .rhs = index,
-    //         });
-    //         try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident, value_name, .capture);
-    //         payload_val_scope = .{
-    //             .parent = &then_scope.base,
-    //             .gen_zir = &then_scope,
-    //             .name = name_str_index,
-    //             .inst = payload_inst,
-    //             .token_src = ident,
-    //             .id_cat = .capture,
-    //         };
-    //         try then_scope.addDbgVar(.dbg_var_val, name_str_index, payload_inst);
-    //         payload_sub_scope = &payload_val_scope.base;
-    //     } else if (is_ptr) {
-    //     } else {
-    //         payload_sub_scope = &then_scope.base;
-    //     }
-
-    //     const index_token = if (token_tags[ident + 1] == .comma)
-    //         ident + 2
-    //     else
-    //         break :blk payload_sub_scope;
-    //     const token_bytes = tree.tokenSlice(index_token);
-    //     const index_name = try astgen.identAsString(index_token);
-    //     try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token, token_bytes, .@"loop index capture");
-    //     index_scope = .{
-    //         .parent = payload_sub_scope,
-    //         .gen_zir = &then_scope,
-    //         .name = index_name,
-    //         .ptr = index_ptr,
-    //         .token_src = index_token,
-    //         .maybe_comptime = is_inline,
-    //         .id_cat = .@"loop index capture",
-    //     };
-    //     try then_scope.addDbgVar(.dbg_var_val, index_name, index_ptr);
-    //     break :blk &index_scope.base;
-    // };
+    try then_scope.addDbgBlockBegin();
+
+    const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len);
+    defer gpa.free(capture_scopes);
+
+    const then_sub_scope = blk: {
+        var capture_token = for_full.payload_token;
+        var capture_sub_scope: *Scope = &then_scope.base;
+        for (for_full.ast.inputs) |input, i_usize| {
+            const i = @intCast(u32, i_usize);
+            const capture_is_ref = token_tags[capture_token] == .asterisk;
+            const ident_tok = capture_token + @boolToInt(capture_is_ref);
+            const capture_name = tree.tokenSlice(ident_tok);
+            // Skip over the comma, and on to the next capture (or the ending pipe character).
+            capture_token = ident_tok + 2;
+
+            if (mem.eql(u8, capture_name, "_")) continue;
+
+            const name_str_index = try astgen.identAsString(ident_tok);
+            try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture);
+
+            const loaded = if (capture_is_ref)
+                loaded_ptrs[i]
+            else
+                try then_scope.addUnNode(.load, loaded_ptrs[i], input);
+
+            capture_scopes[i] = .{
+                .parent = capture_sub_scope,
+                .gen_zir = &then_scope,
+                .name = name_str_index,
+                .inst = loaded,
+                .token_src = ident_tok,
+                .id_cat = .capture,
+            };
+
+            try then_scope.addDbgVar(.dbg_var_val, name_str_index, loaded);
+            capture_sub_scope = &capture_scopes[i].base;
+        }
+
+        break :blk capture_sub_scope;
+    };
 
     const then_result = try expr(&then_scope, then_sub_scope, .{ .rl = .none }, for_full.ast.then_expr);
     _ = try addEnsureResult(&then_scope, then_result, for_full.ast.then_expr);
src/print_zir.zig
@@ -888,7 +888,9 @@ const Writer = struct {
         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 stream.print(", {d}", .{extra.bits.index});
+        try self.writeFlag(stream, ", manyptr", extra.bits.manyptr);
+        try stream.writeAll(") ");
         try self.writeSrc(stream, inst_data.src());
     }
 
src/Sema.zig
@@ -9649,7 +9649,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const array_ptr = try sema.resolveInst(extra.lhs);
     const elem_index = try sema.resolveInst(extra.rhs);
-    return sema.elemPtr(block, src, array_ptr, elem_index, src, false);
+    return sema.elemPtr(block, src, array_ptr, elem_index, src, false, .One);
 }
 
 fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -9662,7 +9662,7 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const array_ptr = try sema.resolveInst(extra.lhs);
     const elem_index = try sema.resolveInst(extra.rhs);
-    return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false);
+    return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, .One);
 }
 
 fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -9673,8 +9673,9 @@ fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
     const array_ptr = try sema.resolveInst(extra.ptr);
-    const elem_index = try sema.addIntUnsigned(Type.usize, extra.index);
-    return sema.elemPtr(block, src, array_ptr, elem_index, src, true);
+    const elem_index = try sema.addIntUnsigned(Type.usize, extra.bits.index);
+    const size: std.builtin.Type.Pointer.Size = if (extra.bits.manyptr) .Many else .One;
+    return sema.elemPtr(block, src, array_ptr, elem_index, src, true, size);
 }
 
 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -22905,7 +22906,7 @@ fn panicSentinelMismatch(
     const actual_sentinel = if (ptr_ty.isSlice())
         try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
     else blk: {
-        const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null);
+        const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null, .One);
         const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
         break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
     };
@@ -24072,6 +24073,7 @@ fn elemPtr(
     elem_index: Air.Inst.Ref,
     elem_index_src: LazySrcLoc,
     init: bool,
+    size: std.builtin.Type.Pointer.Size,
 ) CompileError!Air.Inst.Ref {
     const indexable_ptr_src = src; // TODO better source location
     const indexable_ptr_ty = sema.typeOf(indexable_ptr);
@@ -24098,13 +24100,12 @@ fn elemPtr(
                         const index_val = maybe_index_val orelse break :rs elem_index_src;
                         const index = @intCast(usize, index_val.toUnsignedInt(target));
                         const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
-                        const result_ty = try sema.elemPtrType(indexable_ty, index);
-                        return sema.addConstant(result_ty, elem_ptr);
+                        const elem_ptr_ty = try sema.elemPtrType(indexable_ty, index, size);
+                        return sema.addConstant(elem_ptr_ty, elem_ptr);
                     };
-                    const result_ty = try sema.elemPtrType(indexable_ty, null);
-
+                    const elem_ptr_ty = try sema.elemPtrType(indexable_ty, null, size);
                     try sema.requireRuntimeBlock(block, src, runtime_src);
-                    return block.addPtrElemPtr(indexable, elem_index, result_ty);
+                    return block.addPtrElemPtr(indexable, elem_index, elem_ptr_ty);
                 },
                 .One => {
                     assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
@@ -24166,7 +24167,7 @@ fn elemVal(
             },
             .One => {
                 assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
-                const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false);
+                const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, .One);
                 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
             },
         },
@@ -24404,7 +24405,7 @@ fn elemPtrArray(
         break :o index;
     } else null;
 
-    const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset);
+    const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset, .One);
 
     if (maybe_undef_array_ptr_val) |array_ptr_val| {
         if (array_ptr_val.isUndef()) {
@@ -24509,7 +24510,7 @@ fn elemPtrSlice(
         break :o index;
     } else null;
 
-    const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset);
+    const elem_ptr_ty = try sema.elemPtrType(slice_ty, offset, .One);
 
     if (maybe_undef_slice_val) |slice_val| {
         if (slice_val.isUndef()) {
@@ -26239,7 +26240,7 @@ fn storePtr2(
             const elem_src = operand_src; // TODO better source location
             const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);
             const elem_index = try sema.addIntUnsigned(Type.usize, i);
-            const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false);
+            const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, .One);
             try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
         }
         return;
@@ -33276,7 +33277,12 @@ fn compareVector(
 /// For []T, returns *T
 /// Handles const-ness and address spaces in particular.
 /// This code is duplicated in `analyzePtrArithmetic`.
-fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
+fn elemPtrType(
+    sema: *Sema,
+    ptr_ty: Type,
+    offset: ?usize,
+    size: std.builtin.Type.Pointer.Size,
+) !Type {
     const ptr_info = ptr_ty.ptrInfo().data;
     const elem_ty = ptr_ty.elemType2();
     const allow_zero = ptr_info.@"allowzero" and (offset orelse 0) == 0;
@@ -33321,6 +33327,7 @@ fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
         break :a new_align;
     };
     return try Type.ptr(sema.arena, sema.mod, .{
+        .size = size,
         .pointee_type = elem_ty,
         .mutable = ptr_info.mutable,
         .@"addrspace" = ptr_info.@"addrspace",
src/Zir.zig
@@ -79,6 +79,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en
             Inst.BuiltinCall.Flags => @bitCast(Inst.BuiltinCall.Flags, code.extra[i]),
             Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]),
             Inst.FuncFancy.Bits => @bitCast(Inst.FuncFancy.Bits, code.extra[i]),
+            Inst.ElemPtrImm.Bits => @bitCast(Inst.ElemPtrImm.Bits, code.extra[i]),
             else => @compileError("bad field type"),
         };
         i += 1;
@@ -388,6 +389,8 @@ pub const Inst = struct {
         /// 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`.
+        /// This instruction has a way to set the result type to be a
+        /// single-pointer or a many-pointer.
         elem_ptr_imm,
         /// Given an array, slice, or pointer, returns the element at the provided index.
         /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
@@ -2972,7 +2975,13 @@ pub const Inst = struct {
 
     pub const ElemPtrImm = struct {
         ptr: Ref,
-        index: u32,
+        bits: Bits,
+
+        pub const Bits = packed struct(u32) {
+            index: u31,
+            /// Controls whether the type returned is `*T` or `[*]T`.
+            manyptr: bool = false,
+        };
     };
 
     /// 0. multi_cases_len: u32 // If has_multi_cases is set.