stage2 generics improvements: anytype and param type exprs

AstGen result locations now have a `coerced_ty` tag which is the same as
`ty` except it assumes that Sema will do a coercion, so it does not
redundantly add an `as` instruction into the ZIR code. This results in
cleaner ZIR and about a 14% reduction of ZIR bytes.

param and param_comptime ZIR instructions now have a block body for
their type expressions. This allows Sema to skip evaluation of the
block in the case that the parameter is comptime-provided. It also
allows a new mechanism to function: when evaluating type expressions of
generic functions, if it would depend on another parameter, it returns
`error.GenericPoison` which bubbles up and then is caught by the
param/param_comptime instruction and then handled.

This allows parameters to be evaluated independently so that the type
info for functions which have comptime or anytype parameters will still
have types populated for parameters that do not depend on values of
previous parameters (because evaluation of their param blocks will return
successfully instead of `error.GenericPoison`).

It also makes iteration over the block that contains function parameters
slightly more efficient since it now only contains the param
instructions.

Finally, it fixes the case where a generic function type expression contains
a function prototype. Formerly, this situation would cause shared state
to clobber each other; now it is in a proper tree structure so that
can't happen. This fix also required adding a field to Sema
`comptime_args_fn_inst` to make sure that the `comptime_args` field
passed into Sema is applied to the correct `func` instruction.

Source location for `node_offset_asm_ret_ty` is fixed; it was pointing at
the asm output name rather than the return type as intended.

Generic function instantiation is fixed, notably with respect to
parameter type expressions that depend on previous parameters, and with
respect to types which must be always comptime-known. This involves
passing all the comptime arguments at a callsite of a generic function,
and allowing the generic function semantic analysis to coerce the values
to the proper types (since it has access to the evaluated parameter type
expressions) and then decide based on the type whether the parameter is
runtime known or not. In the case of explicitly marked `comptime`
parameters, there is a check at the semantic analysis of the `call`
instruction.

Semantic analysis of `call` instructions does type coercion on the
arguments, which is needed both for generic functions and to make up for
using `coerced_ty` result locations (mentioned above).

Tasks left in this branch:
 * Implement the memoization table.
 * Add test coverage.
 * Improve error reporting and source locations for compile errors.
This commit is contained in:
Andrew Kelley 2021-08-04 21:11:31 -07:00
parent 382d201781
commit d4468affb7
10 changed files with 520 additions and 248 deletions

View File

@ -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

View File

@ -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(&param_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(&param_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(

View File

@ -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) {

View File

@ -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,
};

View File

@ -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;
}

View File

@ -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());
}

View File

@ -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}),
}
}

View File

@ -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,

View File

@ -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,
};
}

View File

@ -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;