diff --git a/BRANCH_TODO b/BRANCH_TODO index 76b4f7e1aa..4df72d8315 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -1,5 +1,13 @@ - * structs, unions, enums, etc get weird names such as "Point__anon_22" rather - than "Point". + * The next problem is that when trying to deinitialize everything, when we + deinit a Decl that is the owner of a Namespace, there may still be other Decl + objects that reference that Namespace. They want to check if they are the owner + in order to find out if they should destroy it. But they can't check if they are + the owner because the owner_decl field is destroyed. + So there's a memory management problem to solve. We could easily solve this with + ref counting or whatever but the goal is to not introduce extra overhead / unnecessary + fields just to help figure out how to free stuff. So come up with some way to make + this sound, and easily debuggable when something goes wrong. + * get stage2 tests passing * modify stage2 tests so that only 1 uses _start and the rest use pub fn main diff --git a/src/Module.zig b/src/Module.zig index 434e74ca2f..7860f9141c 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3759,17 +3759,19 @@ pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, b } } -pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl { +/// Takes ownership of `name` even if it returns an error. +pub fn createAnonymousDeclNamed( + mod: *Module, + scope: *Scope, + typed_value: TypedValue, + name: [:0]u8, +) !*Decl { + errdefer mod.gpa.free(name); + const scope_decl = scope.ownerDecl().?; const namespace = scope_decl.namespace; try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1); - const name_index = mod.getNextAnonNameIndex(); - const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ - scope_decl.name, name_index, - }); - errdefer mod.gpa.free(name); - const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node); new_decl.name = name; @@ -3793,7 +3795,16 @@ pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) return new_decl; } -fn getNextAnonNameIndex(mod: *Module) usize { +pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl { + const scope_decl = scope.ownerDecl().?; + const name_index = mod.getNextAnonNameIndex(); + const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ + scope_decl.name, name_index, + }); + return mod.createAnonymousDeclNamed(scope, typed_value, name); +} + +pub fn getNextAnonNameIndex(mod: *Module) usize { return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); } diff --git a/src/Sema.zig b/src/Sema.zig index 07fb4b4cd3..35248d8f8b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -719,10 +719,11 @@ fn zirStructDecl( const struct_obj = try new_decl_arena.allocator.create(Module.Struct); const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj); const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty); - const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ + const type_name = try sema.createTypeName(block, small.name_strategy); + const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{ .ty = Type.initTag(.type), .val = struct_val, - }); + }, type_name); struct_obj.* = .{ .owner_decl = new_decl, .fields = .{}, @@ -744,6 +745,32 @@ fn zirStructDecl( return sema.analyzeDeclVal(block, src, new_decl); } +fn createTypeName(sema: *Sema, block: *Scope.Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 { + switch (name_strategy) { + .anon => { + // It would be neat to have "struct:line:column" but this name has + // to survive incremental updates, where it may have been shifted down + // or up to a different line, but unchanged, and thus not unnecessarily + // semantically analyzed. + const name_index = sema.mod.getNextAnonNameIndex(); + return std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ + sema.owner_decl.name, name_index, + }); + }, + .parent => return sema.gpa.dupeZ(u8, mem.spanZ(sema.owner_decl.name)), + .func => { + const name_index = sema.mod.getNextAnonNameIndex(); + const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ + sema.owner_decl.name, name_index, + }); + log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{ + name, + }); + return name; + }, + } +} + fn zirEnumDecl( sema: *Sema, block: *Scope.Block, @@ -807,10 +834,11 @@ fn zirEnumDecl( }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty); - const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ + const type_name = try sema.createTypeName(block, small.name_strategy); + const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{ .ty = Type.initTag(.type), .val = enum_val, - }); + }, type_name); enum_obj.* = .{ .owner_decl = new_decl, .tag_ty = tag_ty, @@ -957,10 +985,11 @@ fn zirUnionDecl( const union_obj = try new_decl_arena.allocator.create(Module.Union); const union_ty = try Type.Tag.@"union".create(&new_decl_arena.allocator, union_obj); const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty); - const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ + const type_name = try sema.createTypeName(block, small.name_strategy); + const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{ .ty = Type.initTag(.type), .val = union_val, - }); + }, type_name); union_obj.* = .{ .owner_decl = new_decl, .tag_ty = Type.initTag(.@"null"), @@ -1021,10 +1050,11 @@ fn zirErrorSetDecl( const error_set = try new_decl_arena.allocator.create(Module.ErrorSet); const error_set_ty = try Type.Tag.error_set.create(&new_decl_arena.allocator, error_set); const error_set_val = try Value.Tag.ty.create(&new_decl_arena.allocator, error_set_ty); - const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ + const type_name = try sema.createTypeName(block, name_strategy); + const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{ .ty = Type.initTag(.type), .val = error_set_val, - }); + }, type_name); const names = try new_decl_arena.allocator.alloc([]const u8, fields.len); for (fields) |str_index, i| { names[i] = try new_decl_arena.allocator.dupe(u8, sema.code.nullTerminatedString(str_index));