From 13f04e3012b6b2eee141923f9780fce55f7a999d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Jul 2021 00:39:23 -0700 Subject: stage2: implement `@panic` and beginnigs of inferred error sets * ZIR: add two instructions: - ret_err_value_code - ret_err_value * AstGen: add countDefers and utilize it to emit more efficient ZIR for return expressions in the presence of defers. * AstGen: implement |err| payloads for `errdefer` syntax. - There is not an "unused capture" error for it yet. * AstGen: `return error.Foo` syntax gets a hot path in return expressions, using the new ZIR instructions. This also is part of implementing inferred error sets, since we need to tell Sema to add an error value to the inferred error set before it gets coerced. * Sema: implement `@setCold`. - Implement `@setCold` support for C backend. * `@panic` and regular safety panics such as `unreachable` now properly invoke `std.builtin.panic`. * C backend: improve pointer and function value rendering. * C linker: fix redundant typedefs. * Add Type.error_set_inferred. * Fix Value.format for enum_literal, enum_field_index, bytes. * Remove the C backend test that checks for identical text I measured a 14% reduction in Total ZIR Bytes from master branch for std/os.zig. --- src/codegen/c.zig | 289 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 229 insertions(+), 60 deletions(-) (limited to 'src/codegen/c.zig') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index ae439693b8..a9521d21a8 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -39,7 +39,12 @@ const BlockData = struct { }; pub const CValueMap = std.AutoHashMap(*Inst, CValue); -pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.HashContext, std.hash_map.default_max_load_percentage); +pub const TypedefMap = std.HashMap( + Type, + struct { name: []const u8, rendered: []u8 }, + Type.HashContext, + std.hash_map.default_max_load_percentage, +); fn formatTypeAsCIdentifier( data: Type, @@ -151,14 +156,49 @@ pub const Object = struct { render_ty = render_ty.elemType(); } - try o.dg.renderType(w, render_ty); - - const const_prefix = switch (mutability) { - .Const => "const ", - .Mut => "", - }; - try w.print(" {s}", .{const_prefix}); - try o.writeCValue(w, name); + if (render_ty.zigTypeTag() == .Fn) { + const ret_ty = render_ty.fnReturnType(); + if (ret_ty.zigTypeTag() == .NoReturn) { + // noreturn attribute is not allowed here. + try w.writeAll("void"); + } else { + try o.dg.renderType(w, ret_ty); + } + try w.writeAll(" (*"); + switch (mutability) { + .Const => try w.writeAll("const "), + .Mut => {}, + } + try o.writeCValue(w, name); + try w.writeAll(")("); + const param_len = render_ty.fnParamLen(); + const is_var_args = render_ty.fnIsVarArgs(); + if (param_len == 0 and !is_var_args) + try w.writeAll("void") + else { + var index: usize = 0; + while (index < param_len) : (index += 1) { + if (index > 0) { + try w.writeAll(", "); + } + try o.dg.renderType(w, render_ty.fnParamType(index)); + } + } + if (is_var_args) { + if (param_len != 0) try w.writeAll(", "); + try w.writeAll("..."); + } + try w.writeByte(')'); + } else { + try o.dg.renderType(w, render_ty); + + const const_prefix = switch (mutability) { + .Const => "const ", + .Mut => "", + }; + try w.print(" {s}", .{const_prefix}); + try o.writeCValue(w, name); + } try w.writeAll(suffix.items); } }; @@ -196,35 +236,72 @@ pub const DeclGen = struct { return writer.print("{d}", .{val.toSignedInt()}); return writer.print("{d}", .{val.toUnsignedInt()}); }, - .Pointer => switch (val.tag()) { - .null_value, .zero => try writer.writeAll("NULL"), - .one => try writer.writeAll("1"), - .decl_ref => { - const decl = val.castTag(.decl_ref).?.data; - - // Determine if we must pointer cast. - assert(decl.has_tv); - if (t.eql(decl.ty)) { - try writer.print("&{s}", .{decl.name}); - } else { - try writer.writeAll("("); - try dg.renderType(writer, t); - try writer.print(")&{s}", .{decl.name}); - } - }, - .function => { - const func = val.castTag(.function).?.data; - try writer.print("{s}", .{func.owner_decl.name}); + .Pointer => switch (t.ptrSize()) { + .Slice => { + try writer.writeByte('('); + try dg.renderType(writer, t); + try writer.writeAll("){"); + var buf: Type.Payload.ElemType = undefined; + try dg.renderValue(writer, t.slicePtrFieldType(&buf), val); + try writer.writeAll(", "); + try writer.print("{d}", .{val.sliceLen()}); + try writer.writeAll("}"); }, - .extern_fn => { - const decl = val.castTag(.extern_fn).?.data; - try writer.print("{s}", .{decl.name}); + else => switch (val.tag()) { + .null_value, .zero => try writer.writeAll("NULL"), + .one => try writer.writeAll("1"), + .decl_ref => { + const decl = val.castTag(.decl_ref).?.data; + + // Determine if we must pointer cast. + assert(decl.has_tv); + if (t.eql(decl.ty)) { + try writer.print("&{s}", .{decl.name}); + } else { + try writer.writeAll("("); + try dg.renderType(writer, t); + try writer.print(")&{s}", .{decl.name}); + } + }, + .function => { + const func = val.castTag(.function).?.data; + try writer.print("{s}", .{func.owner_decl.name}); + }, + .extern_fn => { + const decl = val.castTag(.extern_fn).?.data; + try writer.print("{s}", .{decl.name}); + }, + else => switch (t.ptrSize()) { + .Slice => unreachable, + .Many => { + if (val.castTag(.ref_val)) |ref_val_payload| { + const sub_val = ref_val_payload.data; + if (sub_val.castTag(.bytes)) |bytes_payload| { + const bytes = bytes_payload.data; + try writer.writeByte('('); + try dg.renderType(writer, t); + // TODO: make our own C string escape instead of using std.zig.fmtEscapes + try writer.print(")\"{}\"", .{std.zig.fmtEscapes(bytes)}); + } else { + unreachable; + } + } else { + unreachable; + } + }, + .One => { + var arena = std.heap.ArenaAllocator.init(dg.module.gpa); + defer arena.deinit(); + + const elem_ty = t.elemType(); + const elem_val = try val.pointerDeref(&arena.allocator); + + try writer.writeAll("&"); + try dg.renderValue(writer, elem_ty, elem_val); + }, + .C => unreachable, + }, }, - else => |e| return dg.fail( - .{ .node_offset = 0 }, - "TODO: C backend: implement Pointer value {s}", - .{@tagName(e)}, - ), }, .Array => { // First try specific tag representations for more efficiency. @@ -329,6 +406,32 @@ pub const DeclGen = struct { }, } }, + .Fn => switch (val.tag()) { + .null_value, .zero => try writer.writeAll("NULL"), + .one => try writer.writeAll("1"), + .decl_ref => { + const decl = val.castTag(.decl_ref).?.data; + + // Determine if we must pointer cast. + assert(decl.has_tv); + if (t.eql(decl.ty)) { + try writer.print("&{s}", .{decl.name}); + } else { + try writer.writeAll("("); + try dg.renderType(writer, t); + try writer.print(")&{s}", .{decl.name}); + } + }, + .function => { + const func = val.castTag(.function).?.data; + try writer.print("{s}", .{func.owner_decl.name}); + }, + .extern_fn => { + const decl = val.castTag(.extern_fn).?.data; + try writer.print("{s}", .{decl.name}); + }, + else => unreachable, + }, else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{ @tagName(e), }), @@ -339,6 +442,12 @@ pub const DeclGen = struct { if (!is_global) { try w.writeAll("static "); } + if (dg.decl.val.castTag(.function)) |func_payload| { + const func: *Module.Fn = func_payload.data; + if (func.is_cold) { + try w.writeAll("ZIG_COLD "); + } + } try dg.renderType(w, dg.decl.ty.fnReturnType()); const decl_name = mem.span(dg.decl.name); try w.print(" {s}(", .{decl_name}); @@ -413,7 +522,35 @@ pub const DeclGen = struct { .Pointer => { if (t.isSlice()) { - return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement slices", .{}); + if (dg.typedefs.get(t)) |some| { + return w.writeAll(some.name); + } + + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); + defer buffer.deinit(); + const bw = buffer.writer(); + + try bw.writeAll("typedef struct { "); + const elem_type = t.elemType(); + try dg.renderType(bw, elem_type); + try bw.writeAll(" *"); + if (t.isConstPtr()) { + try bw.writeAll("const "); + } + if (t.isVolatilePtr()) { + try bw.writeAll("volatile "); + } + try bw.writeAll("ptr; size_t len; } "); + const name_index = buffer.items.len; + try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)}); + + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); + const name = rendered[name_index .. rendered.len - 2]; + + try dg.typedefs.ensureUnusedCapacity(1); + try w.writeAll(name); + dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); } else { try dg.renderType(w, t.elemType()); try w.writeAll(" *"); @@ -446,13 +583,13 @@ pub const DeclGen = struct { try dg.renderType(bw, child_type); try bw.writeAll(" payload; bool is_null; } "); const name_index = buffer.items.len; - try bw.print("zig_opt_{s}_t;\n", .{typeToCIdentifier(child_type)}); + try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); const name = rendered[name_index .. rendered.len - 2]; - try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); + try dg.typedefs.ensureUnusedCapacity(1); try w.writeAll(name); dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); }, @@ -465,7 +602,7 @@ pub const DeclGen = struct { return w.writeAll(some.name); } const child_type = t.errorUnionChild(); - const set_type = t.errorUnionSet(); + const err_set_type = t.errorUnionSet(); var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); @@ -475,13 +612,20 @@ pub const DeclGen = struct { try dg.renderType(bw, child_type); try bw.writeAll(" payload; uint16_t error; } "); const name_index = buffer.items.len; - try bw.print("zig_err_union_{s}_{s}_t;\n", .{ typeToCIdentifier(set_type), typeToCIdentifier(child_type) }); + if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| { + const func = inf_err_set_payload.data; + try bw.print("zig_E_{s};\n", .{func.owner_decl.name}); + } else { + try bw.print("zig_E_{s}_{s};\n", .{ + typeToCIdentifier(err_set_type), typeToCIdentifier(child_type), + }); + } const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); const name = rendered[name_index .. rendered.len - 2]; - try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); + try dg.typedefs.ensureUnusedCapacity(1); try w.writeAll(name); dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); }, @@ -514,7 +658,7 @@ pub const DeclGen = struct { errdefer dg.typedefs.allocator.free(rendered); const name = rendered[name_start .. rendered.len - 2]; - try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); + try dg.typedefs.ensureUnusedCapacity(1); try w.writeAll(name); dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); }, @@ -526,7 +670,28 @@ pub const DeclGen = struct { try dg.renderType(w, int_tag_ty); }, .Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}), - .Fn => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Fn", .{}), + .Fn => { + try dg.renderType(w, t.fnReturnType()); + try w.writeAll(" (*)("); + const param_len = t.fnParamLen(); + const is_var_args = t.fnIsVarArgs(); + if (param_len == 0 and !is_var_args) + try w.writeAll("void") + else { + var index: usize = 0; + while (index < param_len) : (index += 1) { + if (index > 0) { + try w.writeAll(", "); + } + try dg.renderType(w, t.fnParamType(index)); + } + } + if (is_var_args) { + if (param_len != 0) try w.writeAll(", "); + try w.writeAll("..."); + } + try w.writeByte(')'); + }, .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}), .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}), .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}), @@ -569,23 +734,27 @@ pub fn genDecl(o: *Object) !void { .val = o.dg.decl.val, }; if (tv.val.castTag(.function)) |func_payload| { - const is_global = o.dg.declIsGlobal(tv); - const fwd_decl_writer = o.dg.fwd_decl.writer(); - if (is_global) { - try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); - } - try o.dg.renderFunctionSignature(fwd_decl_writer, is_global); - try fwd_decl_writer.writeAll(";\n"); - const func: *Module.Fn = func_payload.data; - try o.indent_writer.insertNewline(); - try o.dg.renderFunctionSignature(o.writer(), is_global); + if (func.owner_decl == o.dg.decl) { + const is_global = o.dg.declIsGlobal(tv); + const fwd_decl_writer = o.dg.fwd_decl.writer(); + if (is_global) { + try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); + } + try o.dg.renderFunctionSignature(fwd_decl_writer, is_global); + try fwd_decl_writer.writeAll(";\n"); - try o.writer().writeByte(' '); - try genBody(o, func.body); + try o.indent_writer.insertNewline(); + try o.dg.renderFunctionSignature(o.writer(), is_global); - try o.indent_writer.insertNewline(); - } else if (tv.val.tag() == .extern_fn) { + try o.writer().writeByte(' '); + try genBody(o, func.body); + + try o.indent_writer.insertNewline(); + return; + } + } + if (tv.val.tag() == .extern_fn) { const writer = o.writer(); try writer.writeAll("ZIG_EXTERN_C "); try o.dg.renderFunctionSignature(writer, true); @@ -644,9 +813,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { const is_global = dg.declIsGlobal(tv); if (is_global) { try writer.writeAll("ZIG_EXTERN_C "); + try dg.renderFunctionSignature(writer, is_global); + try dg.fwd_decl.appendSlice(";\n"); } - try dg.renderFunctionSignature(writer, is_global); - try dg.fwd_decl.appendSlice(";\n"); }, else => {}, } -- cgit v1.2.3 From 5c8bd443d92c6306f60857720103ae46ca7b8b3e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Jul 2021 19:50:56 -0700 Subject: stage2: fix if expressions on error unions AstGen had the then-else logic backwards for if expressions on error unions. This commit fixes it. Turns out AstGen only really needs `is_non_null` and `is_non_err`, and does not need the `is_null` or `is_err` variants. So I removed the `is_null{,_ptr}` and `is_err{,_ptr}` ZIR instructions (-4) and added `is_non_err`, `is_non_err_ptr` ZIR instructions (+2) for a total of (-2) ZIR instructions, giving us a tiny bit more headroom within the 256 tag limit. This required swapping the order of then/else blocks in a handful of cases, but ultimately means the ZIR will be in the same as source order, which is convenient when debugging. AIR code on the other hand, gains the `is_non_err` and `is_non_err_ptr` instructions. Sema: fix logic in zirErrUnionCode and zirErrUnionCodePtr returning the wrong result type. --- src/AstGen.zig | 92 ++++++++++++++++++++++++++-------------------------- src/Sema.zig | 55 +++++++++++++++++-------------- src/Zir.zig | 32 ++++++------------ src/air.zig | 12 ++++++- src/codegen.zig | 12 +++++++ src/codegen/c.zig | 11 ++++--- src/codegen/wasm.zig | 9 +++-- 7 files changed, 119 insertions(+), 104 deletions(-) (limited to 'src/codegen/c.zig') diff --git a/src/AstGen.zig b/src/AstGen.zig index f7fea3a99c..a816628f66 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -786,7 +786,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr rl, node, node_datas[node].lhs, - .is_err_ptr, + .is_non_err_ptr, .err_union_payload_unsafe_ptr, .err_union_code_ptr, node_datas[node].rhs, @@ -798,7 +798,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr rl, node, node_datas[node].lhs, - .is_err, + .is_non_err, .err_union_payload_unsafe, .err_union_code, node_datas[node].rhs, @@ -813,7 +813,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr rl, node, node_datas[node].lhs, - .is_null_ptr, + .is_non_null_ptr, .optional_payload_unsafe_ptr, undefined, node_datas[node].rhs, @@ -825,7 +825,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr rl, node, node_datas[node].lhs, - .is_null, + .is_non_null, .optional_payload_unsafe, undefined, node_datas[node].rhs, @@ -1948,11 +1948,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner .float128, .int_type, .is_non_null, - .is_null, .is_non_null_ptr, - .is_null_ptr, - .is_err, - .is_err_ptr, + .is_non_err, + .is_non_err_ptr, .mod_rem, .mul, .mulwrap, @@ -4621,8 +4619,8 @@ fn tryExpr( }; const err_ops = switch (rl) { // zig fmt: off - .ref => [3]Zir.Inst.Tag{ .is_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, - else => [3]Zir.Inst.Tag{ .is_err, .err_union_code, .err_union_payload_unsafe }, + .ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, + else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe }, // zig fmt: on }; // This could be a pointer or value depending on the `operand_rl` parameter. @@ -4640,21 +4638,21 @@ fn tryExpr( var then_scope = parent_gz.makeSubBlock(scope); defer then_scope.instructions.deinit(astgen.gpa); - const err_code = try then_scope.addUnNode(err_ops[1], operand, node); - try genDefers(&then_scope, &fn_block.base, scope, .{ .both = err_code }); - const then_result = try then_scope.addUnNode(.ret_node, err_code, node); - - var else_scope = parent_gz.makeSubBlock(scope); - defer else_scope.instructions.deinit(astgen.gpa); - block_scope.break_count += 1; // This could be a pointer or value depending on `err_ops[2]`. - const unwrapped_payload = try else_scope.addUnNode(err_ops[2], operand, node); - const else_result = switch (rl) { + const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node); + const then_result = switch (rl) { .ref => unwrapped_payload, - else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node), + else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node), }; + var else_scope = parent_gz.makeSubBlock(scope); + defer else_scope.instructions.deinit(astgen.gpa); + + const err_code = try else_scope.addUnNode(err_ops[1], operand, node); + try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); + const else_result = try else_scope.addUnNode(.ret_node, err_code, node); + return finishThenElseBlock( parent_gz, rl, @@ -4711,18 +4709,28 @@ fn orelseCatchExpr( var then_scope = parent_gz.makeSubBlock(scope); defer then_scope.instructions.deinit(astgen.gpa); + // This could be a pointer or value depending on `unwrap_op`. + const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node); + const then_result = switch (rl) { + .ref => unwrapped_payload, + else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node), + }; + + var else_scope = parent_gz.makeSubBlock(scope); + defer else_scope.instructions.deinit(astgen.gpa); + var err_val_scope: Scope.LocalVal = undefined; - const then_sub_scope = blk: { - const payload = payload_token orelse break :blk &then_scope.base; + const else_sub_scope = blk: { + const payload = payload_token orelse break :blk &else_scope.base; if (mem.eql(u8, tree.tokenSlice(payload), "_")) { return astgen.failTok(payload, "discard of error capture; omit it instead", .{}); } const err_name = try astgen.identAsString(payload); err_val_scope = .{ - .parent = &then_scope.base, - .gen_zir = &then_scope, + .parent = &else_scope.base, + .gen_zir = &else_scope, .name = err_name, - .inst = try then_scope.addUnNode(unwrap_code_op, operand, node), + .inst = try else_scope.addUnNode(unwrap_code_op, operand, node), .token_src = payload, .id_cat = .@"capture", }; @@ -4730,23 +4738,13 @@ fn orelseCatchExpr( }; block_scope.break_count += 1; - const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs); - try checkUsed(parent_gz, &then_scope.base, then_sub_scope); + const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs); + try checkUsed(parent_gz, &else_scope.base, else_sub_scope); // We hold off on the break instructions as well as copying the then/else // instructions into place until we know whether to keep store_to_block_ptr // instructions or not. - var else_scope = parent_gz.makeSubBlock(scope); - defer else_scope.instructions.deinit(astgen.gpa); - - // This could be a pointer or value depending on `unwrap_op`. - const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node); - const else_result = switch (rl) { - .ref => unwrapped_payload, - else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node), - }; - return finishThenElseBlock( parent_gz, rl, @@ -4964,7 +4962,7 @@ fn ifExpr( if (if_full.error_token) |_| { const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr); - const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err; + const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; break :c .{ .inst = err_union, .bool_bit = try block_scope.addUnNode(tag, err_union, node), @@ -5221,7 +5219,7 @@ fn whileExpr( if (while_full.error_token) |_| { const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; const err_union = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr); - const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err; + const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; break :c .{ .inst = err_union, .bool_bit = try continue_scope.addUnNode(tag, err_union, node), @@ -6229,23 +6227,25 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref } // Emit conditional branch for generating errdefers. - const is_err = try gz.addUnNode(.is_err, operand, node); + const is_non_err = try gz.addUnNode(.is_non_err, operand, node); const condbr = try gz.addCondBr(.condbr, node); var then_scope = gz.makeSubBlock(scope); defer then_scope.instructions.deinit(astgen.gpa); - const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ - .both = try then_scope.addUnNode(.err_union_code, operand, node), - }; - try genDefers(&then_scope, defer_outer, scope, which_ones); + + try genDefers(&then_scope, defer_outer, scope, .normal_only); _ = try then_scope.addUnNode(.ret_node, operand, node); var else_scope = gz.makeSubBlock(scope); defer else_scope.instructions.deinit(astgen.gpa); - try genDefers(&else_scope, defer_outer, scope, .normal_only); + + const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ + .both = try else_scope.addUnNode(.err_union_code, operand, node), + }; + try genDefers(&else_scope, defer_outer, scope, which_ones); _ = try else_scope.addUnNode(.ret_node, operand, node); - try setCondBrPayload(condbr, is_err, &then_scope, &else_scope); + try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope); return Zir.Inst.Ref.unreachable_value; }, diff --git a/src/Sema.zig b/src/Sema.zig index 24c51bdc46..86e5f59af6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -225,12 +225,10 @@ pub fn analyzeBody( .float => try sema.zirFloat(block, inst), .float128 => try sema.zirFloat128(block, inst), .int_type => try sema.zirIntType(block, inst), - .is_err => try sema.zirIsErr(block, inst), - .is_err_ptr => try sema.zirIsErrPtr(block, inst), - .is_non_null => try sema.zirIsNull(block, inst, true), - .is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true), - .is_null => try sema.zirIsNull(block, inst, false), - .is_null_ptr => try sema.zirIsNullPtr(block, inst, false), + .is_non_err => try sema.zirIsNonErr(block, inst), + .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), + .is_non_null => try sema.zirIsNonNull(block, inst), + .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), .loop => try sema.zirLoop(block, inst), .merge_error_sets => try sema.zirMergeErrorSets(block, inst), .negate => try sema.zirNegate(block, inst, .sub), @@ -2981,17 +2979,19 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner if (operand.ty.zigTypeTag() != .ErrorUnion) return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); + const result_ty = operand.ty.castTag(.error_union).?.data.error_set; + if (operand.value()) |val| { assert(val.getError() != null); const data = val.castTag(.error_union).?.data; return sema.mod.constInst(sema.arena, src, .{ - .ty = operand.ty.castTag(.error_union).?.data.error_set, + .ty = result_ty, .val = data, }); } try sema.requireRuntimeBlock(block, src); - return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand); + return block.addUnOp(src, result_ty, .unwrap_errunion_err, operand); } /// Pointer in, value out @@ -3007,18 +3007,20 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); + const result_ty = operand.ty.elemType().castTag(.error_union).?.data.error_set; + if (operand.value()) |pointer_val| { const val = try pointer_val.pointerDeref(sema.arena); assert(val.getError() != null); const data = val.castTag(.error_union).?.data; return sema.mod.constInst(sema.arena, src, .{ - .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set, + .ty = result_ty, .val = data, }); } try sema.requireRuntimeBlock(block, src); - return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand); + return block.addUnOp(src, result_ty, .unwrap_errunion_err_ptr, operand); } fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { @@ -5298,11 +5300,10 @@ fn zirBoolBr( return &block_inst.base; } -fn zirIsNull( +fn zirIsNonNull( sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, - invert_logic: bool, ) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -5310,14 +5311,13 @@ fn zirIsNull( const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); - return sema.analyzeIsNull(block, src, operand, invert_logic); + return sema.analyzeIsNull(block, src, operand, true); } -fn zirIsNullPtr( +fn zirIsNonNullPtr( sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, - invert_logic: bool, ) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -5326,19 +5326,19 @@ fn zirIsNullPtr( const src = inst_data.src(); const ptr = try sema.resolveInst(inst_data.operand); const loaded = try sema.analyzeLoad(block, src, ptr, src); - return sema.analyzeIsNull(block, src, loaded, invert_logic); + return sema.analyzeIsNull(block, src, loaded, true); } -fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { +fn zirIsNonErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand = try sema.resolveInst(inst_data.operand); - return sema.analyzeIsErr(block, inst_data.src(), operand); + return sema.analyzeIsNonErr(block, inst_data.src(), operand); } -fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { +fn zirIsNonErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -5346,7 +5346,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro const src = inst_data.src(); const ptr = try sema.resolveInst(inst_data.operand); const loaded = try sema.analyzeLoad(block, src, ptr, src); - return sema.analyzeIsErr(block, src, loaded); + return sema.analyzeIsNonErr(block, src, loaded); } fn zirCondbr( @@ -7219,20 +7219,25 @@ fn analyzeIsNull( return block.addUnOp(src, result_ty, inst_tag, operand); } -fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst { +fn analyzeIsNonErr( + sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, + operand: *Inst, +) InnerError!*Inst { const ot = operand.ty.zigTypeTag(); - if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false); - if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true); + if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, true); + if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, false); assert(ot == .ErrorUnion); const result_ty = Type.initTag(.bool); if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| { if (err_union.isUndef()) { return sema.mod.constUndef(sema.arena, src, result_ty); } - return sema.mod.constBool(sema.arena, src, err_union.getError() != null); + return sema.mod.constBool(sema.arena, src, err_union.getError() == null); } try sema.requireRuntimeBlock(block, src); - return block.addUnOp(src, result_ty, .is_err, operand); + return block.addUnOp(src, result_ty, .is_non_err, operand); } fn analyzeSlice( diff --git a/src/Zir.zig b/src/Zir.zig index a80660b5bf..77b71f6caf 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -398,21 +398,15 @@ pub const Inst = struct { /// Return a boolean false if an optional is null. `x != null` /// Uses the `un_node` field. is_non_null, - /// Return a boolean true if an optional is null. `x == null` - /// Uses the `un_node` field. - is_null, /// Return a boolean false if an optional is null. `x.* != null` /// Uses the `un_node` field. is_non_null_ptr, - /// Return a boolean true if an optional is null. `x.* == null` - /// Uses the `un_node` field. - is_null_ptr, - /// Return a boolean true if value is an error + /// Return a boolean false if value is an error /// Uses the `un_node` field. - is_err, - /// Return a boolean true if dereferenced pointer is an error + is_non_err, + /// Return a boolean false if dereferenced pointer is an error /// Uses the `un_node` field. - is_err_ptr, + is_non_err_ptr, /// A labeled block of code that loops forever. At the end of the body will have either /// a `repeat` instruction or a `repeat_inline` instruction. /// Uses the `pl_node` field. The AST node is either a for loop or while loop. @@ -1046,11 +1040,9 @@ pub const Inst = struct { .float128, .int_type, .is_non_null, - .is_null, .is_non_null_ptr, - .is_null_ptr, - .is_err, - .is_err_ptr, + .is_non_err, + .is_non_err_ptr, .mod_rem, .mul, .mulwrap, @@ -1306,11 +1298,9 @@ pub const Inst = struct { .float128 = .pl_node, .int_type = .int_type, .is_non_null = .un_node, - .is_null = .un_node, .is_non_null_ptr = .un_node, - .is_null_ptr = .un_node, - .is_err = .un_node, - .is_err_ptr = .un_node, + .is_non_err = .un_node, + .is_non_err_ptr = .un_node, .loop = .pl_node, .repeat = .node, .repeat_inline = .node, @@ -2857,11 +2847,9 @@ const Writer = struct { .err_union_code, .err_union_code_ptr, .is_non_null, - .is_null, .is_non_null_ptr, - .is_null_ptr, - .is_err, - .is_err_ptr, + .is_non_err, + .is_non_err_ptr, .typeof, .typeof_elem, .struct_init_empty, diff --git a/src/air.zig b/src/air.zig index 1b7faa641b..e73367945b 100644 --- a/src/air.zig +++ b/src/air.zig @@ -90,8 +90,12 @@ pub const Inst = struct { is_non_null_ptr, /// E!T => bool is_err, + /// E!T => bool (inverted logic) + is_non_err, /// *E!T => bool is_err_ptr, + /// *E!T => bool (inverted logic) + is_non_err_ptr, bool_and, bool_or, /// Read a value from a pointer. @@ -154,7 +158,9 @@ pub const Inst = struct { .is_null, .is_null_ptr, .is_err, + .is_non_err, .is_err_ptr, + .is_non_err_ptr, .ptrtoint, .floatcast, .intcast, @@ -759,7 +765,9 @@ const DumpAir = struct { .is_null, .is_null_ptr, .is_err, + .is_non_err, .is_err_ptr, + .is_non_err_ptr, .ptrtoint, .floatcast, .intcast, @@ -888,11 +896,13 @@ const DumpAir = struct { .bitcast, .not, .is_non_null, - .is_null, .is_non_null_ptr, + .is_null, .is_null_ptr, .is_err, .is_err_ptr, + .is_non_err, + .is_non_err_ptr, .ptrtoint, .floatcast, .intcast, diff --git a/src/codegen.zig b/src/codegen.zig index f9f5a861fb..6b9bd633d0 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -859,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?), .is_null => return self.genIsNull(inst.castTag(.is_null).?), .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?), + .is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?), + .is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?), .is_err => return self.genIsErr(inst.castTag(.is_err).?), .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?), .load => return self.genLoad(inst.castTag(.load).?), @@ -2972,6 +2974,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{}); } + fn genIsNonErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement is_non_err for {}", .{self.target.cpu.arch}), + } + } + + fn genIsNonErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { + return self.fail(inst.base.src, "TODO load the operand and call genIsNonErr", .{}); + } + fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { // A loop is a setup to be able to jump back to the beginning. const start_index = self.code.items.len; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a9521d21a8..db0e910643 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -895,8 +895,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .ref => try genRef(o, inst.castTag(.ref).?), .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?), - .is_err => try genIsErr(o, inst.castTag(.is_err).?), - .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), + .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", "!="), + .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", "=="), + .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "[0]", "!="), + .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "[0]", "=="), .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), @@ -1446,15 +1448,14 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { return local; } -fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue { +fn genIsErr(o: *Object, inst: *Inst.UnOp, deref_suffix: []const u8, op_str: []const u8) !CValue { const writer = o.writer(); - const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else ""; const operand = try o.resolveInst(inst.operand); const local = try o.allocLocal(Type.initTag(.bool), .Const); try writer.writeAll(" = ("); try o.writeCValue(writer, operand); - try writer.print("){s}.error != 0;\n", .{maybe_deref}); + try writer.print("){s}.error {s} 0;\n", .{ deref_suffix, op_str }); return local; } diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index ec4ec66b1e..3476ab2ce6 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -814,7 +814,8 @@ pub const Context = struct { .constant => unreachable, .dbg_stmt => WValue.none, .div => self.genBinOp(inst.castTag(.div).?, .div), - .is_err => self.genIsErr(inst.castTag(.is_err).?), + .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne), + .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq), .load => self.genLoad(inst.castTag(.load).?), .loop => self.genLoop(inst.castTag(.loop).?), .mul => self.genBinOp(inst.castTag(.mul).?, .mul), @@ -1278,7 +1279,7 @@ pub const Context = struct { return .none; } - fn genIsErr(self: *Context, inst: *Inst.UnOp) InnerError!WValue { + fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue { const operand = self.resolveInst(inst.operand); const offset = self.code.items.len; const writer = self.code.writer(); @@ -1289,9 +1290,7 @@ pub const Context = struct { try writer.writeByte(wasm.opcode(.i32_const)); try leb.writeILEB128(writer, @as(i32, 0)); - // we want to break out of the condition if they're *not* equal, - // because that means there's an error. - try writer.writeByte(wasm.opcode(.i32_ne)); + try writer.writeByte(@enumToInt(opcode)); return WValue{ .code_offset = offset }; } -- cgit v1.2.3 From c2e66d9bab396a69514ec7c3c41fb0404e542f21 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Jul 2021 20:47:21 -0700 Subject: stage2: basic inferred error set support * Inferred error sets are stored in the return Type of the function, owned by the Module.Fn. So it cleans up that memory in deinit(). * Sema: update the inferred error set in zirRetErrValue - Update relevant code in wrapErrorUnion * C backend: improve some some instructions to take advantage of liveness analysis to avoid being emitted when unused. * C backend: when an error union has a payload type with no runtime bits, emit the error union as the same type as the error set. --- src/Module.zig | 15 ++++++++++-- src/Sema.zig | 25 ++++++++++++++------ src/codegen/c.zig | 67 +++++++++++++++++++++++++++++++++++++++++++++-------- src/type.zig | 7 ++++-- test/stage2/cbe.zig | 20 ++++++++++++++++ 5 files changed, 113 insertions(+), 21 deletions(-) (limited to 'src/codegen/c.zig') diff --git a/src/Module.zig b/src/Module.zig index c48440ccc2..8ae184a377 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -777,8 +777,19 @@ pub const Fn = struct { } pub fn deinit(func: *Fn, gpa: *Allocator) void { - _ = func; - _ = gpa; + if (func.getInferredErrorSet()) |map| { + map.deinit(gpa); + } + } + + pub fn getInferredErrorSet(func: *Fn) ?*std.StringHashMapUnmanaged(void) { + const ret_ty = func.owner_decl.ty.fnReturnType(); + if (ret_ty.zigTypeTag() == .ErrorUnion) { + if (ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| { + return &payload.data.map; + } + } + return null; } }; diff --git a/src/Sema.zig b/src/Sema.zig index 86e5f59af6..d7ce9fdf4f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3139,7 +3139,10 @@ fn funcCommon( } const return_type = if (!inferred_error_set) bare_return_type else blk: { - const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, new_func); + const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, .{ + .func = new_func, + .map = .{}, + }); break :blk try Type.Tag.error_union.create(sema.arena, .{ .error_set = error_set_ty, .payload = bare_return_type, @@ -5424,12 +5427,8 @@ fn zirRetErrValue( // Add the error tag to the inferred error set of the in-scope function. if (sema.func) |func| { - const fn_ty = func.owner_decl.ty; - const fn_ret_ty = fn_ty.fnReturnType(); - if (fn_ret_ty.zigTypeTag() == .ErrorUnion and - fn_ret_ty.errorUnionSet().tag() == .error_set_inferred) - { - return sema.mod.fail(&block.base, src, "TODO: Sema.zirRetErrValue", .{}); + if (func.getInferredErrorSet()) |map| { + _ = try map.getOrPut(sema.gpa, err_name); } } // Return the error code from the function. @@ -7535,6 +7534,18 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst ); } }, + .error_set_inferred => { + const expected_name = val.castTag(.@"error").?.data.name; + const map = &err_union.data.error_set.castTag(.error_set_inferred).?.data.map; + if (!map.contains(expected_name)) { + return sema.mod.fail( + &block.base, + inst.src, + "expected type '{}', found type '{}'", + .{ err_union.data.error_set, inst.ty }, + ); + } + }, else => unreachable, } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index db0e910643..3aaf559802 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -360,6 +360,12 @@ pub const DeclGen = struct { const error_type = t.errorUnionSet(); const payload_type = t.errorUnionChild(); const data = val.castTag(.error_union).?.data; + + if (!payload_type.hasCodeGenBits()) { + // We use the error type directly as the type. + return dg.renderValue(writer, error_type, data); + } + try writer.writeByte('('); try dg.renderType(writer, t); try writer.writeAll("){"); @@ -604,6 +610,10 @@ pub const DeclGen = struct { const child_type = t.errorUnionChild(); const err_set_type = t.errorUnionSet(); + if (!child_type.hasCodeGenBits()) { + return dg.renderType(w, err_set_type); + } + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); const bw = buffer.writer(); @@ -613,7 +623,7 @@ pub const DeclGen = struct { try bw.writeAll(" payload; uint16_t error; } "); const name_index = buffer.items.len; if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| { - const func = inf_err_set_payload.data; + const func = inf_err_set_payload.data.func; try bw.print("zig_E_{s};\n", .{func.owner_decl.name}); } else { try bw.print("zig_E_{s}_{s};\n", .{ @@ -895,10 +905,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .ref => try genRef(o, inst.castTag(.ref).?), .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?), - .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", "!="), - .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", "=="), - .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "[0]", "!="), - .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "[0]", "=="), + .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="), + .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", ".", "=="), + .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "*", "->", "!="), + .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "*", "->", "=="), .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), @@ -1384,9 +1394,25 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue { // *(E!T) -> E NOT *E fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { + if (inst.base.isUnused()) + return CValue.none; + const writer = o.writer(); const operand = try o.resolveInst(inst.operand); + const payload_ty = inst.operand.ty.errorUnionChild(); + if (!payload_ty.hasCodeGenBits()) { + if (inst.operand.ty.zigTypeTag() == .Pointer) { + const local = try o.allocLocal(inst.base.ty, .Const); + try writer.writeAll(" = *"); + try o.writeCValue(writer, operand); + try writer.writeAll(";\n"); + return local; + } else { + return operand; + } + } + const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; const local = try o.allocLocal(inst.base.ty, .Const); @@ -1396,10 +1422,19 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { try writer.print("){s}error;\n", .{maybe_deref}); return local; } + fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { + if (inst.base.isUnused()) + return CValue.none; + const writer = o.writer(); const operand = try o.resolveInst(inst.operand); + const payload_ty = inst.operand.ty.errorUnionChild(); + if (!payload_ty.hasCodeGenBits()) { + return CValue.none; + } + const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else ""; @@ -1448,14 +1483,26 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { return local; } -fn genIsErr(o: *Object, inst: *Inst.UnOp, deref_suffix: []const u8, op_str: []const u8) !CValue { +fn genIsErr( + o: *Object, + inst: *Inst.UnOp, + deref_prefix: [*:0]const u8, + deref_suffix: [*:0]const u8, + op_str: [*:0]const u8, +) !CValue { const writer = o.writer(); const operand = try o.resolveInst(inst.operand); - const local = try o.allocLocal(Type.initTag(.bool), .Const); - try writer.writeAll(" = ("); - try o.writeCValue(writer, operand); - try writer.print("){s}.error {s} 0;\n", .{ deref_suffix, op_str }); + const payload_ty = inst.operand.ty.errorUnionChild(); + if (!payload_ty.hasCodeGenBits()) { + try writer.print(" = {s}", .{deref_prefix}); + try o.writeCValue(writer, operand); + try writer.print(" {s} 0;\n", .{op_str}); + } else { + try writer.writeAll(" = "); + try o.writeCValue(writer, operand); + try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str }); + } return local; } diff --git a/src/type.zig b/src/type.zig index f9385e90bc..e8f0998332 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1041,7 +1041,7 @@ pub const Type = extern union { return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name)); }, .error_set_inferred => { - const func = ty.castTag(.error_set_inferred).?.data; + const func = ty.castTag(.error_set_inferred).?.data.func; return writer.print("(inferred error set of {s})", .{func.owner_decl.name}); }, .error_set_single => { @@ -3154,7 +3154,10 @@ pub const Type = extern union { pub const base_tag = Tag.error_set_inferred; base: Payload = Payload{ .tag = base_tag }, - data: *Module.Fn, + data: struct { + func: *Module.Fn, + map: std.StringHashMapUnmanaged(void), + }, }; pub const Pointer = struct { diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index a064995c13..cbe24d3ec3 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -804,6 +804,26 @@ pub fn addCases(ctx: *TestContext) !void { }); } + { + var case = ctx.exeFromCompiledC("inferred error sets", .{}); + + case.addCompareOutput( + \\pub export fn main() c_int { + \\ if (foo()) |_| { + \\ @panic("test fail"); + \\ } else |err| { + \\ if (err != error.ItBroke) { + \\ @panic("test fail"); + \\ } + \\ } + \\ return 0; + \\} + \\fn foo() !void { + \\ return error.ItBroke; + \\} + , ""); + } + ctx.h("simple header", linux_x64, \\export fn start() void{} , -- cgit v1.2.3