diff --git a/BRANCH_TODO b/BRANCH_TODO index 6d84727fe3..ffaf14fc9e 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -737,3 +737,27 @@ fn errorSetDecl( try mod.analyzeExport(&decl_scope.base, export_src, name, decl); } } + + fn writeFuncExtra( + self: *Writer, + stream: anytype, + inst: Inst.Index, + var_args: bool, + ) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const extra = self.code.extraData(Inst.FuncExtra, inst_data.payload_index); + const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); + const cc = extra.data.cc; + const body = self.code.extra[extra.end + param_types.len ..][0..extra.data.body_len]; + return self.writeFuncCommon( + stream, + param_types, + extra.data.return_type, + var_args, + cc, + body, + src, + ); + } + diff --git a/src/AstGen.zig b/src/AstGen.zig index 88f26baa4f..21b38b905e 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1375,9 +1375,7 @@ fn blockExprStmts( .field_ptr_named, .field_val_named, .func, - .func_var_args, - .func_extra, - .func_extra_var_args, + .func_inferred, .int, .float, .float128, @@ -2129,9 +2127,8 @@ fn fnDecl( } const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; - if (token_tags[maybe_bang] == .bang) { - return astgen.failTok(maybe_bang, "TODO implement inferred error sets", .{}); - } + const is_inferred_error = token_tags[maybe_bang] == .bang; + const return_type_inst = try AstGen.expr( &decl_gz, &decl_gz.base, @@ -2153,31 +2150,24 @@ fn fnDecl( const func_inst: Zir.Inst.Ref = if (body_node == 0) func: { if (is_extern) { - return astgen.failNode(fn_proto.ast.fn_token, "non-extern function has no body", .{}); + return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{}); } - - if (cc != .none or lib_name != 0) { - const tag: Zir.Inst.Tag = if (is_var_args) .func_extra_var_args else .func_extra; - break :func try decl_gz.addFuncExtra(tag, .{ - .src_node = fn_proto.ast.proto_node, - .ret_ty = return_type_inst, - .param_types = param_types, - .cc = cc, - .lib_name = lib_name, - .body = &[0]Zir.Inst.Index{}, - }); + if (is_inferred_error) { + return astgen.failTok(maybe_bang, "function prototype requires explicit error set", .{}); } - - const tag: Zir.Inst.Tag = if (is_var_args) .func_var_args else .func; - break :func try decl_gz.addFunc(tag, .{ + break :func try decl_gz.addFunc(.{ .src_node = fn_proto.ast.proto_node, .ret_ty = return_type_inst, .param_types = param_types, .body = &[0]Zir.Inst.Index{}, + .cc = cc, + .lib_name = lib_name, + .is_var_args = is_var_args, + .is_inferred_error = false, }); } else func: { if (is_var_args) { - return astgen.failNode(fn_proto.ast.fn_token, "non-extern function is variadic", .{}); + return astgen.failTok(fn_proto.ast.fn_token, "non-extern function is variadic", .{}); } var fn_gz: Scope.GenZir = .{ @@ -2224,30 +2214,20 @@ fn fnDecl( if (fn_gz.instructions.items.len == 0 or !astgen.instructions.items(.tag)[fn_gz.instructions.items.len - 1].isNoReturn()) { - // astgen uses result location semantics to coerce return operands. // Since we are adding the return instruction here, we must handle the coercion. // We do this by using the `ret_coerce` instruction. _ = try fn_gz.addUnTok(.ret_coerce, .void_value, tree.lastToken(body_node)); } - if (cc != .none or lib_name != 0) { - const tag: Zir.Inst.Tag = if (is_var_args) .func_extra_var_args else .func_extra; - break :func try decl_gz.addFuncExtra(tag, .{ - .src_node = fn_proto.ast.proto_node, - .ret_ty = return_type_inst, - .param_types = param_types, - .cc = cc, - .lib_name = lib_name, - .body = fn_gz.instructions.items, - }); - } - - const tag: Zir.Inst.Tag = if (is_var_args) .func_var_args else .func; - break :func try decl_gz.addFunc(tag, .{ + break :func try decl_gz.addFunc(.{ .src_node = fn_proto.ast.proto_node, .ret_ty = return_type_inst, .param_types = param_types, .body = fn_gz.instructions.items, + .cc = cc, + .lib_name = lib_name, + .is_var_args = is_var_args, + .is_inferred_error = is_inferred_error, }); }; diff --git a/src/Module.zig b/src/Module.zig index bc6d0d4a6c..996b83c7ed 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1278,79 +1278,90 @@ pub const Scope = struct { } } - pub fn addFuncExtra(gz: *GenZir, tag: Zir.Inst.Tag, args: struct { + pub fn addFunc(gz: *GenZir, args: struct { src_node: ast.Node.Index, param_types: []const Zir.Inst.Ref, + body: []const Zir.Inst.Index, ret_ty: Zir.Inst.Ref, cc: Zir.Inst.Ref, - body: []const Zir.Inst.Index, lib_name: u32, + is_var_args: bool, + is_inferred_error: bool, }) !Zir.Inst.Ref { assert(args.src_node != 0); assert(args.ret_ty != .none); - assert(args.cc != .none); - const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); - try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); - try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + - @typeInfo(Zir.Inst.FuncExtra).Struct.fields.len + args.param_types.len + - args.body.len); + const astgen = gz.astgen; + const gpa = astgen.gpa; - const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.FuncExtra{ - .return_type = args.ret_ty, - .cc = args.cc, - .param_types_len = @intCast(u32, args.param_types.len), - .body_len = @intCast(u32, args.body.len), - .lib_name = args.lib_name, - }); - gz.astgen.appendRefsAssumeCapacity(args.param_types); - gz.astgen.extra.appendSliceAssumeCapacity(args.body); + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try astgen.instructions.ensureUnusedCapacity(gpa, 1); - const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); - gz.astgen.instructions.appendAssumeCapacity(.{ - .tag = tag, - .data = .{ .pl_node = .{ + if (args.cc != .none or args.lib_name != 0 or args.is_var_args) { + try astgen.extra.ensureUnusedCapacity( + gpa, + @typeInfo(Zir.Inst.ExtendedFunc).Struct.fields.len + + args.param_types.len + args.body.len + + @boolToInt(args.lib_name != 0) + + @boolToInt(args.cc != .none), + ); + const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedFunc{ .src_node = gz.nodeIndexToRelative(args.src_node), - .payload_index = payload_index, - } }, - }); - gz.instructions.appendAssumeCapacity(new_index); - return gz.indexToRef(new_index); - } + .return_type = args.ret_ty, + .param_types_len = @intCast(u32, args.param_types.len), + .body_len = @intCast(u32, args.body.len), + }); + if (args.cc != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.cc)); + } + if (args.lib_name != 0) { + astgen.extra.appendAssumeCapacity(args.lib_name); + } + astgen.appendRefsAssumeCapacity(args.param_types); + astgen.extra.appendSliceAssumeCapacity(args.body); - pub fn addFunc(gz: *GenZir, tag: Zir.Inst.Tag, args: struct { - src_node: ast.Node.Index, - ret_ty: Zir.Inst.Ref, - param_types: []const Zir.Inst.Ref, - body: []const Zir.Inst.Index, - }) !Zir.Inst.Ref { - assert(args.src_node != 0); - assert(args.ret_ty != .none); - const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); - try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); - try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + - @typeInfo(Zir.Inst.Func).Struct.fields.len + args.param_types.len + - args.body.len); + const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); + astgen.instructions.appendAssumeCapacity(.{ + .tag = .extended, + .data = .{ .extended = .{ + .opcode = .func, + .small = @bitCast(u16, Zir.Inst.ExtendedFunc.Small{ + .is_var_args = args.is_var_args, + .is_inferred_error = args.is_inferred_error, + .has_lib_name = args.lib_name != 0, + .has_cc = args.cc != .none, + }), + .operand = payload_index, + } }, + }); + gz.instructions.appendAssumeCapacity(new_index); + return gz.indexToRef(new_index); + } else { + try gz.astgen.extra.ensureUnusedCapacity( + gpa, + @typeInfo(Zir.Inst.Func).Struct.fields.len + + args.param_types.len + args.body.len, + ); - const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Func{ - .return_type = args.ret_ty, - .param_types_len = @intCast(u32, args.param_types.len), - .body_len = @intCast(u32, args.body.len), - }); - gz.astgen.appendRefsAssumeCapacity(args.param_types); - gz.astgen.extra.appendSliceAssumeCapacity(args.body); + const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Func{ + .return_type = args.ret_ty, + .param_types_len = @intCast(u32, args.param_types.len), + .body_len = @intCast(u32, args.body.len), + }); + gz.astgen.appendRefsAssumeCapacity(args.param_types); + gz.astgen.extra.appendSliceAssumeCapacity(args.body); - const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); - gz.astgen.instructions.appendAssumeCapacity(.{ - .tag = tag, - .data = .{ .pl_node = .{ - .src_node = gz.nodeIndexToRelative(args.src_node), - .payload_index = payload_index, - } }, - }); - gz.instructions.appendAssumeCapacity(new_index); - return gz.indexToRef(new_index); + const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func; + const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); + gz.astgen.instructions.appendAssumeCapacity(.{ + .tag = tag, + .data = .{ .pl_node = .{ + .src_node = gz.nodeIndexToRelative(args.src_node), + .payload_index = payload_index, + } }, + }); + gz.instructions.appendAssumeCapacity(new_index); + return gz.indexToRef(new_index); + } } pub fn addCall( diff --git a/src/Sema.zig b/src/Sema.zig index 32a4a63ef9..f2a8e798df 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -194,9 +194,7 @@ pub fn analyzeBody( .field_val => try sema.zirFieldVal(block, inst), .field_val_named => try sema.zirFieldValNamed(block, inst), .func => try sema.zirFunc(block, inst, false), - .func_extra => try sema.zirFuncExtra(block, inst, false), - .func_extra_var_args => try sema.zirFuncExtra(block, inst, true), - .func_var_args => try sema.zirFunc(block, inst, true), + .func_inferred => try sema.zirFunc(block, inst, true), .import => try sema.zirImport(block, inst), .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), .int => try sema.zirInt(block, inst), @@ -2646,7 +2644,12 @@ fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde } } -fn zirFunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, var_args: bool) InnerError!*Inst { +fn zirFunc( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, + inferred_error_set: bool, +) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -2654,40 +2657,17 @@ fn zirFunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, var_args: boo const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len); + const body = sema.code.extra[extra.end + param_types.len ..][0..extra.data.body_len]; return sema.funcCommon( block, inst_data.src_node, param_types, + body, extra.data.return_type, .Unspecified, - var_args, - ); -} - -fn zirFuncExtra(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, var_args: bool) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; - const extra = sema.code.extraData(Zir.Inst.FuncExtra, inst_data.payload_index); - const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len); - - const cc_tv = try sema.resolveInstConst(block, cc_src, extra.data.cc); - // TODO once we're capable of importing and analyzing decls from - // std.builtin, this needs to change - const cc_str = cc_tv.val.castTag(.enum_literal).?.data; - const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse - return sema.mod.fail(&block.base, cc_src, "Unknown calling convention {s}", .{cc_str}); - return sema.funcCommon( - block, - inst_data.src_node, - param_types, - extra.data.return_type, - cc, - var_args, + false, + inferred_error_set, ); } @@ -2696,9 +2676,11 @@ fn funcCommon( block: *Scope.Block, src_node_offset: i32, zir_param_types: []const Zir.Inst.Ref, + body: []const Zir.Inst.Index, zir_return_type: Zir.Inst.Ref, cc: std.builtin.CallingConvention, var_args: bool, + inferred_error_set: bool, ) InnerError!*Inst { const src: LazySrcLoc = .{ .node_offset = src_node_offset }; const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; @@ -5307,15 +5289,68 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro const extended = sema.code.instructions.items(.data)[inst].extended; switch (extended.opcode) { // zig fmt: off - .c_undef => return sema.zirCUndef( block, inst, extended), - .c_include => return sema.zirCInclude( block, inst, extended), - .c_define => return sema.zirCDefine( block, inst, extended), - .wasm_memory_size => return sema.zirWasmMemorySize( block, inst, extended), - .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, inst, extended), + .func => return sema.zirFuncExtended( block, inst, extended), + .c_undef => return sema.zirCUndef( block, inst, extended), + .c_include => return sema.zirCInclude( block, inst, extended), + .c_define => return sema.zirCDefine( block, inst, extended), + .wasm_memory_size => return sema.zirWasmMemorySize(block, inst, extended), + .wasm_memory_grow => return sema.zirWasmMemoryGrow(block, inst, extended), // zig fmt: on } } +fn zirFuncExtended( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, + extended: Zir.Inst.Extended.InstData, +) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + + const extra = sema.code.extraData(Zir.Inst.ExtendedFunc, extended.operand); + const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; + const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = extra.data.src_node }; + const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small); + + var extra_index: usize = extra.end; + if (small.has_lib_name) { + const lib_name = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); + extra_index += 1; + return sema.mod.fail(&block.base, src, "TODO: implement Sema func lib name", .{}); + } + const cc: std.builtin.CallingConvention = if (small.has_cc) blk: { + const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); + extra_index += 1; + const cc_tv = try sema.resolveInstConst(block, cc_src, cc_ref); + // TODO this needs to resolve other kinds of Value tags rather than + // assuming the tag will be .enum_field_index. + const cc_field_index = cc_tv.val.castTag(.enum_field_index).?.data; + // TODO should `@intToEnum` do this `@intCast` for you? + const cc = @intToEnum( + std.builtin.CallingConvention, + @intCast(@typeInfo(std.builtin.CallingConvention).Enum.tag_type, cc_field_index), + ); + break :blk cc; + } else .Unspecified; + + const param_types = sema.code.refSlice(extra_index, extra.data.param_types_len); + extra_index += 1; + + const body = sema.code.extra[extra_index..][0..extra.data.body_len]; + + return sema.funcCommon( + block, + extra.data.src_node, + param_types, + body, + extra.data.return_type, + cc, + small.is_var_args, + small.is_inferred_error, + ); +} + fn zirCUndef( sema: *Sema, block: *Scope.Block, diff --git a/src/Zir.zig b/src/Zir.zig index a454ed500f..c8d27e870e 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -371,15 +371,8 @@ pub const Inst = struct { /// the body_len is 0. Calling convention is auto. /// Uses the `pl_node` union field. `payload_index` points to a `Func`. func, - /// Same as `func` but the function is variadic. - func_var_args, - /// Same as `func` but with extra fields: - /// * calling convention - /// * extern lib name - /// Uses the `pl_node` union field. `payload_index` points to a `FuncExtra`. - func_extra, - /// Same as `func_extra` but the function is variadic. - func_extra_var_args, + /// Same as `func` but has an inferred error set. + func_inferred, /// Implements the `@import` builtin. /// Uses the `str_tok` field. import, @@ -1010,9 +1003,7 @@ pub const Inst = struct { .field_ptr_named, .field_val_named, .func, - .func_var_args, - .func_extra, - .func_extra_var_args, + .func_inferred, .has_decl, .int, .float, @@ -1211,6 +1202,11 @@ pub const Inst = struct { /// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum. /// `noreturn` instructions may not go here; they must be part of the main `Tag` enum. pub const Extended = enum(u16) { + /// Represents a function declaration or function prototype, depending on + /// whether body_len is 0. + /// `operand` is payload index to `ExtendedFunc`. + /// `small` is `ExtendedFunc.Small`. + func, /// `operand` is payload index to `UnNode`. c_undef, /// `operand` is payload index to `UnNode`. @@ -1774,15 +1770,23 @@ pub const Inst = struct { }; /// Trailing: - /// 0. param_type: Ref // for each param_types_len - /// 1. body: Index // for each body_len - pub const FuncExtra = struct { - cc: Ref, - /// null terminated string index, or 0 to mean none. - lib_name: u32, + /// 0. lib_name: u32, // null terminated string index, if has_lib_name is set + /// 1. cc: Ref, // if has_cc is set + /// 2. param_type: Ref // for each param_types_len + /// 3. body: Index // for each body_len + pub const ExtendedFunc = struct { + src_node: i32, return_type: Ref, param_types_len: u32, body_len: u32, + + pub const Small = packed struct { + is_var_args: bool, + is_inferred_error: bool, + has_lib_name: bool, + has_cc: bool, + _: u12 = undefined, + }; }; /// Trailing: @@ -2459,9 +2463,7 @@ const Writer = struct { => try self.writeStrTok(stream, inst), .func => try self.writeFunc(stream, inst, false), - .func_extra => try self.writeFuncExtra(stream, inst, false), - .func_var_args => try self.writeFunc(stream, inst, true), - .func_extra_var_args => try self.writeFuncExtra(stream, inst, true), + .func_inferred => try self.writeFunc(stream, inst, true), .@"unreachable" => try self.writeUnreachable(stream, inst), @@ -3099,7 +3101,7 @@ const Writer = struct { self: *Writer, stream: anytype, inst: Inst.Index, - var_args: bool, + inferred_error_set: bool, ) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); @@ -3110,36 +3112,14 @@ const Writer = struct { stream, param_types, extra.data.return_type, - var_args, + inferred_error_set, + false, .none, body, src, ); } - fn writeFuncExtra( - self: *Writer, - stream: anytype, - inst: Inst.Index, - var_args: bool, - ) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const extra = self.code.extraData(Inst.FuncExtra, inst_data.payload_index); - const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); - const cc = extra.data.cc; - const body = self.code.extra[extra.end + param_types.len ..][0..extra.data.body_len]; - return self.writeFuncCommon( - stream, - param_types, - extra.data.return_type, - var_args, - cc, - body, - src, - ); - } - fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].bool_br; const extra = self.code.extraData(Inst.Block, inst_data.payload_index); @@ -3184,6 +3164,7 @@ const Writer = struct { stream: anytype, param_types: []const Inst.Ref, ret_ty: Inst.Ref, + inferred_error_set: bool, var_args: bool, cc: Inst.Ref, body: []const Inst.Index, @@ -3197,7 +3178,8 @@ const Writer = struct { try stream.writeAll("], "); try self.writeInstRef(stream, ret_ty); try self.writeOptionalInstRef(stream, ", cc=", cc); - try self.writeFlag(stream, ", var_args", var_args); + try self.writeFlag(stream, ", vargs", var_args); + try self.writeFlag(stream, ", inferror", inferred_error_set); try stream.writeAll(", {\n"); self.indent += 2;