| author | |
| committer | |
| log | 713f1138222dc40355c34c70d83b0a0805bd46c6 |
| tree | a6060a67d81b1f875e0cfb947fadec9325b37a89 |
| parent | 6aa1ea9c59340a1f5b7560ecd97e45928bd4cf56 |
* 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 | 1709 | setBlockResultLoc(&block_scope, rl); |
| 1710 | 1710 | defer block_scope.instructions.deinit(mod.gpa); |
| 1711 | 1711 | |
| 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 | 1716 | 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); | |
| 1720 | 1719 | const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand); |
| 1721 | 1720 | |
| 1722 | 1721 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ |
| ... | ... | @@ -1768,6 +1767,10 @@ fn orelseCatchExpr( |
| 1768 | 1767 | |
| 1769 | 1768 | // This could be a pointer or value depending on `unwrap_op`. |
| 1770 | 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 | }; | |
| 1771 | 1774 | |
| 1772 | 1775 | return finishThenElseBlock( |
| 1773 | 1776 | mod, |
| ... | ... | @@ -1781,7 +1784,7 @@ fn orelseCatchExpr( |
| 1781 | 1784 | src, |
| 1782 | 1785 | src, |
| 1783 | 1786 | then_result, |
| 1784 | unwrapped_payload, | |
| 1787 | else_result, | |
| 1785 | 1788 | block, |
| 1786 | 1789 | block, |
| 1787 | 1790 | ); |
| ... | ... | @@ -3970,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy { |
| 3970 | 3973 | } |
| 3971 | 3974 | } |
| 3972 | 3975 | |
| 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. | |
| 3980 | fn 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 | ||
| 3973 | 3995 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { |
| 3974 | 3996 | // Depending on whether the result location is a pointer or value, different |
| 3975 | 3997 | // 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 | 299 | xor, |
| 300 | 300 | /// Create an optional type '?T' |
| 301 | 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 | 305 | /// Create a union type. |
| 303 | 306 | union_type, |
| 304 | 307 | /// ?T => T with safety. |
| ... | ... | @@ -397,6 +400,7 @@ pub const Inst = struct { |
| 397 | 400 | .mut_slice_type, |
| 398 | 401 | .const_slice_type, |
| 399 | 402 | .optional_type, |
| 403 | .optional_type_from_ptr_elem, | |
| 400 | 404 | .optional_payload_safe, |
| 401 | 405 | .optional_payload_unsafe, |
| 402 | 406 | .optional_payload_safe_ptr, |
| ... | ... | @@ -597,6 +601,7 @@ pub const Inst = struct { |
| 597 | 601 | .typeof, |
| 598 | 602 | .xor, |
| 599 | 603 | .optional_type, |
| 604 | .optional_type_from_ptr_elem, | |
| 600 | 605 | .optional_payload_safe, |
| 601 | 606 | .optional_payload_unsafe, |
| 602 | 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 | 131 | .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?), |
| 132 | 132 | .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?), |
| 133 | 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 | 135 | .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true), |
| 135 | 136 | .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false), |
| 136 | 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 | 1094 | return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type)); |
| 1094 | 1095 | } |
| 1095 | 1096 | |
| 1097 | fn 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 | ||
| 1096 | 1107 | fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { |
| 1097 | 1108 | const tracy = trace(@src()); |
| 1098 | 1109 | defer tracy.end(); |
test/stage2/llvm.zig+9| ... | ... | @@ -157,6 +157,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 157 | 157 | \\ var ptr_val2 = &(null_val orelse value); |
| 158 | 158 | \\ |
| 159 | 159 | \\ const val3 = opt_val orelse 30; |
| 160 | \\ var val3_var = opt_val orelse 30; | |
| 160 | 161 | \\ |
| 161 | 162 | \\ assert(val1 == 10); |
| 162 | 163 | \\ assert(val1_1 == 10); |
| ... | ... | @@ -168,6 +169,14 @@ pub fn addCases(ctx: *TestContext) !void { |
| 168 | 169 | \\ assert(ptr_val2.* == 20); |
| 169 | 170 | \\ |
| 170 | 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 | 180 | \\ return 0; |
| 172 | 181 | \\} |
| 173 | 182 | , ""); |