From 096763de9808efa996d248815d4ac913381aa1b4 Mon Sep 17 00:00:00 2001 From: Matthew Borkowski Date: Sat, 23 Oct 2021 18:49:23 -0400 Subject: [PATCH 1/3] astgen.zig: fix nodeMayEvalToError --- src/AstGen.zig | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 64cc60ecf5..14ce5a4988 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -8514,10 +8514,10 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never .unwrap_optional, => node = node_datas[node].lhs, - // Forward the question to the RHS sub-expression. + // LHS sub-expression may still be an error under the outer optional or error union .@"catch", .@"orelse", - => node = node_datas[node].rhs, + => return .maybe, .block_two, .block_two_semicolon, @@ -8544,11 +8544,18 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never // If the builtin is an invalid name, we don't cause an error here; instead // let it pass, and the error will be "invalid builtin function" later. const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe; - if (builtin_info.tag == .err_set_cast) { - return .always; - } else { - return .never; - } + return switch (builtin_info.tag) { + .as, + .call, + .field, + => .maybe, + + .err_set_cast, + .int_to_error, + => .always, + + else => .never, + }; }, } } From 8a95bac593ce9aa36b76229a9f2f4aba8c8d30dd Mon Sep 17 00:00:00 2001 From: Matthew Borkowski Date: Sat, 23 Oct 2021 20:05:47 -0400 Subject: [PATCH 2/3] astgen.zig: when ret's operand ResultLoc is .ptr, load from ret_ptr before is_non_err or err_union_code --- src/AstGen.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 14ce5a4988..33f538df1b 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6500,7 +6500,8 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref }, .always => { // Value is always an error. Emit both error defers and regular defers. - const err_code = try gz.addUnNode(.err_union_code, operand, node); + const result = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand; + const err_code = try gz.addUnNode(.err_union_code, result, node); try genDefers(gz, defer_outer, scope, .{ .both = err_code }); try gz.addRet(rl, operand, node); return Zir.Inst.Ref.unreachable_value; @@ -6515,7 +6516,8 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref } // Emit conditional branch for generating errdefers. - const is_non_err = try gz.addUnNode(.is_non_err, operand, node); + const result = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand; + const is_non_err = try gz.addUnNode(.is_non_err, result, node); const condbr = try gz.addCondBr(.condbr, node); var then_scope = gz.makeSubBlock(scope); @@ -6528,7 +6530,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref defer else_scope.instructions.deinit(astgen.gpa); const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ - .both = try else_scope.addUnNode(.err_union_code, operand, node), + .both = try else_scope.addUnNode(.err_union_code, result, node), }; try genDefers(&else_scope, defer_outer, scope, which_ones); try else_scope.addRet(rl, operand, node); From 9c5b852f9bb47e25c878cbccdaa175517ae48fc6 Mon Sep 17 00:00:00 2001 From: Matthew Borkowski Date: Sat, 23 Oct 2021 21:52:21 -0400 Subject: [PATCH 3/3] astgen.zig: emit ZIR for callconv before return type in fnDecl and fnProtoExpr --- src/AstGen.zig | 30 +++++++++++++++--------------- test/behavior/fn_stage1.zig | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 33f538df1b..92d0e3de7c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1156,16 +1156,6 @@ fn fnProtoExpr( return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{}); } - const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; - const is_inferred_error = token_tags[maybe_bang] == .bang; - if (is_inferred_error) { - return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); - } - var ret_gz = gz.makeSubBlock(scope); - defer ret_gz.instructions.deinit(gpa); - const ret_ty = try expr(&ret_gz, scope, coerced_type_rl, fn_proto.ast.return_type); - const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); - const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0) try expr( gz, @@ -1176,6 +1166,16 @@ fn fnProtoExpr( else Zir.Inst.Ref.none; + const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; + const is_inferred_error = token_tags[maybe_bang] == .bang; + if (is_inferred_error) { + return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); + } + var ret_gz = gz.makeSubBlock(scope); + defer ret_gz.instructions.deinit(gpa); + const ret_ty = try expr(&ret_gz, scope, coerced_type_rl, fn_proto.ast.return_type); + const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); + const result = try gz.addFunc(.{ .src_node = fn_proto.ast.proto_node, .param_block = 0, @@ -3182,11 +3182,6 @@ fn fnDecl( break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr); }; - var ret_gz = decl_gz.makeSubBlock(params_scope); - defer ret_gz.instructions.deinit(gpa); - const ret_ty = try expr(&ret_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type); - const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); - const cc: Zir.Inst.Ref = blk: { if (fn_proto.ast.callconv_expr != 0) { if (has_inline_keyword) { @@ -3212,6 +3207,11 @@ fn fnDecl( } }; + var ret_gz = decl_gz.makeSubBlock(params_scope); + defer ret_gz.instructions.deinit(gpa); + const ret_ty = try expr(&ret_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type); + const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); + const func_inst: Zir.Inst.Ref = if (body_node == 0) func: { if (!is_extern) { return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{}); diff --git a/test/behavior/fn_stage1.zig b/test/behavior/fn_stage1.zig index 06c5d8fdb2..9368b39a46 100644 --- a/test/behavior/fn_stage1.zig +++ b/test/behavior/fn_stage1.zig @@ -204,3 +204,19 @@ test "function with inferred error set but returning no error" { const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?; try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len); } + +const nComplexCallconv = 100; +fn fComplexCallconvRet(x: u32) callconv(blk: { + const s: struct { n: u32 } = .{ .n = nComplexCallconv }; + break :blk switch (s.n) { + 0 => .C, + 1 => .Inline, + else => .Unspecified, + }; +}) struct { x: u32 } { + return .{ .x = x * x }; +} + +test "function with complex callconv and return type expressions" { + try expect(fComplexCallconvRet(3).x == 9); +}