Merge pull request #10925 from Vexu/stage2

stage2: support anon init through error unions and optionals
This commit is contained in:
Andrew Kelley 2022-02-21 14:18:17 -05:00 committed by GitHub
commit 74303a3d95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 374 additions and 32 deletions

View File

@ -429,6 +429,9 @@ pub const Inst = struct {
/// *(E!T) -> E. If the value is not an error, undefined behavior.
/// Uses the `ty_op` field.
unwrap_errunion_err_ptr,
/// *(E!T) => *T. Sets the value to non-error with an undefined payload value.
/// Uses the `ty_op` field.
errunion_payload_ptr_set,
/// wrap from T to E!T
/// Uses the `ty_op` field.
wrap_errunion_payload,
@ -865,6 +868,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.optional_payload,
.optional_payload_ptr,
.optional_payload_ptr_set,
.errunion_payload_ptr_set,
.wrap_optional,
.unwrap_errunion_payload,
.unwrap_errunion_err,

View File

@ -1297,6 +1297,7 @@ fn arrayInitExpr(
}
}
const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
_ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, node);
const elem_type = try gz.addUnNode(.elem_type, array_type_inst, array_init.ast.type_expr);
break :inst .{
.array = array_type_inst,
@ -1399,7 +1400,8 @@ fn arrayInitExprRlPtr(
array_ty: Zir.Inst.Ref,
) InnerError!Zir.Inst.Ref {
if (array_ty == .none) {
return arrayInitExprRlPtrInner(gz, scope, node, result_ptr, elements);
const base_ptr = try gz.addUnNode(.array_base_ptr, result_ptr, node);
return arrayInitExprRlPtrInner(gz, scope, node, base_ptr, elements);
}
var as_scope = try gz.makeCoercionScope(scope, array_ty, result_ptr);
@ -1508,8 +1510,11 @@ fn structInitExpr(
switch (rl) {
.discard => {
if (struct_init.ast.type_expr != 0)
_ = try typeExpr(gz, scope, struct_init.ast.type_expr);
// TODO if a type expr is given the fields should be validated for that type
if (struct_init.ast.type_expr != 0) {
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
_ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
}
for (struct_init.ast.fields) |field_init| {
_ = try expr(gz, scope, .discard, field_init);
}
@ -1518,6 +1523,7 @@ fn structInitExpr(
.ref => {
if (struct_init.ast.type_expr != 0) {
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
_ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init_ref);
} else {
return structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon_ref);
@ -1526,6 +1532,7 @@ fn structInitExpr(
.none => {
if (struct_init.ast.type_expr != 0) {
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
_ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init);
} else {
return structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon);
@ -1536,6 +1543,7 @@ fn structInitExpr(
return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init);
}
const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
_ = try gz.addUnNode(.validate_struct_init_ty, inner_ty_inst, node);
const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init);
return rvalue(gz, rl, result, node);
},
@ -1582,9 +1590,11 @@ fn structInitExprRlPtr(
result_ptr: Zir.Inst.Ref,
) InnerError!Zir.Inst.Ref {
if (struct_init.ast.type_expr == 0) {
return structInitExprRlPtrInner(gz, scope, node, struct_init, result_ptr);
const base_ptr = try gz.addUnNode(.field_base_ptr, result_ptr, node);
return structInitExprRlPtrInner(gz, scope, node, struct_init, base_ptr);
}
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
_ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr);
defer as_scope.unstack();
@ -2298,6 +2308,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.ret_err_value_code,
.extended,
.closure_get,
.array_base_ptr,
.field_base_ptr,
=> break :b false,
// ZIR instructions that are always `noreturn`.
@ -2346,6 +2358,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.closure_capture,
.memcpy,
.memset,
.validate_array_init_ty,
.validate_struct_init_ty,
=> break :b true,
}
} else switch (maybe_unused_result) {

View File

@ -293,6 +293,7 @@ fn analyzeInst(
.optional_payload,
.optional_payload_ptr,
.optional_payload_ptr_set,
.errunion_payload_ptr_set,
.wrap_optional,
.unwrap_errunion_payload,
.unwrap_errunion_err,

View File

@ -739,6 +739,8 @@ fn analyzeBodyInner(
.@"await" => try sema.zirAwait(block, inst, false),
.await_nosuspend => try sema.zirAwait(block, inst, true),
.extended => try sema.zirExtended(block, inst),
.array_base_ptr => try sema.zirArrayBasePtr(block, inst),
.field_base_ptr => try sema.zirFieldBasePtr(block, inst),
.clz => try sema.zirBitCount(block, inst, .clz, Value.clz),
.ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),
@ -870,6 +872,16 @@ fn analyzeBodyInner(
i += 1;
continue;
},
.validate_array_init_ty => {
try sema.validateArrayInitTy(block, inst);
i += 1;
continue;
},
.validate_struct_init_ty => {
try sema.validateStructInitTy(block, inst);
i += 1;
continue;
},
.validate_struct_init => {
try sema.zirValidateStructInit(block, inst, false);
i += 1;
@ -1346,6 +1358,14 @@ fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, opt
return sema.fail(block, src, "expected optional type, found {}", .{optional_ty});
}
fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
return sema.fail(block, src, "type '{}' does not support array initialization syntax", .{ty});
}
fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{ty});
}
fn failWithErrorSetCodeMissing(
sema: *Sema,
block: *Block,
@ -1641,7 +1661,13 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_err", .{});
},
.wrap_errunion_payload => {
return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_payload", .{});
const ty_op = air_datas[trash_inst].ty_op;
const payload_ty = sema.getTmpAir().typeOf(ty_op.operand);
const ptr_payload_ty = try Type.ptr(sema.arena, .{
.pointee_type = payload_ty,
.@"addrspace" = addr_space,
});
new_ptr = try block.addTyOp(.errunion_payload_ptr_set, ptr_payload_ty, new_ptr);
},
else => {
if (std.debug.runtime_safety) {
@ -2591,6 +2617,88 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
}
}
fn zirArrayBasePtr(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const start_ptr = sema.resolveInst(inst_data.operand);
var base_ptr = start_ptr;
while (true) switch (sema.typeOf(base_ptr).childType().zigTypeTag()) {
.ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
.Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
else => break,
};
const elem_ty = sema.typeOf(base_ptr).childType();
switch (elem_ty.zigTypeTag()) {
.Array, .Vector => return base_ptr,
.Struct => if (elem_ty.isTuple()) return base_ptr,
else => {},
}
return sema.failWithArrayInitNotSupported(block, src, sema.typeOf(start_ptr).childType());
}
fn zirFieldBasePtr(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const start_ptr = sema.resolveInst(inst_data.operand);
var base_ptr = start_ptr;
while (true) switch (sema.typeOf(base_ptr).childType().zigTypeTag()) {
.ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
.Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
else => break,
};
const elem_ty = sema.typeOf(base_ptr).childType();
switch (elem_ty.zigTypeTag()) {
.Struct, .Union => return base_ptr,
else => {},
}
return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType());
}
fn validateArrayInitTy(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const ty = try sema.resolveType(block, src, inst_data.operand);
switch (ty.zigTypeTag()) {
.Array, .Vector => return,
.Struct => if (ty.isTuple()) return,
else => {},
}
return sema.failWithArrayInitNotSupported(block, src, ty);
}
fn validateStructInitTy(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const ty = try sema.resolveType(block, src, inst_data.operand);
switch (ty.zigTypeTag()) {
.Struct, .Union => return,
else => {},
}
return sema.failWithStructInitNotSupported(block, src, ty);
}
fn zirValidateStructInit(
sema: *Sema,
block: *Block,
@ -4396,7 +4504,9 @@ fn analyzeCall(
if (payload.data.error_set.tag() == .error_set_inferred) {
const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
node.data = .{ .func = module_fn };
parent_func.?.inferred_error_sets.prepend(node);
if (parent_func) |some| {
some.inferred_error_sets.prepend(node);
}
const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
break :blk try Type.Tag.error_union.create(sema.arena, .{
@ -5217,9 +5327,21 @@ fn zirOptionalPayloadPtr(
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const optional_ptr = sema.resolveInst(inst_data.operand);
const src = inst_data.src();
return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false);
}
fn analyzeOptionalPayloadPtr(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
optional_ptr: Air.Inst.Ref,
safety_check: bool,
initializing: bool,
) CompileError!Air.Inst.Ref {
const optional_ptr_ty = sema.typeOf(optional_ptr);
assert(optional_ptr_ty.zigTypeTag() == .Pointer);
const src = inst_data.src();
const opt_type = optional_ptr_ty.elemType();
if (opt_type.zigTypeTag() != .Optional) {
@ -5234,6 +5356,12 @@ fn zirOptionalPayloadPtr(
});
if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
if (initializing) {
return sema.addConstant(
child_pointer,
try Value.Tag.opt_payload_ptr.create(sema.arena, pointer_val),
);
}
if (try sema.pointerDeref(block, src, pointer_val, optional_ptr_ty)) |val| {
if (val.isNull()) {
return sema.fail(block, src, "unable to unwrap null", .{});
@ -5251,7 +5379,10 @@ fn zirOptionalPayloadPtr(
const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
}
return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr);
return block.addTyOp(if (initializing)
.optional_payload_ptr_set
else
.optional_payload_ptr, child_pointer, optional_ptr);
}
/// Value in, value out.
@ -5352,8 +5483,20 @@ fn zirErrUnionPayloadPtr(
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand = sema.resolveInst(inst_data.operand);
const src = inst_data.src();
return sema.analyzeErrUnionPayloadPtr(block, src, operand, safety_check, false);
}
fn analyzeErrUnionPayloadPtr(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
operand: Air.Inst.Ref,
safety_check: bool,
initializing: bool,
) CompileError!Air.Inst.Ref {
const operand_ty = sema.typeOf(operand);
assert(operand_ty.zigTypeTag() == .Pointer);
@ -5368,10 +5511,17 @@ fn zirErrUnionPayloadPtr(
});
if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
if (initializing) {
return sema.addConstant(
operand_pointer_ty,
try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
);
}
if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
if (val.getError()) |name| {
return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
}
return sema.addConstant(
operand_pointer_ty,
try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
@ -5384,7 +5534,10 @@ fn zirErrUnionPayloadPtr(
const is_non_err = try block.addUnOp(.is_err, operand);
try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
}
return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand);
return block.addTyOp(if (initializing)
.errunion_payload_ptr_set
else
.unwrap_errunion_payload_ptr, operand_pointer_ty, operand);
}
/// Value in, value out
@ -10778,7 +10931,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
.Struct => return structInitEmpty(sema, block, obj_ty, src, src),
.Array => return arrayInitEmpty(sema, obj_ty),
.Void => return sema.addConstant(obj_ty, Value.void),
else => unreachable,
else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
}
}
@ -12920,7 +13073,7 @@ fn zirCUndef(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src: LazySrcLoc = .{ .node_offset = extra.node };
const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const name = try sema.resolveConstString(block, src, extra.operand);
try block.c_import_buf.?.writer().print("#undefine {s}\n", .{name});
@ -12933,7 +13086,7 @@ fn zirCInclude(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src: LazySrcLoc = .{ .node_offset = extra.node };
const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const name = try sema.resolveConstString(block, src, extra.operand);
try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name});
@ -12946,12 +13099,13 @@ fn zirCDefine(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
const src: LazySrcLoc = .{ .node_offset = extra.node };
const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
const name = try sema.resolveConstString(block, src, extra.lhs);
const name = try sema.resolveConstString(block, name_src, extra.lhs);
const rhs = sema.resolveInst(extra.rhs);
if (sema.typeOf(rhs).zigTypeTag() != .Void) {
const value = try sema.resolveConstString(block, src, extra.rhs);
const value = try sema.resolveConstString(block, val_src, extra.rhs);
try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value });
} else {
try block.c_import_buf.?.writer().print("#define {s}\n", .{name});

View File

@ -643,6 +643,24 @@ pub const Inst = struct {
/// Result is a pointer to the value.
/// Uses the `switch_capture` field.
switch_capture_multi_ref,
/// Given a
/// *A returns *A
/// *E!A returns *A
/// *?A returns *A
/// Uses the `un_node` field.
array_base_ptr,
/// Given a
/// *S returns *S
/// *E!S returns *S
/// *?S returns *S
/// Uses the `un_node` field.
field_base_ptr,
/// Checks that the type supports array init syntax.
/// Uses the `un_node` field.
validate_array_init_ty,
/// Checks that the type supports struct init syntax.
/// Uses the `un_node` field.
validate_struct_init_ty,
/// Given a set of `field_ptr` instructions, assumes they are all part of a struct
/// initialization expression, and emits compile errors for duplicate fields
/// as well as missing fields, if applicable.
@ -1087,6 +1105,10 @@ pub const Inst = struct {
.switch_block,
.switch_cond,
.switch_cond_ref,
.array_base_ptr,
.field_base_ptr,
.validate_array_init_ty,
.validate_struct_init_ty,
.validate_struct_init,
.validate_struct_init_comptime,
.validate_array_init,
@ -1340,6 +1362,10 @@ pub const Inst = struct {
.switch_capture_ref = .switch_capture,
.switch_capture_multi = .switch_capture,
.switch_capture_multi_ref = .switch_capture,
.array_base_ptr = .un_node,
.field_base_ptr = .un_node,
.validate_array_init_ty = .un_node,
.validate_struct_init_ty = .un_node,
.validate_struct_init = .pl_node,
.validate_struct_init_comptime = .pl_node,
.validate_array_init = .pl_node,

View File

@ -662,6 +662,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
.unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
.unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
.wrap_optional => try self.airWrapOptional(inst),
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@ -1443,6 +1444,12 @@ fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {

View File

@ -646,6 +646,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
.unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
.unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
.wrap_optional => try self.airWrapOptional(inst),
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@ -1154,6 +1155,12 @@ fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {

View File

@ -633,6 +633,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
.unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
.unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
.wrap_optional => try self.airWrapOptional(inst),
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@ -1065,6 +1066,12 @@ fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {

View File

@ -1725,6 +1725,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.atomic_rmw,
.tag_name,
.error_name,
.errunion_payload_ptr_set,
// For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248
// is implemented in the frontend before implementing them here in the wasm backend.

View File

@ -727,6 +727,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
.unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
.unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
.wrap_optional => try self.airWrapOptional(inst),
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@ -1620,6 +1621,15 @@ fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst))
.dead
else
return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {

View File

@ -1753,6 +1753,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst),
.wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
.wrap_errunion_err => try airWrapErrUnionErr(f, inst),
.errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst),
// zig fmt: on
};
switch (result_value) {
@ -3090,17 +3091,18 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
const operand = try f.resolveInst(ty_op.operand);
const operand_ty = f.air.typeOf(ty_op.operand);
const payload_ty = operand_ty.errorUnionPayload();
if (!payload_ty.hasRuntimeBits()) {
if (operand_ty.zigTypeTag() == .Pointer) {
const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = *");
try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
return local;
} else {
if (operand_ty.zigTypeTag() == .Pointer) {
if (!operand_ty.childType().errorUnionPayload().hasRuntimeBits()) {
return operand;
}
const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = *");
try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
return local;
}
if (!operand_ty.errorUnionPayload().hasRuntimeBits()) {
return operand;
}
const local = try f.allocLocal(inst_ty, .Const);
@ -3123,8 +3125,11 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: []cons
const operand = try f.resolveInst(ty_op.operand);
const operand_ty = f.air.typeOf(ty_op.operand);
const payload_ty = operand_ty.errorUnionPayload();
if (!payload_ty.hasRuntimeBits()) {
const error_union_ty = if (operand_ty.zigTypeTag() == .Pointer)
operand_ty.childType()
else
operand_ty;
if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) {
return CValue.none;
}
@ -3160,6 +3165,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll("};\n");
return local;
}
fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
@ -3179,6 +3185,11 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
return local;
}
fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
_ = inst;
return f.fail("TODO: C backend: implement airErrUnionPayloadPtrSet", .{});
}
fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst))
return CValue.none;

View File

@ -2248,6 +2248,7 @@ pub const FuncGen = struct {
.unwrap_errunion_payload_ptr => try self.airErrUnionPayload(inst, true),
.unwrap_errunion_err => try self.airErrUnionErr(inst, false),
.unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true),
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
.wrap_optional => try self.airWrapOptional(inst),
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@ -3175,8 +3176,9 @@ pub const FuncGen = struct {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const err_union_ty = self.air.typeOf(ty_op.operand);
const payload_ty = err_union_ty.errorUnionPayload();
const result_ty = self.air.getRefType(ty_op.ty);
const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty;
if (!payload_ty.hasRuntimeBits()) return null;
if (operand_is_ptr or isByRef(payload_ty)) {
return self.builder.buildStructGEP(operand, 1, "");
@ -3195,14 +3197,15 @@ pub const FuncGen = struct {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const operand_ty = self.air.typeOf(ty_op.operand);
const err_set_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
const payload_ty = operand_ty.errorUnionPayload();
const payload_ty = err_set_ty.errorUnionPayload();
if (!payload_ty.hasRuntimeBits()) {
if (!operand_is_ptr) return operand;
return self.builder.buildLoad(operand, "");
}
if (operand_is_ptr or isByRef(payload_ty)) {
if (operand_is_ptr or isByRef(err_set_ty)) {
const err_field_ptr = self.builder.buildStructGEP(operand, 0, "");
return self.builder.buildLoad(err_field_ptr, "");
}
@ -3210,6 +3213,37 @@ pub const FuncGen = struct {
return self.builder.buildExtractValue(operand, 0, "");
}
fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
const error_set_ty = self.air.typeOf(ty_op.operand).childType();
const error_ty = error_set_ty.errorUnionSet();
const payload_ty = error_set_ty.errorUnionPayload();
const non_error_val = try self.dg.genTypedValue(.{ .ty = error_ty, .val = Value.zero });
if (!payload_ty.hasRuntimeBits()) {
// We have a pointer to a i1. We need to set it to 1 and then return the same pointer.
_ = self.builder.buildStore(non_error_val, operand);
return operand;
}
const index_type = self.context.intType(32);
{
// First set the non-error value.
const indices: [2]*const llvm.Value = .{
index_type.constNull(), // dereference the pointer
index_type.constNull(), // first field is the payload
};
const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
_ = self.builder.buildStore(non_error_val, non_null_ptr);
}
// Then return the payload pointer.
const indices: [2]*const llvm.Value = .{
index_type.constNull(), // dereference the pointer
index_type.constInt(1, .False), // second field is the payload
};
return self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
}
fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) return null;

View File

@ -189,6 +189,7 @@ const Writer = struct {
.optional_payload,
.optional_payload_ptr,
.optional_payload_ptr_set,
.errunion_payload_ptr_set,
.wrap_optional,
.unwrap_errunion_payload,
.unwrap_errunion_err,

View File

@ -235,6 +235,10 @@ const Writer = struct {
.fence,
.switch_cond,
.switch_cond_ref,
.array_base_ptr,
.field_base_ptr,
.validate_array_init_ty,
.validate_struct_init_ty,
=> try self.writeUnNode(stream, inst),
.ref,

View File

@ -119,6 +119,7 @@ test {
_ = @import("behavior/sizeof_and_typeof.zig");
_ = @import("behavior/switch.zig");
_ = @import("behavior/widening.zig");
_ = @import("behavior/bugs/1442.zig");
if (builtin.zig_backend == .stage1) {
// Tests that only pass for the stage1 backend.
@ -135,7 +136,6 @@ test {
_ = @import("behavior/bugs/920.zig");
_ = @import("behavior/bugs/1120.zig");
_ = @import("behavior/bugs/1421.zig");
_ = @import("behavior/bugs/1442.zig");
_ = @import("behavior/bugs/1607.zig");
_ = @import("behavior/bugs/1851.zig");
_ = @import("behavior/bugs/2114.zig");

View File

@ -1199,3 +1199,64 @@ test "for loop over pointers to struct, getting field from struct pointer" {
};
try S.doTheTest();
}
test "anon init through error unions and optionals" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; // TODO
const S = struct {
a: u32,
fn foo() anyerror!?anyerror!@This() {
return .{ .a = 1 };
}
fn bar() ?anyerror![2]u8 {
return .{ 1, 2 };
}
fn doTheTest() !void {
var a = try (try foo()).?;
var b = try bar().?;
try expect(a.a + b[1] == 3);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "anon init through optional" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; // TODO
const S = struct {
a: u32,
fn doTheTest() !void {
var s: ?@This() = null;
s = .{ .a = 1 };
try expect(s.?.a == 1);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "anon init through error union" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; // TODO
const S = struct {
a: u32,
fn doTheTest() !void {
var s: anyerror!@This() = error.Foo;
s = .{ .a = 1 };
try expect((try s).a == 1);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}