diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-03-06 13:35:48 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-06 13:35:48 -0800 |
| commit | 34cb5934ddd7c79fb96ffed87768764247740260 (patch) | |
| tree | d4ca1d9e3c6a335caf311de1383e45758f221438 | |
| parent | 9f722f43ac449caa0e09c1b7bb1bad0581ef323d (diff) | |
| parent | 8c6e7fb2c7488faab0c41a4ea241f0110237ce91 (diff) | |
| download | zig-34cb5934ddd7c79fb96ffed87768764247740260.tar.gz zig-34cb5934ddd7c79fb96ffed87768764247740260.zip | |
Merge pull request #7910 from Vexu/stage2-async
Stage2: astgen async stuff, implement var args functions
| -rw-r--r-- | src/Module.zig | 139 | ||||
| -rw-r--r-- | src/astgen.zig | 115 | ||||
| -rw-r--r-- | src/codegen/c.zig | 9 | ||||
| -rw-r--r-- | src/test.zig | 1 | ||||
| -rw-r--r-- | src/type.zig | 47 | ||||
| -rw-r--r-- | src/zir.zig | 31 | ||||
| -rw-r--r-- | src/zir_sema.zig | 58 | ||||
| -rw-r--r-- | test/stage2/cbe.zig | 13 |
8 files changed, 370 insertions, 43 deletions
diff --git a/src/Module.zig b/src/Module.zig index e6d509ace5..585925c4a0 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -370,6 +370,8 @@ pub const Scope = struct { .gen_zir => return self.cast(GenZIR).?.arena, .local_val => return self.cast(LocalVal).?.gen_zir.arena, .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena, + .gen_suspend => return self.cast(GenZIR).?.arena, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.arena, .file => unreachable, .container => unreachable, } @@ -385,6 +387,8 @@ pub const Scope = struct { .gen_zir => self.cast(GenZIR).?.decl, .local_val => self.cast(LocalVal).?.gen_zir.decl, .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, + .gen_suspend => return self.cast(GenZIR).?.decl, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl, .file => null, .container => null, }; @@ -396,6 +400,8 @@ pub const Scope = struct { .gen_zir => self.cast(GenZIR).?.decl, .local_val => self.cast(LocalVal).?.gen_zir.decl, .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, + .gen_suspend => return self.cast(GenZIR).?.decl, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl, .file => null, .container => null, }; @@ -410,6 +416,8 @@ pub const Scope = struct { .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container, .file => return &self.cast(File).?.root_container, .container => return self.cast(Container).?, + .gen_suspend => return self.cast(GenZIR).?.decl.container, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl.container, } } @@ -422,6 +430,8 @@ pub const Scope = struct { .gen_zir => unreachable, .local_val => unreachable, .local_ptr => unreachable, + .gen_suspend => unreachable, + .gen_nosuspend => unreachable, .file => unreachable, .container => return self.cast(Container).?.fullyQualifiedNameHash(name), } @@ -436,6 +446,8 @@ pub const Scope = struct { .local_val => return &self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree, .local_ptr => return &self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree, .container => return &self.cast(Container).?.file_scope.tree, + .gen_suspend => return &self.cast(GenZIR).?.decl.container.file_scope.tree, + .gen_nosuspend => return &self.cast(Nosuspend).?.gen_zir.decl.container.file_scope.tree, } } @@ -443,9 +455,10 @@ pub const Scope = struct { pub fn getGenZIR(self: *Scope) *GenZIR { return switch (self.tag) { .block => unreachable, - .gen_zir => self.cast(GenZIR).?, + .gen_zir, .gen_suspend => self.cast(GenZIR).?, .local_val => return self.cast(LocalVal).?.gen_zir, .local_ptr => return self.cast(LocalPtr).?.gen_zir, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir, .file => unreachable, .container => unreachable, }; @@ -461,6 +474,8 @@ pub const Scope = struct { .gen_zir => unreachable, .local_val => unreachable, .local_ptr => unreachable, + .gen_suspend => unreachable, + .gen_nosuspend => unreachable, } } @@ -472,6 +487,8 @@ pub const Scope = struct { .local_val => unreachable, .local_ptr => unreachable, .block => unreachable, + .gen_suspend => unreachable, + .gen_nosuspend => unreachable, } } @@ -486,6 +503,36 @@ pub const Scope = struct { .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, .block => return @fieldParentPtr(Block, "base", cur).src_decl.container.file_scope, + .gen_suspend => @fieldParentPtr(GenZIR, "base", cur).parent, + .gen_nosuspend => @fieldParentPtr(Nosuspend, "base", cur).parent, + }; + } + } + + pub fn getSuspend(base: *Scope) ?*Scope.GenZIR { + var cur = base; + while (true) { + cur = switch (cur.tag) { + .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent, + .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, + .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, + .gen_nosuspend => @fieldParentPtr(Nosuspend, "base", cur).parent, + .gen_suspend => return @fieldParentPtr(GenZIR, "base", cur), + else => return null, + }; + } + } + + pub fn getNosuspend(base: *Scope) ?*Scope.Nosuspend { + var cur = base; + while (true) { + cur = switch (cur.tag) { + .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent, + .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, + .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, + .gen_suspend => @fieldParentPtr(GenZIR, "base", cur).parent, + .gen_nosuspend => return @fieldParentPtr(Nosuspend, "base", cur), + else => return null, }; } } @@ -507,6 +554,8 @@ pub const Scope = struct { gen_zir, local_val, local_ptr, + gen_suspend, + gen_nosuspend, }; pub const Container = struct { @@ -740,6 +789,8 @@ pub const Scope = struct { /// so they can possibly be elided later if the labeled block ends up not needing /// a result location pointer. labeled_store_to_block_ptr_list: std.ArrayListUnmanaged(*zir.Inst.BinOp) = .{}, + /// for suspend error notes + src: usize = 0, pub const Label = struct { token: ast.TokenIndex, @@ -773,6 +824,16 @@ pub const Scope = struct { name: []const u8, ptr: *zir.Inst, }; + + pub const Nosuspend = struct { + pub const base_tag: Tag = .gen_nosuspend; + + base: Scope = Scope{ .tag = base_tag }, + /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. + parent: *Scope, + gen_zir: *GenZIR, + src: usize, + }; }; /// This struct holds data necessary to construct API-facing `AllErrors.Message`. @@ -1122,7 +1183,8 @@ fn astgenAndSemaFn( const param_count = blk: { var count: usize = 0; var it = fn_proto.iterate(tree); - while (it.next()) |_| { + while (it.next()) |param| { + if (param.anytype_ellipsis3) |some| if (token_tags[some] == .ellipsis3) break; count += 1; } break :blk count; @@ -1135,6 +1197,7 @@ fn astgenAndSemaFn( }); const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; + var is_var_args = false; { var param_type_i: usize = 0; var it = fn_proto.iterate(tree); @@ -1147,12 +1210,10 @@ fn astgenAndSemaFn( "TODO implement anytype parameter", .{}, ), - .ellipsis3 => return mod.failTok( - &fn_type_scope.base, - token, - "TODO implement var args", - .{}, - ), + .ellipsis3 => { + is_var_args = true; + break; + }, else => unreachable, } } @@ -1234,7 +1295,13 @@ fn astgenAndSemaFn( type_type_rl, fn_proto.ast.return_type, ); - const fn_type_inst = if (fn_proto.ast.callconv_expr != 0) cc: { + + const is_extern = if (fn_proto.extern_export_token) |maybe_export_token| + token_tags[maybe_export_token] == .keyword_extern + else + false; + + const cc_inst = if (fn_proto.ast.callconv_expr != 0) cc: { // TODO instead of enum literal type, this needs to be the // std.builtin.CallingConvention enum. We need to implement importing other files // and enums in order to fix this. @@ -1243,18 +1310,31 @@ fn astgenAndSemaFn( .ty = Type.initTag(.type), .val = Value.initTag(.enum_literal_type), }); - const cc = try astgen.comptimeExpr(mod, &fn_type_scope.base, .{ + break :cc try astgen.comptimeExpr(mod, &fn_type_scope.base, .{ .ty = enum_lit_ty, }, fn_proto.ast.callconv_expr); - break :cc try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{ + } else if (is_extern) cc: { + // note: https://github.com/ziglang/zig/issues/5269 + const src = token_starts[fn_proto.extern_export_token.?]; + break :cc try astgen.addZIRInst(mod, &fn_type_scope.base, src, zir.Inst.EnumLiteral, .{ .name = "C" }, .{}); + } else null; + + const fn_type_inst = if (cc_inst) |cc| fn_type: { + var fn_type = try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type_cc, .{ .return_type = return_type_inst, .param_types = param_types, .cc = cc, }); - } else try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{ - .return_type = return_type_inst, - .param_types = param_types, - }); + if (is_var_args) fn_type.tag = .fn_type_cc_var_args; + break :fn_type fn_type; + } else fn_type: { + var fn_type = try astgen.addZirInstTag(mod, &fn_type_scope.base, fn_src, .fn_type, .{ + .return_type = return_type_inst, + .param_types = param_types, + }); + if (is_var_args) fn_type.tag = .fn_type_var_args; + break :fn_type fn_type; + }; if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { zir.dumpZir(mod.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; @@ -1287,7 +1367,12 @@ fn astgenAndSemaFn( const fn_type = try zir_sema.analyzeBodyValueAsType(mod, &block_scope, fn_type_inst, .{ .instructions = fn_type_scope.instructions.items, }); + if (body_node == 0) { + if (!is_extern) { + return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function has no body", .{}); + } + // Extern function. var type_changed = true; if (decl.typedValueManaged()) |tvm| { @@ -1317,6 +1402,10 @@ fn astgenAndSemaFn( return type_changed; } + if (fn_type.fnIsVarArgs()) { + return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function is variadic", .{}); + } + const new_func = try decl_arena.allocator.create(Fn); const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); @@ -3295,6 +3384,9 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty } pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!*Inst { + if (dest_type.tag() == .var_args_param) { + return self.coerceVarArgParam(scope, inst); + } // If the types are the same, we can return the operand. if (dest_type.eql(inst.ty)) return inst; @@ -3447,6 +3539,15 @@ pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) Inn return null; } +pub fn coerceVarArgParam(mod: *Module, scope: *Scope, inst: *Inst) !*Inst { + switch (inst.ty.zigTypeTag()) { + .ComptimeInt, .ComptimeFloat => return mod.fail(scope, inst.src, "integer and float literals in var args function must be casted", .{}), + else => {}, + } + // TODO implement more of this function. + return inst; +} + pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst { if (ptr.ty.isConstPtr()) return self.fail(scope, src, "cannot assign to constant", .{}); @@ -3586,7 +3687,7 @@ pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) I } self.failed_decls.putAssumeCapacityNoClobber(block.owner_decl, err_msg); }, - .gen_zir => { + .gen_zir, .gen_suspend => { const gen_zir = scope.cast(Scope.GenZIR).?; gen_zir.decl.analysis = .sema_failure; gen_zir.decl.generation = self.generation; @@ -3604,6 +3705,12 @@ pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) I gen_zir.decl.generation = self.generation; self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); }, + .gen_nosuspend => { + const gen_zir = scope.cast(Scope.Nosuspend).?.gen_zir; + gen_zir.decl.analysis = .sema_failure; + gen_zir.decl.generation = self.generation; + self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); + }, .file => unreachable, .container => unreachable, } diff --git a/src/astgen.zig b/src/astgen.zig index aaf38ed1ea..939fba2ff3 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -626,10 +626,13 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs), .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node), + .@"nosuspend" => return nosuspendExpr(mod, scope, rl, node), + .@"suspend" => return rvalue(mod, scope, rl, try suspendExpr(mod, scope, node)), + .@"await" => return awaitExpr(mod, scope, rl, node), + .@"resume" => return rvalue(mod, scope, rl, try resumeExpr(mod, scope, node)), + .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}), - .@"await" => return mod.failNode(scope, node, "TODO implement astgen.expr for .await", .{}), - .@"resume" => return mod.failNode(scope, node, "TODO implement astgen.expr for .resume", .{}), .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), .array_init_one, @@ -652,15 +655,12 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .struct_init_comma, => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}), - .@"suspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .suspend", .{}), .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), .fn_proto_simple, .fn_proto_multi, .fn_proto_one, .fn_proto, => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}), - - .@"nosuspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .nosuspend", .{}), } } @@ -766,6 +766,8 @@ fn breakExpr( }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent, else => if (break_label != 0) { const label_name = try mod.identifierTokenString(parent_scope, break_label); return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); @@ -819,6 +821,8 @@ fn continueExpr( }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent, else => if (break_label != 0) { const label_name = try mod.identifierTokenString(parent_scope, break_label); return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); @@ -893,6 +897,8 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent, else => return, } } @@ -1100,6 +1106,8 @@ fn varDecl( s = local_ptr.parent; }, .gen_zir => s = s.cast(Scope.GenZIR).?.parent, + .gen_suspend => s = s.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => s = s.cast(Scope.Nosuspend).?.parent, else => break, }; } @@ -3021,6 +3029,8 @@ fn identifier( s = local_ptr.parent; }, .gen_zir => s = s.cast(Scope.GenZIR).?.parent, + .gen_suspend => s = s.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => s = s.cast(Scope.Nosuspend).?.parent, else => break, }; } @@ -3633,14 +3643,109 @@ fn callExpr( } const src = token_starts[call.ast.lparen]; + var modifier: std.builtin.CallOptions.Modifier = .auto; + if (call.async_token) |_| modifier = .async_kw; + const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{ .func = lhs, .args = args, + .modifier = modifier, }, .{}); // TODO function call with result location return rvalue(mod, scope, rl, result); } +fn suspendExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]]; + + if (scope.getNosuspend()) |some| { + const msg = msg: { + const msg = try mod.errMsg(scope, src, "suspend in nosuspend block", .{}); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some.src, msg, "nosuspend block here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + + if (scope.getSuspend()) |some| { + const msg = msg: { + const msg = try mod.errMsg(scope, src, "cannot suspend inside suspend block", .{}); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some.src, msg, "other suspend block here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + + var suspend_scope: Scope.GenZIR = .{ + .base = .{ .tag = .gen_suspend }, + .parent = scope, + .decl = scope.ownerDecl().?, + .arena = scope.arena(), + .force_comptime = scope.isComptime(), + .instructions = .{}, + }; + defer suspend_scope.instructions.deinit(mod.gpa); + + const operand = tree.nodes.items(.data)[node].lhs; + if (operand != 0) { + const possibly_unused_result = try expr(mod, &suspend_scope.base, .none, operand); + if (!possibly_unused_result.tag.isNoReturn()) { + _ = try addZIRUnOp(mod, &suspend_scope.base, src, .ensure_result_used, possibly_unused_result); + } + } else { + return addZIRNoOp(mod, scope, src, .@"suspend"); + } + + const block = try addZIRInstBlock(mod, scope, src, .suspend_block, .{ + .instructions = try scope.arena().dupe(*zir.Inst, suspend_scope.instructions.items), + }); + return &block.base; +} + +fn nosuspendExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + var child_scope = Scope.Nosuspend{ + .parent = scope, + .gen_zir = scope.getGenZIR(), + .src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]], + }; + + return expr(mod, &child_scope.base, rl, tree.nodes.items(.data)[node].lhs); +} + +fn awaitExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]]; + const is_nosuspend = scope.getNosuspend() != null; + + // TODO some @asyncCall stuff + + if (scope.getSuspend()) |some| { + const msg = msg: { + const msg = try mod.errMsg(scope, src, "cannot await inside suspend block", .{}); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some.src, msg, "suspend block here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + + const operand = try expr(mod, scope, .ref, tree.nodes.items(.data)[node].lhs); + // TODO pass result location + return addZIRUnOp(mod, scope, src, if (is_nosuspend) .nosuspend_await else .@"await", operand); +} + +fn resumeExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]]; + + const operand = try expr(mod, scope, .ref, tree.nodes.items(.data)[node].lhs); + return addZIRUnOp(mod, scope, src, .@"resume", operand); +} + pub const simple_types = std.ComptimeStringMap(Value.Tag, .{ .{ "u8", .u8_type }, .{ "i8", .i8_type }, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a885b984ac..1a323441d9 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -215,8 +215,9 @@ pub const DeclGen = struct { try dg.renderType(w, tv.ty.fnReturnType()); const decl_name = mem.span(dg.decl.name); try w.print(" {s}(", .{decl_name}); - var param_len = tv.ty.fnParamLen(); - if (param_len == 0) + const param_len = tv.ty.fnParamLen(); + const is_var_args = tv.ty.fnIsVarArgs(); + if (param_len == 0 and !is_var_args) try w.writeAll("void") else { var index: usize = 0; @@ -228,6 +229,10 @@ pub const DeclGen = struct { try w.print(" a{d}", .{index}); } } + if (is_var_args) { + if (param_len != 0) try w.writeAll(", "); + try w.writeAll("..."); + } try w.writeByte(')'); } diff --git a/src/test.zig b/src/test.zig index d2c368d8ea..47aeef3385 100644 --- a/src/test.zig +++ b/src/test.zig @@ -871,6 +871,7 @@ pub const TestContext = struct { "-std=c89", "-pedantic", "-Werror", + "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 "-Wno-declaration-after-statement", "--", "-lc", diff --git a/src/type.zig b/src/type.zig index 38fe6dd3e6..bb8dcea390 100644 --- a/src/type.zig +++ b/src/type.zig @@ -97,6 +97,8 @@ pub const Type = extern union { .@"struct", .empty_struct => return .Struct, .@"enum" => return .Enum, .@"union" => return .Union, + + .var_args_param => unreachable, // can be any type } } @@ -258,6 +260,8 @@ pub const Type = extern union { if (!a.fnParamType(i).eql(b.fnParamType(i))) return false; } + if (a.fnIsVarArgs() != b.fnIsVarArgs()) + return false; return true; }, .Optional => { @@ -323,6 +327,7 @@ pub const Type = extern union { while (i < params_len) : (i += 1) { std.hash.autoHash(&hasher, self.fnParamType(i).hash()); } + std.hash.autoHash(&hasher, self.fnIsVarArgs()); }, .Optional => { var buf: Payload.ElemType = undefined; @@ -397,6 +402,7 @@ pub const Type = extern union { .@"anyframe", .inferred_alloc_const, .inferred_alloc_mut, + .var_args_param, => unreachable, .array_u8, @@ -446,6 +452,7 @@ pub const Type = extern union { .return_type = try payload.return_type.copy(allocator), .param_types = param_types, .cc = payload.cc, + .is_var_args = payload.is_var_args, }); }, .pointer => { @@ -535,6 +542,7 @@ pub const Type = extern union { .comptime_int, .comptime_float, .noreturn, + .var_args_param, => return out_stream.writeAll(@tagName(t)), .enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"), @@ -558,6 +566,12 @@ pub const Type = extern union { if (i != 0) try out_stream.writeAll(", "); try param_type.format("", .{}, out_stream); } + if (payload.is_var_args) { + if (payload.param_types.len != 0) { + try out_stream.writeAll(", "); + } + try out_stream.writeAll("..."); + } try out_stream.writeAll(") callconv(."); try out_stream.writeAll(@tagName(payload.cc)); try out_stream.writeAll(")"); @@ -844,6 +858,7 @@ pub const Type = extern union { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, + .var_args_param => unreachable, }; } @@ -969,6 +984,7 @@ pub const Type = extern union { .inferred_alloc_const, .inferred_alloc_mut, .@"opaque", + .var_args_param, => unreachable, }; } @@ -995,6 +1011,7 @@ pub const Type = extern union { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, .@"opaque" => unreachable, + .var_args_param => unreachable, .u8, .i8, @@ -1179,6 +1196,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .single_const_pointer, @@ -1256,6 +1274,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, .const_slice, @@ -1354,6 +1373,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .const_slice, @@ -1434,6 +1454,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .single_const_pointer, @@ -1523,6 +1544,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .pointer => { @@ -1607,6 +1629,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .pointer => { @@ -1733,6 +1756,7 @@ pub const Type = extern union { .@"struct" => unreachable, .@"union" => unreachable, .@"opaque" => unreachable, + .var_args_param => unreachable, .array => self.castTag(.array).?.data.elem_type, .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type, @@ -1862,6 +1886,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, .array => self.castTag(.array).?.data.len, @@ -1936,6 +1961,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, .single_const_pointer, @@ -2025,6 +2051,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .int_signed, @@ -2110,6 +2137,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .int_unsigned, @@ -2181,6 +2209,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, .int_unsigned => .{ @@ -2280,6 +2309,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, .usize, @@ -2400,6 +2430,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, }; } @@ -2486,6 +2517,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, } } @@ -2571,6 +2603,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, } } @@ -2656,6 +2689,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, }; } @@ -2738,6 +2772,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, }; } @@ -2749,7 +2784,7 @@ pub const Type = extern union { .fn_void_no_args => false, .fn_naked_noreturn_no_args => false, .fn_ccc_void_no_args => false, - .function => false, + .function => self.castTag(.function).?.data.is_var_args, .f16, .f32, @@ -2820,6 +2855,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => unreachable, }; } @@ -2902,6 +2938,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => false, }; } @@ -2962,6 +2999,7 @@ pub const Type = extern union { .error_set, .error_set_single, .@"opaque", + .var_args_param, => return null, .@"enum" => @panic("TODO onePossibleValue enum"), @@ -3079,6 +3117,7 @@ pub const Type = extern union { .@"struct", .@"union", .@"opaque", + .var_args_param, => return false, .c_const_pointer, @@ -3168,6 +3207,7 @@ pub const Type = extern union { .pointer, .inferred_alloc_const, .inferred_alloc_mut, + .var_args_param, => unreachable, .empty_struct => self.castTag(.empty_struct).?.data, @@ -3285,6 +3325,9 @@ pub const Type = extern union { anyerror_void_error_union, @"anyframe", const_slice_u8, + /// This is a special type for variadic parameters of a function call. + /// Casts to it will validate that the type can be passed to a c calling convetion function. + var_args_param, /// This is a special value that tracks a set of types that have been stored /// to an inferred allocation. It does not support most of the normal type queries. /// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc. @@ -3373,6 +3416,7 @@ pub const Type = extern union { .const_slice_u8, .inferred_alloc_const, .inferred_alloc_mut, + .var_args_param, => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), .array_u8, @@ -3479,6 +3523,7 @@ pub const Type = extern union { param_types: []Type, return_type: Type, cc: std.builtin.CallingConvention, + is_var_args: bool, }, }; diff --git a/src/zir.zig b/src/zir.zig index 1331f26dc7..cb1f4561bf 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -61,6 +61,8 @@ pub const Inst = struct { as, /// Inline assembly. @"asm", + /// Await an async function. + @"await", /// Bitwise AND. `&` bit_and, /// TODO delete this instruction, it has no purpose. @@ -176,8 +178,12 @@ pub const Inst = struct { @"fn", /// Returns a function type, assuming unspecified calling convention. fn_type, + /// Same as `fn_type` but the function is variadic. + fn_type_var_args, /// Returns a function type, with a calling convention instruction operand. fn_type_cc, + /// Same as `fn_type_cc` but the function is variadic. + fn_type_cc_var_args, /// @import(operand) import, /// Integer literal. @@ -212,6 +218,8 @@ pub const Inst = struct { mul, /// Twos complement wrapping integer multiplication. mulwrap, + /// An await inside a nosuspend scope. + nosuspend_await, /// Given a reference to a function and a parameter index, returns the /// type of the parameter. TODO what happens when the parameter is `anytype`? param_type, @@ -226,6 +234,8 @@ pub const Inst = struct { /// the memory location is in the stack frame, local to the scope containing the /// instruction. ref, + /// Resume an async function. + @"resume", /// Obtains a pointer to the return value. ret_ptr, /// Obtains the return type of the in-scope function. @@ -348,6 +358,11 @@ pub const Inst = struct { enum_type, /// Does nothing; returns a void value. void_value, + /// Suspend an async function. + @"suspend", + /// Suspend an async function. + /// Same as .suspend but with a block. + suspend_block, /// A switch expression. switchbr, /// Same as `switchbr` but the target is a pointer to the value being switched on. @@ -369,6 +384,7 @@ pub const Inst = struct { .unreachable_unsafe, .unreachable_safe, .void_value, + .@"suspend", => NoOp, .alloc, @@ -417,6 +433,9 @@ pub const Inst = struct { .import, .set_eval_branch_quota, .indexable_ptr_len, + .@"resume", + .@"await", + .nosuspend_await, => UnOp, .add, @@ -461,6 +480,7 @@ pub const Inst = struct { .block_flat, .block_comptime, .block_comptime_flat, + .suspend_block, => Block, .switchbr, .switchbr_ref => SwitchBr, @@ -486,8 +506,8 @@ pub const Inst = struct { .@"export" => Export, .param_type => ParamType, .primitive => Primitive, - .fn_type => FnType, - .fn_type_cc => FnTypeCc, + .fn_type, .fn_type_var_args => FnType, + .fn_type_cc, .fn_type_cc_var_args => FnTypeCc, .elem_ptr, .elem_val => Elem, .condbr => CondBr, .ptr_type => PtrType, @@ -563,7 +583,9 @@ pub const Inst = struct { .field_val_named, .@"fn", .fn_type, + .fn_type_var_args, .fn_type_cc, + .fn_type_cc_var_args, .int, .intcast, .int_type, @@ -633,6 +655,9 @@ pub const Inst = struct { .struct_type, .void_value, .switch_range, + .@"resume", + .@"await", + .nosuspend_await, => false, .@"break", @@ -649,6 +674,8 @@ pub const Inst = struct { .container_field, .switchbr, .switchbr_ref, + .@"suspend", + .suspend_block, => true, }; } diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 27e31c6197..9cbdfd07dd 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -91,8 +91,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .@"fn" => return zirFn(mod, scope, old_inst.castTag(.@"fn").?), .@"export" => return zirExport(mod, scope, old_inst.castTag(.@"export").?), .primitive => return zirPrimitive(mod, scope, old_inst.castTag(.primitive).?), - .fn_type => return zirFnType(mod, scope, old_inst.castTag(.fn_type).?), - .fn_type_cc => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc).?), + .fn_type => return zirFnType(mod, scope, old_inst.castTag(.fn_type).?, false), + .fn_type_cc => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc).?, false), + .fn_type_var_args => return zirFnType(mod, scope, old_inst.castTag(.fn_type_var_args).?, true), + .fn_type_cc_var_args => return zirFnTypeCc(mod, scope, old_inst.castTag(.fn_type_cc_var_args).?, true), .intcast => return zirIntcast(mod, scope, old_inst.castTag(.intcast).?), .bitcast => return zirBitcast(mod, scope, old_inst.castTag(.bitcast).?), .floatcast => return zirFloatcast(mod, scope, old_inst.castTag(.floatcast).?), @@ -160,6 +162,11 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, false), .switchbr_ref => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr_ref).?, true), .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), + .@"await" => return zirAwait(mod, scope, old_inst.castTag(.@"await").?), + .nosuspend_await => return zirAwait(mod, scope, old_inst.castTag(.nosuspend_await).?), + .@"resume" => return zirResume(mod, scope, old_inst.castTag(.@"resume").?), + .@"suspend" => return zirSuspend(mod, scope, old_inst.castTag(.@"suspend").?), + .suspend_block => return zirSuspendBlock(mod, scope, old_inst.castTag(.suspend_block).?), .container_field_named, .container_field_typed, @@ -517,9 +524,11 @@ fn zirParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerErr }, }; - // TODO support C-style var args const param_count = fn_ty.fnParamLen(); if (arg_index >= param_count) { + if (fn_ty.fnIsVarArgs()) { + return mod.constType(scope, inst.base.src, Type.initTag(.var_args_param)); + } return mod.fail(scope, inst.base.src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{ arg_index, fn_ty, @@ -941,6 +950,7 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { const call_params_len = inst.positionals.args.len; const fn_params_len = func.ty.fnParamLen(); if (func.ty.fnIsVarArgs()) { + assert(cc == .C); if (call_params_len < fn_params_len) { // TODO add error note: declared here return mod.fail( @@ -950,7 +960,6 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { .{ fn_params_len, call_params_len }, ); } - return mod.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{}); } else if (fn_params_len != call_params_len) { // TODO add error note: declared here return mod.fail( @@ -969,15 +978,10 @@ fn zirCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { } // TODO handle function calls of generic functions - - const fn_param_types = try mod.gpa.alloc(Type, fn_params_len); - defer mod.gpa.free(fn_param_types); - func.ty.fnParamTypes(fn_param_types); - - const casted_args = try scope.arena().alloc(*Inst, fn_params_len); + const casted_args = try scope.arena().alloc(*Inst, call_params_len); for (inst.positionals.args) |src_arg, i| { - const uncasted_arg = try resolveInst(mod, scope, src_arg); - casted_args[i] = try mod.coerce(scope, fn_param_types[i], uncasted_arg); + // the args are already casted to the result of a param type instruction. + casted_args[i] = try resolveInst(mod, scope, src_arg); } const ret_type = func.ty.fnReturnType(); @@ -1080,6 +1084,22 @@ fn zirFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { }); } +fn zirAwait(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement await", .{}); +} + +fn zirResume(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement resume", .{}); +} + +fn zirSuspend(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement suspend", .{}); +} + +fn zirSuspendBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement suspend", .{}); +} + fn zirIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1482,7 +1502,7 @@ fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) return mod.constVoid(scope, unwrap.base.src); } -fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { +fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType, var_args: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1493,10 +1513,11 @@ fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!* fntype.positionals.param_types, fntype.positionals.return_type, .Unspecified, + var_args, ); } -fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc) InnerError!*Inst { +fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc, var_args: bool) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -1513,6 +1534,7 @@ fn zirFnTypeCc(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnTypeCc) InnerErr fntype.positionals.param_types, fntype.positionals.return_type, cc, + var_args, ); } @@ -1523,11 +1545,12 @@ fn fnTypeCommon( zir_param_types: []*zir.Inst, zir_return_type: *zir.Inst, cc: std.builtin.CallingConvention, + var_args: bool, ) InnerError!*Inst { const return_type = try resolveType(mod, scope, zir_return_type); // Hot path for some common function types. - if (zir_param_types.len == 0) { + if (zir_param_types.len == 0 and !var_args) { if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { return mod.constType(scope, zir_inst.src, Type.initTag(.fn_noreturn_no_args)); } @@ -1560,6 +1583,7 @@ fn fnTypeCommon( .param_types = param_types, .return_type = return_type, .cc = cc, + .is_var_args = var_args, }); return mod.constType(scope, zir_inst.src, fn_ty); } @@ -2046,7 +2070,7 @@ fn zirBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*In rhs.ty.arrayLen(), }); } - return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBitwise", .{}); + return mod.fail(scope, inst.base.src, "TODO implement support for vectors in zirBitwise", .{}); } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ lhs.ty, @@ -2127,7 +2151,7 @@ fn zirArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError! rhs.ty.arrayLen(), }); } - return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{}); + return mod.fail(scope, inst.base.src, "TODO implement support for vectors in zirBinOp", .{}); } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ lhs.ty, diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 35ae1dbf12..873f0e1198 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -42,6 +42,19 @@ pub fn addCases(ctx: *TestContext) !void { } { + var case = ctx.exeFromCompiledC("var args", .{}); + + case.addCompareOutput( + \\extern fn printf(format: [*:0]const u8, ...) c_int; + \\ + \\export fn main() c_int { + \\ _ = printf("Hello, %s!\n", "world"); + \\ return 0; + \\} + , "Hello, world!\n"); + } + + { var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64); // Exit with 0 |
