aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-08-01 22:04:18 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-08-01 22:04:18 -0700
commitdae4c18aa7999a866b6b145ed3c88cb6c06852be (patch)
tree6aa41102b19c8e0af04bf1dd8af5158c4cc8b3a5 /src
parentd5f173d28f2991e05dad399ed4d688297e0a5ca7 (diff)
downloadzig-dae4c18aa7999a866b6b145ed3c88cb6c06852be.tar.gz
zig-dae4c18aa7999a866b6b145ed3c88cb6c06852be.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/AstGen.zig66
-rw-r--r--src/Sema.zig29
-rw-r--r--src/Zir.zig28
-rw-r--r--src/type.zig15
4 files changed, 132 insertions, 6 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig
index b5e5d60b2c..20480ab33b 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -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,
} },
diff --git a/src/Sema.zig b/src/Sema.zig
index 6b281f8569..753ef8fb9c 100644
--- a/src/Sema.zig
+++ b/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,
);
}
diff --git a/src/Zir.zig b/src/Zir.zig
index a8320b65d4..109ae3b186 100644
--- a/src/Zir.zig
+++ b/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("], ");
diff --git a/src/type.zig b/src/type.zig
index 82c28ef398..a8c3d77bbb 100644
--- a/src/type.zig
+++ b/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,
},
};