authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-03 22:34:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-03 22:34:22-07:00
log382d201781eb57d9e950ad07ce814adc5a68b329
treefd066174fcce78af51e925362c619d1ffd584eef
parent609b84611dcde382af5d9fbc2345ede468d31a6f

stage2: basic generic functions are working

The general strategy is that Sema will pre-map comptime arguments into the inst_map, and then re-run the block body that contains the `param` and `func` instructions. This re-runs all the parameter type expressions except with comptime values populated. In Sema, param instructions are now handled specially: they detect whether they are comptime-elided or not. If so, they skip putting a value in the inst_map, since it is already pre-populated. If not, then they append to the `fields` field of `Sema` for use with the `func` instruction. So when the block body is re-run, a new function is generated with all the comptime arguments elided, and the new function type has only runtime parameters in it. TODO: give the generated Decls better names than "foo__anon_x". The new function is then added to the work queue to have its body analyzed and a runtime call AIR instruction to the new function is emitted. When the new function gets semantically analyzed, comptime parameters are pre-mapped to the corresponding `comptime_args` values rather than mapped to an `arg` AIR instruction. `comptime_args` is a new field that `Fn` has which is a `TypedValue` for each parameter. This field is non-null for generic function instantiations only. The values are the comptime arguments. For non-comptime parameters, a sentinel value is used. This is because we need to know the information of which parameters are comptime-known. Additionally: * AstGen: align and section expressions are evaluated in the scope that has comptime parameters in it. There are still some TODO items left; see the BRANCH_TODO file.

6 files changed, 287 insertions(+), 126 deletions(-)

BRANCH_TODO+2-5
...@@ -1,7 +1,4 @@...@@ -1,7 +1,4 @@
1* update arg instructions:
2 - generic instantiation inserts Sema map items for the comptime args only, re-runs the
3 Decl ZIR to get the new Fn.
4* generic function call where it makes a new function
5* memoize the instantiation in a table1* memoize the instantiation in a table
6* anytype with next parameter expression using it2* expressions that depend on comptime stuff need a poison value to use for
3 types when generating the generic function type
7* comptime anytype4* comptime anytype
src/AstGen.zig+8-8
...@@ -2906,14 +2906,7 @@ fn fnDecl(...@@ -2906,14 +2906,7 @@ fn fnDecl(
2906 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;2906 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
2907 break :blk token_tags[maybe_inline_token] == .keyword_inline;2907 break :blk token_tags[maybe_inline_token] == .keyword_inline;
2908 };2908 };
2909 const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {2909 try wip_decls.next(gpa, is_pub, is_export, fn_proto.ast.align_expr != 0, fn_proto.ast.section_expr != 0);
2910 break :inst try expr(&decl_gz, &decl_gz.base, align_rl, fn_proto.ast.align_expr);
2911 };
2912 const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
2913 break :inst try comptimeExpr(&decl_gz, &decl_gz.base, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr);
2914 };
2915
2916 try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, section_inst != .none);
29172910
2918 var params_scope = &fn_gz.base;2911 var params_scope = &fn_gz.base;
2919 const is_var_args = is_var_args: {2912 const is_var_args = is_var_args: {
...@@ -2994,6 +2987,13 @@ fn fnDecl(...@@ -2994,6 +2987,13 @@ fn fnDecl(
2994 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;2987 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
2995 const is_inferred_error = token_tags[maybe_bang] == .bang;2988 const is_inferred_error = token_tags[maybe_bang] == .bang;
29962989
2990 const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
2991 break :inst try expr(&decl_gz, params_scope, align_rl, fn_proto.ast.align_expr);
2992 };
2993 const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
2994 break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr);
2995 };
2996
2997 const return_type_inst = try AstGen.expr(2997 const return_type_inst = try AstGen.expr(
2998 &decl_gz,2998 &decl_gz,
2999 params_scope,2999 params_scope,
src/Module.zig+22-5
...@@ -757,6 +757,10 @@ pub const Union = struct {...@@ -757,6 +757,10 @@ pub const Union = struct {
757pub const Fn = struct {757pub const Fn = struct {
758 /// The Decl that corresponds to the function itself.758 /// The Decl that corresponds to the function itself.
759 owner_decl: *Decl,759 owner_decl: *Decl,
760 /// If this is not null, this function is a generic function instantiation, and
761 /// there is a `Value` here for each parameter of the function. Non-comptime
762 /// parameters are marked with an `unreachable_value`.
763 comptime_args: ?[*]TypedValue = null,
760 /// The ZIR instruction that is a function instruction. Use this to find764 /// The ZIR instruction that is a function instruction. Use this to find
761 /// the body. We store this rather than the body directly so that when ZIR765 /// the body. We store this rather than the body directly so that when ZIR
762 /// is regenerated on update(), we can map this to the new corresponding766 /// is regenerated on update(), we can map this to the new corresponding
...@@ -3657,10 +3661,13 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {...@@ -3657,10 +3661,13 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3657 // Here we are performing "runtime semantic analysis" for a function body, which means3661 // Here we are performing "runtime semantic analysis" for a function body, which means
3658 // we must map the parameter ZIR instructions to `arg` AIR instructions.3662 // we must map the parameter ZIR instructions to `arg` AIR instructions.
3659 // AIR requires the `arg` parameters to be the first N instructions.3663 // AIR requires the `arg` parameters to be the first N instructions.
3660 const params_len = @intCast(u32, fn_ty.fnParamLen());3664 // This could be a generic function instantiation, however, in which case we need to
3661 try inner_block.instructions.ensureTotalCapacity(gpa, params_len);3665 // map the comptime parameters to constant values and only emit arg AIR instructions
3662 try sema.air_instructions.ensureUnusedCapacity(gpa, params_len * 2); // * 2 for the `addType`3666 // for the runtime ones.
3663 try sema.inst_map.ensureUnusedCapacity(gpa, params_len);3667 const runtime_params_len = @intCast(u32, fn_ty.fnParamLen());
3668 try inner_block.instructions.ensureTotalCapacity(gpa, runtime_params_len);
3669 try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType`
3670 try sema.inst_map.ensureUnusedCapacity(gpa, fn_info.total_params_len);
36643671
3665 var param_index: usize = 0;3672 var param_index: usize = 0;
3666 for (fn_info.param_body) |inst| {3673 for (fn_info.param_body) |inst| {
...@@ -3678,8 +3685,17 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {...@@ -3678,8 +3685,17 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
36783685
3679 else => continue,3686 else => continue,
3680 };3687 };
3688 if (func.comptime_args) |comptime_args| {
3689 const arg_tv = comptime_args[param_index];
3690 if (arg_tv.val.tag() != .unreachable_value) {
3691 // We have a comptime value for this parameter.
3692 const arg = try sema.addConstant(arg_tv.ty, arg_tv.val);
3693 sema.inst_map.putAssumeCapacityNoClobber(inst, arg);
3694 param_index += 1;
3695 continue;
3696 }
3697 }
3681 const param_type = fn_ty.fnParamType(param_index);3698 const param_type = fn_ty.fnParamType(param_index);
3682 param_index += 1;
3683 const ty_ref = try sema.addType(param_type);3699 const ty_ref = try sema.addType(param_type);
3684 const arg_index = @intCast(u32, sema.air_instructions.len);3700 const arg_index = @intCast(u32, sema.air_instructions.len);
3685 inner_block.instructions.appendAssumeCapacity(arg_index);3701 inner_block.instructions.appendAssumeCapacity(arg_index);
...@@ -3691,6 +3707,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {...@@ -3691,6 +3707,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3691 } },3707 } },
3692 });3708 });
3693 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));3709 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
3710 param_index += 1;
3694 }3711 }
36953712
3696 func.state = .in_progress;3713 func.state = .in_progress;
src/Sema.zig+240-106
...@@ -36,9 +36,14 @@ branch_count: u32 = 0,...@@ -36,9 +36,14 @@ branch_count: u32 = 0,
36/// access to the source location set by the previous instruction which did36/// access to the source location set by the previous instruction which did
37/// contain a mapped source location.37/// contain a mapped source location.
38src: LazySrcLoc = .{ .token_offset = 0 },38src: LazySrcLoc = .{ .token_offset = 0 },
39next_arg_index: usize = 0,
40params: std.ArrayListUnmanaged(Param) = .{},
41decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{},39decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{},
40/// `param` instructions are collected here to be used by the `func` instruction.
41params: std.ArrayListUnmanaged(Param) = .{},
42/// When doing a generic function instantiation, this array collects a `Value` object for
43/// each parameter that is comptime known and thus elided from the generated function.
44/// This memory is allocated by a parent `Sema` and owned by the values arena of the owner_decl.
45comptime_args: []TypedValue = &.{},
46next_arg_index: usize = 0,
4247
43const std = @import("std");48const std = @import("std");
44const mem = std.mem;49const mem = std.mem;
...@@ -64,8 +69,8 @@ const target_util = @import("target.zig");...@@ -64,8 +69,8 @@ const target_util = @import("target.zig");
6469
65const Param = struct {70const Param = struct {
66 name: [:0]const u8,71 name: [:0]const u8,
67 /// `none` means `anytype`.72 /// `noreturn` means `anytype`.
68 ty: Air.Inst.Ref,73 ty: Type,
69 is_comptime: bool,74 is_comptime: bool,
70};75};
7176
...@@ -366,26 +371,6 @@ pub fn analyzeBody(...@@ -366,26 +371,6 @@ pub fn analyzeBody(
366 // continue the loop.371 // continue the loop.
367 // We also know that they cannot be referenced later, so we avoid372 // We also know that they cannot be referenced later, so we avoid
368 // putting them into the map.373 // putting them into the map.
369 .param => {
370 try sema.zirParam(inst, false);
371 i += 1;
372 continue;
373 },
374 .param_comptime => {
375 try sema.zirParam(inst, true);
376 i += 1;
377 continue;
378 },
379 .param_anytype => {
380 try sema.zirParamAnytype(inst, false);
381 i += 1;
382 continue;
383 },
384 .param_anytype_comptime => {
385 try sema.zirParamAnytype(inst, true);
386 i += 1;
387 continue;
388 },
389 .breakpoint => {374 .breakpoint => {
390 try sema.zirBreakpoint(block, inst);375 try sema.zirBreakpoint(block, inst);
391 i += 1;376 i += 1;
...@@ -519,6 +504,88 @@ pub fn analyzeBody(...@@ -519,6 +504,88 @@ pub fn analyzeBody(
519 return break_inst;504 return break_inst;
520 }505 }
521 },506 },
507 .param => blk: {
508 const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
509 const src = inst_data.src();
510 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;
511 const param_name = sema.code.nullTerminatedString(extra.name);
512
513 if (sema.nextArgIsComptimeElided()) {
514 i += 1;
515 continue;
516 }
517
518 // TODO check if param_name shadows a Decl. This only needs to be done if
519 // usingnamespace is implemented.
520
521 const param_ty = try sema.resolveType(block, src, extra.ty);
522 try sema.params.append(sema.gpa, .{
523 .name = param_name,
524 .ty = param_ty,
525 .is_comptime = false,
526 });
527 break :blk try sema.addConstUndef(param_ty);
528 },
529 .param_comptime => blk: {
530 const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
531 const src = inst_data.src();
532 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;
533 const param_name = sema.code.nullTerminatedString(extra.name);
534
535 if (sema.nextArgIsComptimeElided()) {
536 i += 1;
537 continue;
538 }
539
540 // TODO check if param_name shadows a Decl. This only needs to be done if
541 // usingnamespace is implemented.
542
543 const param_ty = try sema.resolveType(block, src, extra.ty);
544 try sema.params.append(sema.gpa, .{
545 .name = param_name,
546 .ty = param_ty,
547 .is_comptime = true,
548 });
549 break :blk try sema.addConstUndef(param_ty);
550 },
551 .param_anytype => blk: {
552 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
553 const param_name = inst_data.get(sema.code);
554
555 if (sema.nextArgIsComptimeElided()) {
556 i += 1;
557 continue;
558 }
559
560 // TODO check if param_name shadows a Decl. This only needs to be done if
561 // usingnamespace is implemented.
562
563 try sema.params.append(sema.gpa, .{
564 .name = param_name,
565 .ty = Type.initTag(.noreturn),
566 .is_comptime = false,
567 });
568 break :blk try sema.addConstUndef(Type.initTag(.@"undefined"));
569 },
570 .param_anytype_comptime => blk: {
571 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
572 const param_name = inst_data.get(sema.code);
573
574 if (sema.nextArgIsComptimeElided()) {
575 i += 1;
576 continue;
577 }
578
579 // TODO check if param_name shadows a Decl. This only needs to be done if
580 // usingnamespace is implemented.
581
582 try sema.params.append(sema.gpa, .{
583 .name = param_name,
584 .ty = Type.initTag(.noreturn),
585 .is_comptime = true,
586 });
587 break :blk try sema.addConstUndef(Type.initTag(.@"undefined"));
588 },
522 };589 };
523 if (sema.typeOf(air_inst).isNoReturn())590 if (sema.typeOf(air_inst).isNoReturn())
524 return always_noreturn;591 return always_noreturn;
...@@ -1339,36 +1406,6 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co...@@ -1339,36 +1406,6 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
1339 return sema.analyzeLoad(block, src, result_ptr, result_ptr_src);1406 return sema.analyzeLoad(block, src, result_ptr, result_ptr_src);
1340}1407}
13411408
1342fn zirParam(sema: *Sema, inst: Zir.Inst.Index, is_comptime: bool) CompileError!void {
1343 const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
1344 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;
1345 const param_name = sema.code.nullTerminatedString(extra.name);
1346
1347 // TODO check if param_name shadows a Decl. This only needs to be done if
1348 // usingnamespace is implemented.
1349
1350 const param_ty = sema.resolveInst(extra.ty);
1351 try sema.params.append(sema.gpa, .{
1352 .name = param_name,
1353 .ty = param_ty,
1354 .is_comptime = is_comptime,
1355 });
1356}
1357
1358fn zirParamAnytype(sema: *Sema, inst: Zir.Inst.Index, is_comptime: bool) CompileError!void {
1359 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
1360 const param_name = inst_data.get(sema.code);
1361
1362 // TODO check if param_name shadows a Decl. This only needs to be done if
1363 // usingnamespace is implemented.
1364
1365 try sema.params.append(sema.gpa, .{
1366 .name = param_name,
1367 .ty = .none,
1368 .is_comptime = is_comptime,
1369 });
1370}
1371
1372fn zirAllocExtended(1409fn zirAllocExtended(
1373 sema: *Sema,1410 sema: *Sema,
1374 block: *Scope.Block,1411 block: *Scope.Block,
...@@ -2497,10 +2534,6 @@ fn analyzeCall(...@@ -2497,10 +2534,6 @@ fn analyzeCall(
2497 sema.func = module_fn;2534 sema.func = module_fn;
2498 defer sema.func = parent_func;2535 defer sema.func = parent_func;
24992536
2500 const parent_next_arg_index = sema.next_arg_index;
2501 sema.next_arg_index = 0;
2502 defer sema.next_arg_index = parent_next_arg_index;
2503
2504 var child_block: Scope.Block = .{2537 var child_block: Scope.Block = .{
2505 .parent = null,2538 .parent = null,
2506 .sema = sema,2539 .sema = sema,
...@@ -2537,7 +2570,7 @@ fn analyzeCall(...@@ -2537,7 +2570,7 @@ fn analyzeCall(
2537 }2570 }
2538 _ = try sema.analyzeBody(&child_block, fn_info.body);2571 _ = try sema.analyzeBody(&child_block, fn_info.body);
2539 break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges);2572 break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges);
2540 } else if (func_ty_info.is_generic) {2573 } else if (func_ty_info.is_generic) res: {
2541 const func_val = try sema.resolveConstValue(block, func_src, func);2574 const func_val = try sema.resolveConstValue(block, func_src, func);
2542 const module_fn = func_val.castTag(.function).?.data;2575 const module_fn = func_val.castTag(.function).?.data;
2543 // Check the Module's generic function map with an adapted context, so that we2576 // Check the Module's generic function map with an adapted context, so that we
...@@ -2545,37 +2578,142 @@ fn analyzeCall(...@@ -2545,37 +2578,142 @@ fn analyzeCall(
2545 // only to junk it if it matches an existing instantiation.2578 // only to junk it if it matches an existing instantiation.
2546 // TODO2579 // TODO
25472580
2548 // Create a Decl for the new function.2581 const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst);
2549 const generic_namespace = try sema.arena.create(Module.Scope.Namespace);2582 const zir_tags = sema.code.instructions.items(.tag);
2550 generic_namespace.* = .{2583 var non_comptime_args_len: u32 = 0;
2551 .parent = block.src_decl.namespace,2584 const new_func = new_func: {
2552 .file_scope = block.src_decl.namespace.file_scope,2585 const namespace = module_fn.owner_decl.namespace;
2553 .ty = func_ty,2586 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
2587
2588 // Create a Decl for the new function.
2589 const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node);
2590 // TODO better names for generic function instantiations
2591 const name_index = mod.getNextAnonNameIndex();
2592 new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
2593 module_fn.owner_decl.name, name_index,
2594 });
2595 new_decl.src_line = module_fn.owner_decl.src_line;
2596 new_decl.is_pub = module_fn.owner_decl.is_pub;
2597 new_decl.is_exported = module_fn.owner_decl.is_exported;
2598 new_decl.has_align = module_fn.owner_decl.has_align;
2599 new_decl.has_linksection = module_fn.owner_decl.has_linksection;
2600 new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index;
2601 new_decl.alive = true; // This Decl is called at runtime.
2602 new_decl.has_tv = true;
2603 new_decl.owns_tv = true;
2604 new_decl.analysis = .in_progress;
2605 new_decl.generation = mod.generation;
2606
2607 namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {});
2608
2609 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
2610 errdefer new_decl_arena.deinit();
2611
2612 // Re-run the block that creates the function, with the comptime parameters
2613 // pre-populated inside `inst_map`. This causes `param_comptime` and
2614 // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a
2615 // new, monomorphized function, with the comptime parameters elided.
2616 var child_sema: Sema = .{
2617 .mod = mod,
2618 .gpa = gpa,
2619 .arena = sema.arena,
2620 .code = sema.code,
2621 .owner_decl = new_decl,
2622 .namespace = namespace,
2623 .func = null,
2624 .owner_func = null,
2625 .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, args.len),
2626 };
2627 defer child_sema.deinit();
2628
2629 var child_block: Scope.Block = .{
2630 .parent = null,
2631 .sema = &child_sema,
2632 .src_decl = new_decl,
2633 .instructions = .{},
2634 .inlining = null,
2635 .is_comptime = true,
2636 };
2637 defer child_block.instructions.deinit(gpa);
2638
2639 try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, args.len));
2640 var arg_i: usize = 0;
2641 for (fn_info.param_body) |inst| {
2642 const is_comptime = switch (zir_tags[inst]) {
2643 .param_comptime, .param_anytype_comptime => true,
2644 .param, .param_anytype => false, // TODO make true for always comptime types
2645 else => continue,
2646 };
2647 if (is_comptime) {
2648 // TODO: pass .unneeded to resolveConstValue and then if we get
2649 // error.NeededSourceLocation resolve the arg source location and
2650 // try again.
2651 const arg_src = call_src;
2652 const arg = args[arg_i];
2653 const arg_val = try sema.resolveConstValue(block, arg_src, arg);
2654 child_sema.comptime_args[arg_i] = .{
2655 .ty = try sema.typeOf(arg).copy(&new_decl_arena.allocator),
2656 .val = try arg_val.copy(&new_decl_arena.allocator),
2657 };
2658 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
2659 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
2660 } else {
2661 non_comptime_args_len += 1;
2662 child_sema.comptime_args[arg_i] = .{
2663 .ty = Type.initTag(.noreturn),
2664 .val = Value.initTag(.unreachable_value),
2665 };
2666 }
2667 arg_i += 1;
2668 }
2669 const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body);
2670 const new_func_val = try child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst);
2671 const new_func = new_func_val.castTag(.function).?.data;
2672
2673 // Populate the Decl ty/val with the function and its type.
2674 new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator);
2675 new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func);
2676 new_decl.analysis = .complete;
2677
2678 // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
2679 // will be populated, ensuring it will have `analyzeBody` called with the ZIR
2680 // parameters mapped appropriately.
2681 try mod.comp.bin_file.allocateDeclIndexes(new_decl);
2682 try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
2683
2684 try new_decl.finalizeNewArena(&new_decl_arena);
2685 break :new_func try sema.analyzeDeclVal(block, func_src, new_decl);
2554 };2686 };
2555 const new_decl = try mod.allocateNewDecl(generic_namespace, module_fn.owner_decl.src_node);
2556 _ = new_decl;
2557
2558 // Iterate over the parameters that are comptime, evaluating their type expressions
2559 // inside a Scope which contains the previous parameters.
2560 //for (args) |arg, arg_i| {
2561 //}
2562
2563 // Create a new Fn with only the runtime-known parameters.
2564 // TODO
2565
2566 // Populate the Decl ty/val with the function and its type.
2567 // TODO
2568
2569 // Queue up a `codegen_func` work item for the new Fn, making sure it will have
2570 // `analyzeBody` called with the ZIR parameters mapped appropriately.
2571 // TODO
25722687
2573 // Save it into the Module's generic function map.2688 // Save it into the Module's generic function map.
2574 // TODO2689 // TODO
25752690
2576 // Call it the same as a runtime function.2691 // Make a runtime call to the new function, making sure to omit the comptime args.
2577 // TODO2692 try sema.requireRuntimeBlock(block, call_src);
2578 return mod.fail(&block.base, func_src, "TODO implement generic fn call", .{});2693 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
2694 non_comptime_args_len);
2695 const func_inst = try block.addInst(.{
2696 .tag = .call,
2697 .data = .{ .pl_op = .{
2698 .operand = new_func,
2699 .payload = sema.addExtraAssumeCapacity(Air.Call{
2700 .args_len = non_comptime_args_len,
2701 }),
2702 } },
2703 });
2704 var arg_i: usize = 0;
2705 for (fn_info.param_body) |inst| {
2706 const is_comptime = switch (zir_tags[inst]) {
2707 .param_comptime, .param_anytype_comptime => true,
2708 .param, .param_anytype => false, // TODO make true for always comptime types
2709 else => continue,
2710 };
2711 if (is_comptime) {
2712 sema.air_extra.appendAssumeCapacity(@enumToInt(args[arg_i]));
2713 }
2714 arg_i += 1;
2715 }
2716 break :res func_inst;
2579 } else res: {2717 } else res: {
2580 try sema.requireRuntimeBlock(block, call_src);2718 try sema.requireRuntimeBlock(block, call_src);
2581 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +2719 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
...@@ -3302,15 +3440,10 @@ fn funcCommon(...@@ -3302,15 +3440,10 @@ fn funcCommon(
3302 const param_types = try sema.arena.alloc(Type, sema.params.items.len);3440 const param_types = try sema.arena.alloc(Type, sema.params.items.len);
3303 const comptime_params = try sema.arena.alloc(bool, sema.params.items.len);3441 const comptime_params = try sema.arena.alloc(bool, sema.params.items.len);
3304 for (sema.params.items) |param, i| {3442 for (sema.params.items) |param, i| {
3305 if (param.ty == .none) {3443 if (param.ty.tag() == .noreturn) {
3306 param_types[i] = Type.initTag(.noreturn); // indicates anytype3444 param_types[i] = Type.initTag(.noreturn); // indicates anytype
3307 } else {3445 } else {
3308 // TODO make a compile error from `resolveType` report the source location3446 param_types[i] = param.ty;
3309 // of the specific parameter. Will need to take a similar strategy as
3310 // `resolveSwitchItemVal` to avoid resolving the source location unless
3311 // we actually need to report an error.
3312 const param_src = src;
3313 param_types[i] = try sema.analyzeAsType(block, param_src, param.ty);
3314 }3447 }
3315 comptime_params[i] = param.is_comptime;3448 comptime_params[i] = param.is_comptime;
3316 any_are_comptime = any_are_comptime or param.is_comptime;3449 any_are_comptime = any_are_comptime or param.is_comptime;
...@@ -3402,6 +3535,7 @@ fn funcCommon(...@@ -3402,6 +3535,7 @@ fn funcCommon(
3402 .state = anal_state,3535 .state = anal_state,
3403 .zir_body_inst = body_inst,3536 .zir_body_inst = body_inst,
3404 .owner_decl = sema.owner_decl,3537 .owner_decl = sema.owner_decl,
3538 .comptime_args = if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr,
3405 .lbrace_line = src_locs.lbrace_line,3539 .lbrace_line = src_locs.lbrace_line,
3406 .rbrace_line = src_locs.rbrace_line,3540 .rbrace_line = src_locs.rbrace_line,
3407 .lbrace_column = @truncate(u16, src_locs.columns),3541 .lbrace_column = @truncate(u16, src_locs.columns),
...@@ -6819,19 +6953,12 @@ fn safetyPanic(...@@ -6819,19 +6953,12 @@ fn safetyPanic(
6819 const msg_inst = msg_inst: {6953 const msg_inst = msg_inst: {
6820 // TODO instead of making a new decl for every panic in the entire compilation,6954 // TODO instead of making a new decl for every panic in the entire compilation,
6821 // introduce the concept of a reference-counted decl for these6955 // introduce the concept of a reference-counted decl for these
6822 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);6956 var anon_decl = try block.startAnonDecl();
6823 errdefer new_decl_arena.deinit();6957 defer anon_decl.deinit();
68246958 break :msg_inst try sema.analyzeDeclRef(try anon_decl.finish(
6825 const decl_ty = try Type.Tag.array_u8.create(&new_decl_arena.allocator, msg.len);6959 try Type.Tag.array_u8.create(anon_decl.arena(), msg.len),
6826 const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, msg);6960 try Value.Tag.bytes.create(anon_decl.arena(), msg),
68276961 ));
6828 const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{
6829 .ty = decl_ty,
6830 .val = decl_val,
6831 });
6832 errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
6833 try new_decl.finalizeNewArena(&new_decl_arena);
6834 break :msg_inst try sema.analyzeDeclRef(new_decl);
6835 };6962 };
68366963
6837 const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src);6964 const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src);
...@@ -8832,7 +8959,7 @@ fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {...@@ -8832,7 +8959,7 @@ fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
8832 return sema.addConstant(ty, Value.initTag(.undef));8959 return sema.addConstant(ty, Value.initTag(.undef));
8833}8960}
88348961
8835fn addConstant(sema: *Sema, ty: Type, val: Value) CompileError!Air.Inst.Ref {8962pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref {
8836 const gpa = sema.gpa;8963 const gpa = sema.gpa;
8837 const ty_inst = try sema.addType(ty);8964 const ty_inst = try sema.addType(ty);
8838 try sema.air_values.append(gpa, val);8965 try sema.air_values.append(gpa, val);
...@@ -8888,3 +9015,10 @@ fn isComptimeKnown(...@@ -8888,3 +9015,10 @@ fn isComptimeKnown(
8888) !bool {9015) !bool {
8889 return (try sema.resolveMaybeUndefVal(block, src, inst)) != null;9016 return (try sema.resolveMaybeUndefVal(block, src, inst)) != null;
8890}9017}
9018
9019fn nextArgIsComptimeElided(sema: *Sema) bool {
9020 if (sema.comptime_args.len == 0) return false;
9021 const result = sema.comptime_args[sema.next_arg_index].val.tag() != .unreachable_value;
9022 sema.next_arg_index += 1;
9023 return result;
9024}
src/Zir.zig+13-1
...@@ -4909,6 +4909,7 @@ fn findDeclsBody(...@@ -4909,6 +4909,7 @@ fn findDeclsBody(
4909pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) struct {4909pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) struct {
4910 param_body: []const Inst.Index,4910 param_body: []const Inst.Index,
4911 body: []const Inst.Index,4911 body: []const Inst.Index,
4912 total_params_len: u32,
4912} {4913} {
4913 const tags = zir.instructions.items(.tag);4914 const tags = zir.instructions.items(.tag);
4914 const datas = zir.instructions.items(.data);4915 const datas = zir.instructions.items(.data);
...@@ -4944,8 +4945,19 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) struct {...@@ -4944,8 +4945,19 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) struct {
4944 };4945 };
4945 assert(tags[info.param_block] == .block or tags[info.param_block] == .block_inline);4946 assert(tags[info.param_block] == .block or tags[info.param_block] == .block_inline);
4946 const param_block = zir.extraData(Inst.Block, datas[info.param_block].pl_node.payload_index);4947 const param_block = zir.extraData(Inst.Block, datas[info.param_block].pl_node.payload_index);
4948 const param_body = zir.extra[param_block.end..][0..param_block.data.body_len];
4949 var total_params_len: u32 = 0;
4950 for (param_body) |inst| {
4951 switch (tags[inst]) {
4952 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
4953 total_params_len += 1;
4954 },
4955 else => continue,
4956 }
4957 }
4947 return .{4958 return .{
4948 .param_body = zir.extra[param_block.end..][0..param_block.data.body_len],4959 .param_body = param_body,
4949 .body = info.body,4960 .body = info.body,
4961 .total_params_len = total_params_len,
4950 };4962 };
4951}4963}
src/type.zig+2-1
...@@ -1182,7 +1182,6 @@ pub const Type = extern union {...@@ -1182,7 +1182,6 @@ pub const Type = extern union {
1182 .fn_void_no_args,1182 .fn_void_no_args,
1183 .fn_naked_noreturn_no_args,1183 .fn_naked_noreturn_no_args,
1184 .fn_ccc_void_no_args,1184 .fn_ccc_void_no_args,
1185 .function,
1186 .single_const_pointer_to_comptime_int,1185 .single_const_pointer_to_comptime_int,
1187 .const_slice_u8,1186 .const_slice_u8,
1188 .array_u8_sentinel_0,1187 .array_u8_sentinel_0,
...@@ -1207,6 +1206,8 @@ pub const Type = extern union {...@@ -1207,6 +1206,8 @@ pub const Type = extern union {
1207 .anyframe_T,1206 .anyframe_T,
1208 => true,1207 => true,
12091208
1209 .function => !self.castTag(.function).?.data.is_generic,
1210
1210 .@"struct" => {1211 .@"struct" => {
1211 // TODO introduce lazy value mechanism1212 // TODO introduce lazy value mechanism
1212 const struct_obj = self.castTag(.@"struct").?.data;1213 const struct_obj = self.castTag(.@"struct").?.data;