Commit 5cd9afc6b6

Andrew Kelley <andrew@ziglang.org>
2021-05-08 23:34:30
stage2: fully qualified names and better anonymous names
* no more global atomic variable for anonymous decl indexes. Instead the Namespace decl table is used to figure out anonymous decl names. - This prepares for better multi-threaded semantic analysis in the future, with no contention on this global atomic integer. * implement fully qualified names for namespaces.
1 parent aa0352f
Changed files (1)
src/Module.zig
@@ -75,8 +75,6 @@ failed_files: std.AutoArrayHashMapUnmanaged(*Scope.File, ?*ErrorMsg) = .{},
 /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
 failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
 
-next_anon_name_index: usize = 0,
-
 /// Candidates for deletion. After a semantic analysis update completes, this list
 /// contains Decls that need to be deleted if they end up having no references to them.
 deletion_set: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
@@ -912,9 +910,22 @@ pub const Scope = struct {
             _ = ns.decls.orderedRemove(mem.spanZ(child.name));
         }
 
-        pub fn renderFullyQualifiedName(ns: Namespace, name: []const u8, writer: anytype) !void {
-            // TODO this should render e.g. "std.fs.Dir.OpenOptions"
-            return writer.writeAll(name);
+        // This renders e.g. "std.fs.Dir.OpenOptions"
+        pub fn renderFullyQualifiedName(
+            ns: Namespace,
+            name: []const u8,
+            writer: anytype,
+        ) @TypeOf(writer).Error!void {
+            if (ns.parent) |parent| {
+                const decl = ns.getDecl();
+                try parent.renderFullyQualifiedName(mem.spanZ(decl.name), writer);
+            } else {
+                try ns.file_scope.renderFullyQualifiedName(writer);
+            }
+            if (name.len != 0) {
+                try writer.writeAll(".");
+                try writer.writeAll(name);
+            }
         }
 
         pub fn getDecl(ns: Namespace) *Decl {
@@ -1054,16 +1065,21 @@ pub const Scope = struct {
             gpa.destroy(file);
         }
 
-        pub fn fullyQualifiedNameZ(file: File, gpa: *Allocator) ![:0]u8 {
+        pub fn renderFullyQualifiedName(file: File, writer: anytype) !void {
             // Convert all the slashes into dots and truncate the extension.
             const ext = std.fs.path.extension(file.sub_file_path);
             const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len];
-            const duped = try gpa.dupeZ(u8, noext);
-            for (duped) |*byte| switch (byte.*) {
-                '/', '\\' => byte.* = '.',
-                else => continue,
+            for (noext) |byte| switch (byte) {
+                '/', '\\' => try writer.writeByte('.'),
+                else => try writer.writeByte(byte),
             };
-            return duped;
+        }
+
+        pub fn fullyQualifiedNameZ(file: File, gpa: *Allocator) ![:0]u8 {
+            var buf = std.ArrayList(u8).init(gpa);
+            defer buf.deinit();
+            try file.renderFullyQualifiedName(buf.writer());
+            return buf.toOwnedSliceSentinel(0);
         }
 
         pub fn dumpSrc(file: *File, src: LazySrcLoc) void {
@@ -3723,17 +3739,34 @@ pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, b
 }
 
 pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl {
-    const name_index = mod.getNextAnonNameIndex();
     const scope_decl = scope.ownerDecl().?;
     const namespace = scope_decl.namespace;
-    try namespace.decls.ensureCapacity(mod.gpa, namespace.decls.count() + 1);
-    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);
-    namespace.decls.putAssumeCapacityNoClobber(name, new_decl);
+    try namespace.decls.ensureUnusedCapacity(mod.gpa, 1);
+
+    // Find a unique name for the anon decl.
+    var name_buf = std.ArrayList(u8).init(mod.gpa);
+    defer name_buf.deinit();
+
+    try name_buf.appendSlice(mem.spanZ(scope_decl.name));
+    var name_index: usize = namespace.decls.count();
+
+    const new_decl = while (true) {
+        const gop = namespace.decls.getOrPutAssumeCapacity(name_buf.items);
+        if (!gop.found_existing) {
+            const name = try name_buf.toOwnedSliceSentinel(0);
+            const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node);
+            new_decl.name = name;
+            gop.entry.key = name;
+            gop.entry.value = new_decl;
+            break gop.entry.value;
+        }
+
+        name_buf.clearRetainingCapacity();
+        try name_buf.writer().print("{s}__anon_{d}", .{ scope_decl.name, name_index });
+        name_index += 1;
+    } else unreachable; // TODO should not need else unreachable on while(true)
 
     new_decl.src_line = scope_decl.src_line;
-    new_decl.name = name;
     new_decl.ty = typed_value.ty;
     new_decl.val = typed_value.val;
     new_decl.has_tv = true;
@@ -3751,10 +3784,6 @@ pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue)
     return new_decl;
 }
 
-fn getNextAnonNameIndex(mod: *Module) usize {
-    return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic);
-}
-
 /// This looks up a bare identifier in the given scope. This will walk up the tree of namespaces
 /// in scope and check each one for the identifier.
 /// TODO emit a compile error if more than one decl would be matched.