diff options
| author | Matthew Lugg <mlugg@mlugg.co.uk> | 2024-09-02 00:44:11 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-09-02 00:44:11 +0100 |
| commit | 6d2945f1fe387c55eff003ada6e72146daff10f2 (patch) | |
| tree | 5265ef17dbd7e3c95ed22f578da42307222dddfc /src | |
| parent | 227fb4875f5084cdb3436c40c4a08809bd3e4f50 (diff) | |
| parent | 0b9fccf508dc85fa522947d1cf6ff84f78f2dcb4 (diff) | |
| download | zig-6d2945f1fe387c55eff003ada6e72146daff10f2.tar.gz zig-6d2945f1fe387c55eff003ada6e72146daff10f2.zip | |
Merge pull request #21264 from mlugg/decl-literals
compiler: implement decl literals
Diffstat (limited to 'src')
| -rw-r--r-- | src/Sema.zig | 121 | ||||
| -rw-r--r-- | src/arch/riscv64/CodeGen.zig | 41 | ||||
| -rw-r--r-- | src/print_zir.zig | 4 |
3 files changed, 165 insertions, 1 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index a72c749f2e..57a4809f95 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1072,6 +1072,8 @@ fn analyzeBodyInner( .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), .vector_elem_type => try sema.zirVectorElemType(block, inst), .enum_literal => try sema.zirEnumLiteral(block, inst), + .decl_literal => try sema.zirDeclLiteral(block, inst, true), + .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false), .int_from_enum => try sema.zirIntFromEnum(block, inst), .enum_from_int => try sema.zirEnumFromInt(block, inst), .err_union_code => try sema.zirErrUnionCode(block, inst), @@ -1177,6 +1179,8 @@ fn analyzeBodyInner( .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst), + .try_operand_ty => try sema.zirTryOperandTy(block, inst, false), + .try_ref_operand_ty => try sema.zirTryOperandTy(block, inst, true), .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), @@ -2024,6 +2028,22 @@ fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoi const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; cur = un_node.operand; }, + .try_operand_ty => { + // Either the input type was itself poison, or it was a slice, which we cannot translate + // to an overall result type. + const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; + const operand_ref = sema.resolveInst(un_node.operand) catch |err| switch (err) { + error.GenericPoison => unreachable, // this is a type, not a value + }; + if (operand_ref == .generic_poison_type) { + // The input was poison -- keep looking. + cur = un_node.operand; + continue; + } + // We got a poison because the result type was a slice. This is a tricky case -- let's just + // not bother explaining it to the user for now... + return .unknown; + }, .struct_init_field_type => { const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data; @@ -4423,6 +4443,59 @@ fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE } } +fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; + const src = block.nodeOffset(un_node.src_node); + + const operand_ty = sema.resolveType(block, src, un_node.operand) catch |err| switch (err) { + error.GenericPoison => return .generic_poison_type, + else => |e| return e, + }; + + const payload_ty = if (is_ref) ty: { + if (!operand_ty.isSinglePointer(zcu)) { + return .generic_poison_type; // we can't get a meaningful result type here, since it will be `*E![n]T`, and we don't know `n`. + } + break :ty operand_ty.childType(zcu); + } else operand_ty; + + const err_set_ty = err_set: { + // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals + // until we hit an error union or set. + var cur_ty = sema.fn_ret_ty; + while (true) { + switch (cur_ty.zigTypeTag(zcu)) { + .error_set => break :err_set cur_ty, + .error_union => break :err_set cur_ty.errorUnionSet(zcu), + .optional => cur_ty = cur_ty.optionalChild(zcu), + else => return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "expected '{}', found error set", .{sema.fn_ret_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + const ret_ty_src: LazySrcLoc = .{ + .base_node_inst = sema.getOwnerFuncDeclInst(), + .offset = .{ .node_offset_fn_type_ret_ty = 0 }, + }; + try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{}); + break :msg msg; + }), + } + } + }; + + const eu_ty = try pt.errorUnionType(err_set_ty, payload_ty); + + if (is_ref) { + var ptr_info = operand_ty.ptrInfo(zcu); + ptr_info.child = eu_ty.toIntern(); + const eu_ptr_ty = try pt.ptrTypeSema(ptr_info); + return Air.internedToRef(eu_ptr_ty.toIntern()); + } else { + return Air.internedToRef(eu_ty.toIntern()); + } +} + fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; @@ -8803,6 +8876,54 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }))); } +fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: bool) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + + const pt = sema.pt; + const zcu = pt.zcu; + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; + const src = block.nodeOffset(inst_data.src_node); + const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; + const name = try zcu.intern_pool.getOrPutString( + sema.gpa, + pt.tid, + sema.code.nullTerminatedString(extra.field_name_start), + .no_embedded_nulls, + ); + const orig_ty = sema.resolveType(block, src, extra.lhs) catch |err| switch (err) { + error.GenericPoison => { + // Treat this as a normal enum literal. + return Air.internedToRef(try pt.intern(.{ .enum_literal = name })); + }, + else => |e| return e, + }; + + var ty = orig_ty; + while (true) switch (ty.zigTypeTag(zcu)) { + .error_union => ty = ty.errorUnionPayload(zcu), + .optional => ty = ty.optionalChild(zcu), + .enum_literal, .error_set => { + // Treat this as a normal enum literal. + return Air.internedToRef(try pt.intern(.{ .enum_literal = name })); + }, + else => break, + }; + + const result = try sema.fieldVal(block, src, Air.internedToRef(ty.toIntern()), name, src); + + // Decl literals cannot lookup runtime `var`s. + if (!try sema.isComptimeKnown(result)) { + return sema.fail(block, src, "decl literal must be comptime-known", .{}); + } + + if (do_coerce) { + return sema.coerce(block, orig_ty, result, src); + } else { + return result; + } +} + fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index a70618a394..1934cee7f5 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -3471,9 +3471,48 @@ fn airUnwrapErrPayloadPtr(func: *Func, inst: Air.Inst.Index) !void { return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +// *(E!T) => *T fn airErrUnionPayloadPtrSet(func: *Func, inst: Air.Inst.Index) !void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement .errunion_payload_ptr_set for {}", .{func.target.cpu.arch}); + const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { + const zcu = func.pt.zcu; + const src_ty = func.typeOf(ty_op.operand); + const src_mcv = try func.resolveInst(ty_op.operand); + + // `src_reg` contains the pointer to the error union + const src_reg = switch (src_mcv) { + .register => |reg| reg, + else => try func.copyToTmpRegister(src_ty, src_mcv), + }; + const src_lock = func.register_manager.lockRegAssumeUnused(src_reg); + defer func.register_manager.unlockReg(src_lock); + + // we set the place of where the error would have been to 0 + const eu_ty = src_ty.childType(zcu); + const pl_ty = eu_ty.errorUnionPayload(zcu); + const err_ty = eu_ty.errorUnionSet(zcu); + const err_off: i32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); + try func.genSetMem(.{ .reg = src_reg }, err_off, err_ty, .{ .immediate = 0 }); + + const dst_reg, const dst_lock = if (func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) + .{ src_reg, null } + else + try func.allocReg(.int); + defer if (dst_lock) |lock| func.register_manager.unlockReg(lock); + + // move the pointer to be at the payload + const pl_off = errUnionPayloadOffset(pl_ty, zcu); + try func.genBinOp( + .add, + .{ .register = src_reg }, + Type.u64, + .{ .immediate = pl_off }, + Type.u64, + dst_reg, + ); + + break :result .{ .register = dst_reg }; + }; return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } diff --git a/src/print_zir.zig b/src/print_zir.zig index 8d70af5f3c..7910da080c 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -277,6 +277,8 @@ const Writer = struct { .opt_eu_base_ptr_init, .restore_err_ret_index_unconditional, .restore_err_ret_index_fn_entry, + .try_operand_ty, + .try_ref_operand_ty, => try self.writeUnNode(stream, inst), .ref, @@ -460,6 +462,8 @@ const Writer = struct { .field_val, .field_ptr, + .decl_literal, + .decl_literal_no_coerce, => try self.writePlNodeField(stream, inst), .field_ptr_named, |
