Commit b9c5a1fdf5

Andrew Kelley <andrew@ziglang.org>
2021-03-26 03:25:26
astgen: fix for loop expressions
also rename the ZIR instruction `deref_node` to `load`.
1 parent 399bb2e
Changed files (5)
src/astgen.zig
@@ -443,7 +443,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
 
         .deref => {
             const lhs = try expr(mod, scope, .none, node_datas[node].lhs);
-            const result = try gz.addUnNode(.deref_node, lhs, node);
+            const result = try gz.addUnNode(.load, lhs, node);
             return rvalue(mod, scope, rl, result, node);
         },
         .address_of => {
@@ -1042,7 +1042,7 @@ fn blockExprStmts(
                         .coerce_result_ptr,
                         .decl_ref,
                         .decl_val,
-                        .deref_node,
+                        .load,
                         .div,
                         .elem_ptr,
                         .elem_val,
@@ -1396,7 +1396,7 @@ fn assignOp(
     const gz = scope.getGenZir();
 
     const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs);
-    const lhs = try gz.addUnNode(.deref_node, lhs_ptr, infix_node);
+    const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
     const lhs_type = try gz.addUnTok(.typeof, lhs, infix_node);
     const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
 
@@ -2105,7 +2105,7 @@ fn whileExpr(
         try checkLabelRedefinition(mod, scope, label_token);
     }
     const parent_gz = scope.getGenZir();
-    const is_inline = while_full.inline_token != null;
+    const is_inline = parent_gz.force_comptime or while_full.inline_token != null;
     const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
     const loop_block = try parent_gz.addBlock(loop_tag, node);
     try parent_gz.instructions.append(mod.gpa, loop_block);
@@ -2149,7 +2149,6 @@ fn whileExpr(
     // TODO avoid emitting the continue expr when there
     // are no jumps to it. This happens when the last statement of a while body is noreturn
     // and there are no `continue` statements.
-    // The "repeat" at the end of a loop body is implied.
     if (while_full.ast.cont_expr != 0) {
         _ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);
     }
@@ -2236,44 +2235,32 @@ fn forExpr(
     node: ast.Node.Index,
     for_full: ast.full.While,
 ) InnerError!zir.Inst.Ref {
-    if (true) @panic("TODO update for zir-memory-layout");
     if (for_full.label_token) |label_token| {
         try checkLabelRedefinition(mod, scope, label_token);
     }
-
-    if (for_full.inline_token) |inline_token| {
-        return mod.failTok(scope, inline_token, "TODO inline for", .{});
-    }
-
     // Set up variables and constants.
     const parent_gz = scope.getGenZir();
+    const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
     const tree = parent_gz.tree();
-    const main_tokens = tree.nodes.items(.main_token);
     const token_tags = tree.tokens.items(.tag);
 
-    const for_src = token_starts[for_full.ast.while_token];
+    const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr);
+    const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
+
     const index_ptr = blk: {
-        const usize_type = try addZIRInstConst(mod, scope, for_src, .{
-            .ty = Type.initTag(.type),
-            .val = Value.initTag(.usize_type),
-        });
-        const index_ptr = try addZIRUnOp(mod, scope, for_src, .alloc, usize_type);
+        const index_ptr = try parent_gz.addUnNode(.alloc, .usize_type, node);
         // initialize to zero
-        const zero = try addZIRInstConst(mod, scope, for_src, .{
-            .ty = Type.initTag(.usize),
-            .val = Value.initTag(.zero),
-        });
-        _ = try addZIRBinOp(mod, scope, for_src, .store, index_ptr, zero);
+        _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
         break :blk index_ptr;
     };
-    const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr);
-    const cond_src = token_starts[tree.firstToken(for_full.ast.cond_expr)];
-    const len = try addZIRUnOp(mod, scope, cond_src, .indexable_ptr_len, array_ptr);
+
+    const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
+    const loop_block = try parent_gz.addBlock(loop_tag, node);
+    try parent_gz.instructions.append(mod.gpa, loop_block);
 
     var loop_scope: Scope.GenZir = .{
         .parent = scope,
-        .decl = scope.ownerDecl().?,
-        .arena = scope.arena(),
+        .zir_code = parent_gz.zir_code,
         .force_comptime = parent_gz.force_comptime,
         .instructions = .{},
     };
@@ -2282,66 +2269,49 @@ fn forExpr(
 
     var cond_scope: Scope.GenZir = .{
         .parent = &loop_scope.base,
-        .decl = loop_scope.decl,
-        .arena = loop_scope.arena,
+        .zir_code = parent_gz.zir_code,
         .force_comptime = loop_scope.force_comptime,
         .instructions = .{},
     };
     defer cond_scope.instructions.deinit(mod.gpa);
 
     // check condition i < array_expr.len
-    const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
-    const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
-
-    const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
-        .condition = cond,
-        .then_body = undefined, // populated below
-        .else_body = undefined, // populated below
-    }, .{});
-    const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
-        .instructions = try loop_scope.arena.dupe(zir.Inst.Ref, cond_scope.instructions.items),
+    const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
+    const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{
+        .lhs = index,
+        .rhs = len,
     });
 
-    // increment index variable
-    const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
-        .ty = Type.initTag(.usize),
-        .val = Value.initTag(.one),
-    });
-    const index_2 = try addZIRUnOp(mod, &loop_scope.base, cond_src, .deref, index_ptr);
-    const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index_2, one);
-    _ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
-
-    const loop = try scope.arena().create(zir.Inst.Loop);
-    loop.* = .{
-        .base = .{
-            .tag = .loop,
-            .src = for_src,
-        },
-        .positionals = .{
-            .body = .{
-                .instructions = try scope.arena().dupe(zir.Inst.Ref, loop_scope.instructions.items),
-            },
-        },
-        .kw_args = .{},
-    };
-    const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
-        .instructions = try scope.arena().dupe(zir.Inst.Ref, &[1]zir.Inst.Ref{&loop.base}),
+    const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
+    const condbr = try cond_scope.addCondBr(condbr_tag, node);
+    const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
+    const cond_block = try loop_scope.addBlock(block_tag, node);
+    try loop_scope.instructions.append(mod.gpa, cond_block);
+    try cond_scope.setBlockBody(cond_block);
+
+    // Increment the index variable.
+    const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
+    const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{
+        .lhs = index_2,
+        .rhs = .one_usize,
     });
-    loop_scope.break_block = for_block;
+    _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
+    const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
+    _ = try loop_scope.addNode(repeat_tag, node);
+
+    try loop_scope.setBlockBody(loop_block);
+    loop_scope.break_block = loop_block;
     loop_scope.continue_block = cond_block;
     if (for_full.label_token) |label_token| {
         loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{
             .token = label_token,
-            .block_inst = for_block,
+            .block_inst = loop_block,
         });
     }
 
-    // while body
-    const then_src = token_starts[tree.lastToken(for_full.ast.then_expr)];
     var then_scope: Scope.GenZir = .{
         .parent = &cond_scope.base,
-        .decl = cond_scope.decl,
-        .arena = cond_scope.arena,
+        .zir_code = parent_gz.zir_code,
         .force_comptime = cond_scope.force_comptime,
         .instructions = .{},
     };
@@ -2375,6 +2345,7 @@ fn forExpr(
             .gen_zir = &then_scope,
             .name = index_name,
             .ptr = index_ptr,
+            .src = parent_gz.tokSrcLoc(index_token),
         };
         break :blk &index_scope.base;
     };
@@ -2382,34 +2353,36 @@ fn forExpr(
     loop_scope.break_count += 1;
     const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
 
-    // else branch
     var else_scope: Scope.GenZir = .{
         .parent = &cond_scope.base,
-        .decl = cond_scope.decl,
-        .arena = cond_scope.arena,
+        .zir_code = parent_gz.zir_code,
         .force_comptime = cond_scope.force_comptime,
         .instructions = .{},
     };
     defer else_scope.instructions.deinit(mod.gpa);
 
     const else_node = for_full.ast.else_expr;
-    const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
+    const else_info: struct {
+        src: ast.Node.Index,
+        result: zir.Inst.Ref,
+    } = if (else_node != 0) blk: {
         loop_scope.break_count += 1;
         const sub_scope = &else_scope.base;
         break :blk .{
-            .src = token_starts[tree.lastToken(else_node)],
+            .src = else_node,
             .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
         };
     } else .{
-        .src = token_starts[tree.lastToken(for_full.ast.then_expr)],
-        .result = null,
+        .src = for_full.ast.then_expr,
+        .result = .none,
     };
 
     if (loop_scope.label) |some| {
         if (!some.used) {
-            return mod.fail(scope, token_starts[some.token], "unused for loop label", .{});
+            return mod.failTok(scope, some.token, "unused for loop label", .{});
         }
     }
+    const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
     return finishThenElseBlock(
         mod,
         scope,
@@ -2420,13 +2393,13 @@ fn forExpr(
         &else_scope,
         condbr,
         cond,
-        then_src,
+        for_full.ast.then_expr,
         else_info.src,
         then_result,
         else_info.result,
-        for_block,
+        loop_block,
         cond_block,
-        .@"break",
+        break_tag,
     );
 }
 
@@ -2862,7 +2835,7 @@ fn identifier(
                 const local_ptr = s.cast(Scope.LocalPtr).?;
                 if (mem.eql(u8, local_ptr.name, ident_name)) {
                     if (rl == .ref) return local_ptr.ptr;
-                    const loaded = try gz.addUnNode(.deref_node, local_ptr.ptr, ident);
+                    const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);
                     return rvalue(mod, scope, rl, loaded, ident);
                 }
                 s = local_ptr.parent;
src/Sema.zig
@@ -160,7 +160,7 @@ pub fn analyzeBody(
             .@"const" => try sema.zirConst(block, inst),
             .decl_ref => try sema.zirDeclRef(block, inst),
             .decl_val => try sema.zirDeclVal(block, inst),
-            .deref_node => try sema.zirDerefNode(block, inst),
+            .load => try sema.zirLoad(block, inst),
             .div => try sema.zirArithmetic(block, inst),
             .elem_ptr => try sema.zirElemPtr(block, inst),
             .elem_ptr_node => try sema.zirElemPtrNode(block, inst),
@@ -576,7 +576,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) In
         return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
     }
     const result_ptr = try sema.namedFieldPtr(block, src, array_ptr, "len", src);
-    return sema.analyzeDeref(block, src, result_ptr, result_ptr.src);
+    return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
 }
 
 fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -1911,7 +1911,7 @@ fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErro
     const object = try sema.resolveInst(extra.lhs);
     const object_ptr = try sema.analyzeRef(block, src, object);
     const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
-    return sema.analyzeDeref(block, src, result_ptr, result_ptr.src);
+    return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
 }
 
 fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -1939,7 +1939,7 @@ fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne
     const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
     const object_ptr = try sema.analyzeRef(block, src, object);
     const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
-    return sema.analyzeDeref(block, src, result_ptr, src);
+    return sema.analyzeLoad(block, src, result_ptr, src);
 }
 
 fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -2060,7 +2060,7 @@ fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError
     const array_ptr = try sema.analyzeRef(block, sema.src, array);
     const elem_index = try sema.resolveInst(bin_inst.rhs);
     const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
-    return sema.analyzeDeref(block, sema.src, result_ptr, sema.src);
+    return sema.analyzeLoad(block, sema.src, result_ptr, sema.src);
 }
 
 fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -2075,7 +2075,7 @@ fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerE
     const array_ptr = try sema.analyzeRef(block, src, array);
     const elem_index = try sema.resolveInst(extra.rhs);
     const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
-    return sema.analyzeDeref(block, src, result_ptr, src);
+    return sema.analyzeLoad(block, src, result_ptr, src);
 }
 
 fn zirElemPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -2183,7 +2183,7 @@ fn zirSwitchBr(
 
     const target_ptr = try sema.resolveInst(inst.positionals.target);
     const target = if (ref)
-        try sema.analyzeDeref(parent_block, inst.base.src, target_ptr, inst.positionals.target.src)
+        try sema.analyzeLoad(parent_block, inst.base.src, target_ptr, inst.positionals.target.src)
     else
         target_ptr;
     try sema.validateSwitch(parent_block, target, inst);
@@ -2639,7 +2639,7 @@ fn analyzeArithmetic(
     return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
 }
 
-fn zirDerefNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirLoad(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -2647,7 +2647,7 @@ fn zirDerefNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErr
     const src = inst_data.src();
     const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node };
     const ptr = try sema.resolveInst(inst_data.operand);
-    return sema.analyzeDeref(block, src, ptr, ptr_src);
+    return sema.analyzeLoad(block, src, ptr, ptr_src);
 }
 
 fn zirAsm(
@@ -2958,7 +2958,7 @@ fn zirIsNullPtr(
     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
     const src = inst_data.src();
     const ptr = try sema.resolveInst(inst_data.operand);
-    const loaded = try sema.analyzeDeref(block, src, ptr, src);
+    const loaded = try sema.analyzeLoad(block, src, ptr, src);
     return sema.analyzeIsNull(block, src, loaded, invert_logic);
 }
 
@@ -2978,7 +2978,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErro
     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
     const src = inst_data.src();
     const ptr = try sema.resolveInst(inst_data.operand);
-    const loaded = try sema.analyzeDeref(block, src, ptr, src);
+    const loaded = try sema.analyzeLoad(block, src, ptr, src);
     return sema.analyzeIsErr(block, src, loaded);
 }
 
@@ -3343,7 +3343,7 @@ fn namedFieldPtr(
         },
         .Type => {
             _ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);
-            const result = try sema.analyzeDeref(block, src, object_ptr, object_ptr.src);
+            const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);
             const val = result.value().?;
             const child_type = try val.toType(sema.arena);
             switch (child_type.zigTypeTag()) {
@@ -3409,7 +3409,7 @@ fn elemPtr(
 
     if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
         // we have to deref the ptr operand to get the actual array pointer
-        const array_ptr_deref = try sema.analyzeDeref(block, src, array_ptr, array_ptr.src);
+        const array_ptr_deref = try sema.analyzeLoad(block, src, array_ptr, array_ptr.src);
         if (array_ptr_deref.value()) |array_ptr_val| {
             if (elem_index.value()) |index_val| {
                 // Both array pointer and index are compile-time known.
@@ -3669,7 +3669,7 @@ fn coerceArrayPtrToMany(sema: *Sema, block: *Scope.Block, dest_type: Type, inst:
 
 fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
     const decl_ref = try sema.analyzeDeclRef(block, src, decl);
-    return sema.analyzeDeref(block, src, decl_ref, src);
+    return sema.analyzeLoad(block, src, decl_ref, src);
 }
 
 fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
@@ -3737,7 +3737,7 @@ fn analyzeRef(
     return block.addUnOp(src, ptr_type, .ref, operand);
 }
 
-fn analyzeDeref(
+fn analyzeLoad(
     sema: *Sema,
     block: *Scope.Block,
     src: LazySrcLoc,
src/zir.zig
@@ -281,7 +281,7 @@ pub const Inst = struct {
         decl_val,
         /// Load the value from a pointer. Assumes `x.*` syntax.
         /// Uses `un_node` field. AST node is the `x.*` syntax.
-        deref_node,
+        load,
         /// Arithmetic division. Asserts no integer overflow.
         /// Uses the `pl_node` union field. Payload is `Bin`.
         div,
@@ -661,7 +661,7 @@ pub const Inst = struct {
                 .dbg_stmt_node,
                 .decl_ref,
                 .decl_val,
-                .deref_node,
+                .load,
                 .div,
                 .elem_ptr,
                 .elem_val,
@@ -841,6 +841,10 @@ pub const Inst = struct {
         bool_true,
         /// `false`
         bool_false,
+        /// `0` (usize)
+        zero_usize,
+        /// `1` (usize)
+        one_usize,
 
         _,
 
@@ -1016,10 +1020,18 @@ pub const Inst = struct {
                 .ty = Type.initTag(.comptime_int),
                 .val = Value.initTag(.zero),
             },
+            .zero_usize = .{
+                .ty = Type.initTag(.usize),
+                .val = Value.initTag(.zero),
+            },
             .one = .{
                 .ty = Type.initTag(.comptime_int),
                 .val = Value.initTag(.one),
             },
+            .one_usize = .{
+                .ty = Type.initTag(.usize),
+                .val = Value.initTag(.one),
+            },
             .void_value = .{
                 .ty = Type.initTag(.void),
                 .val = Value.initTag(.void_value),
@@ -1377,7 +1389,7 @@ const Writer = struct {
             .call_none,
             .call_none_chkused,
             .compile_error,
-            .deref_node,
+            .load,
             .ensure_result_used,
             .ensure_result_non_error,
             .import,
test/stage2/test.zig
@@ -968,37 +968,37 @@ pub fn addCases(ctx: *TestContext) !void {
         );
 
         // Basic for loop
-        //case.addCompareOutput(
-        //    \\export fn _start() noreturn {
-        //    \\    for ("hello") |_| print();
-        //    \\
-        //    \\    exit();
-        //    \\}
-        //    \\
-        //    \\fn print() void {
-        //    \\    asm volatile ("syscall"
-        //    \\        :
-        //    \\        : [number] "{rax}" (1),
-        //    \\          [arg1] "{rdi}" (1),
-        //    \\          [arg2] "{rsi}" (@ptrToInt("hello\n")),
-        //    \\          [arg3] "{rdx}" (6)
-        //    \\        : "rcx", "r11", "memory"
-        //    \\    );
-        //    \\    return;
-        //    \\}
-        //    \\
-        //    \\fn exit() noreturn {
-        //    \\    asm volatile ("syscall"
-        //    \\        :
-        //    \\        : [number] "{rax}" (231),
-        //    \\          [arg1] "{rdi}" (0)
-        //    \\        : "rcx", "r11", "memory"
-        //    \\    );
-        //    \\    unreachable;
-        //    \\}
-        //,
-        //    "hello\nhello\nhello\nhello\nhello\n",
-        //);
+        case.addCompareOutput(
+            \\export fn _start() noreturn {
+            \\    for ("hello") |_| print();
+            \\
+            \\    exit();
+            \\}
+            \\
+            \\fn print() void {
+            \\    asm volatile ("syscall"
+            \\        :
+            \\        : [number] "{rax}" (1),
+            \\          [arg1] "{rdi}" (1),
+            \\          [arg2] "{rsi}" (@ptrToInt("hello\n")),
+            \\          [arg3] "{rdx}" (6)
+            \\        : "rcx", "r11", "memory"
+            \\    );
+            \\    return;
+            \\}
+            \\
+            \\fn exit() noreturn {
+            \\    asm volatile ("syscall"
+            \\        :
+            \\        : [number] "{rax}" (231),
+            \\          [arg1] "{rdi}" (0)
+            \\        : "rcx", "r11", "memory"
+            \\    );
+            \\    unreachable;
+            \\}
+        ,
+            "hello\nhello\nhello\nhello\nhello\n",
+        );
     }
 
     //{
BRANCH_TODO
@@ -35,3 +35,6 @@ Performance optimizations to look into:
  * enum literals can use small strings
  * string literals can use small strings
  * don't need the Sema coercion on condbr condition, it's done with result locations
+ * astgen for loops using pointer arithmetic because it's faster and if the programmer
+   wants an index capture, that will just be a convenience variable that zig sets up
+   independently.