aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatthew Lugg <mlugg@mlugg.co.uk>2024-09-02 00:44:11 +0100
committerGitHub <noreply@github.com>2024-09-02 00:44:11 +0100
commit6d2945f1fe387c55eff003ada6e72146daff10f2 (patch)
tree5265ef17dbd7e3c95ed22f578da42307222dddfc /src
parent227fb4875f5084cdb3436c40c4a08809bd3e4f50 (diff)
parent0b9fccf508dc85fa522947d1cf6ff84f78f2dcb4 (diff)
downloadzig-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.zig121
-rw-r--r--src/arch/riscv64/CodeGen.zig41
-rw-r--r--src/print_zir.zig4
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,