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.
This commit is contained in:
@@ -1709,14 +1709,13 @@ fn orelseCatchExpr(
|
||||
setBlockResultLoc(&block_scope, rl);
|
||||
defer block_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
// This could be a pointer or value depending on the `rl` parameter.
|
||||
// This could be a pointer or value depending on the `operand_rl` parameter.
|
||||
// We cannot use `block_scope.break_result_loc` because that has the bare
|
||||
// type, whereas this expression has the optional type. Later we make
|
||||
// up for this fact by calling rvalue on the else branch.
|
||||
block_scope.break_count += 1;
|
||||
const operand = try expr(
|
||||
mod,
|
||||
&block_scope.base,
|
||||
if (block_scope.break_result_loc == .ref) .ref else .none,
|
||||
lhs,
|
||||
);
|
||||
const operand_rl = try makeOptionalTypeResultLoc(mod, &block_scope.base, src, block_scope.break_result_loc);
|
||||
const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
|
||||
const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
|
||||
|
||||
const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
|
||||
@@ -1768,6 +1767,10 @@ fn orelseCatchExpr(
|
||||
|
||||
// This could be a pointer or value depending on `unwrap_op`.
|
||||
const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);
|
||||
const else_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload),
|
||||
};
|
||||
|
||||
return finishThenElseBlock(
|
||||
mod,
|
||||
@@ -1781,7 +1784,7 @@ fn orelseCatchExpr(
|
||||
src,
|
||||
src,
|
||||
then_result,
|
||||
unwrapped_payload,
|
||||
else_result,
|
||||
block,
|
||||
block,
|
||||
);
|
||||
@@ -3970,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy {
|
||||
}
|
||||
}
|
||||
|
||||
/// If the input ResultLoc is ref, returns ResultLoc.ref. Otherwise:
|
||||
/// Returns ResultLoc.ty, where the type is determined by the input
|
||||
/// ResultLoc type, wrapped in an optional type. If the input ResultLoc
|
||||
/// has no type, .none is returned.
|
||||
fn makeOptionalTypeResultLoc(mod: *Module, scope: *Scope, src: usize, rl: ResultLoc) !ResultLoc {
|
||||
switch (rl) {
|
||||
.ref => return ResultLoc.ref,
|
||||
.discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => return ResultLoc.none,
|
||||
.ty => |elem_ty| {
|
||||
const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type, elem_ty);
|
||||
return ResultLoc{ .ty = wrapped_ty };
|
||||
},
|
||||
.ptr => |ptr_ty| {
|
||||
const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type_from_ptr_elem, ptr_ty);
|
||||
return ResultLoc{ .ty = wrapped_ty };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void {
|
||||
// Depending on whether the result location is a pointer or value, different
|
||||
// ZIR needs to be generated. In the former case we rely on storing to the
|
||||
|
||||
@@ -299,6 +299,9 @@ pub const Inst = struct {
|
||||
xor,
|
||||
/// Create an optional type '?T'
|
||||
optional_type,
|
||||
/// Create an optional type '?T'. The operand is a pointer value. The optional type will
|
||||
/// be the type of the pointer element, wrapped in an optional.
|
||||
optional_type_from_ptr_elem,
|
||||
/// Create a union type.
|
||||
union_type,
|
||||
/// ?T => T with safety.
|
||||
@@ -397,6 +400,7 @@ pub const Inst = struct {
|
||||
.mut_slice_type,
|
||||
.const_slice_type,
|
||||
.optional_type,
|
||||
.optional_type_from_ptr_elem,
|
||||
.optional_payload_safe,
|
||||
.optional_payload_unsafe,
|
||||
.optional_payload_safe_ptr,
|
||||
@@ -597,6 +601,7 @@ pub const Inst = struct {
|
||||
.typeof,
|
||||
.xor,
|
||||
.optional_type,
|
||||
.optional_type_from_ptr_elem,
|
||||
.optional_payload_safe,
|
||||
.optional_payload_unsafe,
|
||||
.optional_payload_safe_ptr,
|
||||
|
||||
@@ -131,6 +131,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
|
||||
.typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?),
|
||||
.typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?),
|
||||
.optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
|
||||
.optional_type_from_ptr_elem => return zirOptionalTypeFromPtrElem(mod, scope, old_inst.castTag(.optional_type_from_ptr_elem).?),
|
||||
.optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),
|
||||
.optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),
|
||||
.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
|
||||
return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));
|
||||
}
|
||||
|
||||
fn zirOptionalTypeFromPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const ptr = try resolveInst(mod, scope, inst.positionals.operand);
|
||||
const elem_ty = ptr.ty.elemType();
|
||||
|
||||
return mod.constType(scope, inst.base.src, try mod.optionalType(scope, elem_ty));
|
||||
}
|
||||
|
||||
fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@@ -157,6 +157,7 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\ var ptr_val2 = &(null_val orelse value);
|
||||
\\
|
||||
\\ const val3 = opt_val orelse 30;
|
||||
\\ var val3_var = opt_val orelse 30;
|
||||
\\
|
||||
\\ assert(val1 == 10);
|
||||
\\ assert(val1_1 == 10);
|
||||
@@ -168,6 +169,14 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\ assert(ptr_val2.* == 20);
|
||||
\\
|
||||
\\ assert(val3 == 10);
|
||||
\\ assert(val3_var == 10);
|
||||
\\
|
||||
\\ (null_val orelse val2) = 1234;
|
||||
\\ assert(val2 == 1234);
|
||||
\\
|
||||
\\ (opt_val orelse val2) = 5678;
|
||||
\\ assert(opt_val.? == 5678);
|
||||
\\
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
|
||||
Reference in New Issue
Block a user