From 713f1138222dc40355c34c70d83b0a0805bd46c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 2 Mar 2021 21:59:23 -0700 Subject: [PATCH] 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. --- src/astgen.zig | 38 ++++++++++++++++++++++++++++++-------- src/zir.zig | 5 +++++ src/zir_sema.zig | 11 +++++++++++ test/stage2/llvm.zig | 9 +++++++++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/astgen.zig b/src/astgen.zig index 2c2f41c3f6..9f112531ee 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -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 diff --git a/src/zir.zig b/src/zir.zig index 7a3a1e2684..1331f26dc7 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -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, diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 1cf550852f..cfc1ea3280 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -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(); diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig index 7e672d8a20..4b00ed124c 100644 --- a/test/stage2/llvm.zig +++ b/test/stage2/llvm.zig @@ -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; \\} , "");