diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-08-02 20:35:55 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-08-02 21:56:10 -0700 |
| commit | 1472dc3ddb6fd7932ff530e7a2fd3f0185c7353f (patch) | |
| tree | 77c6cae8dbc822e9fc5ec069b9d61d6e769694f8 /src | |
| parent | b465037a65dd6a31c5865086ec4392a1d3a372bc (diff) | |
| download | zig-1472dc3ddb6fd7932ff530e7a2fd3f0185c7353f.tar.gz zig-1472dc3ddb6fd7932ff530e7a2fd3f0185c7353f.zip | |
stage2: update ZIR for generic functions
ZIR encoding for functions is changed in preparation for generic
function support. As an example:
```zig
const std = @import("std");
const expect = std.testing.expect;
test "example" {
var x: usize = 0;
x += checkSize(i32, 1);
x += checkSize(bool, true);
try expect(x == 5);
}
fn checkSize(comptime T: type, x: T) usize {
_ = x;
return @sizeOf(T);
}
```
Previous ZIR for the `checkSize` function:
```zir
[165] checkSize line(10) hash(0226f62e189fd0b1c5fca02cf4617562): %55 = block_inline({
%56 = decl_val("T") token_offset:11:35
%57 = as_node(@Ref.type_type, %56) node_offset:11:35
%69 = extended(func([comptime @Ref.type_type, %57], @Ref.usize_type, {
%58 = arg("T") token_offset:11:23
%59 = as_node(@Ref.type_type, %58) node_offset:11:35
%60 = arg("x") token_offset:11:32
%61 = dbg_stmt(11, 4)
```
ZIR for the `checkSize` function after this commit:
```zir
[157] checkSize line(10) hash(0226f62e189fd0b1c5fca02cf4617562): %55 = block_inline({
%56 = param_comptime("T", @Ref.type_type) token_offset:11:23
%57 = as_node(@Ref.type_type, %56) node_offset:11:35
%58 = param("x", %57) token_offset:11:32
%67 = func(@Ref.usize_type, {
%59 = dbg_stmt(11, 4)
```
Noted differences:
* Previously the type expression was redundantly repeated.
* Previously the parameter names were redundantly stored in the ZIR
extra array.
* Instead of `arg` ZIR instructions as the first instructions within a
function body, they are now outside the function body, in the same
block as the `func` instruction. There are variants:
- param
- param_comptime
- param_anytype
- param_anytype_comptime
* The param instructions additionally encode the type.
* Because of the param instructions, the `func` instruction no longer
encodes the list of parameter types or the comptime bits.
It's implied that Sema will collect the parameters so that when a `func`
instruction is encountered, they will be implicitly used to construct
the function's type. This is so that we can satisfy all 3 ways of
performing semantic analysis on a function:
1. runtime: Sema will insert AIR arg instructions for each parameter,
and insert into the Sema inst_map ZIR param => AIR arg.
2. comptime/inline: Sema will insert into the inst_map ZIR param =>
callsite arguments.
3. generic: Sema will map *only the comptime* ZIR param instructions to
the AIR instructions for the comptime arguments at the callsite, and
then re-run Sema for the function's Decl. This will produce a new
function which is the monomorphized function.
Additionally:
* AstGen: Update usage of deprecated `ensureCapacity` to
`ensureUnusedCapacity` or `ensureTotalCapacity`.
* Introduce `Type.fnInfo` for getting a bunch of data about a function
type at once, and use it in `analyzeCall`.
This commit starts a branch to implement generic functions in stage2.
Test regressions have not been addressed yet.
Diffstat (limited to 'src')
| -rw-r--r-- | src/AstGen.zig | 424 | ||||
| -rw-r--r-- | src/Module.zig | 2 | ||||
| -rw-r--r-- | src/Sema.zig | 193 | ||||
| -rw-r--r-- | src/Zir.zig | 121 | ||||
| -rw-r--r-- | src/type.zig | 60 |
5 files changed, 422 insertions, 378 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig index 20480ab33b..0b78c839a0 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -42,7 +42,7 @@ const InnerError = error{ OutOfMemory, AnalysisFail }; fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 { const fields = std.meta.fields(@TypeOf(extra)); - try astgen.extra.ensureCapacity(astgen.gpa, astgen.extra.items.len + fields.len); + try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len); return addExtraAssumeCapacity(astgen, extra); } @@ -259,6 +259,7 @@ 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 }; fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!Zir.Inst.Ref { const prev_force_comptime = gz.force_comptime; @@ -1036,7 +1037,6 @@ fn fnProtoExpr( fn_proto: ast.full.FnProto, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; - const gpa = astgen.gpa; const tree = astgen.tree; const token_tags = tree.tokens.items(.tag); @@ -1046,71 +1046,53 @@ fn fnProtoExpr( }; assert(!is_extern); - // The AST params array does not contain anytype and ... parameters. - // We must iterate to count how many param types to allocate. - const param_count = blk: { - var count: usize = 0; - var it = fn_proto.iterate(tree.*); - while (it.next()) |param| { - if (param.anytype_ellipsis3) |token| switch (token_tags[token]) { - .ellipsis3 => break, - .keyword_anytype => {}, - else => unreachable, - }; - count += 1; - } - break :blk count; - }; - 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; - { + const is_var_args = is_var_args: { 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| { + const is_anytype = if (param.anytype_ellipsis3) |token| blk: { switch (token_tags[token]) { - .keyword_anytype => { - param_types[param_type_i] = .none; - continue; - }, - .ellipsis3 => { - is_var_args = true; - break; - }, + .keyword_anytype => break :blk true, + .ellipsis3 => break :is_var_args true, else => unreachable, } - } - const param_type_node = param.type_expr; - assert(param_type_node != 0); - param_types[param_type_i] = - try expr(gz, scope, .{ .ty = .type_type }, param_type_node); - } - assert(param_type_i == param_count); + } else false; + + const param_name: u32 = if (param.name_token) |name_token| blk: { + if (mem.eql(u8, "_", tree.tokenSlice(name_token))) + break :blk 0; + + break :blk try astgen.identAsString(name_token); + } else 0; - 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); + if (is_anytype) { + const name_token = param.name_token orelse param.anytype_ellipsis3.?; + + const tag: Zir.Inst.Tag = if (is_comptime) + .param_anytype_comptime + else + .param_anytype; + _ = try gz.addStrTok(tag, param_name, name_token); + } else { + const param_type_node = param.type_expr; + assert(param_type_node != 0); + const param_type = try expr(gz, scope, type_rl, param_type_node); + 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, + }); + } } - } + break :is_var_args false; + }; const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { break :inst try expr(gz, scope, align_rl, fn_proto.ast.align_expr); @@ -1144,7 +1126,6 @@ fn fnProtoExpr( const result = try gz.addFunc(.{ .src_node = fn_proto.ast.proto_node, .ret_ty = return_type_inst, - .param_types = param_types, .body = &[0]Zir.Inst.Index{}, .cc = cc, .align_inst = align_inst, @@ -1153,8 +1134,6 @@ 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); } @@ -1447,8 +1426,8 @@ fn structInitExprRlNone( const init_inst = try gz.addPlNode(tag, node, Zir.Inst.StructInitAnon{ .fields_len = @intCast(u32, fields_list.len), }); - try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + - fields_list.len * @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len); + try astgen.extra.ensureUnusedCapacity(gpa, fields_list.len * + @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len); for (fields_list) |field| { _ = gz.astgen.addExtraAssumeCapacity(field); } @@ -1520,8 +1499,8 @@ fn structInitExprRlTy( const init_inst = try gz.addPlNode(tag, node, Zir.Inst.StructInit{ .fields_len = @intCast(u32, fields_list.len), }); - try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + - fields_list.len * @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len); + try astgen.extra.ensureUnusedCapacity(gpa, fields_list.len * + @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len); for (fields_list) |field| { _ = gz.astgen.addExtraAssumeCapacity(field); } @@ -1918,7 +1897,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner // ZIR instructions that might be a type other than `noreturn` or `void`. .add, .addwrap, - .arg, + .param, + .param_comptime, + .param_anytype, + .param_anytype_comptime, .alloc, .alloc_mut, .alloc_comptime, @@ -2488,7 +2470,7 @@ fn varDecl( // Move the init_scope instructions into the parent scope, swapping // store_to_block_ptr for store_to_inferred_ptr. const expected_len = parent_zir.items.len + init_scope.instructions.items.len; - try parent_zir.ensureCapacity(gpa, expected_len); + try parent_zir.ensureTotalCapacity(gpa, expected_len); for (init_scope.instructions.items) |src_inst| { if (zir_tags[src_inst] == .store_to_block_ptr) { if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) { @@ -2750,10 +2732,10 @@ fn ptrType( } const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); - try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); - try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + - @typeInfo(Zir.Inst.PtrType).Struct.fields.len + trailing_count); + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).Struct.fields.len + + trailing_count); const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{ .elem_type = elem_type }); if (sentinel_ref != .none) { @@ -2899,6 +2881,16 @@ fn fnDecl( }; defer decl_gz.instructions.deinit(gpa); + var fn_gz: GenZir = .{ + .force_comptime = false, + .in_defer = false, + .decl_node_index = fn_proto.ast.proto_node, + .decl_line = decl_gz.decl_line, + .parent = &decl_gz.base, + .astgen = astgen, + }; + defer fn_gz.instructions.deinit(gpa); + // TODO: support noinline const is_pub = fn_proto.visib_token != null; const is_export = blk: { @@ -2922,71 +2914,76 @@ fn fnDecl( try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, section_inst != .none); - // The AST params array does not contain anytype and ... parameters. - // We must iterate to count how many param types to allocate. - const param_count = blk: { - var count: usize = 0; - var it = fn_proto.iterate(tree.*); - while (it.next()) |param| { - if (param.anytype_ellipsis3) |token| switch (token_tags[token]) { - .ellipsis3 => break, - .keyword_anytype => {}, - else => unreachable, - }; - count += 1; - } - break :blk count; - }; - 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 params_scope = &fn_gz.base; + const is_var_args = is_var_args: { 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| { + const is_anytype = if (param.anytype_ellipsis3) |token| blk: { switch (token_tags[token]) { - .keyword_anytype => { - param_types[param_type_i] = .none; - continue; - }, - .ellipsis3 => { - is_var_args = true; - break; - }, + .keyword_anytype => break :blk true, + .ellipsis3 => break :is_var_args true, else => unreachable, } - } - const param_type_node = param.type_expr; - assert(param_type_node != 0); - param_types[param_type_i] = - try expr(&decl_gz, &decl_gz.base, .{ .ty = .type_type }, param_type_node); - } - assert(param_type_i == param_count); + } else false; + + const param_name: u32 = if (param.name_token) |name_token| blk: { + if (mem.eql(u8, "_", tree.tokenSlice(name_token))) + break :blk 0; + + const param_name = try astgen.identAsString(name_token); + if (!is_extern) { + try astgen.detectLocalShadowing(params_scope, param_name, name_token); + } + break :blk param_name; + } else if (!is_extern) { + if (param.anytype_ellipsis3) |tok| { + return astgen.failTok(tok, "missing parameter name", .{}); + } else { + return astgen.failNode(param.type_expr, "missing parameter name", .{}); + } + } else 0; + + const param_inst = if (is_anytype) param: { + const name_token = param.name_token orelse param.anytype_ellipsis3.?; + const tag: Zir.Inst.Tag = if (is_comptime) + .param_anytype_comptime + else + .param_anytype; + break :param try decl_gz.addStrTok(tag, param_name, name_token); + } 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); + 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, + }); + }; + + if (param_name == 0) continue; - 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 sub_scope = try astgen.arena.create(Scope.LocalVal); + sub_scope.* = .{ + .parent = params_scope, + .gen_zir = &decl_gz, + .name = param_name, + .inst = param_inst, + .token_src = param.name_token.?, + .id_cat = .@"function parameter", + }; + params_scope = &sub_scope.base; } - } + break :is_var_args false; + }; const lib_name: u32 = if (fn_proto.lib_name) |lib_name_token| blk: { const lib_name_str = try astgen.strLitAsString(lib_name_token); @@ -2998,7 +2995,7 @@ fn fnDecl( const return_type_inst = try AstGen.expr( &decl_gz, - &decl_gz.base, + params_scope, .{ .ty = .type_type }, fn_proto.ast.return_type, ); @@ -3014,7 +3011,7 @@ fn fnDecl( } break :blk try AstGen.expr( &decl_gz, - &decl_gz.base, + params_scope, .{ .ty = .calling_convention_type }, fn_proto.ast.callconv_expr, ); @@ -3038,7 +3035,6 @@ fn fnDecl( break :func try decl_gz.addFunc(.{ .src_node = decl_node, .ret_ty = return_type_inst, - .param_types = param_types, .body = &[0]Zir.Inst.Index{}, .cc = cc, .align_inst = .none, // passed in the per-decl data @@ -3047,75 +3043,18 @@ 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) { return astgen.failTok(fn_proto.ast.fn_token, "non-extern function is variadic", .{}); } - var fn_gz: GenZir = .{ - .force_comptime = false, - .in_defer = false, - .decl_node_index = fn_proto.ast.proto_node, - .decl_line = decl_gz.decl_line, - .parent = &decl_gz.base, - .astgen = astgen, - }; - defer fn_gz.instructions.deinit(gpa); - const prev_fn_block = astgen.fn_block; astgen.fn_block = &fn_gz; defer astgen.fn_block = prev_fn_block; - // Iterate over the parameters. We put the param names as the first N - // items inside `extra` so that debug info later can refer to the parameter names - // even while the respective source code is unloaded. - try astgen.extra.ensureUnusedCapacity(gpa, param_count); - - { - var params_scope = &fn_gz.base; - var i: usize = 0; - var it = fn_proto.iterate(tree.*); - while (it.next()) |param| : (i += 1) { - const name_token = param.name_token orelse { - if (param.anytype_ellipsis3) |tok| { - return astgen.failTok(tok, "missing parameter name", .{}); - } else { - return astgen.failNode(param.type_expr, "missing parameter name", .{}); - } - }; - if (param.type_expr != 0) - _ = try typeExpr(&fn_gz, params_scope, param.type_expr); - if (mem.eql(u8, "_", tree.tokenSlice(name_token))) - continue; - const param_name = try astgen.identAsString(name_token); - // Create an arg instruction. This is needed to emit a semantic analysis - // error for shadowing decls. - try astgen.detectLocalShadowing(params_scope, param_name, name_token); - const arg_inst = try fn_gz.addStrTok(.arg, param_name, name_token); - const sub_scope = try astgen.arena.create(Scope.LocalVal); - sub_scope.* = .{ - .parent = params_scope, - .gen_zir = &fn_gz, - .name = param_name, - .inst = arg_inst, - .token_src = name_token, - .id_cat = .@"function parameter", - }; - params_scope = &sub_scope.base; - - // Additionally put the param name into `string_bytes` and reference it with - // `extra` so that we have access to the data in codegen, for debug info. - const str_index = try astgen.identAsString(name_token); - try astgen.extra.append(astgen.gpa, str_index); - } - _ = try typeExpr(&fn_gz, params_scope, fn_proto.ast.return_type); - - _ = try expr(&fn_gz, params_scope, .none, body_node); - try checkUsed(gz, &fn_gz.base, params_scope); - } + _ = try expr(&fn_gz, params_scope, .none, body_node); + try checkUsed(gz, &fn_gz.base, params_scope); const need_implicit_ret = blk: { if (fn_gz.instructions.items.len == 0) @@ -3133,7 +3072,6 @@ fn fnDecl( break :func try decl_gz.addFunc(.{ .src_node = decl_node, .ret_ty = return_type_inst, - .param_types = param_types, .body = fn_gz.instructions.items, .cc = cc, .align_inst = .none, // passed in the per-decl data @@ -3142,8 +3080,6 @@ 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, }); }; @@ -3480,7 +3416,6 @@ fn testDecl( const func_inst = try decl_block.addFunc(.{ .src_node = node, .ret_ty = .void_type, - .param_types = &[0]Zir.Inst.Ref{}, .body = fn_block.instructions.items, .cc = .none, .align_inst = .none, @@ -3489,8 +3424,6 @@ 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); @@ -4238,7 +4171,7 @@ fn containerDecl( var fields_data = ArrayListUnmanaged(u32){}; defer fields_data.deinit(gpa); - try fields_data.ensureCapacity(gpa, counts.total_fields + counts.values); + try fields_data.ensureTotalCapacity(gpa, counts.total_fields + counts.values); // We only need this if there are greater than 32 fields. var bit_bag = ArrayListUnmanaged(u32){}; @@ -5184,8 +5117,7 @@ fn setCondBrPayload( ) !void { const astgen = then_scope.astgen; - try astgen.extra.ensureCapacity(astgen.gpa, astgen.extra.items.len + - @typeInfo(Zir.Inst.CondBr).Struct.fields.len + + try astgen.extra.ensureUnusedCapacity(astgen.gpa, @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_scope.instructions.items.len + else_scope.instructions.items.len); const zir_datas = astgen.instructions.items(.data); @@ -5839,10 +5771,9 @@ fn switchExpr( _ = try case_scope.addBreak(.@"break", switch_block, case_result); } // Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`. - try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + + try scalar_cases_payload.ensureUnusedCapacity(gpa, case_scope.instructions.items.len + 3 + // operand, scalar_cases_len, else body len - @boolToInt(multi_cases_len != 0) + - case_scope.instructions.items.len); + @boolToInt(multi_cases_len != 0)); scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand)); scalar_cases_payload.appendAssumeCapacity(scalar_cases_len); if (multi_cases_len != 0) { @@ -5852,9 +5783,11 @@ fn switchExpr( scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items); } else { // Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`. - try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + - 2 + // operand, scalar_cases_len - @boolToInt(multi_cases_len != 0)); + try scalar_cases_payload.ensureUnusedCapacity( + gpa, + @as(usize, 2) + // operand, scalar_cases_len + @boolToInt(multi_cases_len != 0), + ); scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand)); scalar_cases_payload.appendAssumeCapacity(scalar_cases_len); if (multi_cases_len != 0) { @@ -5975,8 +5908,8 @@ fn switchExpr( block_scope.break_count += 1; _ = try case_scope.addBreak(.@"break", switch_block, case_result); } - try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + - 2 + case_scope.instructions.items.len); + try scalar_cases_payload.ensureUnusedCapacity(gpa, 2 + + case_scope.instructions.items.len); scalar_cases_payload.appendAssumeCapacity(@enumToInt(item_inst)); scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len)); scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items); @@ -6012,8 +5945,8 @@ fn switchExpr( const payload_index = astgen.extra.items.len; const zir_datas = astgen.instructions.items(.data); zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index); - try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + - scalar_cases_payload.items.len + multi_cases_payload.items.len); + try astgen.extra.ensureUnusedCapacity(gpa, scalar_cases_payload.items.len + + multi_cases_payload.items.len); const strat = rl.strategy(&block_scope); switch (strat.tag) { .break_operand => { @@ -8659,7 +8592,7 @@ fn failNodeNotes( } const notes_index: u32 = if (notes.len != 0) blk: { const notes_start = astgen.extra.items.len; - try astgen.extra.ensureCapacity(astgen.gpa, notes_start + 1 + notes.len); + try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len); astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); astgen.extra.appendSliceAssumeCapacity(notes); break :blk @intCast(u32, notes_start); @@ -8700,7 +8633,7 @@ fn failTokNotes( } const notes_index: u32 = if (notes.len != 0) blk: { const notes_start = astgen.extra.items.len; - try astgen.extra.ensureCapacity(astgen.gpa, notes_start + 1 + notes.len); + try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len); astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); astgen.extra.appendSliceAssumeCapacity(notes); break :blk @intCast(u32, notes_start); @@ -8864,7 +8797,7 @@ fn strLitNodeAsString(astgen: *AstGen, node: ast.Node.Index) !IndexSlice { while (tok_i <= end) : (tok_i += 1) { const slice = tree.tokenSlice(tok_i); const line_bytes = slice[2 .. slice.len - 1]; - try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1); + try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1); string_bytes.appendAssumeCapacity('\n'); string_bytes.appendSliceAssumeCapacity(line_bytes); } @@ -9131,8 +9064,8 @@ const GenZir = struct { fn setBoolBrBody(gz: GenZir, inst: Zir.Inst.Index) !void { const gpa = gz.astgen.gpa; - try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + - @typeInfo(Zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); + try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Block).Struct.fields.len + + gz.instructions.items.len); const zir_datas = gz.astgen.instructions.items(.data); zir_datas[inst].bool_br.payload_index = gz.astgen.addExtraAssumeCapacity( Zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, @@ -9142,8 +9075,8 @@ const GenZir = struct { fn setBlockBody(gz: GenZir, inst: Zir.Inst.Index) !void { const gpa = gz.astgen.gpa; - try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + - @typeInfo(Zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); + try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Block).Struct.fields.len + + gz.instructions.items.len); const zir_datas = gz.astgen.instructions.items(.data); zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity( Zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, @@ -9155,8 +9088,8 @@ const GenZir = struct { /// `store_to_block_ptr` instructions with lhs set to .none. fn setBlockBodyEliding(gz: GenZir, inst: Zir.Inst.Index) !void { const gpa = gz.astgen.gpa; - try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + - @typeInfo(Zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); + try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Block).Struct.fields.len + + gz.instructions.items.len); const zir_datas = gz.astgen.instructions.items(.data); const zir_tags = gz.astgen.instructions.items(.tag); const block_pl_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Block{ @@ -9177,7 +9110,6 @@ const GenZir = struct { fn addFunc(gz: *GenZir, args: struct { src_node: ast.Node.Index, - param_types: []const Zir.Inst.Ref, body: []const Zir.Inst.Index, ret_ty: Zir.Inst.Ref, cc: Zir.Inst.Ref, @@ -9187,8 +9119,6 @@ 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); @@ -9226,19 +9156,14 @@ 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 or any_are_comptime) + args.is_extern) { 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 + + args.body.len + src_locs.len + @boolToInt(args.lib_name != 0) + @boolToInt(args.align_inst != .none) + @boolToInt(args.cc != .none), @@ -9246,7 +9171,6 @@ const GenZir = struct { const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedFunc{ .src_node = gz.nodeIndexToRelative(args.src_node), .return_type = args.ret_ty, - .param_types_len = @intCast(u32, args.param_types.len), .body_len = @intCast(u32, args.body.len), }); if (args.lib_name != 0) { @@ -9258,11 +9182,6 @@ 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); @@ -9279,7 +9198,6 @@ 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, } }, @@ -9290,15 +9208,13 @@ const GenZir = struct { try gz.astgen.extra.ensureUnusedCapacity( gpa, @typeInfo(Zir.Inst.Func).Struct.fields.len + - args.param_types.len + args.body.len + src_locs.len, + args.body.len + src_locs.len, ); const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Func{ .return_type = args.ret_ty, - .param_types_len = @intCast(u32, args.param_types.len), .body_len = @intCast(u32, args.body.len), }); - gz.astgen.appendRefsAssumeCapacity(args.param_types); gz.astgen.extra.appendSliceAssumeCapacity(args.body); gz.astgen.extra.appendSliceAssumeCapacity(src_locs); @@ -9380,10 +9296,10 @@ const GenZir = struct { assert(callee != .none); assert(src_node != 0); const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); - try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); - try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + - @typeInfo(Zir.Inst.Call).Struct.fields.len + args.len); + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Call).Struct.fields.len + + args.len); const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Call{ .callee = callee, @@ -9412,8 +9328,8 @@ const GenZir = struct { ) !Zir.Inst.Index { assert(lhs != .none); const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); - try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); gz.astgen.instructions.appendAssumeCapacity(.{ @@ -9486,8 +9402,8 @@ const GenZir = struct { extra: anytype, ) !Zir.Inst.Ref { const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); - try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); const payload_index = try gz.astgen.addExtra(extra); const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); @@ -9502,6 +9418,30 @@ const GenZir = struct { return indexToRef(new_index); } + fn addPlTok( + 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 { + const gpa = gz.astgen.gpa; + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); + + 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, + .data = .{ .pl_tok = .{ + .src_tok = gz.tokenIndexToRelative(abs_tok_index), + .payload_index = payload_index, + } }, + }); + gz.instructions.appendAssumeCapacity(new_index); + return indexToRef(new_index); + } + fn addExtendedPayload( gz: *GenZir, opcode: Zir.Inst.Extended, @@ -9509,8 +9449,8 @@ const GenZir = struct { ) !Zir.Inst.Ref { const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); - try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); const payload_index = try gz.astgen.addExtra(extra); const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); @@ -9566,8 +9506,8 @@ const GenZir = struct { elem_type: Zir.Inst.Ref, ) !Zir.Inst.Ref { const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); - try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); const payload_index = try gz.astgen.addExtra(Zir.Inst.ArrayTypeSentinel{ .sentinel = sentinel, @@ -9822,7 +9762,7 @@ const GenZir = struct { /// Leaves the `payload_index` field undefined. fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: ast.Node.Index) !Zir.Inst.Index { const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); + try gz.instructions.ensureUnusedCapacity(gpa, 1); const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); try gz.astgen.instructions.append(gpa, .{ .tag = tag, diff --git a/src/Module.zig b/src/Module.zig index 84b721369d..fa8b4ca768 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3714,7 +3714,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { decl.analysis = .outdated; } -fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node.Index) !*Decl { +pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node.Index) !*Decl { // If we have emit-h then we must allocate a bigger structure to store the emit-h state. const new_decl: *Decl = if (mod.emit_h != null) blk: { const parent_struct = try mod.gpa.create(DeclPlusEmitH); diff --git a/src/Sema.zig b/src/Sema.zig index 753ef8fb9c..5fd3c149a2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -44,6 +44,7 @@ branch_count: u32 = 0, /// contain a mapped source location. src: LazySrcLoc = .{ .token_offset = 0 }, next_arg_index: usize = 0, +params: std.ArrayListUnmanaged(Param) = .{}, decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{}, const std = @import("std"); @@ -68,6 +69,13 @@ const LazySrcLoc = Module.LazySrcLoc; const RangeSet = @import("RangeSet.zig"); const target_util = @import("target.zig"); +const Param = struct { + name: [:0]const u8, + /// `none` means `anytype`. + ty: Air.Inst.Ref, + is_comptime: bool, +}; + pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); pub fn deinit(sema: *Sema) void { @@ -91,8 +99,7 @@ pub fn analyzeFnBody( .func, .func_inferred => blk: { const inst_data = datas[fn_body_inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); - const param_types_len = extra.data.param_types_len; - const body = sema.code.extra[extra.end + param_types_len ..][0..extra.data.body_len]; + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; break :blk body; }, .extended => blk: { @@ -104,10 +111,6 @@ 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; }, @@ -162,7 +165,6 @@ pub fn analyzeBody( const inst = body[i]; const air_inst: Air.Inst.Ref = switch (tags[inst]) { // zig fmt: off - .arg => try sema.zirArg(block, inst), .alloc => try sema.zirAlloc(block, inst), .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)), .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)), @@ -404,6 +406,26 @@ pub fn analyzeBody( // continue the loop. // We also know that they cannot be referenced later, so we avoid // putting them into the map. + .param => { + try sema.zirParam(inst, false); + i += 1; + continue; + }, + .param_comptime => { + try sema.zirParam(inst, true); + i += 1; + continue; + }, + .param_anytype => { + try sema.zirParamAnytype(inst, false); + i += 1; + continue; + }, + .param_anytype_comptime => { + try sema.zirParamAnytype(inst, true); + i += 1; + continue; + }, .breakpoint => { try sema.zirBreakpoint(block, inst); i += 1; @@ -1358,23 +1380,34 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co return sema.analyzeLoad(block, src, result_ptr, result_ptr_src); } -fn zirArg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[inst].str_tok; - const arg_name = inst_data.get(sema.code); - const arg_index = sema.next_arg_index; - sema.next_arg_index += 1; +fn zirParam(sema: *Sema, inst: Zir.Inst.Index, is_comptime: bool) CompileError!void { + const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; + const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data; + const param_name = sema.code.nullTerminatedString(extra.name); - // TODO check if arg_name shadows a Decl - _ = arg_name; + // TODO check if param_name shadows a Decl. This only needs to be done if + // usingnamespace is implemented. - if (block.inlining) |_| { - return sema.param_inst_list[arg_index]; - } + const param_ty = sema.resolveInst(extra.ty); + try sema.params.append(sema.gpa, .{ + .name = param_name, + .ty = param_ty, + .is_comptime = is_comptime, + }); +} + +fn zirParamAnytype(sema: *Sema, 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); - // Set the name of the Air.Arg instruction for use by codegen debug info. - const air_arg = sema.param_inst_list[arg_index]; - sema.air_instructions.items(.data)[Air.refToIndex(air_arg).?].ty_str.str = inst_data.start; - return air_arg; + // 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 = .none, + .is_comptime = is_comptime, + }); } fn zirAllocExtended( @@ -2395,26 +2428,29 @@ fn analyzeCall( ensure_result_used: bool, args: []const Air.Inst.Ref, ) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const func_ty = sema.typeOf(func); if (func_ty.zigTypeTag() != .Fn) - return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func_ty}); + return mod.fail(&block.base, func_src, "type '{}' not a function", .{func_ty}); - const cc = func_ty.fnCallingConvention(); + const func_ty_info = func_ty.fnInfo(); + const cc = func_ty_info.cc; if (cc == .Naked) { // TODO add error note: declared here - return sema.mod.fail( + return mod.fail( &block.base, func_src, "unable to call function with naked calling convention", .{}, ); } - const fn_params_len = func_ty.fnParamLen(); - if (func_ty.fnIsVarArgs()) { + 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) { // TODO add error note: declared here - return sema.mod.fail( + return mod.fail( &block.base, func_src, "expected at least {d} argument(s), found {d}", @@ -2423,7 +2459,7 @@ fn analyzeCall( } } else if (fn_params_len != args.len) { // TODO add error note: declared here - return sema.mod.fail( + return mod.fail( &block.base, func_src, "expected {d} argument(s), found {d}", @@ -2442,7 +2478,7 @@ fn analyzeCall( .never_inline, .no_async, .always_tail, - => return sema.mod.fail(&block.base, call_src, "TODO implement call with modifier {}", .{ + => return mod.fail(&block.base, call_src, "TODO implement call with modifier {}", .{ modifier, }), } @@ -2451,12 +2487,12 @@ fn analyzeCall( const is_comptime_call = block.is_comptime or modifier == .compile_time; const is_inline_call = is_comptime_call or modifier == .always_inline or - func_ty.fnCallingConvention() == .Inline; + func_ty_info.cc == .Inline; const result: Air.Inst.Ref = if (is_inline_call) res: { const func_val = try sema.resolveConstValue(block, func_src, func); const module_fn = switch (func_val.tag()) { .function => func_val.castTag(.function).?.data, - .extern_fn => return sema.mod.fail(&block.base, call_src, "{s} call of extern function", .{ + .extern_fn => return mod.fail(&block.base, call_src, "{s} call of extern function", .{ @as([]const u8, if (is_comptime_call) "comptime" else "inline"), }), else => unreachable, @@ -2535,10 +2571,46 @@ fn analyzeCall( const result = try sema.analyzeBlockBody(block, call_src, &child_block, merges); break :res result; + } else if (func_ty_info.is_generic) { + 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. + // TODO + + // Create a Decl for the new function. + const generic_namespace = try sema.arena.create(Module.Scope.Namespace); + generic_namespace.* = .{ + .parent = block.src_decl.namespace, + .file_scope = block.src_decl.namespace.file_scope, + .ty = func_ty, + }; + const new_decl = try mod.allocateNewDecl(generic_namespace, module_fn.owner_decl.src_node); + _ = new_decl; + + // Iterate over the parameters that are comptime, evaluating their type expressions + // inside a Scope which contains the previous parameters. + //for (args) |arg, arg_i| { + //} + + // Create a new Fn with only the runtime-known parameters. + // TODO + + // Populate the Decl ty/val with the function and its type. + // TODO + + // Queue up a `codegen_func` work item for the new Fn, making sure it will have + // `analyzeFnBody` called with the Scope which contains the comptime parameters. + // TODO + + // Save it into the Module's generic function map. + // TODO + + // Call it the same as a runtime function. + // TODO + return mod.fail(&block.base, func_src, "TODO implement generic fn call", .{}); } 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); @@ -3186,13 +3258,12 @@ fn zirFunc( const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); - const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len); var body_inst: Zir.Inst.Index = 0; var src_locs: Zir.Inst.Func.SrcLocs = undefined; if (extra.data.body_len != 0) { body_inst = inst; - const extra_index = extra.end + extra.data.param_types_len + extra.data.body_len; + const extra_index = extra.end + extra.data.body_len; src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; } @@ -3204,7 +3275,6 @@ fn zirFunc( return sema.funcCommon( block, inst_data.src_node, - param_types, body_inst, extra.data.return_type, cc, @@ -3214,7 +3284,6 @@ fn zirFunc( false, src_locs, null, - &.{}, ); } @@ -3222,7 +3291,6 @@ fn funcCommon( sema: *Sema, block: *Scope.Block, src_node_offset: i32, - zir_param_types: []const Zir.Inst.Ref, body_inst: Zir.Inst.Index, zir_return_type: Zir.Inst.Ref, cc: std.builtin.CallingConvention, @@ -3232,7 +3300,6 @@ 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 }; @@ -3245,7 +3312,7 @@ fn funcCommon( const fn_ty: Type = fn_ty: { // Hot path for some common function types. - if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value and + if (sema.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) { @@ -3266,22 +3333,21 @@ 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. - 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; - }; + 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 == .none) { + param_types[i] = Type.initTag(.noreturn); // indicates anytype + } else { + // 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. + const param_src = src; + param_types[i] = try sema.resolveType(block, param_src, param.ty); + } + comptime_params[i] = param.is_comptime; + any_are_comptime = any_are_comptime or param.is_comptime; } if (align_val.tag() != .null_value) { @@ -3301,6 +3367,7 @@ fn funcCommon( break :fn_ty try Type.Tag.function.create(sema.arena, .{ .param_types = param_types, + .comptime_params = comptime_params.ptr, .return_type = return_type, .cc = cc, .is_var_args = var_args, @@ -6545,16 +6612,6 @@ 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; - var body_inst: Zir.Inst.Index = 0; var src_locs: Zir.Inst.Func.SrcLocs = undefined; if (extra.data.body_len != 0) { @@ -6570,7 +6627,6 @@ fn zirFuncExtended( return sema.funcCommon( block, extra.data.src_node, - param_types, body_inst, extra.data.return_type, cc, @@ -6580,7 +6636,6 @@ fn zirFuncExtended( is_extern, src_locs, lib_name, - comptime_bits, ); } diff --git a/src/Zir.zig b/src/Zir.zig index 109ae3b186..6445d73af5 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -173,11 +173,22 @@ pub const Inst = struct { /// Twos complement wrapping integer addition. /// Uses the `pl_node` union field. Payload is `Bin`. addwrap, - /// Declares a parameter of the current function. Used for debug info and - /// for checking shadowing against declarations in the current namespace. - /// Uses the `str_tok` field. Token is the parameter name, string is the - /// parameter name. - arg, + /// Declares a parameter of the current function. Used for: + /// * debug info + /// * checking shadowing against declarations in the current namespace + /// * parameter type expressions referencing other parameters + /// These occur in the block outside a function body (the same block as + /// contains the func instruction). + /// Uses the `pl_tok` field. Token is the parameter name, payload is a `Param`. + param, + /// Same as `param` except the parameter is marked comptime. + param_comptime, + /// Same as `param` except the parameter is marked anytype. + /// Uses the `str_tok` field. Token is the parameter name. String is the parameter name. + param_anytype, + /// Same as `param` except the parameter is marked both comptime and anytype. + /// Uses the `str_tok` field. Token is the parameter name. String is the parameter name. + param_anytype_comptime, /// Array concatenation. `a ++ b` /// Uses the `pl_node` union field. Payload is `Bin`. array_cat, @@ -971,7 +982,10 @@ pub const Inst = struct { /// Function calls do not count. pub fn isNoReturn(tag: Tag) bool { return switch (tag) { - .arg, + .param, + .param_comptime, + .param_anytype, + .param_anytype_comptime, .add, .addwrap, .alloc, @@ -1233,7 +1247,10 @@ pub const Inst = struct { break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{ .add = .pl_node, .addwrap = .pl_node, - .arg = .str_tok, + .param = .pl_tok, + .param_comptime = .pl_tok, + .param_anytype = .str_tok, + .param_anytype_comptime = .str_tok, .array_cat = .pl_node, .array_mul = .pl_node, .array_type = .bin, @@ -2047,6 +2064,17 @@ pub const Inst = struct { return .{ .node_offset = self.src_node }; } }, + pl_tok: struct { + /// Offset from Decl AST token index. + src_tok: ast.TokenIndex, + /// index into extra. + /// `Tag` determines what lives there. + payload_index: u32, + + pub fn src(self: @This()) LazySrcLoc { + return .{ .token_offset = self.src_tok }; + } + }, bin: Bin, /// For strings which may contain null bytes. str: struct { @@ -2170,6 +2198,7 @@ pub const Inst = struct { un_node, un_tok, pl_node, + pl_tok, bin, str, str_tok, @@ -2226,17 +2255,11 @@ 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. 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 + /// 3. body: Index // for each body_len + /// 4. src_locs: Func.SrcLocs // if body_len != 0 pub const ExtendedFunc = struct { src_node: i32, return_type: Ref, - param_types_len: u32, body_len: u32, pub const Small = packed struct { @@ -2247,8 +2270,7 @@ pub const Inst = struct { has_align: bool, is_test: bool, is_extern: bool, - has_comptime_bits: bool, - _: u8 = undefined, + _: u9 = undefined, }; }; @@ -2271,13 +2293,10 @@ pub const Inst = struct { }; /// Trailing: - /// 0. param_type: Ref // for each param_types_len - /// - `none` indicates that the param type is `anytype`. - /// 1. body: Index // for each body_len - /// 2. src_locs: SrcLocs // if body_len != 0 + /// 0. body: Index // for each body_len + /// 1. src_locs: SrcLocs // if body_len != 0 pub const Func = struct { return_type: Ref, - param_types_len: u32, body_len: u32, pub const SrcLocs = struct { @@ -2764,6 +2783,12 @@ pub const Inst = struct { args: Ref, }; + pub const Param = struct { + /// Null-terminated string index. + name: u32, + ty: Ref, + }; + /// Trailing: /// 0. type_inst: Ref, // if small 0b000X is set /// 1. align_inst: Ref, // if small 0b00X0 is set @@ -3108,11 +3133,14 @@ const Writer = struct { .decl_ref, .decl_val, .import, - .arg, .ret_err_value, .ret_err_value_code, + .param_anytype, + .param_anytype_comptime, => try self.writeStrTok(stream, inst), + .param, .param_comptime => try self.writeParam(stream, inst), + .func => try self.writeFunc(stream, inst, false), .func_inferred => try self.writeFunc(stream, inst, true), @@ -3314,6 +3342,17 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + 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; + try stream.print("\"{}\", ", .{ + std.zig.fmtEscapes(self.code.nullTerminatedString(extra.name)), + }); + try self.writeInstRef(stream, extra.ty); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data; @@ -4277,16 +4316,14 @@ const Writer = struct { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = self.code.extraData(Inst.Func, inst_data.payload_index); - const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); - const body = self.code.extra[extra.end + param_types.len ..][0..extra.data.body_len]; + const body = self.code.extra[extra.end..][0..extra.data.body_len]; var src_locs: Zir.Inst.Func.SrcLocs = undefined; if (body.len != 0) { - const extra_index = extra.end + param_types.len + body.len; + const extra_index = extra.end + body.len; src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; } return self.writeFuncCommon( stream, - param_types, extra.data.return_type, inferred_error_set, false, @@ -4296,7 +4333,6 @@ const Writer = struct { body, src, src_locs, - &.{}, ); } @@ -4323,16 +4359,6 @@ 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; - const body = self.code.extra[extra_index..][0..extra.data.body_len]; extra_index += body.len; @@ -4342,7 +4368,6 @@ const Writer = struct { } return self.writeFuncCommon( stream, - param_types, extra.data.return_type, small.is_inferred_error, small.is_var_args, @@ -4352,7 +4377,6 @@ const Writer = struct { body, src, src_locs, - comptime_bits, ); } @@ -4426,7 +4450,6 @@ const Writer = struct { fn writeFuncCommon( self: *Writer, stream: anytype, - param_types: []const Inst.Ref, ret_ty: Inst.Ref, inferred_error_set: bool, var_args: bool, @@ -4436,19 +4459,7 @@ 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("], "); try self.writeInstRef(stream, ret_ty); try self.writeOptionalInstRef(stream, ", cc=", cc); try self.writeOptionalInstRef(stream, ", align=", align_inst); @@ -4714,8 +4725,7 @@ fn findDeclsInner( const inst_data = datas[inst].pl_node; const extra = zir.extraData(Inst.Func, inst_data.payload_index); - const param_types_len = extra.data.param_types_len; - const body = zir.extra[extra.end + param_types_len ..][0..extra.data.body_len]; + const body = zir.extra[extra.end..][0..extra.data.body_len]; return zir.findDeclsBody(list, body); }, .extended => { @@ -4730,7 +4740,6 @@ fn findDeclsInner( extra_index += @boolToInt(small.has_lib_name); extra_index += @boolToInt(small.has_cc); extra_index += @boolToInt(small.has_align); - extra_index += extra.data.param_types_len; const body = zir.extra[extra_index..][0..extra.data.body_len]; return zir.findDeclsBody(list, body); }, diff --git a/src/type.zig b/src/type.zig index a8c3d77bbb..feb16fd47c 100644 --- a/src/type.zig +++ b/src/type.zig @@ -759,12 +759,15 @@ pub const Type = extern union { for (payload.param_types) |param_type, i| { param_types[i] = try param_type.copy(allocator); } + const other_comptime_params = payload.comptime_params[0..payload.param_types.len]; + const comptime_params = try allocator.dupe(bool, other_comptime_params); return Tag.function.create(allocator, .{ .return_type = try payload.return_type.copy(allocator), .param_types = param_types, .cc = payload.cc, .is_var_args = payload.is_var_args, .is_generic = payload.is_generic, + .comptime_params = comptime_params.ptr, }); }, .pointer => { @@ -2408,14 +2411,41 @@ 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, + pub fn fnInfo(ty: Type) Payload.Function.Data { + return switch (ty.tag()) { + .fn_noreturn_no_args => .{ + .param_types = &.{}, + .comptime_params = undefined, + .return_type = initTag(.noreturn), + .cc = .Unspecified, + .is_var_args = false, + .is_generic = false, + }, + .fn_void_no_args => .{ + .param_types = &.{}, + .comptime_params = undefined, + .return_type = initTag(.void), + .cc = .Unspecified, + .is_var_args = false, + .is_generic = false, + }, + .fn_naked_noreturn_no_args => .{ + .param_types = &.{}, + .comptime_params = undefined, + .return_type = initTag(.noreturn), + .cc = .Naked, + .is_var_args = false, + .is_generic = false, + }, + .fn_ccc_void_no_args => .{ + .param_types = &.{}, + .comptime_params = undefined, + .return_type = initTag(.void), + .cc = .C, + .is_var_args = false, + .is_generic = false, + }, + .function => ty.castTag(.function).?.data, else => unreachable, }; @@ -3223,13 +3253,23 @@ pub const Type = extern union { pub const base_tag = Tag.function; base: Payload = Payload{ .tag = base_tag }, - data: struct { + data: Data, + + // TODO look into optimizing this memory to take fewer bytes + const Data = struct { param_types: []Type, + comptime_params: [*]bool, return_type: Type, cc: std.builtin.CallingConvention, is_var_args: bool, is_generic: bool, - }, + + fn paramIsComptime(self: @This(), i: usize) bool { + if (!self.is_generic) return false; + assert(i < self.param_types.len); + return self.comptime_params[i]; + } + }; }; pub const ErrorSet = struct { |
