| ... | @@ -4083,6 +4083,9 @@ pub fn scanNamespace( | ... | @@ -4083,6 +4083,9 @@ pub fn scanNamespace( |
| 4083 | const gpa = zcu.gpa; | 4083 | const gpa = zcu.gpa; |
| 4084 | const namespace = zcu.namespacePtr(namespace_index); | 4084 | const namespace = zcu.namespacePtr(namespace_index); |
| 4085 | | 4085 | |
| | 4086 | var seen_decls: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; |
| | 4087 | defer seen_decls.deinit(gpa); |
| | 4088 | |
| 4086 | try zcu.comp.work_queue.ensureUnusedCapacity(decls.len); | 4089 | try zcu.comp.work_queue.ensureUnusedCapacity(decls.len); |
| 4087 | try namespace.decls.ensureTotalCapacity(gpa, decls.len); | 4090 | try namespace.decls.ensureTotalCapacity(gpa, decls.len); |
| 4088 | | 4091 | |
| ... | @@ -4090,19 +4093,44 @@ pub fn scanNamespace( | ... | @@ -4090,19 +4093,44 @@ pub fn scanNamespace( |
| 4090 | .zcu = zcu, | 4093 | .zcu = zcu, |
| 4091 | .namespace_index = namespace_index, | 4094 | .namespace_index = namespace_index, |
| 4092 | .parent_decl = parent_decl, | 4095 | .parent_decl = parent_decl, |
| | 4096 | .seen_decls = &seen_decls, |
| | 4097 | .pass = .named, |
| 4093 | }; | 4098 | }; |
| 4094 | for (decls) |decl_inst| { | 4099 | for (decls) |decl_inst| { |
| 4095 | try scanDecl(&scan_decl_iter, decl_inst); | 4100 | try scanDecl(&scan_decl_iter, decl_inst); |
| 4096 | } | 4101 | } |
| | 4102 | scan_decl_iter.pass = .unnamed; |
| | 4103 | for (decls) |decl_inst| { |
| | 4104 | try scanDecl(&scan_decl_iter, decl_inst); |
| | 4105 | } |
| 4097 | } | 4106 | } |
| 4098 | | 4107 | |
| 4099 | const ScanDeclIter = struct { | 4108 | const ScanDeclIter = struct { |
| 4100 | zcu: *Zcu, | 4109 | zcu: *Zcu, |
| 4101 | namespace_index: Namespace.Index, | 4110 | namespace_index: Namespace.Index, |
| 4102 | parent_decl: *Decl, | 4111 | parent_decl: *Decl, |
| | 4112 | seen_decls: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void), |
| | 4113 | /// Decl scanning is run in two passes, so that we can detect when a generated |
| | 4114 | /// name would clash with an explicit name and use a different one. |
| | 4115 | pass: enum { named, unnamed }, |
| 4103 | usingnamespace_index: usize = 0, | 4116 | usingnamespace_index: usize = 0, |
| 4104 | comptime_index: usize = 0, | 4117 | comptime_index: usize = 0, |
| 4105 | unnamed_test_index: usize = 0, | 4118 | unnamed_test_index: usize = 0, |
| | 4119 | |
| | 4120 | fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString { |
| | 4121 | const zcu = iter.zcu; |
| | 4122 | const gpa = zcu.gpa; |
| | 4123 | const ip = &zcu.intern_pool; |
| | 4124 | var name = try ip.getOrPutStringFmt(gpa, fmt, args); |
| | 4125 | var gop = try iter.seen_decls.getOrPut(gpa, name); |
| | 4126 | var next_suffix: u32 = 0; |
| | 4127 | while (gop.found_existing) { |
| | 4128 | name = try ip.getOrPutStringFmt(gpa, fmt ++ "_{d}", args ++ .{next_suffix}); |
| | 4129 | gop = try iter.seen_decls.getOrPut(gpa, name); |
| | 4130 | next_suffix += 1; |
| | 4131 | } |
| | 4132 | return name; |
| | 4133 | } |
| 4106 | }; | 4134 | }; |
| 4107 | | 4135 | |
| 4108 | fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void { | 4136 | fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void { |
| ... | @@ -4126,54 +4154,63 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void | ... | @@ -4126,54 +4154,63 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void |
| 4126 | // Every Decl needs a name. | 4154 | // Every Decl needs a name. |
| 4127 | const decl_name: InternPool.NullTerminatedString, const kind: Decl.Kind, const is_named_test: bool = switch (declaration.name) { | 4155 | const decl_name: InternPool.NullTerminatedString, const kind: Decl.Kind, const is_named_test: bool = switch (declaration.name) { |
| 4128 | .@"comptime" => info: { | 4156 | .@"comptime" => info: { |
| | 4157 | if (iter.pass != .unnamed) return; |
| 4129 | const i = iter.comptime_index; | 4158 | const i = iter.comptime_index; |
| 4130 | iter.comptime_index += 1; | 4159 | iter.comptime_index += 1; |
| 4131 | // TODO: avoid collisions with named decls with this name | | |
| 4132 | break :info .{ | 4160 | break :info .{ |
| 4133 | try ip.getOrPutStringFmt(gpa, "comptime_{d}", .{i}), | 4161 | try iter.avoidNameConflict("comptime_{d}", .{i}), |
| 4134 | .@"comptime", | 4162 | .@"comptime", |
| 4135 | false, | 4163 | false, |
| 4136 | }; | 4164 | }; |
| 4137 | }, | 4165 | }, |
| 4138 | .@"usingnamespace" => info: { | 4166 | .@"usingnamespace" => info: { |
| | 4167 | if (iter.pass != .unnamed) return; |
| 4139 | const i = iter.usingnamespace_index; | 4168 | const i = iter.usingnamespace_index; |
| 4140 | iter.usingnamespace_index += 1; | 4169 | iter.usingnamespace_index += 1; |
| 4141 | // TODO: avoid collisions with named decls with this name | | |
| 4142 | break :info .{ | 4170 | break :info .{ |
| 4143 | try ip.getOrPutStringFmt(gpa, "usingnamespace_{d}", .{i}), | 4171 | try iter.avoidNameConflict("usingnamespace_{d}", .{i}), |
| 4144 | .@"usingnamespace", | 4172 | .@"usingnamespace", |
| 4145 | false, | 4173 | false, |
| 4146 | }; | 4174 | }; |
| 4147 | }, | 4175 | }, |
| 4148 | .unnamed_test => info: { | 4176 | .unnamed_test => info: { |
| | 4177 | if (iter.pass != .unnamed) return; |
| 4149 | const i = iter.unnamed_test_index; | 4178 | const i = iter.unnamed_test_index; |
| 4150 | iter.unnamed_test_index += 1; | 4179 | iter.unnamed_test_index += 1; |
| 4151 | // TODO: avoid collisions with named decls with this name | | |
| 4152 | break :info .{ | 4180 | break :info .{ |
| 4153 | try ip.getOrPutStringFmt(gpa, "test_{d}", .{i}), | 4181 | try iter.avoidNameConflict("test_{d}", .{i}), |
| 4154 | .@"test", | 4182 | .@"test", |
| 4155 | false, | 4183 | false, |
| 4156 | }; | 4184 | }; |
| 4157 | }, | 4185 | }, |
| 4158 | .decltest => info: { | 4186 | .decltest => info: { |
| | 4187 | // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary. |
| | 4188 | if (iter.pass != .unnamed) return; |
| 4159 | assert(declaration.flags.has_doc_comment); | 4189 | assert(declaration.flags.has_doc_comment); |
| 4160 | const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end])); | 4190 | const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end])); |
| 4161 | // TODO: avoid collisions with named decls with this name | | |
| 4162 | break :info .{ | 4191 | break :info .{ |
| 4163 | try ip.getOrPutStringFmt(gpa, "decltest.{s}", .{name}), | 4192 | try iter.avoidNameConflict("decltest.{s}", .{name}), |
| 4164 | .@"test", | 4193 | .@"test", |
| 4165 | true, | 4194 | true, |
| 4166 | }; | 4195 | }; |
| 4167 | }, | 4196 | }, |
| 4168 | _ => if (declaration.name.isNamedTest(zir)) .{ | 4197 | _ => if (declaration.name.isNamedTest(zir)) info: { |
| 4169 | // TODO: avoid collisions with named decls with this name | 4198 | // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary. |
| 4170 | try ip.getOrPutStringFmt(gpa, "test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}), | 4199 | if (iter.pass != .unnamed) return; |
| 4171 | .@"test", | 4200 | break :info .{ |
| 4172 | true, | 4201 | try iter.avoidNameConflict("test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}), |
| 4173 | } else .{ | 4202 | .@"test", |
| 4174 | try ip.getOrPutString(gpa, zir.nullTerminatedString(declaration.name.toString(zir).?)), | 4203 | true, |
| 4175 | .named, | 4204 | }; |
| 4176 | false, | 4205 | } else info: { |
| | 4206 | if (iter.pass != .named) return; |
| | 4207 | const name = try ip.getOrPutString(gpa, zir.nullTerminatedString(declaration.name.toString(zir).?)); |
| | 4208 | try iter.seen_decls.putNoClobber(gpa, name, {}); |
| | 4209 | break :info .{ |
| | 4210 | name, |
| | 4211 | .named, |
| | 4212 | false, |
| | 4213 | }; |
| 4177 | }, | 4214 | }, |
| 4178 | }; | 4215 | }; |
| 4179 | | 4216 | |