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(
17091709 setBlockResultLoc(&block_scope, rl);
17101710 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.
17131716 block_scope.break_count += 1;
1714 const operand = try expr(
1715 mod,
1716 &block_scope.base,
1717 if (block_scope.break_result_loc == .ref) .ref else .none,
1718 lhs,
1719 );
1717 const operand_rl = try makeOptionalTypeResultLoc(mod, &block_scope.base, src, block_scope.break_result_loc);
1718 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
17201719 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
17211720
17221721 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
......@@ -1768,6 +1767,10 @@ fn orelseCatchExpr(
17681767
17691768 // This could be a pointer or value depending on `unwrap_op`.
17701769 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
17721775 return finishThenElseBlock(
17731776 mod,
......@@ -1781,7 +1784,7 @@ fn orelseCatchExpr(
17811784 src,
17821785 src,
17831786 then_result,
1784 unwrapped_payload,
1787 else_result,
17851788 block,
17861789 block,
17871790 );
......@@ -3970,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy {
39703973 }
39713974}
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
39733995fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void {
39743996 // Depending on whether the result location is a pointer or value, different
39753997 // 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 {
299299 xor,
300300 /// Create an optional type '?T'
301301 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,
302305 /// Create a union type.
303306 union_type,
304307 /// ?T => T with safety.
......@@ -397,6 +400,7 @@ pub const Inst = struct {
397400 .mut_slice_type,
398401 .const_slice_type,
399402 .optional_type,
403 .optional_type_from_ptr_elem,
400404 .optional_payload_safe,
401405 .optional_payload_unsafe,
402406 .optional_payload_safe_ptr,
......@@ -597,6 +601,7 @@ pub const Inst = struct {
597601 .typeof,
598602 .xor,
599603 .optional_type,
604 .optional_type_from_ptr_elem,
600605 .optional_payload_safe,
601606 .optional_payload_unsafe,
602607 .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!
131131 .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?),
132132 .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?),
133133 .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).?),
134135 .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),
135136 .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),
136137 .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
10931094 return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));
10941095}
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
10961107fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {
10971108 const tracy = trace(@src());
10981109 defer tracy.end();
test/stage2/llvm.zig+9
......@@ -157,6 +157,7 @@ pub fn addCases(ctx: *TestContext) !void {
157157 \\ var ptr_val2 = &(null_val orelse value);
158158 \\
159159 \\ const val3 = opt_val orelse 30;
160 \\ var val3_var = opt_val orelse 30;
160161 \\
161162 \\ assert(val1 == 10);
162163 \\ assert(val1_1 == 10);
......@@ -168,6 +169,14 @@ pub fn addCases(ctx: *TestContext) !void {
168169 \\ assert(ptr_val2.* == 20);
169170 \\
170171 \\ 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 \\
171180 \\ return 0;
172181 \\}
173182 , "");