Commit d4468affb7
Changed files (10)
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}),
}
}
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(
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) {
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,
};
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,41 +2576,59 @@ 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;
+ } else if (is_comptime) {
+ return sema.failWithNeededComptime(block, arg_src);
+ }
+ arg_i += 1;
+ }
+ const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body);
+ 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;
}
- const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body);
- const new_func_val = try child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst);
- const new_func = new_func_val.castTag(.function).?.data;
// 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);
@@ -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;
-}
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,
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,
};
}
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());
}
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;
BRANCH_TODO
@@ -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