authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 14:20:49-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 14:20:49-04:00
log2ea08561cf69dabc99722ffc24cb0e4327605506
tree3fc68df2a5794f9a899dee698bc49daa2d465eaa
parent1d4a94b63525b7f9a980069de1807d03d0ad98e0

self-hosted: function types use table lookup


5 files changed, 356 insertions(+), 70 deletions(-)

src-self-hosted/codegen.zig+2-1
......@@ -168,6 +168,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
168168 //}
169169
170170 const fn_type = fn_val.base.typ.cast(Type.Fn).?;
171 const fn_type_normal = &fn_type.key.data.Normal;
171172
172173 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
173174 //add_uwtable_attr(g, fn_table_entry->llvm_value);
......@@ -209,7 +210,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
209210 // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
210211 //}
211212
212 const cur_ret_ptr = if (fn_type.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null;
213 const cur_ret_ptr = if (fn_type_normal.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null;
213214
214215 // build all basic blocks
215216 for (code.basic_block_list.toSlice()) |bb| {
src-self-hosted/compilation.zig+65-4
......@@ -220,12 +220,14 @@ pub const Compilation = struct {
220220 int_type_table: event.Locked(IntTypeTable),
221221 array_type_table: event.Locked(ArrayTypeTable),
222222 ptr_type_table: event.Locked(PtrTypeTable),
223 fn_type_table: event.Locked(FnTypeTable),
223224
224225 c_int_types: [CInt.list.len]*Type.Int,
225226
226227 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
227228 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
228229 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
230 const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql);
229231 const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
230232
231233 const CompileErrList = std.ArrayList(*Msg);
......@@ -384,6 +386,7 @@ pub const Compilation = struct {
384386 .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
385387 .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),
386388 .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)),
389 .fn_type_table = event.Locked(FnTypeTable).init(loop, FnTypeTable.init(loop.allocator)),
387390 .c_int_types = undefined,
388391
389392 .meta_type = undefined,
......@@ -414,6 +417,7 @@ pub const Compilation = struct {
414417 comp.int_type_table.private_data.deinit();
415418 comp.array_type_table.private_data.deinit();
416419 comp.ptr_type_table.private_data.deinit();
420 comp.fn_type_table.private_data.deinit();
417421 comp.arena_allocator.deinit();
418422 comp.loop.allocator.destroy(comp);
419423 }
......@@ -1160,10 +1164,47 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
11601164 fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
11611165 symbol_name_consumed = true;
11621166
1167 // Define local parameter variables
1168 //for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
1169 // FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
1170 // AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
1171 // Buf *param_name;
1172 // bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
1173 // if (param_decl_node && !is_var_args) {
1174 // param_name = param_decl_node->data.param_decl.name;
1175 // } else {
1176 // param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
1177 // }
1178 // if (param_name == nullptr) {
1179 // continue;
1180 // }
1181
1182 // TypeTableEntry *param_type = param_info->type;
1183 // bool is_noalias = param_info->is_noalias;
1184
1185 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
1186 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
1187 // }
1188
1189 // VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
1190 // param_name, true, create_const_runtime(param_type), nullptr);
1191 // var->src_arg_index = i;
1192 // fn_table_entry->child_scope = var->child_scope;
1193 // var->shadowable = var->shadowable || is_var_args;
1194
1195 // if (type_has_bits(param_type)) {
1196 // fn_table_entry->variable_list.append(var);
1197 // }
1198
1199 // if (fn_type->data.fn.gen_param_info) {
1200 // var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
1201 // }
1202 //}
1203
11631204 const analyzed_code = try await (async comp.genAndAnalyzeCode(
11641205 &fndef_scope.base,
11651206 body_node,
1166 fn_type.return_type,
1207 fn_type.key.data.Normal.return_type,
11671208 ) catch unreachable);
11681209 errdefer analyzed_code.destroy(comp.gpa());
11691210
......@@ -1199,14 +1240,13 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
11991240
12001241 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
12011242 var params_consumed = false;
1202 defer if (params_consumed) {
1243 defer if (!params_consumed) {
12031244 for (params.toSliceConst()) |param| {
12041245 param.typ.base.deref(comp);
12051246 }
12061247 params.deinit();
12071248 };
12081249
1209 const is_var_args = false;
12101250 {
12111251 var it = fn_proto.params.iterator(0);
12121252 while (it.next()) |param_node_ptr| {
......@@ -1219,8 +1259,29 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
12191259 });
12201260 }
12211261 }
1222 const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args);
1262
1263 const key = Type.Fn.Key{
1264 .alignment = null,
1265 .data = Type.Fn.Key.Data{
1266 .Normal = Type.Fn.Normal{
1267 .return_type = return_type,
1268 .params = params.toOwnedSlice(),
1269 .is_var_args = false, // TODO
1270 .cc = Type.Fn.CallingConvention.Auto, // TODO
1271 },
1272 },
1273 };
12231274 params_consumed = true;
1275 var key_consumed = false;
1276 defer if (!key_consumed) {
1277 for (key.data.Normal.params) |param| {
1278 param.typ.base.deref(comp);
1279 }
1280 comp.gpa().free(key.data.Normal.params);
1281 };
1282
1283 const fn_type = try await (async Type.Fn.get(comp, key) catch unreachable);
1284 key_consumed = true;
12241285 errdefer fn_type.base.base.deref(comp);
12251286
12261287 return fn_type;
src-self-hosted/ir.zig+5-3
......@@ -281,11 +281,13 @@ pub const Inst = struct {
281281 return error.SemanticAnalysisFailed;
282282 };
283283
284 if (fn_type.params.len != self.params.args.len) {
284 const fn_type_param_count = fn_type.paramCount();
285
286 if (fn_type_param_count != self.params.args.len) {
285287 try ira.addCompileError(
286288 self.base.span,
287289 "expected {} arguments, found {}",
288 fn_type.params.len,
290 fn_type_param_count,
289291 self.params.args.len,
290292 );
291293 return error.SemanticAnalysisFailed;
......@@ -299,7 +301,7 @@ pub const Inst = struct {
299301 .fn_ref = fn_ref,
300302 .args = args,
301303 });
302 new_inst.val = IrVal{ .KnownType = fn_type.return_type };
304 new_inst.val = IrVal{ .KnownType = fn_type.key.data.Normal.return_type };
303305 return new_inst;
304306 }
305307
src-self-hosted/type.zig+282-56
......@@ -221,57 +221,267 @@ pub const Type = struct {
221221
222222 pub const Fn = struct {
223223 base: Type,
224 return_type: *Type,
225 params: []Param,
226 is_var_args: bool,
224 key: Key,
225 garbage_node: std.atomic.Stack(*Fn).Node,
226
227 pub const Key = struct {
228 data: Data,
229 alignment: ?u32,
230
231 pub const Data = union(enum) {
232 Generic: Generic,
233 Normal: Normal,
234 };
235
236 pub fn hash(self: *const Key) u32 {
237 var result: u32 = 0;
238 result +%= hashAny(self.alignment, 0);
239 switch (self.data) {
240 Data.Generic => |generic| {
241 result +%= hashAny(generic.param_count, 1);
242 switch (generic.cc) {
243 CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2),
244 else => result +%= hashAny(CallingConvention(generic.cc), 3),
245 }
246 },
247 Data.Normal => |normal| {
248 result +%= hashAny(normal.return_type, 4);
249 result +%= hashAny(normal.is_var_args, 5);
250 result +%= hashAny(normal.cc, 6);
251 for (normal.params) |param| {
252 result +%= hashAny(param.is_noalias, 7);
253 result +%= hashAny(param.typ, 8);
254 }
255 },
256 }
257 return result;
258 }
259
260 pub fn eql(self: *const Key, other: *const Key) bool {
261 if ((self.alignment == null) != (other.alignment == null)) return false;
262 if (self.alignment) |self_align| {
263 if (self_align != other.alignment.?) return false;
264 }
265 if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false;
266 switch (self.data) {
267 Data.Generic => |*self_generic| {
268 const other_generic = &other.data.Generic;
269 if (self_generic.param_count != other_generic.param_count) return false;
270 if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false;
271 switch (self_generic.cc) {
272 CallingConvention.Async => |self_allocator_type| {
273 const other_allocator_type = other_generic.cc.Async;
274 if (self_allocator_type != other_allocator_type) return false;
275 },
276 else => {},
277 }
278 },
279 Data.Normal => |*self_normal| {
280 const other_normal = &other.data.Normal;
281 if (self_normal.cc != other_normal.cc) return false;
282 if (self_normal.is_var_args != other_normal.is_var_args) return false;
283 if (self_normal.return_type != other_normal.return_type) return false;
284 for (self_normal.params) |*self_param, i| {
285 const other_param = &other_normal.params[i];
286 if (self_param.is_noalias != other_param.is_noalias) return false;
287 if (self_param.typ != other_param.typ) return false;
288 }
289 },
290 }
291 return true;
292 }
293
294 pub fn deref(key: Key, comp: *Compilation) void {
295 switch (key.data) {
296 Key.Data.Generic => |generic| {
297 switch (generic.cc) {
298 CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp),
299 else => {},
300 }
301 },
302 Key.Data.Normal => |normal| {
303 normal.return_type.base.deref(comp);
304 for (normal.params) |param| {
305 param.typ.base.deref(comp);
306 }
307 },
308 }
309 }
310
311 pub fn ref(key: Key) void {
312 switch (key.data) {
313 Key.Data.Generic => |generic| {
314 switch (generic.cc) {
315 CallingConvention.Async => |allocator_type| allocator_type.base.ref(),
316 else => {},
317 }
318 },
319 Key.Data.Normal => |normal| {
320 normal.return_type.base.ref();
321 for (normal.params) |param| {
322 param.typ.base.ref();
323 }
324 },
325 }
326 }
327 };
328
329 pub const Normal = struct {
330 params: []Param,
331 return_type: *Type,
332 is_var_args: bool,
333 cc: CallingConvention,
334 };
335
336 pub const Generic = struct {
337 param_count: usize,
338 cc: CC,
339
340 pub const CC = union(CallingConvention) {
341 Auto,
342 C,
343 Cold,
344 Naked,
345 Stdcall,
346 Async: *Type, // allocator type
347 };
348 };
349
350 pub const CallingConvention = enum {
351 Auto,
352 C,
353 Cold,
354 Naked,
355 Stdcall,
356 Async,
357 };
227358
228359 pub const Param = struct {
229360 is_noalias: bool,
230361 typ: *Type,
231362 };
232363
233 pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
234 const result = try comp.gpa().create(Fn{
364 fn ccFnTypeStr(cc: CallingConvention) []const u8 {
365 return switch (cc) {
366 CallingConvention.Auto => "",
367 CallingConvention.C => "extern ",
368 CallingConvention.Cold => "coldcc ",
369 CallingConvention.Naked => "nakedcc ",
370 CallingConvention.Stdcall => "stdcallcc ",
371 CallingConvention.Async => unreachable,
372 };
373 }
374
375 pub fn paramCount(self: *Fn) usize {
376 return switch (self.key.data) {
377 Key.Data.Generic => |generic| generic.param_count,
378 Key.Data.Normal => |normal| normal.params.len,
379 };
380 }
381
382 /// takes ownership of key.Normal.params on success
383 pub async fn get(comp: *Compilation, key: Key) !*Fn {
384 {
385 const held = await (async comp.fn_type_table.acquire() catch unreachable);
386 defer held.release();
387
388 if (held.value.get(&key)) |entry| {
389 entry.value.base.base.ref();
390 return entry.value;
391 }
392 }
393
394 key.ref();
395 errdefer key.deref(comp);
396
397 const self = try comp.gpa().create(Fn{
235398 .base = undefined,
236 .return_type = return_type,
237 .params = params,
238 .is_var_args = is_var_args,
399 .key = key,
400 .garbage_node = undefined,
239401 });
240 errdefer comp.gpa().destroy(result);
402 errdefer comp.gpa().destroy(self);
241403
242 result.base.init(comp, Id.Fn, "TODO fn type name");
404 var name_buf = try std.Buffer.initSize(comp.gpa(), 0);
405 defer name_buf.deinit();
406
407 const name_stream = &std.io.BufferOutStream.init(&name_buf).stream;
408
409 switch (key.data) {
410 Key.Data.Generic => |generic| {
411 switch (generic.cc) {
412 CallingConvention.Async => |async_allocator_type| {
413 try name_stream.print("async<{}> ", async_allocator_type.name);
414 },
415 else => {
416 const cc_str = ccFnTypeStr(generic.cc);
417 try name_stream.write(cc_str);
418 },
419 }
420 try name_stream.write("fn(");
421 var param_i: usize = 0;
422 while (param_i < generic.param_count) : (param_i += 1) {
423 const arg = if (param_i == 0) "var" else ", var";
424 try name_stream.write(arg);
425 }
426 try name_stream.write(")");
427 if (key.alignment) |alignment| {
428 try name_stream.print(" align<{}>", alignment);
429 }
430 try name_stream.write(" var");
431 },
432 Key.Data.Normal => |normal| {
433 const cc_str = ccFnTypeStr(normal.cc);
434 try name_stream.print("{}fn(", cc_str);
435 for (normal.params) |param, i| {
436 if (i != 0) try name_stream.write(", ");
437 if (param.is_noalias) try name_stream.write("noalias ");
438 try name_stream.write(param.typ.name);
439 }
440 if (normal.is_var_args) {
441 if (normal.params.len != 0) try name_stream.write(", ");
442 try name_stream.write("...");
443 }
444 try name_stream.write(")");
445 if (key.alignment) |alignment| {
446 try name_stream.print(" align<{}>", alignment);
447 }
448 try name_stream.print(" {}", normal.return_type.name);
449 },
450 }
451
452 self.base.init(comp, Id.Fn, name_buf.toOwnedSlice());
243453
244 result.return_type.base.ref();
245 for (result.params) |param| {
246 param.typ.base.ref();
454 {
455 const held = await (async comp.fn_type_table.acquire() catch unreachable);
456 defer held.release();
457
458 _ = try held.value.put(&self.key, self);
247459 }
248 return result;
460 return self;
249461 }
250462
251463 pub fn destroy(self: *Fn, comp: *Compilation) void {
252 self.return_type.base.deref(comp);
253 for (self.params) |param| {
254 param.typ.base.deref(comp);
255 }
464 self.key.deref(comp);
256465 comp.gpa().destroy(self);
257466 }
258467
259468 pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
260 const llvm_return_type = switch (self.return_type.id) {
469 const normal = &self.key.data.Normal;
470 const llvm_return_type = switch (normal.return_type.id) {
261471 Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
262 else => try self.return_type.getLlvmType(allocator, llvm_context),
472 else => try normal.return_type.getLlvmType(allocator, llvm_context),
263473 };
264 const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);
474 const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len);
265475 defer allocator.free(llvm_param_types);
266476 for (llvm_param_types) |*llvm_param_type, i| {
267 llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context);
477 llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context);
268478 }
269479
270480 return llvm.FunctionType(
271481 llvm_return_type,
272482 llvm_param_types.ptr,
273483 @intCast(c_uint, llvm_param_types.len),
274 @boolToInt(self.is_var_args),
484 @boolToInt(normal.is_var_args),
275485 ) orelse error.OutOfMemory;
276486 }
277487 };
......@@ -347,8 +557,10 @@ pub const Type = struct {
347557 is_signed: bool,
348558
349559 pub fn hash(self: *const Key) u32 {
350 const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 };
351 return rands[@boolToInt(self.is_signed)] *% self.bit_count;
560 var result: u32 = 0;
561 result +%= hashAny(self.is_signed, 0);
562 result +%= hashAny(self.bit_count, 1);
563 return result;
352564 }
353565
354566 pub fn eql(self: *const Key, other: *const Key) bool {
......@@ -443,15 +655,16 @@ pub const Type = struct {
443655 alignment: Align,
444656
445657 pub fn hash(self: *const Key) u32 {
446 const align_hash = switch (self.alignment) {
658 var result: u32 = 0;
659 result +%= switch (self.alignment) {
447660 Align.Abi => 0xf201c090,
448 Align.Override => |x| x,
661 Align.Override => |x| hashAny(x, 0),
449662 };
450 return hash_usize(@ptrToInt(self.child_type)) *%
451 hash_enum(self.mut) *%
452 hash_enum(self.vol) *%
453 hash_enum(self.size) *%
454 align_hash;
663 result +%= hashAny(self.child_type, 1);
664 result +%= hashAny(self.mut, 2);
665 result +%= hashAny(self.vol, 3);
666 result +%= hashAny(self.size, 4);
667 return result;
455668 }
456669
457670 pub fn eql(self: *const Key, other: *const Key) bool {
......@@ -605,7 +818,10 @@ pub const Type = struct {
605818 len: usize,
606819
607820 pub fn hash(self: *const Key) u32 {
608 return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len);
821 var result: u32 = 0;
822 result +%= hashAny(self.elem_type, 0);
823 result +%= hashAny(self.len, 1);
824 return result;
609825 }
610826
611827 pub fn eql(self: *const Key, other: *const Key) bool {
......@@ -818,27 +1034,37 @@ pub const Type = struct {
8181034 };
8191035};
8201036
821fn hash_usize(x: usize) u32 {
822 return switch (@sizeOf(usize)) {
823 4 => x,
824 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),
825 else => @compileError("implement this hash function"),
826 };
827}
828
829fn hash_enum(x: var) u32 {
830 const rands = []u32{
831 0x85ebf64f,
832 0x3fcb3211,
833 0x240a4e8e,
834 0x40bb0e3c,
835 0x78be45af,
836 0x1ca98e37,
837 0xec56053a,
838 0x906adc48,
839 0xd4fe9763,
840 0x54c80dac,
841 };
842 comptime assert(@memberCount(@typeOf(x)) < rands.len);
843 return rands[@enumToInt(x)];
1037fn hashAny(x: var, comptime seed: u64) u32 {
1038 switch (@typeInfo(@typeOf(x))) {
1039 builtin.TypeId.Int => |info| {
1040 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
1041 const unsigned_x = @bitCast(@IntType(false, info.bits), x);
1042 if (info.bits <= 32) {
1043 return u32(unsigned_x) *% comptime rng.random.scalar(u32);
1044 } else {
1045 return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@typeOf(unsigned_x)));
1046 }
1047 },
1048 builtin.TypeId.Pointer => |info| {
1049 switch (info.size) {
1050 builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed),
1051 builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"),
1052 builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"),
1053 }
1054 },
1055 builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed),
1056 builtin.TypeId.Bool => {
1057 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
1058 const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) };
1059 return vals[@boolToInt(x)];
1060 },
1061 builtin.TypeId.Optional => {
1062 if (x) |non_opt| {
1063 return hashAny(non_opt, seed);
1064 } else {
1065 return hashAny(u32(1), seed);
1066 }
1067 },
1068 else => @compileError("implement hash function for " ++ @typeName(@typeOf(x))),
1069 }
8441070}
src/analyze.cpp+2-6
......@@ -3941,7 +3941,7 @@ AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) {
39413941 return nullptr;
39423942}
39433943
3944static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars) {
3944static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry) {
39453945 TypeTableEntry *fn_type = fn_table_entry->type_entry;
39463946 assert(!fn_type->data.fn.is_generic);
39473947 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
......@@ -3979,10 +3979,6 @@ static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entr
39793979 if (fn_type->data.fn.gen_param_info) {
39803980 var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
39813981 }
3982
3983 if (arg_vars) {
3984 arg_vars[i] = var;
3985 }
39863982 }
39873983}
39883984
......@@ -4082,7 +4078,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
40824078 if (!fn_table_entry->child_scope)
40834079 fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;
40844080
4085 define_local_param_variables(g, fn_table_entry, nullptr);
4081 define_local_param_variables(g, fn_table_entry);
40864082
40874083 TypeTableEntry *fn_type = fn_table_entry->type_entry;
40884084 assert(!fn_type->data.fn.is_generic);