| 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,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); |
| 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 | 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); |
| 1721 | 1720 | ||
| 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( |
| 1768 | 1767 | ||
| 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 | }; | ||
| 1771 | 1774 | ||
| 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 | } |
| 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 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { | 3995 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { |
| 3974 | // Depending on whether the result location is a pointer or value, different | 3996 | // 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 the | 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,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 | } |
| 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 | fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { | 1107 | fn 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 | , ""); |