authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-15 23:13:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-15 23:13:44-07:00
log7f41e20802fd8f9eb19c0218f1e2000e2751592a
tree535a39fbae2c811e4c7af03b36cf52b3f164ad73
parent7c6f5d26eadb137ab3fb2be340485ebd860a85fe

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.

3 files changed, 31 insertions(+), 22 deletions(-)

src/AstGen.zig+17-8
...@@ -281,6 +281,15 @@ pub const ResultLoc = union(enum) {...@@ -281,6 +281,15 @@ pub const ResultLoc = union(enum) {
281 },281 },
282 }282 }
283 }283 }
284
285 /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
286 /// such as if and switch expressions.
287 fn br(rl: ResultLoc) ResultLoc {
288 return switch (rl) {
289 .coerced_ty => |ty| .{ .ty = ty },
290 else => rl,
291 };
292 }
284};293};
285294
286pub const align_rl: ResultLoc = .{ .ty = .u16_type };295pub const align_rl: ResultLoc = .{ .ty = .u16_type };
...@@ -748,15 +757,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -748,15 +757,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
748 .field_access => return fieldAccess(gz, scope, rl, node),757 .field_access => return fieldAccess(gz, scope, rl, node),
749 .float_literal => return floatLiteral(gz, rl, node),758 .float_literal => return floatLiteral(gz, rl, node),
750759
751 .if_simple => return ifExpr(gz, scope, rl, node, tree.ifSimple(node)),760 .if_simple => return ifExpr(gz, scope, rl.br(), node, tree.ifSimple(node)),
752 .@"if" => return ifExpr(gz, scope, rl, node, tree.ifFull(node)),761 .@"if" => return ifExpr(gz, scope, rl.br(), node, tree.ifFull(node)),
753762
754 .while_simple => return whileExpr(gz, scope, rl, node, tree.whileSimple(node)),763 .while_simple => return whileExpr(gz, scope, rl.br(), node, tree.whileSimple(node)),
755 .while_cont => return whileExpr(gz, scope, rl, node, tree.whileCont(node)),764 .while_cont => return whileExpr(gz, scope, rl.br(), node, tree.whileCont(node)),
756 .@"while" => return whileExpr(gz, scope, rl, node, tree.whileFull(node)),765 .@"while" => return whileExpr(gz, scope, rl.br(), node, tree.whileFull(node)),
757766
758 .for_simple => return forExpr(gz, scope, rl, node, tree.forSimple(node)),767 .for_simple => return forExpr(gz, scope, rl.br(), node, tree.forSimple(node)),
759 .@"for" => return forExpr(gz, scope, rl, node, tree.forFull(node)),768 .@"for" => return forExpr(gz, scope, rl.br(), node, tree.forFull(node)),
760769
761 .slice_open => {770 .slice_open => {
762 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);771 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...@@ -943,7 +952,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
943 .error_set_decl => return errorSetDecl(gz, rl, node),952 .error_set_decl => return errorSetDecl(gz, rl, node),
944 .array_access => return arrayAccess(gz, scope, rl, node),953 .array_access => return arrayAccess(gz, scope, rl, node),
945 .@"comptime" => return comptimeExprAst(gz, scope, rl, node),954 .@"comptime" => return comptimeExprAst(gz, scope, rl, node),
946 .@"switch", .switch_comma => return switchExpr(gz, scope, rl, node),955 .@"switch", .switch_comma => return switchExpr(gz, scope, rl.br(), node),
947956
948 .@"nosuspend" => return nosuspendExpr(gz, scope, rl, node),957 .@"nosuspend" => return nosuspendExpr(gz, scope, rl, node),
949 .@"suspend" => return suspendExpr(gz, scope, node),958 .@"suspend" => return suspendExpr(gz, scope, node),
test/behavior/array_llvm.zig+14
...@@ -179,3 +179,17 @@ test "access the null element of a null terminated array" {...@@ -179,3 +179,17 @@ test "access the null element of a null terminated array" {
179 try S.doTheTest();179 try S.doTheTest();
180 comptime try S.doTheTest();180 comptime try S.doTheTest();
181}181}
182
183test "type deduction for array subscript expression" {
184 const S = struct {
185 fn doTheTest() !void {
186 var array = [_]u8{ 0x55, 0xAA };
187 var v0 = true;
188 try expect(@as(u8, 0xAA) == array[if (v0) 1 else 0]);
189 var v1 = false;
190 try expect(@as(u8, 0x55) == array[if (v1) 1 else 0]);
191 }
192 };
193 try S.doTheTest();
194 comptime try S.doTheTest();
195}
test/behavior/array_stage1.zig-14
...@@ -4,20 +4,6 @@ const mem = std.mem;...@@ -4,20 +4,6 @@ const mem = std.mem;
4const expect = testing.expect;4const expect = testing.expect;
5const expectEqual = testing.expectEqual;5const expectEqual = testing.expectEqual;
66
7test "type deduction for array subscript expression" {
8 const S = struct {
9 fn doTheTest() !void {
10 var array = [_]u8{ 0x55, 0xAA };
11 var v0 = true;
12 try expect(@as(u8, 0xAA) == array[if (v0) 1 else 0]);
13 var v1 = false;
14 try expect(@as(u8, 0x55) == array[if (v1) 1 else 0]);
15 }
16 };
17 try S.doTheTest();
18 comptime try S.doTheTest();
19}
20
21test "sentinel element count towards the ABI size calculation" {7test "sentinel element count towards the ABI size calculation" {
22 const S = struct {8 const S = struct {
23 fn doTheTest() !void {9 fn doTheTest() !void {