Commit 7f41e20802

Andrew Kelley <andrew@ziglang.org>
2022-01-16 07:13:44
AstGen: emit `as` instructions for branching expressions
There is a mechanism to avoid redundant `as` ZIR instructions which is to pass `ResultLoc.coerced_ty` instead of `ResultLoc.ty` when it is known by AstGen that Sema will do the coercion. This commit downgrades `coerced_ty` to `ty` when a result location passes through an expression that branches, such as `if`, `switch`, `while`, and `for`, causing the `as` ZIR instruction to be emitted. This ensures that the type of a result location will be applied to, e.g. a `comptime_int` on either side of a branch on a runtime condition.
1 parent 7c6f5d2
Changed files (3)
src/AstGen.zig
@@ -281,6 +281,15 @@ pub const ResultLoc = union(enum) {
             },
         }
     }
+
+    /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
+    /// such as if and switch expressions.
+    fn br(rl: ResultLoc) ResultLoc {
+        return switch (rl) {
+            .coerced_ty => |ty| .{ .ty = ty },
+            else => rl,
+        };
+    }
 };
 
 pub const align_rl: ResultLoc = .{ .ty = .u16_type };
@@ -748,15 +757,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
         .field_access => return fieldAccess(gz, scope, rl, node),
         .float_literal => return floatLiteral(gz, rl, node),
 
-        .if_simple => return ifExpr(gz, scope, rl, node, tree.ifSimple(node)),
-        .@"if" => return ifExpr(gz, scope, rl, node, tree.ifFull(node)),
+        .if_simple => return ifExpr(gz, scope, rl.br(), node, tree.ifSimple(node)),
+        .@"if" => return ifExpr(gz, scope, rl.br(), node, tree.ifFull(node)),
 
-        .while_simple => return whileExpr(gz, scope, rl, node, tree.whileSimple(node)),
-        .while_cont => return whileExpr(gz, scope, rl, node, tree.whileCont(node)),
-        .@"while" => return whileExpr(gz, scope, rl, node, tree.whileFull(node)),
+        .while_simple => return whileExpr(gz, scope, rl.br(), node, tree.whileSimple(node)),
+        .while_cont => return whileExpr(gz, scope, rl.br(), node, tree.whileCont(node)),
+        .@"while" => return whileExpr(gz, scope, rl.br(), node, tree.whileFull(node)),
 
-        .for_simple => return forExpr(gz, scope, rl, node, tree.forSimple(node)),
-        .@"for" => return forExpr(gz, scope, rl, node, tree.forFull(node)),
+        .for_simple => return forExpr(gz, scope, rl.br(), node, tree.forSimple(node)),
+        .@"for" => return forExpr(gz, scope, rl.br(), node, tree.forFull(node)),
 
         .slice_open => {
             const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
@@ -943,7 +952,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
         .error_set_decl => return errorSetDecl(gz, rl, node),
         .array_access => return arrayAccess(gz, scope, rl, node),
         .@"comptime" => return comptimeExprAst(gz, scope, rl, node),
-        .@"switch", .switch_comma => return switchExpr(gz, scope, rl, node),
+        .@"switch", .switch_comma => return switchExpr(gz, scope, rl.br(), node),
 
         .@"nosuspend" => return nosuspendExpr(gz, scope, rl, node),
         .@"suspend" => return suspendExpr(gz, scope, node),
test/behavior/array_llvm.zig
@@ -179,3 +179,17 @@ test "access the null element of a null terminated array" {
     try S.doTheTest();
     comptime try S.doTheTest();
 }
+
+test "type deduction for array subscript expression" {
+    const S = struct {
+        fn doTheTest() !void {
+            var array = [_]u8{ 0x55, 0xAA };
+            var v0 = true;
+            try expect(@as(u8, 0xAA) == array[if (v0) 1 else 0]);
+            var v1 = false;
+            try expect(@as(u8, 0x55) == array[if (v1) 1 else 0]);
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
test/behavior/array_stage1.zig
@@ -4,20 +4,6 @@ const mem = std.mem;
 const expect = testing.expect;
 const expectEqual = testing.expectEqual;
 
-test "type deduction for array subscript expression" {
-    const S = struct {
-        fn doTheTest() !void {
-            var array = [_]u8{ 0x55, 0xAA };
-            var v0 = true;
-            try expect(@as(u8, 0xAA) == array[if (v0) 1 else 0]);
-            var v1 = false;
-            try expect(@as(u8, 0x55) == array[if (v1) 1 else 0]);
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
 test "sentinel element count towards the ABI size calculation" {
     const S = struct {
         fn doTheTest() !void {