From e02749224357a33cffcaf9970d14c199adc3d892 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 19 Feb 2022 11:35:49 +0200 Subject: [PATCH 1/6] stage2: support anon init through error unions and optionals --- src/AstGen.zig | 9 +++- src/Sema.zig | 109 ++++++++++++++++++++++++++++++++++----- src/Zir.zig | 16 ++++++ src/print_zir.zig | 2 + test/behavior/struct.zig | 61 ++++++++++++++++++++++ 5 files changed, 183 insertions(+), 14 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 0652a6232fe1..8f34a58ed461 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1384,7 +1384,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); @@ -1493,6 +1494,7 @@ fn structInitExpr( switch (rl) { .discard => { + // TODO if a type expr is given the fields should be validated for that type if (struct_init.ast.type_expr != 0) _ = try typeExpr(gz, scope, struct_init.ast.type_expr); for (struct_init.ast.fields) |field_init| { @@ -1567,7 +1569,8 @@ 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); @@ -2281,6 +2284,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`. diff --git a/src/Sema.zig b/src/Sema.zig index 38cfb63f27c2..acccc0e3bff2 100644 --- a/src/Sema.zig +++ b/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), @@ -2584,6 +2586,59 @@ 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), + .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false), + 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.fail(block, src, "type '{}' does not support array initialization syntax", .{ + 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), + .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false), + else => break, + }; + + const elem_ty = sema.typeOf(base_ptr).childType(); + switch (elem_ty.zigTypeTag()) { + .Struct, .Union => return base_ptr, + else => {}, + } + return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{ + sema.typeOf(start_ptr).childType(), + }); +} + fn zirValidateStructInit( sema: *Sema, block: *Block, @@ -4377,7 +4432,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, .{ @@ -5198,9 +5255,20 @@ 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); +} + +fn analyzeOptionalPayloadPtr( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + optional_ptr: Air.Inst.Ref, + safety_check: 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) { @@ -5216,8 +5284,10 @@ fn zirOptionalPayloadPtr( if (try sema.resolveDefinedValue(block, src, optional_ptr)) |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", .{}); + if (safety_check) { + if (val.isNull()) { + return sema.fail(block, src, "unable to unwrap null", .{}); + } } // The same Value represents the pointer to the optional and the payload. return sema.addConstant( @@ -5333,8 +5403,19 @@ 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); +} + +fn analyzeErrUnionPayloadPtr( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + operand: Air.Inst.Ref, + safety_check: bool, +) CompileError!Air.Inst.Ref { const operand_ty = sema.typeOf(operand); assert(operand_ty.zigTypeTag() == .Pointer); @@ -5350,9 +5431,12 @@ fn zirErrUnionPayloadPtr( if (try sema.resolveDefinedValue(block, src, operand)) |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}); + if (safety_check) { + 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), @@ -12859,7 +12943,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}); @@ -12872,7 +12956,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}); @@ -12885,12 +12969,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}); diff --git a/src/Zir.zig b/src/Zir.zig index b8ff7ae50fa7..339a141c159c 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -643,6 +643,18 @@ 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, /// 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 +1099,8 @@ pub const Inst = struct { .switch_block, .switch_cond, .switch_cond_ref, + .array_base_ptr, + .field_base_ptr, .validate_struct_init, .validate_struct_init_comptime, .validate_array_init, @@ -1340,6 +1354,8 @@ 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_struct_init = .pl_node, .validate_struct_init_comptime = .pl_node, .validate_array_init = .pl_node, diff --git a/src/print_zir.zig b/src/print_zir.zig index 6396f114672b..5349d515aa57 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -235,6 +235,8 @@ const Writer = struct { .fence, .switch_cond, .switch_cond_ref, + .array_base_ptr, + .field_base_ptr, => try self.writeUnNode(stream, inst), .ref, diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 3e92b4374bb3..7428aed62ad8 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.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 (true) return error.SkipZigTest; // TODO + + const S = struct { + a: u32, + + fn foo() anyerror!?anyerror!@This() { + return @This(){ .a = 1 }; + } + fn bar() ?anyerror![2]u8 { + return [2]u8{ 1, 2 }; + } + + fn doTheTest() !void { + var a = ((foo() catch unreachable).?) catch unreachable; + var b = (bar().?) catch unreachable; + 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; + // not sure why this is needed, we only do the test at comptime + if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; + + const S = struct { + a: u32, + + fn doTheTest() !void { + var s: ?@This() = null; + s = .{ .a = 1 }; + try expect(s.?.a == 1); + } + }; + // try S.doTheTest(); // TODO + comptime try S.doTheTest(); +} + +test "anon init through error union" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + // not sure why this is needed, we only do the test at comptime + if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; + + 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(); // TODO + comptime try S.doTheTest(); +} From 89f6ff177178dba4e66680227a02de7b4068123c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 19 Feb 2022 15:24:22 +0200 Subject: [PATCH 2/6] stage2: correct use of .unwrap_err_union_* in LLVM and C backend --- src/codegen/c.zig | 26 +++++++++++++++----------- src/codegen/llvm.zig | 10 ++++++---- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 776cec645786..f6e2d467a7b4 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3090,17 +3090,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 +3124,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; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ed57562e4c3b..8af74fce6458 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3165,8 +3165,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, ""); @@ -3185,14 +3186,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, ""); } From 27c63bf433f27b06bed92111cbc96e8574de8d11 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 19 Feb 2022 19:21:49 +0200 Subject: [PATCH 3/6] stage2: implement errunion_payload_ptr_set --- src/Air.zig | 4 ++++ src/Liveness.zig | 1 + src/Sema.zig | 8 +++++++- src/arch/aarch64/CodeGen.zig | 7 +++++++ src/arch/arm/CodeGen.zig | 7 +++++++ src/arch/riscv64/CodeGen.zig | 7 +++++++ src/arch/wasm/CodeGen.zig | 1 + src/arch/x86_64/CodeGen.zig | 10 ++++++++++ src/codegen/c.zig | 7 +++++++ src/codegen/llvm.zig | 32 ++++++++++++++++++++++++++++++++ src/print_air.zig | 1 + test/behavior.zig | 2 +- 12 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 070b43ac49fb..b9a565ad2efd 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -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, diff --git a/src/Liveness.zig b/src/Liveness.zig index 71f1221b23d4..5a329d2a7ab3 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -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, diff --git a/src/Sema.zig b/src/Sema.zig index acccc0e3bff2..2d9381493a1d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1636,7 +1636,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) { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 7a3d049ae6d1..2267a156f981 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -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: { diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 6f87c565030b..3633b5ccd0f6 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -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), @@ -1128,6 +1129,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: { diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index d73e083dbce6..10764ad6cdf6 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -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: { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2bd6b929639b..b776f3034626 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -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. diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 441af51de2ff..d7d6ac9da8f5 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -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), @@ -1406,6 +1407,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: { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f6e2d467a7b4..fe0184849db4 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -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) { @@ -3164,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; @@ -3183,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; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8af74fce6458..d8c19fba7156 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2238,6 +2238,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), @@ -3202,6 +3203,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; diff --git a/src/print_air.zig b/src/print_air.zig index 5dc8219b12a3..289fcca669db 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -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, diff --git a/test/behavior.zig b/test/behavior.zig index 5b575ba1909d..68d0c40cd6e4 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -118,6 +118,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. @@ -134,7 +135,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"); From 6f0601c79336d688c80a3b8592cf758f3c94a636 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 19 Feb 2022 21:49:30 +0200 Subject: [PATCH 4/6] stage2: support anon init through error unions and optionals at runtime --- src/Sema.zig | 28 ++++++++++++++++++---------- test/behavior/struct.zig | 24 ++++++++++++------------ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 2d9381493a1d..5dadb9492273 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2603,8 +2603,8 @@ fn zirArrayBasePtr( 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), - .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false), + .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, }; @@ -2630,8 +2630,8 @@ fn zirFieldBasePtr( 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), - .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false), + .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, }; @@ -5263,7 +5263,7 @@ fn zirOptionalPayloadPtr( const optional_ptr = sema.resolveInst(inst_data.operand); const src = inst_data.src(); - return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check); + return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false); } fn analyzeOptionalPayloadPtr( @@ -5272,6 +5272,7 @@ fn analyzeOptionalPayloadPtr( 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); @@ -5290,7 +5291,7 @@ fn analyzeOptionalPayloadPtr( if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| { if (try sema.pointerDeref(block, src, pointer_val, optional_ptr_ty)) |val| { - if (safety_check) { + if (!initializing) { if (val.isNull()) { return sema.fail(block, src, "unable to unwrap null", .{}); } @@ -5308,7 +5309,10 @@ fn analyzeOptionalPayloadPtr( 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. @@ -5412,7 +5416,7 @@ fn zirErrUnionPayloadPtr( const operand = sema.resolveInst(inst_data.operand); const src = inst_data.src(); - return sema.analyzeErrUnionPayloadPtr(block, src, operand, safety_check); + return sema.analyzeErrUnionPayloadPtr(block, src, operand, safety_check, false); } fn analyzeErrUnionPayloadPtr( @@ -5421,6 +5425,7 @@ fn analyzeErrUnionPayloadPtr( 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); @@ -5437,7 +5442,7 @@ fn analyzeErrUnionPayloadPtr( if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { - if (safety_check) { + if (!initializing) { if (val.getError()) |name| { return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); } @@ -5455,7 +5460,10 @@ fn analyzeErrUnionPayloadPtr( 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 diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 7428aed62ad8..a3e8bbdab364 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1202,33 +1202,32 @@ test "for loop over pointers to struct, getting field from struct pointer" { test "anon init through error unions and optionals" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (true) return error.SkipZigTest; // TODO + if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; // TODO const S = struct { a: u32, fn foo() anyerror!?anyerror!@This() { - return @This(){ .a = 1 }; + return .{ .a = 1 }; } fn bar() ?anyerror![2]u8 { - return [2]u8{ 1, 2 }; + return .{ 1, 2 }; } fn doTheTest() !void { - var a = ((foo() catch unreachable).?) catch unreachable; - var b = (bar().?) catch unreachable; + var a = try (try foo()).?; + var b = try bar().?; try expect(a.a + b[1] == 3); } }; try S.doTheTest(); - comptime try S.doTheTest(); + // comptime try S.doTheTest(); // TODO } test "anon init through optional" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - // not sure why this is needed, we only do the test at comptime - if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; // TODO const S = struct { a: u32, @@ -1239,14 +1238,14 @@ test "anon init through optional" { try expect(s.?.a == 1); } }; - // try S.doTheTest(); // TODO + + try S.doTheTest(); comptime try S.doTheTest(); } test "anon init through error union" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - // not sure why this is needed, we only do the test at comptime - if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; // TODO const S = struct { a: u32, @@ -1257,6 +1256,7 @@ test "anon init through error union" { try expect((try s).a == 1); } }; - // try S.doTheTest(); // TODO + + try S.doTheTest(); comptime try S.doTheTest(); } From a2533e6fca0f28aa64717d8e4c13fe6a780b8b15 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 19 Feb 2022 22:31:24 +0200 Subject: [PATCH 5/6] stage2: validate struct/array init ty --- src/AstGen.zig | 13 ++++++++-- src/Sema.zig | 61 +++++++++++++++++++++++++++++++++++++++++------ src/Zir.zig | 10 ++++++++ src/print_zir.zig | 2 ++ 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 8f34a58ed461..f2217f9cc3a0 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1282,6 +1282,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, @@ -1495,8 +1496,10 @@ fn structInitExpr( switch (rl) { .discard => { // TODO if a type expr is given the fields should be validated for that type - if (struct_init.ast.type_expr != 0) - _ = try typeExpr(gz, scope, struct_init.ast.type_expr); + 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); } @@ -1505,6 +1508,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); @@ -1513,6 +1517,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); @@ -1523,6 +1528,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); }, @@ -1573,6 +1579,7 @@ fn structInitExprRlPtr( 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(); @@ -2334,6 +2341,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) { diff --git a/src/Sema.zig b/src/Sema.zig index 5dadb9492273..e7220640a0a8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -872,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; @@ -1341,6 +1351,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, @@ -2614,9 +2632,7 @@ fn zirArrayBasePtr( .Struct => if (elem_ty.isTuple()) return base_ptr, else => {}, } - return sema.fail(block, src, "type '{}' does not support array initialization syntax", .{ - sema.typeOf(start_ptr).childType(), - }); + return sema.failWithArrayInitNotSupported(block, src, sema.typeOf(start_ptr).childType()); } fn zirFieldBasePtr( @@ -2640,9 +2656,40 @@ fn zirFieldBasePtr( .Struct, .Union => return base_ptr, else => {}, } - return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{ - sema.typeOf(start_ptr).childType(), - }); + 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( @@ -10815,7 +10862,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), } } diff --git a/src/Zir.zig b/src/Zir.zig index 339a141c159c..750caad4a0d8 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -655,6 +655,12 @@ pub const Inst = struct { /// *?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. @@ -1101,6 +1107,8 @@ pub const Inst = struct { .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, @@ -1356,6 +1364,8 @@ pub const Inst = struct { .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, diff --git a/src/print_zir.zig b/src/print_zir.zig index 5349d515aa57..7aa315b1144c 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -237,6 +237,8 @@ const Writer = struct { .switch_cond_ref, .array_base_ptr, .field_base_ptr, + .validate_array_init_ty, + .validate_struct_init_ty, => try self.writeUnNode(stream, inst), .ref, From a5ac06268972bd7279a1bb928a40d70cc7d515ed Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 20 Feb 2022 11:25:19 +0200 Subject: [PATCH 6/6] stage2: make field/array base ptr work at comptime --- src/Sema.zig | 24 ++++++++++++++++-------- test/behavior/struct.zig | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e7220640a0a8..b196746c8400 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5337,11 +5337,15 @@ fn analyzeOptionalPayloadPtr( }); 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 (!initializing) { - if (val.isNull()) { - return sema.fail(block, src, "unable to unwrap null", .{}); - } + if (val.isNull()) { + return sema.fail(block, src, "unable to unwrap null", .{}); } // The same Value represents the pointer to the optional and the payload. return sema.addConstant( @@ -5488,11 +5492,15 @@ fn analyzeErrUnionPayloadPtr( }); 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 (!initializing) { - if (val.getError()) |name| { - return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); - } + if (val.getError()) |name| { + return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); } return sema.addConstant( diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index a3e8bbdab364..d059cccb60df 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1222,7 +1222,7 @@ test "anon init through error unions and optionals" { }; try S.doTheTest(); - // comptime try S.doTheTest(); // TODO + comptime try S.doTheTest(); } test "anon init through optional" {