mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
stage2: ZIR encodes comptime parameters
`func_extended` ZIR instructions now have a one of the unused flags used as a `has_comptime_bits` boolean. When set, it means 1 or more parameters are `comptime`. In this case, there is a u32 per every 32 parameters (usually just 1 u32) with each bit indicating whether the corresponding parameter is `comptime`. Sema uses this information to correctly mark generic functions as generic. There is now a TODO compile error in place in case a generic function call happens. A future commit will do the generic function call implementation.
This commit is contained in:
parent
d5f173d28f
commit
dae4c18aa7
@ -1064,11 +1064,28 @@ fn fnProtoExpr(
|
||||
const param_types = try gpa.alloc(Zir.Inst.Ref, param_count);
|
||||
defer gpa.free(param_types);
|
||||
|
||||
const bits_per_param = 1;
|
||||
const params_per_u32 = 32 / bits_per_param;
|
||||
// We only need this if there are greater than params_per_u32 fields.
|
||||
var bit_bag = ArrayListUnmanaged(u32){};
|
||||
defer bit_bag.deinit(gpa);
|
||||
var cur_bit_bag: u32 = 0;
|
||||
var is_var_args = false;
|
||||
{
|
||||
var param_type_i: usize = 0;
|
||||
var it = fn_proto.iterate(tree.*);
|
||||
while (it.next()) |param| : (param_type_i += 1) {
|
||||
if (param_type_i % params_per_u32 == 0 and param_type_i != 0) {
|
||||
try bit_bag.append(gpa, cur_bit_bag);
|
||||
cur_bit_bag = 0;
|
||||
}
|
||||
const is_comptime = if (param.comptime_noalias) |token|
|
||||
token_tags[token] == .keyword_comptime
|
||||
else
|
||||
false;
|
||||
cur_bit_bag = (cur_bit_bag >> bits_per_param) |
|
||||
(@as(u32, @boolToInt(is_comptime)) << 31);
|
||||
|
||||
if (param.anytype_ellipsis3) |token| {
|
||||
switch (token_tags[token]) {
|
||||
.keyword_anytype => {
|
||||
@ -1088,6 +1105,11 @@ fn fnProtoExpr(
|
||||
try expr(gz, scope, .{ .ty = .type_type }, param_type_node);
|
||||
}
|
||||
assert(param_type_i == param_count);
|
||||
|
||||
const empty_slot_count = params_per_u32 - (param_type_i % params_per_u32);
|
||||
if (empty_slot_count < params_per_u32) {
|
||||
cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_param);
|
||||
}
|
||||
}
|
||||
|
||||
const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
|
||||
@ -1131,6 +1153,8 @@ fn fnProtoExpr(
|
||||
.is_inferred_error = false,
|
||||
.is_test = false,
|
||||
.is_extern = false,
|
||||
.cur_bit_bag = cur_bit_bag,
|
||||
.bit_bag = bit_bag.items,
|
||||
});
|
||||
return rvalue(gz, rl, result, fn_proto.ast.proto_node);
|
||||
}
|
||||
@ -2916,11 +2940,28 @@ fn fnDecl(
|
||||
const param_types = try gpa.alloc(Zir.Inst.Ref, param_count);
|
||||
defer gpa.free(param_types);
|
||||
|
||||
const bits_per_param = 1;
|
||||
const params_per_u32 = 32 / bits_per_param;
|
||||
// We only need this if there are greater than params_per_u32 fields.
|
||||
var bit_bag = ArrayListUnmanaged(u32){};
|
||||
defer bit_bag.deinit(gpa);
|
||||
var cur_bit_bag: u32 = 0;
|
||||
var is_var_args = false;
|
||||
{
|
||||
var param_type_i: usize = 0;
|
||||
var it = fn_proto.iterate(tree.*);
|
||||
while (it.next()) |param| : (param_type_i += 1) {
|
||||
if (param_type_i % params_per_u32 == 0 and param_type_i != 0) {
|
||||
try bit_bag.append(gpa, cur_bit_bag);
|
||||
cur_bit_bag = 0;
|
||||
}
|
||||
const is_comptime = if (param.comptime_noalias) |token|
|
||||
token_tags[token] == .keyword_comptime
|
||||
else
|
||||
false;
|
||||
cur_bit_bag = (cur_bit_bag >> bits_per_param) |
|
||||
(@as(u32, @boolToInt(is_comptime)) << 31);
|
||||
|
||||
if (param.anytype_ellipsis3) |token| {
|
||||
switch (token_tags[token]) {
|
||||
.keyword_anytype => {
|
||||
@ -2940,6 +2981,11 @@ fn fnDecl(
|
||||
try expr(&decl_gz, &decl_gz.base, .{ .ty = .type_type }, param_type_node);
|
||||
}
|
||||
assert(param_type_i == param_count);
|
||||
|
||||
const empty_slot_count = params_per_u32 - (param_type_i % params_per_u32);
|
||||
if (empty_slot_count < params_per_u32) {
|
||||
cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_param);
|
||||
}
|
||||
}
|
||||
|
||||
const lib_name: u32 = if (fn_proto.lib_name) |lib_name_token| blk: {
|
||||
@ -3001,6 +3047,8 @@ fn fnDecl(
|
||||
.is_inferred_error = false,
|
||||
.is_test = false,
|
||||
.is_extern = true,
|
||||
.cur_bit_bag = cur_bit_bag,
|
||||
.bit_bag = bit_bag.items,
|
||||
});
|
||||
} else func: {
|
||||
if (is_var_args) {
|
||||
@ -3094,6 +3142,8 @@ fn fnDecl(
|
||||
.is_inferred_error = is_inferred_error,
|
||||
.is_test = false,
|
||||
.is_extern = false,
|
||||
.cur_bit_bag = cur_bit_bag,
|
||||
.bit_bag = bit_bag.items,
|
||||
});
|
||||
};
|
||||
|
||||
@ -3439,6 +3489,8 @@ fn testDecl(
|
||||
.is_inferred_error = true,
|
||||
.is_test = true,
|
||||
.is_extern = false,
|
||||
.cur_bit_bag = 0,
|
||||
.bit_bag = &.{},
|
||||
});
|
||||
|
||||
_ = try decl_block.addBreak(.break_inline, block_inst, func_inst);
|
||||
@ -9135,6 +9187,8 @@ const GenZir = struct {
|
||||
is_inferred_error: bool,
|
||||
is_test: bool,
|
||||
is_extern: bool,
|
||||
cur_bit_bag: u32,
|
||||
bit_bag: []const u32,
|
||||
}) !Zir.Inst.Ref {
|
||||
assert(args.src_node != 0);
|
||||
assert(args.ret_ty != .none);
|
||||
@ -9172,13 +9226,18 @@ const GenZir = struct {
|
||||
src_locs = &src_locs_buffer;
|
||||
}
|
||||
|
||||
const any_are_comptime = args.cur_bit_bag != 0 or for (args.bit_bag) |x| {
|
||||
if (x != 0) break true;
|
||||
} else false;
|
||||
|
||||
if (args.cc != .none or args.lib_name != 0 or
|
||||
args.is_var_args or args.is_test or args.align_inst != .none or
|
||||
args.is_extern)
|
||||
args.is_extern or any_are_comptime)
|
||||
{
|
||||
try astgen.extra.ensureUnusedCapacity(
|
||||
gpa,
|
||||
@typeInfo(Zir.Inst.ExtendedFunc).Struct.fields.len +
|
||||
@boolToInt(any_are_comptime) + args.bit_bag.len +
|
||||
args.param_types.len + args.body.len + src_locs.len +
|
||||
@boolToInt(args.lib_name != 0) +
|
||||
@boolToInt(args.align_inst != .none) +
|
||||
@ -9199,6 +9258,10 @@ const GenZir = struct {
|
||||
if (args.align_inst != .none) {
|
||||
astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst));
|
||||
}
|
||||
if (any_are_comptime) {
|
||||
astgen.extra.appendSliceAssumeCapacity(args.bit_bag); // Likely empty.
|
||||
astgen.extra.appendAssumeCapacity(args.cur_bit_bag);
|
||||
}
|
||||
astgen.appendRefsAssumeCapacity(args.param_types);
|
||||
astgen.extra.appendSliceAssumeCapacity(args.body);
|
||||
astgen.extra.appendSliceAssumeCapacity(src_locs);
|
||||
@ -9216,6 +9279,7 @@ const GenZir = struct {
|
||||
.has_align = args.align_inst != .none,
|
||||
.is_test = args.is_test,
|
||||
.is_extern = args.is_extern,
|
||||
.has_comptime_bits = any_are_comptime,
|
||||
}),
|
||||
.operand = payload_index,
|
||||
} },
|
||||
|
||||
29
src/Sema.zig
29
src/Sema.zig
@ -104,6 +104,9 @@ pub fn analyzeFnBody(
|
||||
extra_index += @boolToInt(small.has_lib_name);
|
||||
extra_index += @boolToInt(small.has_cc);
|
||||
extra_index += @boolToInt(small.has_align);
|
||||
if (small.has_comptime_bits) {
|
||||
extra_index += (extra.data.param_types_len + 31) / 32;
|
||||
}
|
||||
extra_index += extra.data.param_types_len;
|
||||
const body = sema.code.extra[extra_index..][0..extra.data.body_len];
|
||||
break :blk body;
|
||||
@ -2533,6 +2536,9 @@ fn analyzeCall(
|
||||
|
||||
break :res result;
|
||||
} else res: {
|
||||
if (func_ty.fnIsGeneric()) {
|
||||
return sema.mod.fail(&block.base, func_src, "TODO implement generic fn call", .{});
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, call_src);
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
|
||||
args.len);
|
||||
@ -3208,6 +3214,7 @@ fn zirFunc(
|
||||
false,
|
||||
src_locs,
|
||||
null,
|
||||
&.{},
|
||||
);
|
||||
}
|
||||
|
||||
@ -3225,6 +3232,7 @@ fn funcCommon(
|
||||
is_extern: bool,
|
||||
src_locs: Zir.Inst.Func.SrcLocs,
|
||||
opt_lib_name: ?[]const u8,
|
||||
comptime_bits: []const u32,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const src: LazySrcLoc = .{ .node_offset = src_node_offset };
|
||||
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
|
||||
@ -3257,13 +3265,23 @@ fn funcCommon(
|
||||
}
|
||||
}
|
||||
|
||||
var any_are_comptime = false;
|
||||
const param_types = try sema.arena.alloc(Type, zir_param_types.len);
|
||||
for (zir_param_types) |param_type, i| {
|
||||
// TODO make a compile error from `resolveType` report the source location
|
||||
// of the specific parameter. Will need to take a similar strategy as
|
||||
// `resolveSwitchItemVal` to avoid resolving the source location unless
|
||||
// we actually need to report an error.
|
||||
param_types[i] = try sema.resolveType(block, src, param_type);
|
||||
const param_src = src;
|
||||
param_types[i] = try sema.resolveType(block, param_src, param_type);
|
||||
|
||||
any_are_comptime = any_are_comptime or blk: {
|
||||
if (comptime_bits.len == 0)
|
||||
break :blk false;
|
||||
const bag = comptime_bits[i / 32];
|
||||
const is_comptime = @truncate(u1, bag >> @intCast(u5, i % 32)) != 0;
|
||||
break :blk is_comptime;
|
||||
};
|
||||
}
|
||||
|
||||
if (align_val.tag() != .null_value) {
|
||||
@ -3286,6 +3304,7 @@ fn funcCommon(
|
||||
.return_type = return_type,
|
||||
.cc = cc,
|
||||
.is_var_args = var_args,
|
||||
.is_generic = any_are_comptime,
|
||||
});
|
||||
};
|
||||
|
||||
@ -6526,6 +6545,13 @@ fn zirFuncExtended(
|
||||
break :blk align_tv.val;
|
||||
} else Value.initTag(.null_value);
|
||||
|
||||
const comptime_bits: []const u32 = if (!small.has_comptime_bits) &.{} else blk: {
|
||||
const amt = (extra.data.param_types_len + 31) / 32;
|
||||
const bit_bags = sema.code.extra[extra_index..][0..amt];
|
||||
extra_index += amt;
|
||||
break :blk bit_bags;
|
||||
};
|
||||
|
||||
const param_types = sema.code.refSlice(extra_index, extra.data.param_types_len);
|
||||
extra_index += param_types.len;
|
||||
|
||||
@ -6554,6 +6580,7 @@ fn zirFuncExtended(
|
||||
is_extern,
|
||||
src_locs,
|
||||
lib_name,
|
||||
comptime_bits,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
28
src/Zir.zig
28
src/Zir.zig
@ -2226,9 +2226,13 @@ pub const Inst = struct {
|
||||
/// 0. lib_name: u32, // null terminated string index, if has_lib_name is set
|
||||
/// 1. cc: Ref, // if has_cc is set
|
||||
/// 2. align: Ref, // if has_align is set
|
||||
/// 3. param_type: Ref // for each param_types_len
|
||||
/// 4. body: Index // for each body_len
|
||||
/// 5. src_locs: Func.SrcLocs // if body_len != 0
|
||||
/// 3. comptime_bits: u32 // for every 32 parameters, if has_comptime_bits is set
|
||||
/// - sets of 1 bit:
|
||||
/// 0bX: whether corresponding parameter is comptime
|
||||
/// 4. param_type: Ref // for each param_types_len
|
||||
/// - `none` indicates that the param type is `anytype`.
|
||||
/// 5. body: Index // for each body_len
|
||||
/// 6. src_locs: Func.SrcLocs // if body_len != 0
|
||||
pub const ExtendedFunc = struct {
|
||||
src_node: i32,
|
||||
return_type: Ref,
|
||||
@ -2243,7 +2247,8 @@ pub const Inst = struct {
|
||||
has_align: bool,
|
||||
is_test: bool,
|
||||
is_extern: bool,
|
||||
_: u9 = undefined,
|
||||
has_comptime_bits: bool,
|
||||
_: u8 = undefined,
|
||||
};
|
||||
};
|
||||
|
||||
@ -4291,6 +4296,7 @@ const Writer = struct {
|
||||
body,
|
||||
src,
|
||||
src_locs,
|
||||
&.{},
|
||||
);
|
||||
}
|
||||
|
||||
@ -4317,6 +4323,13 @@ const Writer = struct {
|
||||
break :blk align_inst;
|
||||
};
|
||||
|
||||
const comptime_bits: []const u32 = if (!small.has_comptime_bits) &.{} else blk: {
|
||||
const amt = (extra.data.param_types_len + 31) / 32;
|
||||
const bit_bags = self.code.extra[extra_index..][0..amt];
|
||||
extra_index += amt;
|
||||
break :blk bit_bags;
|
||||
};
|
||||
|
||||
const param_types = self.code.refSlice(extra_index, extra.data.param_types_len);
|
||||
extra_index += param_types.len;
|
||||
|
||||
@ -4339,6 +4352,7 @@ const Writer = struct {
|
||||
body,
|
||||
src,
|
||||
src_locs,
|
||||
comptime_bits,
|
||||
);
|
||||
}
|
||||
|
||||
@ -4422,10 +4436,16 @@ const Writer = struct {
|
||||
body: []const Inst.Index,
|
||||
src: LazySrcLoc,
|
||||
src_locs: Zir.Inst.Func.SrcLocs,
|
||||
comptime_bits: []const u32,
|
||||
) !void {
|
||||
try stream.writeAll("[");
|
||||
for (param_types) |param_type, i| {
|
||||
if (i != 0) try stream.writeAll(", ");
|
||||
if (comptime_bits.len != 0) {
|
||||
const bag = comptime_bits[i / 32];
|
||||
const is_comptime = @truncate(u1, bag >> @intCast(u5, i % 32)) != 0;
|
||||
try self.writeFlag(stream, "comptime ", is_comptime);
|
||||
}
|
||||
try self.writeInstRef(stream, param_type);
|
||||
}
|
||||
try stream.writeAll("], ");
|
||||
|
||||
15
src/type.zig
15
src/type.zig
@ -764,6 +764,7 @@ pub const Type = extern union {
|
||||
.param_types = param_types,
|
||||
.cc = payload.cc,
|
||||
.is_var_args = payload.is_var_args,
|
||||
.is_generic = payload.is_generic,
|
||||
});
|
||||
},
|
||||
.pointer => {
|
||||
@ -2407,6 +2408,19 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is a function.
|
||||
pub fn fnIsGeneric(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.fn_noreturn_no_args => false,
|
||||
.fn_void_no_args => false,
|
||||
.fn_naked_noreturn_no_args => false,
|
||||
.fn_ccc_void_no_args => false,
|
||||
.function => self.castTag(.function).?.data.is_generic,
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isNumeric(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.f16,
|
||||
@ -3214,6 +3228,7 @@ pub const Type = extern union {
|
||||
return_type: Type,
|
||||
cc: std.builtin.CallingConvention,
|
||||
is_var_args: bool,
|
||||
is_generic: bool,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user