diff --git a/BRANCH_TODO b/BRANCH_TODO deleted file mode 100644 index e8606332d7..0000000000 --- a/BRANCH_TODO +++ /dev/null @@ -1,4 +0,0 @@ -* memoize the instantiation in a table -* expressions that depend on comptime stuff need a poison value to use for - types when generating the generic function type -* comptime anytype diff --git a/src/AstGen.zig b/src/AstGen.zig index 7534afe961..493e0a75f4 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -195,6 +195,9 @@ pub const ResultLoc = union(enum) { none_or_ref, /// The expression will be coerced into this type, but it will be evaluated as an rvalue. ty: Zir.Inst.Ref, + /// Same as `ty` but it is guaranteed that Sema will additionall perform the coercion, + /// so no `as` instruction needs to be emitted. + coerced_ty: Zir.Inst.Ref, /// The expression must store its result into this typed pointer. The result instruction /// from the expression must be ignored. ptr: Zir.Inst.Ref, @@ -225,7 +228,7 @@ pub const ResultLoc = union(enum) { fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy { switch (rl) { // In this branch there will not be any store_to_block_ptr instructions. - .discard, .none, .none_or_ref, .ty, .ref => return .{ + .discard, .none, .none_or_ref, .ty, .coerced_ty, .ref => return .{ .tag = .break_operand, .elide_store_to_block_ptr_instructions = false, }, @@ -260,13 +263,14 @@ pub const ResultLoc = union(enum) { pub const align_rl: ResultLoc = .{ .ty = .u16_type }; pub const bool_rl: ResultLoc = .{ .ty = .bool_type }; pub const type_rl: ResultLoc = .{ .ty = .type_type }; +pub const coerced_type_rl: ResultLoc = .{ .coerced_ty = .type_type }; fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!Zir.Inst.Ref { const prev_force_comptime = gz.force_comptime; gz.force_comptime = true; defer gz.force_comptime = prev_force_comptime; - return expr(gz, scope, .{ .ty = .type_type }, type_node); + return expr(gz, scope, coerced_type_rl, type_node); } /// Same as `expr` but fails with a compile error if the result type is `noreturn`. @@ -1079,16 +1083,19 @@ fn fnProtoExpr( .param_anytype; _ = try gz.addStrTok(tag, param_name, name_token); } else { + const gpa = astgen.gpa; const param_type_node = param.type_expr; assert(param_type_node != 0); - const param_type = try expr(gz, scope, type_rl, param_type_node); + var param_gz = gz.makeSubBlock(scope); + defer param_gz.instructions.deinit(gpa); + const param_type = try expr(¶m_gz, scope, coerced_type_rl, param_type_node); + const param_inst_expected = @intCast(u32, astgen.instructions.len + 1); + _ = try param_gz.addBreak(.break_inline, param_inst_expected, param_type); const main_tokens = tree.nodes.items(.main_token); const name_token = param.name_token orelse main_tokens[param_type_node]; const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; - _ = try gz.addPlTok(tag, name_token, Zir.Inst.Param{ - .name = param_name, - .ty = param_type, - }); + const param_inst = try gz.addParam(tag, name_token, param_name, param_gz.instructions.items); + assert(param_inst_expected == param_inst); } } break :is_var_args false; @@ -1219,7 +1226,7 @@ fn arrayInitExpr( return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); } }, - .ty => |ty_inst| { + .ty, .coerced_ty => |ty_inst| { if (types.array != .none) { const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, .array_init); return rvalue(gz, rl, result, node); @@ -1388,7 +1395,7 @@ fn structInitExpr( return structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon); } }, - .ty => |ty_inst| { + .ty, .coerced_ty => |ty_inst| { if (struct_init.ast.type_expr == 0) { return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); } @@ -2617,7 +2624,7 @@ fn assignOp( const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); - const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs); + const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs); const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ .lhs = lhs, @@ -2953,14 +2960,18 @@ fn fnDecl( } else param: { const param_type_node = param.type_expr; assert(param_type_node != 0); - const param_type = try expr(&decl_gz, params_scope, type_rl, param_type_node); + var param_gz = decl_gz.makeSubBlock(scope); + defer param_gz.instructions.deinit(gpa); + const param_type = try expr(¶m_gz, params_scope, coerced_type_rl, param_type_node); + const param_inst_expected = @intCast(u32, astgen.instructions.len + 1); + _ = try param_gz.addBreak(.break_inline, param_inst_expected, param_type); + const main_tokens = tree.nodes.items(.main_token); const name_token = param.name_token orelse main_tokens[param_type_node]; const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; - break :param try decl_gz.addPlTok(tag, name_token, Zir.Inst.Param{ - .name = param_name, - .ty = param_type, - }); + const param_inst = try decl_gz.addParam(tag, name_token, param_name, param_gz.instructions.items); + assert(param_inst_expected == param_inst); + break :param indexToRef(param_inst); }; if (param_name == 0) continue; @@ -6758,7 +6769,7 @@ fn as( ) InnerError!Zir.Inst.Ref { const dest_type = try typeExpr(gz, scope, lhs); switch (rl) { - .none, .none_or_ref, .discard, .ref, .ty => { + .none, .none_or_ref, .discard, .ref, .ty, .coerced_ty => { const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node); return rvalue(gz, rl, result, node); }, @@ -6781,7 +6792,7 @@ fn unionInit( const union_type = try typeExpr(gz, scope, params[0]); const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); switch (rl) { - .none, .none_or_ref, .discard, .ref, .ty, .inferred_ptr => { + .none, .none_or_ref, .discard, .ref, .ty, .coerced_ty, .inferred_ptr => { _ = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{ .container_type = union_type, .field_name = field_name, @@ -6867,7 +6878,7 @@ fn bitCast( const astgen = gz.astgen; const dest_type = try typeExpr(gz, scope, lhs); switch (rl) { - .none, .none_or_ref, .discard, .ty => { + .none, .none_or_ref, .discard, .ty, .coerced_ty => { const operand = try expr(gz, scope, .none, rhs); const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{ .lhs = dest_type, @@ -7677,7 +7688,7 @@ fn callExpr( .param_index = @intCast(u32, i), } }, }); - args[i] = try expr(gz, scope, .{ .ty = param_type }, param_node); + args[i] = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node); } const modifier: std.builtin.CallOptions.Modifier = blk: { @@ -8370,7 +8381,7 @@ fn rvalue( src_node: ast.Node.Index, ) InnerError!Zir.Inst.Ref { switch (rl) { - .none, .none_or_ref => return result, + .none, .none_or_ref, .coerced_ty => return result, .discard => { // Emit a compile error for discarding error values. _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); @@ -9042,7 +9053,7 @@ const GenZir = struct { // we emit ZIR for the block break instructions to have the result values, // and then rvalue() on that to pass the value to the result location. switch (parent_rl) { - .ty => |ty_inst| { + .ty, .coerced_ty => |ty_inst| { gz.rl_ty_inst = ty_inst; gz.break_result_loc = parent_rl; }, @@ -9425,18 +9436,26 @@ const GenZir = struct { return indexToRef(new_index); } - fn addPlTok( + fn addParam( gz: *GenZir, tag: Zir.Inst.Tag, /// Absolute token index. This function does the conversion to Decl offset. abs_tok_index: ast.TokenIndex, - extra: anytype, - ) !Zir.Inst.Ref { + name: u32, + body: []const u32, + ) !Zir.Inst.Index { const gpa = gz.astgen.gpa; try gz.instructions.ensureUnusedCapacity(gpa, 1); try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).Struct.fields.len + + body.len); + + const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{ + .name = name, + .body_len = @intCast(u32, body.len), + }); + gz.astgen.extra.appendSliceAssumeCapacity(body); - const payload_index = try gz.astgen.addExtra(extra); const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); gz.astgen.instructions.appendAssumeCapacity(.{ .tag = tag, @@ -9446,7 +9465,7 @@ const GenZir = struct { } }, }); gz.instructions.appendAssumeCapacity(new_index); - return indexToRef(new_index); + return new_index; } fn addExtendedPayload( diff --git a/src/Compilation.zig b/src/Compilation.zig index f8f8cea328..adb4940243 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2118,7 +2118,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor if (builtin.mode == .Debug and self.verbose_air) { std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); @import("print_air.zig").dump(gpa, air, decl.namespace.file_scope.zir, liveness); - std.debug.print("# End Function AIR: {s}:\n", .{decl.name}); + std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); } self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) { diff --git a/src/Module.zig b/src/Module.zig index 184ea617b1..2c3e745c11 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1173,6 +1173,8 @@ pub const Scope = struct { /// for the one that will be the same for all Block instances. src_decl: *Decl, instructions: ArrayListUnmanaged(Air.Inst.Index), + // `param` instructions are collected here to be used by the `func` instruction. + params: std.ArrayListUnmanaged(Param) = .{}, label: ?*Label = null, inlining: ?*Inlining, /// If runtime_index is not 0 then one of these is guaranteed to be non null. @@ -1187,6 +1189,12 @@ pub const Scope = struct { /// when null, it is determined by build mode, changed by @setRuntimeSafety want_safety: ?bool = null, + const Param = struct { + /// `noreturn` means `anytype`. + ty: Type, + is_comptime: bool, + }; + /// This `Block` maps a block ZIR instruction to the corresponding /// AIR instruction for break instruction analysis. pub const Label = struct { @@ -1634,8 +1642,11 @@ pub const SrcLoc = struct { .@"asm" => tree.asmFull(node), else => unreachable, }; + const asm_output = full.outputs[0]; + const node_datas = tree.nodes.items(.data); + const ret_ty_node = node_datas[asm_output].lhs; const main_tokens = tree.nodes.items(.main_token); - const tok_index = main_tokens[full.outputs[0]]; + const tok_index = main_tokens[ret_ty_node]; const token_starts = tree.tokens.items(.start); return token_starts[tok_index]; }, @@ -2099,7 +2110,20 @@ pub const LazySrcLoc = union(enum) { }; pub const SemaError = error{ OutOfMemory, AnalysisFail }; -pub const CompileError = error{ OutOfMemory, AnalysisFail, NeededSourceLocation }; +pub const CompileError = error{ + OutOfMemory, + /// When this is returned, the compile error for the failure has already been recorded. + AnalysisFail, + /// Returned when a compile error needed to be reported but a provided LazySrcLoc was set + /// to the `unneeded` tag. The source location was, in fact, needed. It is expected that + /// somewhere up the call stack, the operation will be retried after doing expensive work + /// to compute a source location. + NeededSourceLocation, + /// A Type or Value was needed to be used during semantic analysis, but it was not available + /// because the function is generic. This is only seen when analyzing the body of a param + /// instruction. + GenericPoison, +}; pub fn deinit(mod: *Module) void { const gpa = mod.gpa; @@ -2796,14 +2820,16 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { } return error.AnalysisFail; }, - else => { + error.NeededSourceLocation => unreachable, + error.GenericPoison => unreachable, + else => |e| { decl.analysis = .sema_failure_retryable; try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( mod.gpa, decl.srcLoc(), "unable to analyze: {s}", - .{@errorName(err)}, + .{@errorName(e)}, )); return error.AnalysisFail; }, @@ -2982,7 +3008,10 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { .inlining = null, .is_comptime = true, }; - defer block_scope.instructions.deinit(gpa); + defer { + block_scope.instructions.deinit(gpa); + block_scope.params.deinit(gpa); + } const zir_block_index = decl.zirBlockIndex(); const inst_data = zir_datas[zir_block_index].pl_node; @@ -3669,7 +3698,8 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType` try sema.inst_map.ensureUnusedCapacity(gpa, fn_info.total_params_len); - var param_index: usize = 0; + var runtime_param_index: usize = 0; + var total_param_index: usize = 0; for (fn_info.param_body) |inst| { const name = switch (zir_tags[inst]) { .param, .param_comptime => blk: { @@ -3686,16 +3716,16 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { else => continue, }; if (func.comptime_args) |comptime_args| { - const arg_tv = comptime_args[param_index]; + const arg_tv = comptime_args[total_param_index]; if (arg_tv.val.tag() != .unreachable_value) { // We have a comptime value for this parameter. const arg = try sema.addConstant(arg_tv.ty, arg_tv.val); sema.inst_map.putAssumeCapacityNoClobber(inst, arg); - param_index += 1; + total_param_index += 1; continue; } } - const param_type = fn_ty.fnParamType(param_index); + const param_type = fn_ty.fnParamType(runtime_param_index); const ty_ref = try sema.addType(param_type); const arg_index = @intCast(u32, sema.air_instructions.len); inner_block.instructions.appendAssumeCapacity(arg_index); @@ -3707,7 +3737,8 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { } }, }); sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index)); - param_index += 1; + total_param_index += 1; + runtime_param_index += 1; } func.state = .in_progress; @@ -3715,6 +3746,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { _ = sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) { error.NeededSourceLocation => unreachable, + error.GenericPoison => unreachable, else => |e| return e, }; diff --git a/src/Sema.zig b/src/Sema.zig index 9a7eb6fc40..8bcdcb63c9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -37,13 +37,15 @@ branch_count: u32 = 0, /// contain a mapped source location. src: LazySrcLoc = .{ .token_offset = 0 }, decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{}, -/// `param` instructions are collected here to be used by the `func` instruction. -params: std.ArrayListUnmanaged(Param) = .{}, -/// When doing a generic function instantiation, this array collects a `Value` object for -/// each parameter that is comptime known and thus elided from the generated function. -/// This memory is allocated by a parent `Sema` and owned by the values arena of the owner_decl. +/// When doing a generic function instantiation, this array collects a +/// `Value` object for each parameter that is comptime known and thus elided +/// from the generated function. This memory is allocated by a parent `Sema` and +/// owned by the values arena of the Sema owner_decl. comptime_args: []TypedValue = &.{}, -next_arg_index: usize = 0, +/// Marks the function instruction that `comptime_args` applies to so that we +/// don't accidentally apply it to a function prototype which is used in the +/// type expression of a generic function parameter. +comptime_args_fn_inst: Zir.Inst.Index = 0, const std = @import("std"); const mem = std.mem; @@ -67,13 +69,6 @@ const LazySrcLoc = Module.LazySrcLoc; const RangeSet = @import("RangeSet.zig"); const target_util = @import("target.zig"); -const Param = struct { - name: [:0]const u8, - /// `noreturn` means `anytype`. - ty: Type, - is_comptime: bool, -}; - pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); pub fn deinit(sema: *Sema) void { @@ -83,7 +78,6 @@ pub fn deinit(sema: *Sema) void { sema.air_values.deinit(gpa); sema.inst_map.deinit(gpa); sema.decl_val_table.deinit(gpa); - sema.params.deinit(gpa); sema.* = undefined; } @@ -466,6 +460,26 @@ pub fn analyzeBody( i += 1; continue; }, + .param => { + try sema.zirParam(block, inst, false); + i += 1; + continue; + }, + .param_comptime => { + try sema.zirParam(block, inst, true); + i += 1; + continue; + }, + .param_anytype => { + try sema.zirParamAnytype(block, inst, false); + i += 1; + continue; + }, + .param_anytype_comptime => { + try sema.zirParamAnytype(block, inst, true); + i += 1; + continue; + }, // Special case instructions to handle comptime control flow. .repeat_inline => { @@ -504,88 +518,6 @@ pub fn analyzeBody( return break_inst; } }, - .param => blk: { - const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; - const src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data; - const param_name = sema.code.nullTerminatedString(extra.name); - - if (sema.nextArgIsComptimeElided()) { - i += 1; - continue; - } - - // TODO check if param_name shadows a Decl. This only needs to be done if - // usingnamespace is implemented. - - const param_ty = try sema.resolveType(block, src, extra.ty); - try sema.params.append(sema.gpa, .{ - .name = param_name, - .ty = param_ty, - .is_comptime = false, - }); - break :blk try sema.addConstUndef(param_ty); - }, - .param_comptime => blk: { - const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; - const src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data; - const param_name = sema.code.nullTerminatedString(extra.name); - - if (sema.nextArgIsComptimeElided()) { - i += 1; - continue; - } - - // TODO check if param_name shadows a Decl. This only needs to be done if - // usingnamespace is implemented. - - const param_ty = try sema.resolveType(block, src, extra.ty); - try sema.params.append(sema.gpa, .{ - .name = param_name, - .ty = param_ty, - .is_comptime = true, - }); - break :blk try sema.addConstUndef(param_ty); - }, - .param_anytype => blk: { - const inst_data = sema.code.instructions.items(.data)[inst].str_tok; - const param_name = inst_data.get(sema.code); - - if (sema.nextArgIsComptimeElided()) { - i += 1; - continue; - } - - // TODO check if param_name shadows a Decl. This only needs to be done if - // usingnamespace is implemented. - - try sema.params.append(sema.gpa, .{ - .name = param_name, - .ty = Type.initTag(.noreturn), - .is_comptime = false, - }); - break :blk try sema.addConstUndef(Type.initTag(.@"undefined")); - }, - .param_anytype_comptime => blk: { - const inst_data = sema.code.instructions.items(.data)[inst].str_tok; - const param_name = inst_data.get(sema.code); - - if (sema.nextArgIsComptimeElided()) { - i += 1; - continue; - } - - // TODO check if param_name shadows a Decl. This only needs to be done if - // usingnamespace is implemented. - - try sema.params.append(sema.gpa, .{ - .name = param_name, - .ty = Type.initTag(.noreturn), - .is_comptime = true, - }); - break :blk try sema.addConstUndef(Type.initTag(.@"undefined")); - }, }; if (sema.typeOf(air_inst).isNoReturn()) return always_noreturn; @@ -697,6 +629,7 @@ fn resolveValue( air_ref: Air.Inst.Ref, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(block, src, air_ref)) |val| { + if (val.tag() == .generic_poison) return error.GenericPoison; return val; } return sema.failWithNeededComptime(block, src); @@ -714,6 +647,7 @@ fn resolveConstValue( switch (val.tag()) { .undef => return sema.failWithUseOfUndef(block, src), .variable => return sema.failWithNeededComptime(block, src), + .generic_poison => return error.GenericPoison, else => return val, } } @@ -2422,7 +2356,7 @@ fn analyzeCall( call_src: LazySrcLoc, modifier: std.builtin.CallOptions.Modifier, ensure_result_used: bool, - args: []const Air.Inst.Ref, + uncasted_args: []const Air.Inst.Ref, ) CompileError!Air.Inst.Ref { const mod = sema.mod; @@ -2444,22 +2378,22 @@ fn analyzeCall( const fn_params_len = func_ty_info.param_types.len; if (func_ty_info.is_var_args) { assert(cc == .C); - if (args.len < fn_params_len) { + if (uncasted_args.len < fn_params_len) { // TODO add error note: declared here return mod.fail( &block.base, func_src, "expected at least {d} argument(s), found {d}", - .{ fn_params_len, args.len }, + .{ fn_params_len, uncasted_args.len }, ); } - } else if (fn_params_len != args.len) { + } else if (fn_params_len != uncasted_args.len) { // TODO add error note: declared here return mod.fail( &block.base, func_src, "expected {d} argument(s), found {d}", - .{ fn_params_len, args.len }, + .{ fn_params_len, uncasted_args.len }, ); } @@ -2485,6 +2419,14 @@ fn analyzeCall( const is_inline_call = is_comptime_call or modifier == .always_inline or func_ty_info.cc == .Inline; const result: Air.Inst.Ref = if (is_inline_call) res: { + // TODO look into not allocating this args array + const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); + for (uncasted_args) |uncasted_arg, i| { + const param_ty = func_ty.fnParamType(i); + const arg_src = call_src; // TODO: better source location + args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + } + const func_val = try sema.resolveConstValue(block, func_src, func); const module_fn = switch (func_val.tag()) { .function => func_val.castTag(.function).?.data, @@ -2574,13 +2516,12 @@ fn analyzeCall( const func_val = try sema.resolveConstValue(block, func_src, func); const module_fn = func_val.castTag(.function).?.data; // Check the Module's generic function map with an adapted context, so that we - // can match against `args` rather than doing the work below to create a generic Scope - // only to junk it if it matches an existing instantiation. + // can match against `uncasted_args` rather than doing the work below to create a + // generic Scope only to junk it if it matches an existing instantiation. // TODO const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst); const zir_tags = sema.code.instructions.items(.tag); - var non_comptime_args_len: u32 = 0; const new_func = new_func: { const namespace = module_fn.owner_decl.namespace; try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); @@ -2622,7 +2563,8 @@ fn analyzeCall( .namespace = namespace, .func = null, .owner_func = null, - .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, args.len), + .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len), + .comptime_args_fn_inst = module_fn.zir_body_inst, }; defer child_sema.deinit(); @@ -2634,35 +2576,29 @@ fn analyzeCall( .inlining = null, .is_comptime = true, }; - defer child_block.instructions.deinit(gpa); + defer { + child_block.instructions.deinit(gpa); + child_block.params.deinit(gpa); + } - try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, args.len)); + try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len)); var arg_i: usize = 0; for (fn_info.param_body) |inst| { const is_comptime = switch (zir_tags[inst]) { .param_comptime, .param_anytype_comptime => true, - .param, .param_anytype => false, // TODO make true for always comptime types + .param, .param_anytype => false, else => continue, }; - if (is_comptime) { - // TODO: pass .unneeded to resolveConstValue and then if we get - // error.NeededSourceLocation resolve the arg source location and - // try again. - const arg_src = call_src; - const arg = args[arg_i]; - const arg_val = try sema.resolveConstValue(block, arg_src, arg); - child_sema.comptime_args[arg_i] = .{ - .ty = try sema.typeOf(arg).copy(&new_decl_arena.allocator), - .val = try arg_val.copy(&new_decl_arena.allocator), - }; + // TODO: pass .unneeded to resolveConstValue and then if we get + // error.NeededSourceLocation resolve the arg source location and + // try again. + const arg_src = call_src; + const arg = uncasted_args[arg_i]; + if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); - } else { - non_comptime_args_len += 1; - child_sema.comptime_args[arg_i] = .{ - .ty = Type.initTag(.noreturn), - .val = Value.initTag(.unreachable_value), - }; + } else if (is_comptime) { + return sema.failWithNeededComptime(block, arg_src); } arg_i += 1; } @@ -2670,6 +2606,30 @@ fn analyzeCall( const new_func_val = try child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst); const new_func = new_func_val.castTag(.function).?.data; + arg_i = 0; + for (fn_info.param_body) |inst| { + switch (zir_tags[inst]) { + .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, + else => continue, + } + const arg = child_sema.inst_map.get(inst).?; + const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(&child_block, .unneeded, arg) catch unreachable).?; + + if (arg_val.tag() == .generic_poison) { + child_sema.comptime_args[arg_i] = .{ + .ty = Type.initTag(.noreturn), + .val = Value.initTag(.unreachable_value), + }; + } else { + child_sema.comptime_args[arg_i] = .{ + .ty = try child_sema.typeOf(arg).copy(&new_decl_arena.allocator), + .val = try arg_val.copy(&new_decl_arena.allocator), + }; + } + + arg_i += 1; + } + // Populate the Decl ty/val with the function and its type. new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator); new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func); @@ -2690,31 +2650,72 @@ fn analyzeCall( // Make a runtime call to the new function, making sure to omit the comptime args. try sema.requireRuntimeBlock(block, call_src); + const new_func_val = sema.resolveConstValue(block, .unneeded, new_func) catch unreachable; + const new_module_func = new_func_val.castTag(.function).?.data; + const comptime_args = new_module_func.comptime_args.?; + const runtime_args_len = count: { + var count: u32 = 0; + var arg_i: usize = 0; + for (fn_info.param_body) |inst| { + switch (zir_tags[inst]) { + .param_comptime, .param_anytype_comptime, .param, .param_anytype => { + if (comptime_args[arg_i].val.tag() == .unreachable_value) { + count += 1; + } + arg_i += 1; + }, + else => continue, + } + } + break :count count; + }; + const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len); + { + const new_fn_ty = new_module_func.owner_decl.ty; + var runtime_i: u32 = 0; + var total_i: u32 = 0; + for (fn_info.param_body) |inst| { + switch (zir_tags[inst]) { + .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, + else => continue, + } + const is_runtime = comptime_args[total_i].val.tag() == .unreachable_value; + if (is_runtime) { + const param_ty = new_fn_ty.fnParamType(runtime_i); + const arg_src = call_src; // TODO: better source location + const uncasted_arg = uncasted_args[total_i]; + const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + runtime_args[runtime_i] = casted_arg; + runtime_i += 1; + } + total_i += 1; + } + } try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + - non_comptime_args_len); + runtime_args_len); const func_inst = try block.addInst(.{ .tag = .call, .data = .{ .pl_op = .{ .operand = new_func, .payload = sema.addExtraAssumeCapacity(Air.Call{ - .args_len = non_comptime_args_len, + .args_len = runtime_args_len, }), } }, }); - var arg_i: usize = 0; - for (fn_info.param_body) |inst| { - const is_comptime = switch (zir_tags[inst]) { - .param_comptime, .param_anytype_comptime => true, - .param, .param_anytype => false, // TODO make true for always comptime types - else => continue, - }; - if (is_comptime) { - sema.air_extra.appendAssumeCapacity(@enumToInt(args[arg_i])); - } - arg_i += 1; - } + sema.appendRefsAssumeCapacity(runtime_args); break :res func_inst; } else res: { + const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); + for (uncasted_args) |uncasted_arg, i| { + if (i < fn_params_len) { + const param_ty = func_ty.fnParamType(i); + const arg_src = call_src; // TODO: better source location + args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + } else { + args[i] = uncasted_arg; + } + } + try sema.requireRuntimeBlock(block, call_src); try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + args.len); @@ -3416,7 +3417,7 @@ fn funcCommon( const fn_ty: Type = fn_ty: { // Hot path for some common function types. - if (sema.params.items.len == 0 and !var_args and align_val.tag() == .null_value and + if (block.params.items.len == 0 and !var_args and align_val.tag() == .null_value and !inferred_error_set) { if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { @@ -3436,19 +3437,15 @@ fn funcCommon( } } - var any_are_comptime = false; - const param_types = try sema.arena.alloc(Type, sema.params.items.len); - const comptime_params = try sema.arena.alloc(bool, sema.params.items.len); - for (sema.params.items) |param, i| { - if (param.ty.tag() == .noreturn) { - param_types[i] = Type.initTag(.noreturn); // indicates anytype - } else { - param_types[i] = param.ty; - } + var is_generic = false; + const param_types = try sema.arena.alloc(Type, block.params.items.len); + const comptime_params = try sema.arena.alloc(bool, block.params.items.len); + for (block.params.items) |param, i| { + param_types[i] = param.ty; comptime_params[i] = param.is_comptime; - any_are_comptime = any_are_comptime or param.is_comptime; + is_generic = is_generic or param.is_comptime or + param.ty.tag() == .generic_poison or param.ty.requiresComptime(); } - sema.params.clearRetainingCapacity(); if (align_val.tag() != .null_value) { return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{}); @@ -3471,7 +3468,7 @@ fn funcCommon( .return_type = return_type, .cc = cc, .is_var_args = var_args, - .is_generic = any_are_comptime, + .is_generic = is_generic, }); }; @@ -3530,12 +3527,16 @@ fn funcCommon( const is_inline = fn_ty.fnCallingConvention() == .Inline; const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued; + const comptime_args: ?[*]TypedValue = if (sema.comptime_args_fn_inst == body_inst) blk: { + break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr; + } else null; + const fn_payload = try sema.arena.create(Value.Payload.Function); new_func.* = .{ .state = anal_state, .zir_body_inst = body_inst, .owner_decl = sema.owner_decl, - .comptime_args = if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr, + .comptime_args = comptime_args, .lbrace_line = src_locs.lbrace_line, .rbrace_line = src_locs.rbrace_line, .lbrace_column = @truncate(u16, src_locs.columns), @@ -3548,6 +3549,113 @@ fn funcCommon( return sema.addConstant(fn_ty, Value.initPayload(&fn_payload.base)); } +fn zirParam( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, + is_comptime: bool, +) CompileError!void { + const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; + const src = inst_data.src(); + const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); + const param_name = sema.code.nullTerminatedString(extra.data.name); + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; + + // TODO check if param_name shadows a Decl. This only needs to be done if + // usingnamespace is implemented. + _ = param_name; + + // We could be in a generic function instantiation, or we could be evaluating a generic + // function without any comptime args provided. + const param_ty = param_ty: { + const err = err: { + // Make sure any nested param instructions don't clobber our work. + const prev_params = block.params; + block.params = .{}; + defer { + block.params.deinit(sema.gpa); + block.params = prev_params; + } + + if (sema.resolveBody(block, body)) |param_ty_inst| { + if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| { + break :param_ty param_ty; + } else |err| break :err err; + } else |err| break :err err; + }; + switch (err) { + error.GenericPoison => { + // The type is not available until the generic instantiation. + // We result the param instruction with a poison value and + // insert an anytype parameter. + try block.params.append(sema.gpa, .{ + .ty = Type.initTag(.generic_poison), + .is_comptime = is_comptime, + }); + try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison); + return; + }, + else => |e| return e, + } + }; + if (sema.inst_map.get(inst)) |arg| { + if (is_comptime or param_ty.requiresComptime()) { + // We have a comptime value for this parameter so it should be elided from the + // function type of the function instruction in this block. + const coerced_arg = try sema.coerce(block, param_ty, arg, src); + sema.inst_map.putAssumeCapacity(inst, coerced_arg); + return; + } + // Even though a comptime argument is provided, the generic function wants to treat + // this as a runtime parameter. + assert(sema.inst_map.remove(inst)); + } + + try block.params.append(sema.gpa, .{ + .ty = param_ty, + .is_comptime = is_comptime, + }); + const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); + try sema.inst_map.putNoClobber(sema.gpa, inst, result); +} + +fn zirParamAnytype( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, + is_comptime: bool, +) CompileError!void { + const inst_data = sema.code.instructions.items(.data)[inst].str_tok; + const param_name = inst_data.get(sema.code); + + // TODO check if param_name shadows a Decl. This only needs to be done if + // usingnamespace is implemented. + _ = param_name; + + if (sema.inst_map.get(inst)) |air_ref| { + const param_ty = sema.typeOf(air_ref); + if (is_comptime or param_ty.requiresComptime()) { + // We have a comptime value for this parameter so it should be elided from the + // function type of the function instruction in this block. + return; + } + // The map is already populated but we do need to add a runtime parameter. + try block.params.append(sema.gpa, .{ + .ty = param_ty, + .is_comptime = false, + }); + return; + } + + // We are evaluating a generic function without any comptime args provided. + + try block.params.append(sema.gpa, .{ + .ty = Type.initTag(.generic_poison), + .is_comptime = is_comptime, + }); + try sema.inst_map.put(sema.gpa, inst, .generic_poison); +} + fn zirAs(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -7618,8 +7726,10 @@ fn coerce( inst: Air.Inst.Ref, inst_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { - if (dest_type_unresolved.tag() == .var_args_param) { - return sema.coerceVarArgParam(block, inst, inst_src); + switch (dest_type_unresolved.tag()) { + .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src), + .generic_poison => return inst, + else => {}, } const dest_type_src = inst_src; // TODO better source location const dest_type = try sema.resolveTypeFields(block, dest_type_src, dest_type_unresolved); @@ -8820,6 +8930,7 @@ fn typeHasOnePossibleValue( .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, + .generic_poison => unreachable, }; } @@ -8942,6 +9053,8 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { .fn_ccc_void_no_args => return .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int => return .single_const_pointer_to_comptime_int_type, .const_slice_u8 => return .const_slice_u8_type, + .anyerror_void_error_union => return .anyerror_void_error_union_type, + .generic_poison => return .generic_poison_type, else => {}, } try sema.air_instructions.append(sema.gpa, .{ @@ -9015,10 +9128,3 @@ fn isComptimeKnown( ) !bool { return (try sema.resolveMaybeUndefVal(block, src, inst)) != null; } - -fn nextArgIsComptimeElided(sema: *Sema) bool { - if (sema.comptime_args.len == 0) return false; - const result = sema.comptime_args[sema.next_arg_index].val.tag() != .unreachable_value; - sema.next_arg_index += 1; - return result; -} diff --git a/src/Zir.zig b/src/Zir.zig index 0b93208564..b4cbd9c875 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1704,6 +1704,8 @@ pub const Inst = struct { fn_ccc_void_no_args_type, single_const_pointer_to_comptime_int_type, const_slice_u8_type, + anyerror_void_error_union_type, + generic_poison_type, /// `undefined` (untyped) undef, @@ -1731,6 +1733,9 @@ pub const Inst = struct { calling_convention_c, /// `std.builtin.CallingConvention.Inline` calling_convention_inline, + /// Used for generic parameters where the type and value + /// is not known until generic function instantiation. + generic_poison, _, @@ -1909,6 +1914,14 @@ pub const Inst = struct { .ty = Type.initTag(.type), .val = Value.initTag(.const_slice_u8_type), }, + .anyerror_void_error_union_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.anyerror_void_error_union_type), + }, + .generic_poison_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.generic_poison_type), + }, .enum_literal_type = .{ .ty = Type.initTag(.type), .val = Value.initTag(.enum_literal_type), @@ -2006,6 +2019,10 @@ pub const Inst = struct { .ty = Type.initTag(.calling_convention), .val = .{ .ptr_otherwise = &calling_convention_inline_payload.base }, }, + .generic_poison = .{ + .ty = Type.initTag(.generic_poison), + .val = Value.initTag(.generic_poison), + }, }); }; @@ -2787,10 +2804,12 @@ pub const Inst = struct { args: Ref, }; + /// Trailing: inst: Index // for every body_len pub const Param = struct { /// Null-terminated string index. name: u32, - ty: Ref, + /// The body contains the type of the parameter. + body_len: u32, }; /// Trailing: @@ -3348,11 +3367,16 @@ const Writer = struct { fn writeParam(self: *Writer, stream: anytype, inst: Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_tok; - const extra = self.code.extraData(Inst.Param, inst_data.payload_index).data; + const extra = self.code.extraData(Inst.Param, inst_data.payload_index); + const body = self.code.extra[extra.end..][0..extra.data.body_len]; try stream.print("\"{}\", ", .{ - std.zig.fmtEscapes(self.code.nullTerminatedString(extra.name)), + std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)), }); - try self.writeInstRef(stream, extra.ty); + try stream.writeAll("{\n"); + self.indent += 2; + try self.writeBody(stream, body); + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8b3edcfe23..4a589ea66d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -839,6 +839,10 @@ pub const DeclGen = struct { .False, ); }, + .ComptimeInt => unreachable, + .ComptimeFloat => unreachable, + .Type => unreachable, + .EnumLiteral => unreachable, else => return self.todo("implement const of type '{}'", .{tv.ty}), } } diff --git a/src/type.zig b/src/type.zig index 237614e372..0e99a929f0 100644 --- a/src/type.zig +++ b/src/type.zig @@ -130,6 +130,7 @@ pub const Type = extern union { => return .Union, .var_args_param => unreachable, // can be any type + .generic_poison => unreachable, // must be handled earlier } } @@ -699,6 +700,7 @@ pub const Type = extern union { .export_options, .extern_options, .@"anyframe", + .generic_poison, => unreachable, .array_u8, @@ -1083,11 +1085,117 @@ pub const Type = extern union { }, .inferred_alloc_const => return writer.writeAll("(inferred_alloc_const)"), .inferred_alloc_mut => return writer.writeAll("(inferred_alloc_mut)"), + .generic_poison => return writer.writeAll("(generic poison)"), } unreachable; } } + /// Anything that reports hasCodeGenBits() false returns false here as well. + pub fn requiresComptime(ty: Type) bool { + return switch (ty.tag()) { + .u1, + .u8, + .i8, + .u16, + .i16, + .u32, + .i32, + .u64, + .i64, + .u128, + .i128, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .f16, + .f32, + .f64, + .f128, + .c_void, + .bool, + .void, + .anyerror, + .noreturn, + .@"anyframe", + .@"null", + .@"undefined", + .atomic_ordering, + .atomic_rmw_op, + .calling_convention, + .float_mode, + .reduce_op, + .call_options, + .export_options, + .extern_options, + .manyptr_u8, + .manyptr_const_u8, + .fn_noreturn_no_args, + .fn_void_no_args, + .fn_naked_noreturn_no_args, + .fn_ccc_void_no_args, + .single_const_pointer_to_comptime_int, + .const_slice_u8, + .anyerror_void_error_union, + .empty_struct_literal, + .function, + .empty_struct, + .error_set, + .error_set_single, + .error_set_inferred, + .@"opaque", + => false, + + .type, + .comptime_int, + .comptime_float, + .enum_literal, + => true, + + .var_args_param => unreachable, + .inferred_alloc_mut => unreachable, + .inferred_alloc_const => unreachable, + .generic_poison => unreachable, + + .array_u8, + .array_u8_sentinel_0, + .array, + .array_sentinel, + .vector, + .pointer, + .single_const_pointer, + .single_mut_pointer, + .many_const_pointer, + .many_mut_pointer, + .c_const_pointer, + .c_mut_pointer, + .const_slice, + .mut_slice, + .int_signed, + .int_unsigned, + .optional, + .optional_single_mut_pointer, + .optional_single_const_pointer, + .error_union, + .anyframe_T, + .@"struct", + .@"union", + .union_tagged, + .enum_simple, + .enum_full, + .enum_nonexhaustive, + => false, // TODO some of these should be `true` depending on their child types + }; + } + pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value { switch (self.tag()) { .u1 => return Value.initTag(.u1_type), @@ -1287,6 +1395,7 @@ pub const Type = extern union { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, .var_args_param => unreachable, + .generic_poison => unreachable, }; } @@ -1509,6 +1618,8 @@ pub const Type = extern union { .@"opaque", .var_args_param, => unreachable, + + .generic_poison => unreachable, }; } @@ -1536,6 +1647,7 @@ pub const Type = extern union { .inferred_alloc_mut => unreachable, .@"opaque" => unreachable, .var_args_param => unreachable, + .generic_poison => unreachable, .@"struct" => { const s = self.castTag(.@"struct").?.data; @@ -1702,6 +1814,7 @@ pub const Type = extern union { .inferred_alloc_mut => unreachable, .@"opaque" => unreachable, .var_args_param => unreachable, + .generic_poison => unreachable, .@"struct" => { @panic("TODO bitSize struct"); @@ -2626,6 +2739,7 @@ pub const Type = extern union { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, + .generic_poison => unreachable, }; } @@ -3039,6 +3153,7 @@ pub const Type = extern union { single_const_pointer_to_comptime_int, const_slice_u8, anyerror_void_error_union, + generic_poison, /// 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, @@ -3136,6 +3251,7 @@ pub const Type = extern union { .single_const_pointer_to_comptime_int, .anyerror_void_error_union, .const_slice_u8, + .generic_poison, .inferred_alloc_const, .inferred_alloc_mut, .var_args_param, diff --git a/src/value.zig b/src/value.zig index 134b51e494..bd1cc57416 100644 --- a/src/value.zig +++ b/src/value.zig @@ -76,6 +76,8 @@ pub const Value = extern union { fn_ccc_void_no_args_type, single_const_pointer_to_comptime_int_type, const_slice_u8_type, + anyerror_void_error_union_type, + generic_poison_type, undef, zero, @@ -85,6 +87,7 @@ pub const Value = extern union { null_value, bool_true, bool_false, + generic_poison, abi_align_default, empty_struct_value, @@ -188,6 +191,8 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .anyframe_type, .const_slice_u8_type, + .anyerror_void_error_union_type, + .generic_poison_type, .enum_literal_type, .undef, .zero, @@ -210,6 +215,7 @@ pub const Value = extern union { .call_options_type, .export_options_type, .extern_options_type, + .generic_poison, => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"), .int_big_positive, @@ -366,6 +372,8 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .anyframe_type, .const_slice_u8_type, + .anyerror_void_error_union_type, + .generic_poison_type, .enum_literal_type, .undef, .zero, @@ -388,6 +396,7 @@ pub const Value = extern union { .call_options_type, .export_options_type, .extern_options_type, + .generic_poison, => unreachable, .ty => { @@ -556,6 +565,9 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), .anyframe_type => return out_stream.writeAll("anyframe"), .const_slice_u8_type => return out_stream.writeAll("[]const u8"), + .anyerror_void_error_union_type => return out_stream.writeAll("anyerror!void"), + .generic_poison_type => return out_stream.writeAll("(generic poison type)"), + .generic_poison => return out_stream.writeAll("(generic poison)"), .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"), .manyptr_u8_type => return out_stream.writeAll("[*]u8"), .manyptr_const_u8_type => return out_stream.writeAll("[*]const u8"), @@ -709,6 +721,8 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), .anyframe_type => Type.initTag(.@"anyframe"), .const_slice_u8_type => Type.initTag(.const_slice_u8), + .anyerror_void_error_union_type => Type.initTag(.anyerror_void_error_union), + .generic_poison_type => Type.initTag(.generic_poison), .enum_literal_type => Type.initTag(.enum_literal), .manyptr_u8_type => Type.initTag(.manyptr_u8), .manyptr_const_u8_type => Type.initTag(.manyptr_const_u8), @@ -732,46 +746,7 @@ pub const Value = extern union { return Type.initPayload(&buffer.base); }, - .undef, - .zero, - .one, - .void_value, - .unreachable_value, - .empty_array, - .bool_true, - .bool_false, - .null_value, - .int_u64, - .int_i64, - .int_big_positive, - .int_big_negative, - .function, - .extern_fn, - .variable, - .decl_ref, - .decl_ref_mut, - .elem_ptr, - .field_ptr, - .bytes, - .repeated, - .array, - .slice, - .float_16, - .float_32, - .float_64, - .float_128, - .enum_literal, - .enum_field_index, - .@"error", - .error_union, - .empty_struct_value, - .@"struct", - .@"union", - .inferred_alloc, - .inferred_alloc_comptime, - .abi_align_default, - .eu_payload_ptr, - => unreachable, + else => unreachable, }; } diff --git a/test/cases.zig b/test/cases.zig index 840ee7a4ac..a342b91a6f 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -1572,7 +1572,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ const x = asm volatile ("syscall" \\ : [o] "{rax}" (-> number) \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) + \\ [arg1] "{rdi}" (60) \\ : "rcx", "r11", "memory" \\ ); \\ _ = x;