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.
This commit is contained in:
Andrew Kelley 2021-05-08 14:34:30 -07:00
parent aa0352fad6
commit 5cd9afc6b6

View File

@ -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.