authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-02 21:59:23-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-02 21:59:23-07:00
log713f1138222dc40355c34c70d83b0a0805bd46c6
treea6060a67d81b1f875e0cfb947fadec9325b37a89
parent6aa1ea9c59340a1f5b7560ecd97e45928bd4cf56

stage2: improve orelse implementation

* Now it supports being an lvalue (see additional lines in the test case). * Properly handles a pointer result location (see additional lines in the test case that assign the result of the orelse to a variable rather than a const). * Properly sets the result location type when possible, so that type inference of an `orelse` operand expression knows its result type.

4 files changed, 55 insertions(+), 8 deletions(-)

src/astgen.zig+30-8
...@@ -1709,14 +1709,13 @@ fn orelseCatchExpr(...@@ -1709,14 +1709,13 @@ fn orelseCatchExpr(
1709 setBlockResultLoc(&block_scope, rl);1709 setBlockResultLoc(&block_scope, rl);
1710 defer block_scope.instructions.deinit(mod.gpa);1710 defer block_scope.instructions.deinit(mod.gpa);
17111711
1712 // This could be a pointer or value depending on the `rl` parameter.1712 // This could be a pointer or value depending on the `operand_rl` parameter.
1713 // We cannot use `block_scope.break_result_loc` because that has the bare
1714 // type, whereas this expression has the optional type. Later we make
1715 // up for this fact by calling rvalue on the else branch.
1713 block_scope.break_count += 1;1716 block_scope.break_count += 1;
1714 const operand = try expr(1717 const operand_rl = try makeOptionalTypeResultLoc(mod, &block_scope.base, src, block_scope.break_result_loc);
1715 mod,1718 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
1716 &block_scope.base,
1717 if (block_scope.break_result_loc == .ref) .ref else .none,
1718 lhs,
1719 );
1720 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);1719 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
17211720
1722 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{1721 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
...@@ -1768,6 +1767,10 @@ fn orelseCatchExpr(...@@ -1768,6 +1767,10 @@ fn orelseCatchExpr(
17681767
1769 // This could be a pointer or value depending on `unwrap_op`.1768 // This could be a pointer or value depending on `unwrap_op`.
1770 const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);1769 const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);
1770 const else_result = switch (rl) {
1771 .ref => unwrapped_payload,
1772 else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload),
1773 };
17711774
1772 return finishThenElseBlock(1775 return finishThenElseBlock(
1773 mod,1776 mod,
...@@ -1781,7 +1784,7 @@ fn orelseCatchExpr(...@@ -1781,7 +1784,7 @@ fn orelseCatchExpr(
1781 src,1784 src,
1782 src,1785 src,
1783 then_result,1786 then_result,
1784 unwrapped_payload,1787 else_result,
1785 block,1788 block,
1786 block,1789 block,
1787 );1790 );
...@@ -3970,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy {...@@ -3970,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy {
3970 }3973 }
3971}3974}
39723975
3976/// If the input ResultLoc is ref, returns ResultLoc.ref. Otherwise:
3977/// Returns ResultLoc.ty, where the type is determined by the input
3978/// ResultLoc type, wrapped in an optional type. If the input ResultLoc
3979/// has no type, .none is returned.
3980fn makeOptionalTypeResultLoc(mod: *Module, scope: *Scope, src: usize, rl: ResultLoc) !ResultLoc {
3981 switch (rl) {
3982 .ref => return ResultLoc.ref,
3983 .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => return ResultLoc.none,
3984 .ty => |elem_ty| {
3985 const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type, elem_ty);
3986 return ResultLoc{ .ty = wrapped_ty };
3987 },
3988 .ptr => |ptr_ty| {
3989 const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type_from_ptr_elem, ptr_ty);
3990 return ResultLoc{ .ty = wrapped_ty };
3991 },
3992 }
3993}
3994
3973fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void {3995fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void {
3974 // Depending on whether the result location is a pointer or value, different3996 // Depending on whether the result location is a pointer or value, different
3975 // ZIR needs to be generated. In the former case we rely on storing to the3997 // ZIR needs to be generated. In the former case we rely on storing to the
src/zir.zig+5
...@@ -299,6 +299,9 @@ pub const Inst = struct {...@@ -299,6 +299,9 @@ pub const Inst = struct {
299 xor,299 xor,
300 /// Create an optional type '?T'300 /// Create an optional type '?T'
301 optional_type,301 optional_type,
302 /// Create an optional type '?T'. The operand is a pointer value. The optional type will
303 /// be the type of the pointer element, wrapped in an optional.
304 optional_type_from_ptr_elem,
302 /// Create a union type.305 /// Create a union type.
303 union_type,306 union_type,
304 /// ?T => T with safety.307 /// ?T => T with safety.
...@@ -397,6 +400,7 @@ pub const Inst = struct {...@@ -397,6 +400,7 @@ pub const Inst = struct {
397 .mut_slice_type,400 .mut_slice_type,
398 .const_slice_type,401 .const_slice_type,
399 .optional_type,402 .optional_type,
403 .optional_type_from_ptr_elem,
400 .optional_payload_safe,404 .optional_payload_safe,
401 .optional_payload_unsafe,405 .optional_payload_unsafe,
402 .optional_payload_safe_ptr,406 .optional_payload_safe_ptr,
...@@ -597,6 +601,7 @@ pub const Inst = struct {...@@ -597,6 +601,7 @@ pub const Inst = struct {
597 .typeof,601 .typeof,
598 .xor,602 .xor,
599 .optional_type,603 .optional_type,
604 .optional_type_from_ptr_elem,
600 .optional_payload_safe,605 .optional_payload_safe,
601 .optional_payload_unsafe,606 .optional_payload_unsafe,
602 .optional_payload_safe_ptr,607 .optional_payload_safe_ptr,
src/zir_sema.zig+11
...@@ -131,6 +131,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -131,6 +131,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
131 .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?),131 .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?),
132 .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?),132 .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?),
133 .optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?),133 .optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
134 .optional_type_from_ptr_elem => return zirOptionalTypeFromPtrElem(mod, scope, old_inst.castTag(.optional_type_from_ptr_elem).?),
134 .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),135 .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),
135 .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),136 .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),
136 .optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true),137 .optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true),
...@@ -1093,6 +1094,16 @@ fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerE...@@ -1093,6 +1094,16 @@ fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerE
1093 return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));1094 return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));
1094}1095}
10951096
1097fn zirOptionalTypeFromPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1098 const tracy = trace(@src());
1099 defer tracy.end();
1100
1101 const ptr = try resolveInst(mod, scope, inst.positionals.operand);
1102 const elem_ty = ptr.ty.elemType();
1103
1104 return mod.constType(scope, inst.base.src, try mod.optionalType(scope, elem_ty));
1105}
1106
1096fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {1107fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {
1097 const tracy = trace(@src());1108 const tracy = trace(@src());
1098 defer tracy.end();1109 defer tracy.end();
test/stage2/llvm.zig+9
...@@ -157,6 +157,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -157,6 +157,7 @@ pub fn addCases(ctx: *TestContext) !void {
157 \\ var ptr_val2 = &(null_val orelse value);157 \\ var ptr_val2 = &(null_val orelse value);
158 \\158 \\
159 \\ const val3 = opt_val orelse 30;159 \\ const val3 = opt_val orelse 30;
160 \\ var val3_var = opt_val orelse 30;
160 \\161 \\
161 \\ assert(val1 == 10);162 \\ assert(val1 == 10);
162 \\ assert(val1_1 == 10);163 \\ assert(val1_1 == 10);
...@@ -168,6 +169,14 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -168,6 +169,14 @@ pub fn addCases(ctx: *TestContext) !void {
168 \\ assert(ptr_val2.* == 20);169 \\ assert(ptr_val2.* == 20);
169 \\170 \\
170 \\ assert(val3 == 10);171 \\ assert(val3 == 10);
172 \\ assert(val3_var == 10);
173 \\
174 \\ (null_val orelse val2) = 1234;
175 \\ assert(val2 == 1234);
176 \\
177 \\ (opt_val orelse val2) = 5678;
178 \\ assert(opt_val.? == 5678);
179 \\
171 \\ return 0;180 \\ return 0;
172 \\}181 \\}
173 , "");182 , "");