mirror of
https://github.com/ziglang/zig.git
synced 2026-01-21 06:45:24 +00:00
Merge pull request #10925 from Vexu/stage2
stage2: support anon init through error unions and optionals
This commit is contained in:
commit
74303a3d95
@ -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,
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
|
||||
178
src/Sema.zig
178
src/Sema.zig
@ -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});
|
||||
|
||||
26
src/Zir.zig
26
src/Zir.zig
@ -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,
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user