Commit fc402bdbbb

Andrew Kelley <andrew@ziglang.org>
2020-08-13 20:03:13
stage2: zir_sema for loops
Also remove the "repeat" instruction and make it implied to be at the end of a Loop body.
1 parent a9590f3
Changed files (3)
src-self-hosted/astgen.zig
@@ -531,13 +531,12 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
     const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .{
         .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
     });
+    // 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_node.continue_expr) |cont_expr| {
-        const cont_expr_result = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
-        if (!cont_expr_result.tag.isNoReturn()) {
-            _ = try addZIRNoOp(mod, &loop_scope.base, while_src, .repeat);
-        }
-    } else {
-        _ = try addZIRNoOp(mod, &loop_scope.base, while_src, .repeat);
+        _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
     }
     const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{
         .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
src-self-hosted/zir.zig
@@ -151,7 +151,8 @@ pub const Inst = struct {
         isnonnull,
         /// Return a boolean true if an optional is null. `x == null`
         isnull,
-        /// A labeled block of code that loops forever.
+        /// A labeled block of code that loops forever. At the end of the body it is implied
+        /// to repeat; no explicit "repeat" instruction terminates loop bodies.
         loop,
         /// Ambiguously remainder division or modulus. If the computation would possibly have
         /// a different value depending on whether the operation is remainder division or modulus,
@@ -175,8 +176,6 @@ pub const Inst = struct {
         /// the memory location is in the stack frame, local to the scope containing the
         /// instruction.
         ref,
-        /// Sends control flow back to the loop block operand.
-        repeat,
         /// Obtains a pointer to the return value.
         ret_ptr,
         /// Obtains the return type of the in-scope function.
@@ -294,7 +293,6 @@ pub const Inst = struct {
                 .compileerror => CompileError,
                 .loop => Loop,
                 .@"const" => Const,
-                .repeat => Repeat,
                 .str => Str,
                 .int => Int,
                 .inttype => IntType,
@@ -390,7 +388,6 @@ pub const Inst = struct {
                 .breakvoid,
                 .condbr,
                 .compileerror,
-                .repeat,
                 .@"return",
                 .returnvoid,
                 .unreach_nocheck,
@@ -587,16 +584,6 @@ pub const Inst = struct {
         kw_args: struct {},
     };
 
-    pub const Repeat = struct {
-        pub const base_tag = Tag.repeat;
-        base: Inst,
-
-        positionals: struct {
-            loop: *Loop,
-        },
-        kw_args: struct {},
-    };
-
     pub const Str = struct {
         pub const base_tag = Tag.str;
         base: Inst,
src-self-hosted/zir_sema.zig
@@ -61,7 +61,6 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
         },
         .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?),
         .loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?),
-        .repeat => return analyzeInstRepeat(mod, scope, old_inst.castTag(.repeat).?),
         .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?),
         .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?),
         .fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?),
@@ -440,12 +439,38 @@ fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*
     return mod.addArg(b, inst.base.src, param_type, name);
 }
 
-fn analyzeInstRepeat(mod: *Module, scope: *Scope, inst: *zir.Inst.Repeat) InnerError!*Inst {
-    return mod.fail(scope, inst.base.src, "TODO analyze .repeat ZIR", .{});
-}
-
 fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst {
-    return mod.fail(scope, inst.base.src, "TODO analyze .loop ZIR", .{});
+    const parent_block = scope.cast(Scope.Block).?;
+
+    // Reserve space for a Loop instruction so that generated Break instructions can
+    // point to it, even if it doesn't end up getting used because the code ends up being
+    // comptime evaluated.
+    const loop_inst = try parent_block.arena.create(Inst.Loop);
+    loop_inst.* = .{
+        .base = .{
+            .tag = Inst.Loop.base_tag,
+            .ty = Type.initTag(.noreturn),
+            .src = inst.base.src,
+        },
+        .body = undefined,
+    };
+
+    var child_block: Scope.Block = .{
+        .parent = parent_block,
+        .func = parent_block.func,
+        .decl = parent_block.decl,
+        .instructions = .{},
+        .arena = parent_block.arena,
+    };
+    defer child_block.instructions.deinit(mod.gpa);
+
+    try analyzeBody(mod, &child_block.base, inst.positionals.body);
+
+    // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
+
+    try parent_block.instructions.append(mod.gpa, &loop_inst.base);
+    loop_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
+    return &loop_inst.base;
 }
 
 fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {