Commit 093cbeb018

Andrew Kelley <andrew@ziglang.org>
2021-01-21 09:30:26
astgen: `@as` with block_ptr result location
1 parent b7452fe
Changed files (3)
src/astgen.zig
@@ -663,11 +663,27 @@ fn varDecl(
 
     switch (tree.token_ids[node.mut_token]) {
         .Keyword_const => {
-            var resolve_inferred_alloc: ?*zir.Inst = null;
             // Depending on the type of AST the initialization expression is, we may need an lvalue
             // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
             // the variable, no memory location needed.
-            const result_loc = if (nodeMayNeedMemoryLocation(init_node, scope)) r: {
+            if (!nodeMayNeedMemoryLocation(init_node, scope)) {
+                const result_loc: ResultLoc = if (node.getTypeNode()) |type_node|
+                    .{ .ty = try typeExpr(mod, scope, type_node) }
+                else
+                    .none;
+                const init_inst = try expr(mod, scope, result_loc, init_node);
+                const sub_scope = try block_arena.create(Scope.LocalVal);
+                sub_scope.* = .{
+                    .parent = scope,
+                    .gen_zir = scope.getGenZIR(),
+                    .name = ident_name,
+                    .inst = init_inst,
+                };
+                return &sub_scope.base;
+            }
+
+            var resolve_inferred_alloc: ?*zir.Inst = null;
+            const result_loc = r: {
                 if (node.getTypeNode()) |type_node| {
                     const type_inst = try typeExpr(mod, scope, type_node);
                     const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
@@ -677,11 +693,6 @@ fn varDecl(
                     resolve_inferred_alloc = &alloc.base;
                     break :r ResultLoc{ .inferred_ptr = alloc };
                 }
-            } else r: {
-                if (node.getTypeNode()) |type_node|
-                    break :r ResultLoc{ .ty = try typeExpr(mod, scope, type_node) }
-                else
-                    break :r .none;
             };
             const init_inst = try expr(mod, scope, result_loc, init_node);
             if (resolve_inferred_alloc) |inst| {
@@ -1718,14 +1729,20 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
     switch (strategy) {
         .break_void => {
             if (!then_result.tag.isNoReturn()) {
-                _ = try addZIRNoOp(mod, then_sub_scope, then_src, .break_void);
+                _ = try addZirInstTag(mod, then_sub_scope, then_src, .break_void, .{
+                    .block = block,
+                });
             }
             if (else_result) |inst| {
                 if (!inst.tag.isNoReturn()) {
-                    _ = try addZIRNoOp(mod, else_sub_scope, else_src, .break_void);
+                    _ = try addZirInstTag(mod, else_sub_scope, else_src, .break_void, .{
+                        .block = block,
+                    });
                 }
             } else {
-                _ = try addZIRNoOp(mod, else_sub_scope, else_src, .break_void);
+                _ = try addZirInstTag(mod, else_sub_scope, else_src, .break_void, .{
+                    .block = block,
+                });
             }
             assert(!elide_store_to_block_ptr_instructions);
             try copyBodyNoEliding(&condbr.positionals.then_body, then_scope);
@@ -1747,7 +1764,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
                     });
                 }
             } else {
-                _ = try addZIRNoOp(mod, else_sub_scope, else_src, .break_void);
+                _ = try addZirInstTag(mod, else_sub_scope, else_src, .break_void, .{
+                    .block = block,
+                });
             }
             if (elide_store_to_block_ptr_instructions) {
                 try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.then_body, then_scope);
@@ -2728,31 +2747,30 @@ fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError
     return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
 }
 
-fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+fn as(
+    mod: *Module,
+    scope: *Scope,
+    rl: ResultLoc,
+    call: *ast.Node.BuiltinCall,
+) InnerError!*zir.Inst {
     try ensureBuiltinParamCount(mod, scope, call, 2);
     const tree = scope.tree();
     const src = tree.token_locs[call.builtin_token].start;
     const params = call.params();
     const dest_type = try typeExpr(mod, scope, params[0]);
     switch (rl) {
-        .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
-        .discard => {
+        .none, .discard, .ref, .ty => {
             const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
-            _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
-            return result;
-        },
-        .ref => {
-            const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
-            return addZIRUnOp(mod, scope, result.src, .ref, result);
-        },
-        .ty => |result_ty| {
-            const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
-            return addZIRBinOp(mod, scope, src, .as, result_ty, result);
+            return rvalue(mod, scope, rl, result);
         },
+
         .ptr => |result_ptr| {
-            const casted_result_ptr = try addZIRBinOp(mod, scope, src, .coerce_result_ptr, dest_type, result_ptr);
-            return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
+            return asRlPtr(mod, scope, rl, src, result_ptr, params[1], dest_type);
         },
+        .block_ptr => |block_scope| {
+            return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, params[1], dest_type);
+        },
+
         .bitcasted_ptr => |bitcasted_ptr| {
             // TODO here we should be able to resolve the inference; we now have a type for the result.
             return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{});
@@ -2761,13 +2779,47 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I
             // TODO here we should be able to resolve the inference; we now have a type for the result.
             return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
         },
-        .block_ptr => |block_scope| {
-            const casted_block_ptr = try addZirInstTag(mod, scope, src, .coerce_result_block_ptr, .{
-                .dest_type = dest_type,
-                .block_ptr = block_scope.rl_ptr.?,
-            });
-            return expr(mod, scope, .{ .ptr = casted_block_ptr }, params[1]);
-        },
+    }
+}
+
+fn asRlPtr(
+    mod: *Module,
+    scope: *Scope,
+    rl: ResultLoc,
+    src: usize,
+    result_ptr: *zir.Inst,
+    operand_node: *ast.Node,
+    dest_type: *zir.Inst,
+) InnerError!*zir.Inst {
+    // Detect whether this expr() call goes into rvalue() to store the result into the
+    // result location. If it does, elide the coerce_result_ptr instruction
+    // as well as the store instruction, instead passing the result as an rvalue.
+    var as_scope: Scope.GenZIR = .{
+        .parent = scope,
+        .decl = scope.ownerDecl().?,
+        .arena = scope.arena(),
+        .instructions = .{},
+    };
+    defer as_scope.instructions.deinit(mod.gpa);
+
+    as_scope.rl_ptr = try addZIRBinOp(mod, &as_scope.base, src, .coerce_result_ptr, dest_type, result_ptr);
+    const result = try expr(mod, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
+    const parent_zir = &scope.getGenZIR().instructions;
+    if (as_scope.rvalue_rl_count == 1) {
+        // Busted! This expression didn't actually need a pointer.
+        const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2;
+        try parent_zir.ensureCapacity(mod.gpa, expected_len);
+        for (as_scope.instructions.items) |src_inst| {
+            switch (src_inst.tag) {
+                .store_to_block_ptr, .coerce_result_ptr => continue,
+                else => parent_zir.appendAssumeCapacity(src_inst),
+            }
+        }
+        assert(parent_zir.items.len == expected_len);
+        return rvalue(mod, scope, rl, result);
+    } else {
+        try parent_zir.appendSlice(mod.gpa, as_scope.instructions.items);
+        return result;
     }
 }
 
src/zir.zig
@@ -112,10 +112,6 @@ pub const Inst = struct {
         /// as type coercion from the new element type to the old element type.
         /// LHS is destination element type, RHS is result pointer.
         coerce_result_ptr,
-        /// This instruction does a `coerce_result_ptr` operation on a `Block`'s
-        /// result location pointer, whose type is inferred by peer type resolution on the
-        /// `Block`'s corresponding `break` instructions.
-        coerce_result_block_ptr,
         /// Emit an error message and fail compilation.
         compile_error,
         /// Log compile time variables and emit an error message.
@@ -460,7 +456,6 @@ pub const Inst = struct {
                 .decl_ref => DeclRef,
                 .decl_ref_str => DeclRefStr,
                 .decl_val => DeclVal,
-                .coerce_result_block_ptr => CoerceResultBlockPtr,
                 .compile_log => CompileLog,
                 .loop => Loop,
                 .@"const" => Const,
@@ -531,7 +526,6 @@ pub const Inst = struct {
                 .cmp_gt,
                 .cmp_neq,
                 .coerce_result_ptr,
-                .coerce_result_block_ptr,
                 .@"const",
                 .dbg_stmt,
                 .decl_ref,
@@ -771,17 +765,6 @@ pub const Inst = struct {
         kw_args: struct {},
     };
 
-    pub const CoerceResultBlockPtr = struct {
-        pub const base_tag = Tag.coerce_result_block_ptr;
-        base: Inst,
-
-        positionals: struct {
-            dest_type: *Inst,
-            block_ptr: *Inst,
-        },
-        kw_args: struct {},
-    };
-
     pub const CompileLog = struct {
         pub const base_tag = Tag.compile_log;
         base: Inst,
@@ -1464,7 +1447,7 @@ const Writer = struct {
             TypedValue => return stream.print("TypedValue{{ .ty = {}, .val = {}}}", .{ param.ty, param.val }),
             *IrModule.Decl => return stream.print("Decl({s})", .{param.name}),
             *Inst.Block => {
-                const name = self.block_table.get(param).?;
+                const name = self.block_table.get(param) orelse "!BADREF!";
                 return stream.print("\"{}\"", .{std.zig.fmtEscapes(name)});
             },
             *Inst.Loop => {
src/zir_sema.zig
@@ -43,7 +43,6 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
         .breakpoint => return zirBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?),
         .break_void => return zirBreakVoid(mod, scope, old_inst.castTag(.break_void).?),
         .call => return zirCall(mod, scope, old_inst.castTag(.call).?),
-        .coerce_result_block_ptr => return zirCoerceResultBlockPtr(mod, scope, old_inst.castTag(.coerce_result_block_ptr).?),
         .coerce_result_ptr => return zirCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?),
         .compile_error => return zirCompileError(mod, scope, old_inst.castTag(.compile_error).?),
         .compile_log => return zirCompileLog(mod, scope, old_inst.castTag(.compile_log).?),
@@ -265,16 +264,6 @@ fn analyzeConstInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError
     };
 }
 
-fn zirCoerceResultBlockPtr(
-    mod: *Module,
-    scope: *Scope,
-    inst: *zir.Inst.CoerceResultBlockPtr,
-) InnerError!*Inst {
-    const tracy = trace(@src());
-    defer tracy.end();
-    return mod.fail(scope, inst.base.src, "TODO implement zirCoerceResultBlockPtr", .{});
-}
-
 fn zirBitcastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();