| ... | @@ -3995,7 +3995,7 @@ pub fn analyzeExport( | ... | @@ -3995,7 +3995,7 @@ pub fn analyzeExport( |
| 3995 | try mod.ensureDeclAnalyzed(exported_decl); | 3995 | try mod.ensureDeclAnalyzed(exported_decl); |
| 3996 | // TODO run the same checks as we do for C ABI struct fields | 3996 | // TODO run the same checks as we do for C ABI struct fields |
| 3997 | switch (exported_decl.ty.zigTypeTag()) { | 3997 | switch (exported_decl.ty.zigTypeTag()) { |
| 3998 | .Fn, .Int, .Struct, .Array, .Float => {}, | 3998 | .Fn, .Int, .Enum, .Struct, .Union, .Array, .Float => {}, |
| 3999 | else => return sema.fail(block, src, "unable to export type '{}'", .{exported_decl.ty}), | 3999 | else => return sema.fail(block, src, "unable to export type '{}'", .{exported_decl.ty}), |
| 4000 | } | 4000 | } |
| 4001 | | 4001 | |
| ... | @@ -4474,8 +4474,26 @@ fn analyzeCall( | ... | @@ -4474,8 +4474,26 @@ fn analyzeCall( |
| 4474 | | 4474 | |
| 4475 | const is_comptime_call = block.is_comptime or modifier == .compile_time or | 4475 | const is_comptime_call = block.is_comptime or modifier == .compile_time or |
| 4476 | try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type); | 4476 | try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type); |
| 4477 | const is_inline_call = is_comptime_call or modifier == .always_inline or | 4477 | var is_inline_call = is_comptime_call or modifier == .always_inline or |
| 4478 | func_ty_info.cc == .Inline; | 4478 | func_ty_info.cc == .Inline; |
| | 4479 | |
| | 4480 | if (!is_inline_call and func_ty_info.is_generic) { |
| | 4481 | if (sema.instantiateGenericCall( |
| | 4482 | block, |
| | 4483 | func, |
| | 4484 | func_src, |
| | 4485 | call_src, |
| | 4486 | func_ty_info, |
| | 4487 | ensure_result_used, |
| | 4488 | uncasted_args, |
| | 4489 | )) |some| { |
| | 4490 | return some; |
| | 4491 | } else |err| switch (err) { |
| | 4492 | error.GenericPoison => is_inline_call = true, |
| | 4493 | else => |e| return e, |
| | 4494 | } |
| | 4495 | } |
| | 4496 | |
| 4479 | const result: Air.Inst.Ref = if (is_inline_call) res: { | 4497 | const result: Air.Inst.Ref = if (is_inline_call) res: { |
| 4480 | const func_val = try sema.resolveConstValue(block, func_src, func); | 4498 | const func_val = try sema.resolveConstValue(block, func_src, func); |
| 4481 | const module_fn = switch (func_val.tag()) { | 4499 | const module_fn = switch (func_val.tag()) { |
| ... | @@ -4728,278 +4746,8 @@ fn analyzeCall( | ... | @@ -4728,278 +4746,8 @@ fn analyzeCall( |
| 4728 | try wip_captures.finalize(); | 4746 | try wip_captures.finalize(); |
| 4729 | | 4747 | |
| 4730 | break :res res2; | 4748 | break :res res2; |
| 4731 | } else if (func_ty_info.is_generic) res: { | | |
| 4732 | const func_val = try sema.resolveConstValue(block, func_src, func); | | |
| 4733 | const module_fn = switch (func_val.tag()) { | | |
| 4734 | .function => func_val.castTag(.function).?.data, | | |
| 4735 | .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data, | | |
| 4736 | else => unreachable, | | |
| 4737 | }; | | |
| 4738 | // Check the Module's generic function map with an adapted context, so that we | | |
| 4739 | // can match against `uncasted_args` rather than doing the work below to create a | | |
| 4740 | // generic Scope only to junk it if it matches an existing instantiation. | | |
| 4741 | const namespace = module_fn.owner_decl.src_namespace; | | |
| 4742 | const fn_zir = namespace.file_scope.zir; | | |
| 4743 | const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst); | | |
| 4744 | const zir_tags = fn_zir.instructions.items(.tag); | | |
| 4745 | | | |
| 4746 | // This hash must match `Module.MonomorphedFuncsContext.hash`. | | |
| 4747 | // For parameters explicitly marked comptime and simple parameter type expressions, | | |
| 4748 | // we know whether a parameter is elided from a monomorphed function, and can | | |
| 4749 | // use it in the hash here. However, for parameter type expressions that are not | | |
| 4750 | // explicitly marked comptime and rely on previous parameter comptime values, we | | |
| 4751 | // don't find out until after generating a monomorphed function whether the parameter | | |
| 4752 | // type ended up being a "must-be-comptime-known" type. | | |
| 4753 | var hasher = std.hash.Wyhash.init(0); | | |
| 4754 | std.hash.autoHash(&hasher, @ptrToInt(module_fn)); | | |
| 4755 | | | |
| 4756 | const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len); | | |
| 4757 | | | |
| 4758 | for (func_ty_info.param_types) |param_ty, i| { | | |
| 4759 | const is_comptime = func_ty_info.paramIsComptime(i); | | |
| 4760 | if (is_comptime) { | | |
| 4761 | const arg_src = call_src; // TODO better source location | | |
| 4762 | const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src); | | |
| 4763 | if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| { | | |
| 4764 | if (param_ty.tag() != .generic_poison) { | | |
| 4765 | arg_val.hash(param_ty, &hasher); | | |
| 4766 | } | | |
| 4767 | comptime_tvs[i] = .{ | | |
| 4768 | // This will be different than `param_ty` in the case of `generic_poison`. | | |
| 4769 | .ty = sema.typeOf(casted_arg), | | |
| 4770 | .val = arg_val, | | |
| 4771 | }; | | |
| 4772 | } else { | | |
| 4773 | return sema.failWithNeededComptime(block, arg_src); | | |
| 4774 | } | | |
| 4775 | } else { | | |
| 4776 | comptime_tvs[i] = .{ | | |
| 4777 | .ty = sema.typeOf(uncasted_args[i]), | | |
| 4778 | .val = Value.initTag(.generic_poison), | | |
| 4779 | }; | | |
| 4780 | } | | |
| 4781 | } | | |
| 4782 | | | |
| 4783 | const precomputed_hash = hasher.final(); | | |
| 4784 | | | |
| 4785 | const adapter: GenericCallAdapter = .{ | | |
| 4786 | .generic_fn = module_fn, | | |
| 4787 | .precomputed_hash = precomputed_hash, | | |
| 4788 | .func_ty_info = func_ty_info, | | |
| 4789 | .comptime_tvs = comptime_tvs, | | |
| 4790 | }; | | |
| 4791 | const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter); | | |
| 4792 | if (gop.found_existing) { | | |
| 4793 | const callee_func = gop.key_ptr.*; | | |
| 4794 | break :res try sema.finishGenericCall( | | |
| 4795 | block, | | |
| 4796 | call_src, | | |
| 4797 | callee_func, | | |
| 4798 | func_src, | | |
| 4799 | uncasted_args, | | |
| 4800 | fn_info, | | |
| 4801 | zir_tags, | | |
| 4802 | ); | | |
| 4803 | } | | |
| 4804 | const new_module_func = try gpa.create(Module.Fn); | | |
| 4805 | gop.key_ptr.* = new_module_func; | | |
| 4806 | { | | |
| 4807 | errdefer gpa.destroy(new_module_func); | | |
| 4808 | const remove_adapter: GenericRemoveAdapter = .{ | | |
| 4809 | .precomputed_hash = precomputed_hash, | | |
| 4810 | }; | | |
| 4811 | errdefer assert(mod.monomorphed_funcs.removeAdapted(new_module_func, remove_adapter)); | | |
| 4812 | | | |
| 4813 | try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); | | |
| 4814 | | | |
| 4815 | // Create a Decl for the new function. | | |
| 4816 | const src_decl = namespace.getDecl(); | | |
| 4817 | // TODO better names for generic function instantiations | | |
| 4818 | const name_index = mod.getNextAnonNameIndex(); | | |
| 4819 | const decl_name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{ | | |
| 4820 | module_fn.owner_decl.name, name_index, | | |
| 4821 | }); | | |
| 4822 | const new_decl = try mod.allocateNewDecl(decl_name, namespace, module_fn.owner_decl.src_node, src_decl.src_scope); | | |
| 4823 | new_decl.src_line = module_fn.owner_decl.src_line; | | |
| 4824 | new_decl.is_pub = module_fn.owner_decl.is_pub; | | |
| 4825 | new_decl.is_exported = module_fn.owner_decl.is_exported; | | |
| 4826 | new_decl.has_align = module_fn.owner_decl.has_align; | | |
| 4827 | new_decl.has_linksection_or_addrspace = module_fn.owner_decl.has_linksection_or_addrspace; | | |
| 4828 | new_decl.@"addrspace" = module_fn.owner_decl.@"addrspace"; | | |
| 4829 | new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index; | | |
| 4830 | new_decl.alive = true; // This Decl is called at runtime. | | |
| 4831 | new_decl.has_tv = true; | | |
| 4832 | new_decl.owns_tv = true; | | |
| 4833 | new_decl.analysis = .in_progress; | | |
| 4834 | new_decl.generation = mod.generation; | | |
| 4835 | | | |
| 4836 | namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {}); | | |
| 4837 | | | |
| 4838 | // The generic function Decl is guaranteed to be the first dependency | | |
| 4839 | // of each of its instantiations. | | |
| 4840 | assert(new_decl.dependencies.keys().len == 0); | | |
| 4841 | try mod.declareDeclDependency(new_decl, module_fn.owner_decl); | | |
| 4842 | | | |
| 4843 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | | |
| 4844 | errdefer new_decl_arena.deinit(); | | |
| 4845 | const new_decl_arena_allocator = new_decl_arena.allocator(); | | |
| 4846 | | | |
| 4847 | // Re-run the block that creates the function, with the comptime parameters | | |
| 4848 | // pre-populated inside `inst_map`. This causes `param_comptime` and | | |
| 4849 | // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a | | |
| 4850 | // new, monomorphized function, with the comptime parameters elided. | | |
| 4851 | var child_sema: Sema = .{ | | |
| 4852 | .mod = mod, | | |
| 4853 | .gpa = gpa, | | |
| 4854 | .arena = sema.arena, | | |
| 4855 | .perm_arena = new_decl_arena_allocator, | | |
| 4856 | .code = fn_zir, | | |
| 4857 | .owner_decl = new_decl, | | |
| 4858 | .func = null, | | |
| 4859 | .fn_ret_ty = Type.void, | | |
| 4860 | .owner_func = null, | | |
| 4861 | .comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len), | | |
| 4862 | .comptime_args_fn_inst = module_fn.zir_body_inst, | | |
| 4863 | .preallocated_new_func = new_module_func, | | |
| 4864 | }; | | |
| 4865 | defer child_sema.deinit(); | | |
| 4866 | | | |
| 4867 | var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope); | | |
| 4868 | defer wip_captures.deinit(); | | |
| 4869 | | | |
| 4870 | var child_block: Block = .{ | | |
| 4871 | .parent = null, | | |
| 4872 | .sema = &child_sema, | | |
| 4873 | .src_decl = new_decl, | | |
| 4874 | .namespace = namespace, | | |
| 4875 | .wip_capture_scope = wip_captures.scope, | | |
| 4876 | .instructions = .{}, | | |
| 4877 | .inlining = null, | | |
| 4878 | .is_comptime = true, | | |
| 4879 | }; | | |
| 4880 | defer { | | |
| 4881 | child_block.instructions.deinit(gpa); | | |
| 4882 | child_block.params.deinit(gpa); | | |
| 4883 | } | | |
| 4884 | | | |
| 4885 | try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len)); | | |
| 4886 | var arg_i: usize = 0; | | |
| 4887 | for (fn_info.param_body) |inst| { | | |
| 4888 | var is_comptime = false; | | |
| 4889 | var is_anytype = false; | | |
| 4890 | switch (zir_tags[inst]) { | | |
| 4891 | .param => { | | |
| 4892 | is_comptime = func_ty_info.paramIsComptime(arg_i); | | |
| 4893 | }, | | |
| 4894 | .param_comptime => { | | |
| 4895 | is_comptime = true; | | |
| 4896 | }, | | |
| 4897 | .param_anytype => { | | |
| 4898 | is_anytype = true; | | |
| 4899 | is_comptime = func_ty_info.paramIsComptime(arg_i); | | |
| 4900 | }, | | |
| 4901 | .param_anytype_comptime => { | | |
| 4902 | is_anytype = true; | | |
| 4903 | is_comptime = true; | | |
| 4904 | }, | | |
| 4905 | else => continue, | | |
| 4906 | } | | |
| 4907 | const arg_src = call_src; // TODO: better source location | | |
| 4908 | const arg = uncasted_args[arg_i]; | | |
| 4909 | if (is_comptime) { | | |
| 4910 | if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { | | |
| 4911 | const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); | | |
| 4912 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); | | |
| 4913 | } else { | | |
| 4914 | return sema.failWithNeededComptime(block, arg_src); | | |
| 4915 | } | | |
| 4916 | } else if (is_anytype) { | | |
| 4917 | const arg_ty = sema.typeOf(arg); | | |
| 4918 | if (try sema.typeRequiresComptime(block, arg_src, arg_ty)) { | | |
| 4919 | const arg_val = try sema.resolveConstValue(block, arg_src, arg); | | |
| 4920 | const child_arg = try child_sema.addConstant(arg_ty, arg_val); | | |
| 4921 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); | | |
| 4922 | } else { | | |
| 4923 | // We insert into the map an instruction which is runtime-known | | |
| 4924 | // but has the type of the argument. | | |
| 4925 | const child_arg = try child_block.addArg(arg_ty); | | |
| 4926 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); | | |
| 4927 | } | | |
| 4928 | } | | |
| 4929 | arg_i += 1; | | |
| 4930 | } | | |
| 4931 | const new_func_inst = child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst) catch |err| { | | |
| 4932 | // TODO look up the compile error that happened here and attach a note to it | | |
| 4933 | // pointing here, at the generic instantiation callsite. | | |
| 4934 | if (sema.owner_func) |owner_func| { | | |
| 4935 | owner_func.state = .dependency_failure; | | |
| 4936 | } else { | | |
| 4937 | sema.owner_decl.analysis = .dependency_failure; | | |
| 4938 | } | | |
| 4939 | return err; | | |
| 4940 | }; | | |
| 4941 | const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst) catch unreachable; | | |
| 4942 | const new_func = new_func_val.castTag(.function).?.data; | | |
| 4943 | assert(new_func == new_module_func); | | |
| 4944 | | | |
| 4945 | arg_i = 0; | | |
| 4946 | for (fn_info.param_body) |inst| { | | |
| 4947 | switch (zir_tags[inst]) { | | |
| 4948 | .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, | | |
| 4949 | else => continue, | | |
| 4950 | } | | |
| 4951 | const arg = child_sema.inst_map.get(inst).?; | | |
| 4952 | const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator); | | |
| 4953 | if (child_sema.resolveMaybeUndefValAllowVariables( | | |
| 4954 | &child_block, | | |
| 4955 | .unneeded, | | |
| 4956 | arg, | | |
| 4957 | ) catch unreachable) |arg_val| { | | |
| 4958 | child_sema.comptime_args[arg_i] = .{ | | |
| 4959 | .ty = copied_arg_ty, | | |
| 4960 | .val = try arg_val.copy(new_decl_arena_allocator), | | |
| 4961 | }; | | |
| 4962 | } else { | | |
| 4963 | child_sema.comptime_args[arg_i] = .{ | | |
| 4964 | .ty = copied_arg_ty, | | |
| 4965 | .val = Value.initTag(.generic_poison), | | |
| 4966 | }; | | |
| 4967 | } | | |
| 4968 | | | |
| 4969 | arg_i += 1; | | |
| 4970 | } | | |
| 4971 | | | |
| 4972 | try wip_captures.finalize(); | | |
| 4973 | | | |
| 4974 | // Populate the Decl ty/val with the function and its type. | | |
| 4975 | new_decl.ty = try child_sema.typeOf(new_func_inst).copy(new_decl_arena_allocator); | | |
| 4976 | new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func); | | |
| 4977 | new_decl.analysis = .complete; | | |
| 4978 | | | |
| 4979 | log.debug("generic function '{s}' instantiated with type {}", .{ | | |
| 4980 | new_decl.name, new_decl.ty, | | |
| 4981 | }); | | |
| 4982 | assert(!new_decl.ty.fnInfo().is_generic); | | |
| 4983 | | | |
| 4984 | // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field | | |
| 4985 | // will be populated, ensuring it will have `analyzeBody` called with the ZIR | | |
| 4986 | // parameters mapped appropriately. | | |
| 4987 | try mod.comp.bin_file.allocateDeclIndexes(new_decl); | | |
| 4988 | try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); | | |
| 4989 | | | |
| 4990 | try new_decl.finalizeNewArena(&new_decl_arena); | | |
| 4991 | } | | |
| 4992 | | | |
| 4993 | break :res try sema.finishGenericCall( | | |
| 4994 | block, | | |
| 4995 | call_src, | | |
| 4996 | new_module_func, | | |
| 4997 | func_src, | | |
| 4998 | uncasted_args, | | |
| 4999 | fn_info, | | |
| 5000 | zir_tags, | | |
| 5001 | ); | | |
| 5002 | } else res: { | 4749 | } else res: { |
| | 4750 | assert(!func_ty_info.is_generic); |
| 5003 | try sema.requireRuntimeBlock(block, call_src); | 4751 | try sema.requireRuntimeBlock(block, call_src); |
| 5004 | | 4752 | |
| 5005 | const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); | 4753 | const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); |
| ... | @@ -5037,16 +4785,274 @@ fn analyzeCall( | ... | @@ -5037,16 +4785,274 @@ fn analyzeCall( |
| 5037 | return result; | 4785 | return result; |
| 5038 | } | 4786 | } |
| 5039 | | 4787 | |
| 5040 | fn finishGenericCall( | 4788 | fn instantiateGenericCall( |
| 5041 | sema: *Sema, | 4789 | sema: *Sema, |
| 5042 | block: *Block, | 4790 | block: *Block, |
| 5043 | call_src: LazySrcLoc, | 4791 | func: Air.Inst.Ref, |
| 5044 | callee: *Module.Fn, | | |
| 5045 | func_src: LazySrcLoc, | 4792 | func_src: LazySrcLoc, |
| | 4793 | call_src: LazySrcLoc, |
| | 4794 | func_ty_info: Type.Payload.Function.Data, |
| | 4795 | ensure_result_used: bool, |
| 5046 | uncasted_args: []const Air.Inst.Ref, | 4796 | uncasted_args: []const Air.Inst.Ref, |
| 5047 | fn_info: Zir.FnInfo, | | |
| 5048 | zir_tags: []const Zir.Inst.Tag, | | |
| 5049 | ) CompileError!Air.Inst.Ref { | 4797 | ) CompileError!Air.Inst.Ref { |
| | 4798 | const mod = sema.mod; |
| | 4799 | const gpa = sema.gpa; |
| | 4800 | |
| | 4801 | const func_val = try sema.resolveConstValue(block, func_src, func); |
| | 4802 | const module_fn = switch (func_val.tag()) { |
| | 4803 | .function => func_val.castTag(.function).?.data, |
| | 4804 | .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data, |
| | 4805 | else => unreachable, |
| | 4806 | }; |
| | 4807 | // Check the Module's generic function map with an adapted context, so that we |
| | 4808 | // can match against `uncasted_args` rather than doing the work below to create a |
| | 4809 | // generic Scope only to junk it if it matches an existing instantiation. |
| | 4810 | const namespace = module_fn.owner_decl.src_namespace; |
| | 4811 | const fn_zir = namespace.file_scope.zir; |
| | 4812 | const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst); |
| | 4813 | const zir_tags = fn_zir.instructions.items(.tag); |
| | 4814 | |
| | 4815 | // This hash must match `Module.MonomorphedFuncsContext.hash`. |
| | 4816 | // For parameters explicitly marked comptime and simple parameter type expressions, |
| | 4817 | // we know whether a parameter is elided from a monomorphed function, and can |
| | 4818 | // use it in the hash here. However, for parameter type expressions that are not |
| | 4819 | // explicitly marked comptime and rely on previous parameter comptime values, we |
| | 4820 | // don't find out until after generating a monomorphed function whether the parameter |
| | 4821 | // type ended up being a "must-be-comptime-known" type. |
| | 4822 | var hasher = std.hash.Wyhash.init(0); |
| | 4823 | std.hash.autoHash(&hasher, @ptrToInt(module_fn)); |
| | 4824 | |
| | 4825 | const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len); |
| | 4826 | |
| | 4827 | for (func_ty_info.param_types) |param_ty, i| { |
| | 4828 | const is_comptime = func_ty_info.paramIsComptime(i); |
| | 4829 | if (is_comptime) { |
| | 4830 | const arg_src = call_src; // TODO better source location |
| | 4831 | const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src); |
| | 4832 | if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| { |
| | 4833 | if (param_ty.tag() != .generic_poison) { |
| | 4834 | arg_val.hash(param_ty, &hasher); |
| | 4835 | } |
| | 4836 | comptime_tvs[i] = .{ |
| | 4837 | // This will be different than `param_ty` in the case of `generic_poison`. |
| | 4838 | .ty = sema.typeOf(casted_arg), |
| | 4839 | .val = arg_val, |
| | 4840 | }; |
| | 4841 | } else { |
| | 4842 | return sema.failWithNeededComptime(block, arg_src); |
| | 4843 | } |
| | 4844 | } else { |
| | 4845 | comptime_tvs[i] = .{ |
| | 4846 | .ty = sema.typeOf(uncasted_args[i]), |
| | 4847 | .val = Value.initTag(.generic_poison), |
| | 4848 | }; |
| | 4849 | } |
| | 4850 | } |
| | 4851 | |
| | 4852 | const precomputed_hash = hasher.final(); |
| | 4853 | |
| | 4854 | const adapter: GenericCallAdapter = .{ |
| | 4855 | .generic_fn = module_fn, |
| | 4856 | .precomputed_hash = precomputed_hash, |
| | 4857 | .func_ty_info = func_ty_info, |
| | 4858 | .comptime_tvs = comptime_tvs, |
| | 4859 | }; |
| | 4860 | const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter); |
| | 4861 | if (!gop.found_existing) { |
| | 4862 | const new_module_func = try gpa.create(Module.Fn); |
| | 4863 | gop.key_ptr.* = new_module_func; |
| | 4864 | errdefer gpa.destroy(new_module_func); |
| | 4865 | const remove_adapter: GenericRemoveAdapter = .{ |
| | 4866 | .precomputed_hash = precomputed_hash, |
| | 4867 | }; |
| | 4868 | errdefer assert(mod.monomorphed_funcs.removeAdapted(new_module_func, remove_adapter)); |
| | 4869 | |
| | 4870 | try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); |
| | 4871 | |
| | 4872 | // Create a Decl for the new function. |
| | 4873 | const src_decl = namespace.getDecl(); |
| | 4874 | // TODO better names for generic function instantiations |
| | 4875 | const name_index = mod.getNextAnonNameIndex(); |
| | 4876 | const decl_name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{ |
| | 4877 | module_fn.owner_decl.name, name_index, |
| | 4878 | }); |
| | 4879 | const new_decl = try mod.allocateNewDecl(decl_name, namespace, module_fn.owner_decl.src_node, src_decl.src_scope); |
| | 4880 | errdefer new_decl.destroy(mod); |
| | 4881 | new_decl.src_line = module_fn.owner_decl.src_line; |
| | 4882 | new_decl.is_pub = module_fn.owner_decl.is_pub; |
| | 4883 | new_decl.is_exported = module_fn.owner_decl.is_exported; |
| | 4884 | new_decl.has_align = module_fn.owner_decl.has_align; |
| | 4885 | new_decl.has_linksection_or_addrspace = module_fn.owner_decl.has_linksection_or_addrspace; |
| | 4886 | new_decl.@"addrspace" = module_fn.owner_decl.@"addrspace"; |
| | 4887 | new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index; |
| | 4888 | new_decl.alive = true; // This Decl is called at runtime. |
| | 4889 | new_decl.analysis = .in_progress; |
| | 4890 | new_decl.generation = mod.generation; |
| | 4891 | |
| | 4892 | namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {}); |
| | 4893 | errdefer assert(namespace.anon_decls.orderedRemove(new_decl)); |
| | 4894 | |
| | 4895 | // The generic function Decl is guaranteed to be the first dependency |
| | 4896 | // of each of its instantiations. |
| | 4897 | assert(new_decl.dependencies.keys().len == 0); |
| | 4898 | try mod.declareDeclDependency(new_decl, module_fn.owner_decl); |
| | 4899 | errdefer assert(module_fn.owner_decl.dependants.orderedRemove(new_decl)); |
| | 4900 | |
| | 4901 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); |
| | 4902 | errdefer new_decl_arena.deinit(); |
| | 4903 | const new_decl_arena_allocator = new_decl_arena.allocator(); |
| | 4904 | |
| | 4905 | // Re-run the block that creates the function, with the comptime parameters |
| | 4906 | // pre-populated inside `inst_map`. This causes `param_comptime` and |
| | 4907 | // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a |
| | 4908 | // new, monomorphized function, with the comptime parameters elided. |
| | 4909 | var child_sema: Sema = .{ |
| | 4910 | .mod = mod, |
| | 4911 | .gpa = gpa, |
| | 4912 | .arena = sema.arena, |
| | 4913 | .perm_arena = new_decl_arena_allocator, |
| | 4914 | .code = fn_zir, |
| | 4915 | .owner_decl = new_decl, |
| | 4916 | .func = null, |
| | 4917 | .fn_ret_ty = Type.void, |
| | 4918 | .owner_func = null, |
| | 4919 | .comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len), |
| | 4920 | .comptime_args_fn_inst = module_fn.zir_body_inst, |
| | 4921 | .preallocated_new_func = new_module_func, |
| | 4922 | }; |
| | 4923 | defer child_sema.deinit(); |
| | 4924 | |
| | 4925 | var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope); |
| | 4926 | defer wip_captures.deinit(); |
| | 4927 | |
| | 4928 | var child_block: Block = .{ |
| | 4929 | .parent = null, |
| | 4930 | .sema = &child_sema, |
| | 4931 | .src_decl = new_decl, |
| | 4932 | .namespace = namespace, |
| | 4933 | .wip_capture_scope = wip_captures.scope, |
| | 4934 | .instructions = .{}, |
| | 4935 | .inlining = null, |
| | 4936 | .is_comptime = true, |
| | 4937 | }; |
| | 4938 | defer { |
| | 4939 | child_block.instructions.deinit(gpa); |
| | 4940 | child_block.params.deinit(gpa); |
| | 4941 | } |
| | 4942 | |
| | 4943 | try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len)); |
| | 4944 | var arg_i: usize = 0; |
| | 4945 | for (fn_info.param_body) |inst| { |
| | 4946 | var is_comptime = false; |
| | 4947 | var is_anytype = false; |
| | 4948 | switch (zir_tags[inst]) { |
| | 4949 | .param => { |
| | 4950 | is_comptime = func_ty_info.paramIsComptime(arg_i); |
| | 4951 | }, |
| | 4952 | .param_comptime => { |
| | 4953 | is_comptime = true; |
| | 4954 | }, |
| | 4955 | .param_anytype => { |
| | 4956 | is_anytype = true; |
| | 4957 | is_comptime = func_ty_info.paramIsComptime(arg_i); |
| | 4958 | }, |
| | 4959 | .param_anytype_comptime => { |
| | 4960 | is_anytype = true; |
| | 4961 | is_comptime = true; |
| | 4962 | }, |
| | 4963 | else => continue, |
| | 4964 | } |
| | 4965 | const arg_src = call_src; // TODO: better source location |
| | 4966 | const arg = uncasted_args[arg_i]; |
| | 4967 | if (is_comptime) { |
| | 4968 | if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { |
| | 4969 | const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); |
| | 4970 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); |
| | 4971 | } else { |
| | 4972 | return sema.failWithNeededComptime(block, arg_src); |
| | 4973 | } |
| | 4974 | } else if (is_anytype) { |
| | 4975 | const arg_ty = sema.typeOf(arg); |
| | 4976 | if (try sema.typeRequiresComptime(block, arg_src, arg_ty)) { |
| | 4977 | const arg_val = try sema.resolveConstValue(block, arg_src, arg); |
| | 4978 | const child_arg = try child_sema.addConstant(arg_ty, arg_val); |
| | 4979 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); |
| | 4980 | } else { |
| | 4981 | // We insert into the map an instruction which is runtime-known |
| | 4982 | // but has the type of the argument. |
| | 4983 | const child_arg = try child_block.addArg(arg_ty); |
| | 4984 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); |
| | 4985 | } |
| | 4986 | } |
| | 4987 | arg_i += 1; |
| | 4988 | } |
| | 4989 | const new_func_inst = child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst) catch |err| { |
| | 4990 | // TODO look up the compile error that happened here and attach a note to it |
| | 4991 | // pointing here, at the generic instantiation callsite. |
| | 4992 | if (sema.owner_func) |owner_func| { |
| | 4993 | owner_func.state = .dependency_failure; |
| | 4994 | } else { |
| | 4995 | sema.owner_decl.analysis = .dependency_failure; |
| | 4996 | } |
| | 4997 | return err; |
| | 4998 | }; |
| | 4999 | const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst) catch unreachable; |
| | 5000 | const new_func = new_func_val.castTag(.function).?.data; |
| | 5001 | assert(new_func == new_module_func); |
| | 5002 | |
| | 5003 | arg_i = 0; |
| | 5004 | for (fn_info.param_body) |inst| { |
| | 5005 | switch (zir_tags[inst]) { |
| | 5006 | .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, |
| | 5007 | else => continue, |
| | 5008 | } |
| | 5009 | const arg = child_sema.inst_map.get(inst).?; |
| | 5010 | const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator); |
| | 5011 | if (child_sema.resolveMaybeUndefValAllowVariables( |
| | 5012 | &child_block, |
| | 5013 | .unneeded, |
| | 5014 | arg, |
| | 5015 | ) catch unreachable) |arg_val| { |
| | 5016 | child_sema.comptime_args[arg_i] = .{ |
| | 5017 | .ty = copied_arg_ty, |
| | 5018 | .val = try arg_val.copy(new_decl_arena_allocator), |
| | 5019 | }; |
| | 5020 | } else { |
| | 5021 | child_sema.comptime_args[arg_i] = .{ |
| | 5022 | .ty = copied_arg_ty, |
| | 5023 | .val = Value.initTag(.generic_poison), |
| | 5024 | }; |
| | 5025 | } |
| | 5026 | |
| | 5027 | arg_i += 1; |
| | 5028 | } |
| | 5029 | |
| | 5030 | try wip_captures.finalize(); |
| | 5031 | |
| | 5032 | // Populate the Decl ty/val with the function and its type. |
| | 5033 | new_decl.ty = try child_sema.typeOf(new_func_inst).copy(new_decl_arena_allocator); |
| | 5034 | // If the call evaluated to a generic type return errror and call inline. |
| | 5035 | if (new_decl.ty.fnInfo().is_generic) return error.GenericPoison; |
| | 5036 | |
| | 5037 | new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func); |
| | 5038 | new_decl.has_tv = true; |
| | 5039 | new_decl.owns_tv = true; |
| | 5040 | new_decl.analysis = .complete; |
| | 5041 | |
| | 5042 | log.debug("generic function '{s}' instantiated with type {}", .{ |
| | 5043 | new_decl.name, new_decl.ty, |
| | 5044 | }); |
| | 5045 | |
| | 5046 | // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field |
| | 5047 | // will be populated, ensuring it will have `analyzeBody` called with the ZIR |
| | 5048 | // parameters mapped appropriately. |
| | 5049 | try mod.comp.bin_file.allocateDeclIndexes(new_decl); |
| | 5050 | try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); |
| | 5051 | |
| | 5052 | try new_decl.finalizeNewArena(&new_decl_arena); |
| | 5053 | } |
| | 5054 | |
| | 5055 | const callee = gop.key_ptr.*; |
| 5050 | const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl); | 5056 | const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl); |
| 5051 | | 5057 | |
| 5052 | // Make a runtime call to the new function, making sure to omit the comptime args. | 5058 | // Make a runtime call to the new function, making sure to omit the comptime args. |
| ... | @@ -5106,6 +5112,10 @@ fn finishGenericCall( | ... | @@ -5106,6 +5112,10 @@ fn finishGenericCall( |
| 5106 | } }, | 5112 | } }, |
| 5107 | }); | 5113 | }); |
| 5108 | sema.appendRefsAssumeCapacity(runtime_args); | 5114 | sema.appendRefsAssumeCapacity(runtime_args); |
| | 5115 | |
| | 5116 | if (ensure_result_used) { |
| | 5117 | try sema.ensureResultUsed(block, func_inst, call_src); |
| | 5118 | } |
| 5109 | return func_inst; | 5119 | return func_inst; |
| 5110 | } | 5120 | } |
| 5111 | | 5121 | |
| ... | @@ -11664,18 +11674,25 @@ fn zirStructInit( | ... | @@ -11664,18 +11674,25 @@ fn zirStructInit( |
| 11664 | const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); | 11674 | const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); |
| 11665 | const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); | 11675 | const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); |
| 11666 | | 11676 | |
| 11667 | if (is_ref) { | | |
| 11668 | return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true union", .{}); | | |
| 11669 | } | | |
| 11670 | | | |
| 11671 | const init_inst = sema.resolveInst(item.data.init); | 11677 | const init_inst = sema.resolveInst(item.data.init); |
| 11672 | if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| { | 11678 | if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| { |
| 11673 | const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index); | 11679 | const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index); |
| 11674 | return sema.addConstant( | 11680 | return sema.addConstantMaybeRef( |
| | 11681 | block, |
| | 11682 | src, |
| 11675 | resolved_ty, | 11683 | resolved_ty, |
| 11676 | try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = val }), | 11684 | try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = val }), |
| | 11685 | is_ref, |
| 11677 | ); | 11686 | ); |
| 11678 | } | 11687 | } |
| | 11688 | |
| | 11689 | if (is_ref) { |
| | 11690 | const alloc = try block.addTy(.alloc, resolved_ty); |
| | 11691 | const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty); |
| | 11692 | try sema.storePtr(block, src, field_ptr, init_inst); |
| | 11693 | return alloc; |
| | 11694 | } |
| | 11695 | |
| 11679 | return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known union values", .{}); | 11696 | return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known union values", .{}); |
| 11680 | } | 11697 | } |
| 11681 | unreachable; | 11698 | unreachable; |
| ... | @@ -16033,7 +16050,6 @@ fn coerce( | ... | @@ -16033,7 +16050,6 @@ fn coerce( |
| 16033 | }, | 16050 | }, |
| 16034 | .Pointer => p: { | 16051 | .Pointer => p: { |
| 16035 | const inst_info = inst_ty.ptrInfo().data; | 16052 | const inst_info = inst_ty.ptrInfo().data; |
| 16036 | if (inst_info.size == .Slice) break :p; | | |
| 16037 | switch (try sema.coerceInMemoryAllowed( | 16053 | switch (try sema.coerceInMemoryAllowed( |
| 16038 | block, | 16054 | block, |
| 16039 | dest_info.pointee_type, | 16055 | dest_info.pointee_type, |
| ... | @@ -16046,6 +16062,14 @@ fn coerce( | ... | @@ -16046,6 +16062,14 @@ fn coerce( |
| 16046 | .ok => {}, | 16062 | .ok => {}, |
| 16047 | .no_match => break :p, | 16063 | .no_match => break :p, |
| 16048 | } | 16064 | } |
| | 16065 | if (inst_info.size == .Slice) { |
| | 16066 | if (dest_info.sentinel == null or inst_info.sentinel == null or |
| | 16067 | !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type)) |
| | 16068 | break :p; |
| | 16069 | |
| | 16070 | const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); |
| | 16071 | return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); |
| | 16072 | } |
| 16049 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); | 16073 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 16050 | }, | 16074 | }, |
| 16051 | else => {}, | 16075 | else => {}, |
| ... | @@ -16089,7 +16113,30 @@ fn coerce( | ... | @@ -16089,7 +16113,30 @@ fn coerce( |
| 16089 | return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); | 16113 | return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); |
| 16090 | } | 16114 | } |
| 16091 | }, | 16115 | }, |
| 16092 | .Many => {}, | 16116 | .Many => p: { |
| | 16117 | const inst_info = inst_ty.ptrInfo().data; |
| | 16118 | if (inst_info.size != .Slice) break :p; |
| | 16119 | |
| | 16120 | switch (try sema.coerceInMemoryAllowed( |
| | 16121 | block, |
| | 16122 | dest_info.pointee_type, |
| | 16123 | inst_info.pointee_type, |
| | 16124 | dest_info.mutable, |
| | 16125 | target, |
| | 16126 | dest_ty_src, |
| | 16127 | inst_src, |
| | 16128 | )) { |
| | 16129 | .ok => {}, |
| | 16130 | .no_match => break :p, |
| | 16131 | } |
| | 16132 | |
| | 16133 | if (dest_info.sentinel == null or inst_info.sentinel == null or |
| | 16134 | !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type)) |
| | 16135 | break :p; |
| | 16136 | |
| | 16137 | const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); |
| | 16138 | return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); |
| | 16139 | }, |
| 16093 | } | 16140 | } |
| 16094 | | 16141 | |
| 16095 | // This will give an extra hint on top of what the bottom of this func would provide. | 16142 | // This will give an extra hint on top of what the bottom of this func would provide. |