Commit 7ec729b3ae

Veikka Tuominen <git@vexu.eu>
2023-05-27 23:48:09
aro-translate-c: move shared types to a common namespace
1 parent 31ecf75
Changed files (3)
src/translate_c/common.zig
@@ -0,0 +1,311 @@
+const std = @import("std");
+const ast = @import("ast.zig");
+const Node = ast.Node;
+const Tag = Node.Tag;
+
+const CallingConvention = std.builtin.CallingConvention;
+
+pub const Error = std.mem.Allocator.Error;
+pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
+pub const TypeError = Error || error{UnsupportedType};
+pub const TransError = TypeError || error{UnsupportedTranslation};
+
+pub const SymbolTable = std.StringArrayHashMap(Node);
+pub const AliasList = std.ArrayList(struct {
+    alias: []const u8,
+    name: []const u8,
+});
+
+pub const ResultUsed = enum {
+    used,
+    unused,
+};
+
+pub fn ScopeExtra(comptime Context: type, comptime Type: type) type {
+    return struct {
+        id: Id,
+        parent: ?*Scope,
+
+        const Scope = @This();
+
+        pub const Id = enum {
+            block,
+            root,
+            condition,
+            loop,
+            do_loop,
+        };
+
+        /// Used for the scope of condition expressions, for example `if (cond)`.
+        /// The block is lazily initialised because it is only needed for rare
+        /// cases of comma operators being used.
+        pub const Condition = struct {
+            base: Scope,
+            block: ?Block = null,
+
+            pub fn getBlockScope(self: *Condition, c: *Context) !*Block {
+                if (self.block) |*b| return b;
+                self.block = try Block.init(c, &self.base, true);
+                return &self.block.?;
+            }
+
+            pub fn deinit(self: *Condition) void {
+                if (self.block) |*b| b.deinit();
+            }
+        };
+
+        /// Represents an in-progress Node.Block. This struct is stack-allocated.
+        /// When it is deinitialized, it produces an Node.Block which is allocated
+        /// into the main arena.
+        pub const Block = struct {
+            base: Scope,
+            statements: std.ArrayList(Node),
+            variables: AliasList,
+            mangle_count: u32 = 0,
+            label: ?[]const u8 = null,
+
+            /// By default all variables are discarded, since we do not know in advance if they
+            /// will be used. This maps the variable's name to the Discard payload, so that if
+            /// the variable is subsequently referenced we can indicate that the discard should
+            /// be skipped during the intermediate AST -> Zig AST render step.
+            variable_discards: std.StringArrayHashMap(*ast.Payload.Discard),
+
+            /// When the block corresponds to a function, keep track of the return type
+            /// so that the return expression can be cast, if necessary
+            return_type: ?Type = null,
+
+            /// C static local variables are wrapped in a block-local struct. The struct
+            /// is named after the (mangled) variable name, the Zig variable within the
+            /// struct itself is given this name.
+            pub const static_inner_name = "static";
+
+            pub fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
+                var blk = Block{
+                    .base = .{
+                        .id = .block,
+                        .parent = parent,
+                    },
+                    .statements = std.ArrayList(Node).init(c.gpa),
+                    .variables = AliasList.init(c.gpa),
+                    .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa),
+                };
+                if (labeled) {
+                    blk.label = try blk.makeMangledName(c, "blk");
+                }
+                return blk;
+            }
+
+            pub fn deinit(self: *Block) void {
+                self.statements.deinit();
+                self.variables.deinit();
+                self.variable_discards.deinit();
+                self.* = undefined;
+            }
+
+            pub fn complete(self: *Block, c: *Context) !Node {
+                if (self.base.parent.?.id == .do_loop) {
+                    // We reserve 1 extra statement if the parent is a do_loop. This is in case of
+                    // do while, we want to put `if (cond) break;` at the end.
+                    const alloc_len = self.statements.items.len + @intFromBool(self.base.parent.?.id == .do_loop);
+                    var stmts = try c.arena.alloc(Node, alloc_len);
+                    stmts.len = self.statements.items.len;
+                    @memcpy(stmts[0..self.statements.items.len], self.statements.items);
+                    return Tag.block.create(c.arena, .{
+                        .label = self.label,
+                        .stmts = stmts,
+                    });
+                }
+                if (self.statements.items.len == 0) return Tag.empty_block.init();
+                return Tag.block.create(c.arena, .{
+                    .label = self.label,
+                    .stmts = try c.arena.dupe(Node, self.statements.items),
+                });
+            }
+
+            /// Given the desired name, return a name that does not shadow anything from outer scopes.
+            /// Inserts the returned name into the scope.
+            /// The name will not be visible to callers of getAlias.
+            pub fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
+                return scope.createMangledName(c, name, true);
+            }
+
+            /// Same as reserveMangledName, but enables the alias immediately.
+            pub fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
+                return scope.createMangledName(c, name, false);
+            }
+
+            pub fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 {
+                const name_copy = try c.arena.dupe(u8, name);
+                var proposed_name = name_copy;
+                while (scope.contains(proposed_name)) {
+                    scope.mangle_count += 1;
+                    proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count });
+                }
+                const new_mangle = try scope.variables.addOne();
+                if (reservation) {
+                    new_mangle.* = .{ .name = name_copy, .alias = name_copy };
+                } else {
+                    new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
+                }
+                return proposed_name;
+            }
+
+            pub fn getAlias(scope: *Block, name: []const u8) []const u8 {
+                for (scope.variables.items) |p| {
+                    if (std.mem.eql(u8, p.name, name))
+                        return p.alias;
+                }
+                return scope.base.parent.?.getAlias(name);
+            }
+
+            pub fn localContains(scope: *Block, name: []const u8) bool {
+                for (scope.variables.items) |p| {
+                    if (std.mem.eql(u8, p.alias, name))
+                        return true;
+                }
+                return false;
+            }
+
+            pub fn contains(scope: *Block, name: []const u8) bool {
+                if (scope.localContains(name))
+                    return true;
+                return scope.base.parent.?.contains(name);
+            }
+
+            pub fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void {
+                const name_node = try Tag.identifier.create(c.arena, name);
+                const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = name_node });
+                try scope.statements.append(discard);
+                try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?);
+            }
+        };
+
+        pub const Root = struct {
+            base: Scope,
+            sym_table: SymbolTable,
+            macro_table: SymbolTable,
+            context: *Context,
+            nodes: std.ArrayList(Node),
+
+            pub fn init(c: *Context) Root {
+                return .{
+                    .base = .{
+                        .id = .root,
+                        .parent = null,
+                    },
+                    .sym_table = SymbolTable.init(c.gpa),
+                    .macro_table = SymbolTable.init(c.gpa),
+                    .context = c,
+                    .nodes = std.ArrayList(Node).init(c.gpa),
+                };
+            }
+
+            pub fn deinit(scope: *Root) void {
+                scope.sym_table.deinit();
+                scope.macro_table.deinit();
+                scope.nodes.deinit();
+            }
+
+            /// Check if the global scope contains this name, without looking into the "future", e.g.
+            /// ignore the preprocessed decl and macro names.
+            pub fn containsNow(scope: *Root, name: []const u8) bool {
+                return scope.sym_table.contains(name) or scope.macro_table.contains(name);
+            }
+
+            /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
+            pub fn contains(scope: *Root, name: []const u8) bool {
+                return scope.containsNow(name) or scope.context.global_names.contains(name) or scope.context.weak_global_names.contains(name);
+            }
+        };
+
+        pub fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
+            var scope = inner;
+            while (true) {
+                switch (scope.id) {
+                    .root => unreachable,
+                    .block => return @fieldParentPtr(Block, "base", scope),
+                    .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
+                    else => scope = scope.parent.?,
+                }
+            }
+        }
+
+        pub fn findBlockReturnType(inner: *Scope) Type {
+            var scope = inner;
+            while (true) {
+                switch (scope.id) {
+                    .root => unreachable,
+                    .block => {
+                        const block = @fieldParentPtr(Block, "base", scope);
+                        if (block.return_type) |ty| return ty;
+                        scope = scope.parent.?;
+                    },
+                    else => scope = scope.parent.?,
+                }
+            }
+        }
+
+        pub fn getAlias(scope: *Scope, name: []const u8) []const u8 {
+            return switch (scope.id) {
+                .root => return name,
+                .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
+                .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
+            };
+        }
+
+        pub fn contains(scope: *Scope, name: []const u8) bool {
+            return switch (scope.id) {
+                .root => @fieldParentPtr(Root, "base", scope).contains(name),
+                .block => @fieldParentPtr(Block, "base", scope).contains(name),
+                .loop, .do_loop, .condition => scope.parent.?.contains(name),
+            };
+        }
+
+        pub fn getBreakableScope(inner: *Scope) *Scope {
+            var scope = inner;
+            while (true) {
+                switch (scope.id) {
+                    .root => unreachable,
+                    .loop, .do_loop => return scope,
+                    else => scope = scope.parent.?,
+                }
+            }
+        }
+
+        /// Appends a node to the first block scope if inside a function, or to the root tree if not.
+        pub fn appendNode(inner: *Scope, node: Node) !void {
+            var scope = inner;
+            while (true) {
+                switch (scope.id) {
+                    .root => {
+                        const root = @fieldParentPtr(Root, "base", scope);
+                        return root.nodes.append(node);
+                    },
+                    .block => {
+                        const block = @fieldParentPtr(Block, "base", scope);
+                        return block.statements.append(node);
+                    },
+                    else => scope = scope.parent.?,
+                }
+            }
+        }
+
+        pub fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
+            var scope = inner;
+            while (true) {
+                switch (scope.id) {
+                    .root => return,
+                    .block => {
+                        const block = @fieldParentPtr(Block, "base", scope);
+                        if (block.variable_discards.get(name)) |discard| {
+                            discard.data.should_skip = true;
+                            return;
+                        }
+                    },
+                    else => {},
+                }
+                scope = scope.parent.?;
+            }
+        }
+    };
+}
src/aro_translate_c.zig
@@ -10,305 +10,23 @@ const Type = aro.Type;
 const ast = @import("translate_c/ast.zig");
 const ZigNode = ast.Node;
 const ZigTag = ZigNode.Tag;
+const common = @import("translate_c/common.zig");
+const Error = common.Error;
+const MacroProcessingError = common.MacroProcessingError;
+const TypeError = common.TypeError;
+const TransError = common.TransError;
+const SymbolTable = common.SymbolTable;
+const AliasList = common.AliasList;
+const ResultUsed = common.ResultUsed;
+const Scope = common.ScopeExtra(Context, Type);
 
-const Error = mem.Allocator.Error;
-const TransError = translate_c.TransError;
-const TypeError = translate_c.TypeError;
-const ResultUsed = translate_c.ResultUsed;
-const AliasList = translate_c.AliasList;
-const SymbolTable = translate_c.SymbolTable;
 pub const Compilation = aro.Compilation;
 
-const Scope = struct {
-    id: Id,
-    parent: ?*Scope,
-
-    const Id = enum {
-        block,
-        root,
-        condition,
-        loop,
-        do_loop,
-    };
-
-    /// Used for the scope of condition expressions, for example `if (cond)`.
-    /// The block is lazily initialised because it is only needed for rare
-    /// cases of comma operators being used.
-    const Condition = struct {
-        base: Scope,
-        block: ?Block = null,
-
-        fn getBlockScope(self: *Condition, c: *Context) !*Block {
-            if (self.block) |*b| return b;
-            self.block = try Block.init(c, &self.base, true);
-            return &self.block.?;
-        }
-
-        fn deinit(self: *Condition) void {
-            if (self.block) |*b| b.deinit();
-        }
-    };
-
-    /// Represents an in-progress ZigNode.Block. This struct is stack-allocated.
-    /// When it is deinitialized, it produces an ZigNode.Block which is allocated
-    /// into the main arena.
-    const Block = struct {
-        base: Scope,
-        statements: std.ArrayList(ZigNode),
-        variables: AliasList,
-        mangle_count: u32 = 0,
-        label: ?[]const u8 = null,
-
-        /// By default all variables are discarded, since we do not know in advance if they
-        /// will be used. This maps the variable's name to the Discard payload, so that if
-        /// the variable is subsequently referenced we can indicate that the discard should
-        /// be skipped during the intermediate AST -> Zig AST render step.
-        variable_discards: std.StringArrayHashMap(*ast.Payload.Discard),
-
-        /// When the block corresponds to a function, keep track of the return type
-        /// so that the return expression can be cast, if necessary
-        return_type: ?Type = null,
-
-        /// C static local variables are wrapped in a block-local struct. The struct
-        /// is named after the (mangled) variable name, the Zig variable within the
-        /// struct itself is given this name.
-        const StaticInnerName = "static";
-
-        fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
-            var blk = Block{
-                .base = .{
-                    .id = .block,
-                    .parent = parent,
-                },
-                .statements = std.ArrayList(ZigNode).init(c.gpa),
-                .variables = AliasList.init(c.gpa),
-                .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa),
-            };
-            if (labeled) {
-                blk.label = try blk.makeMangledName(c, "blk");
-            }
-            return blk;
-        }
-
-        fn deinit(self: *Block) void {
-            self.statements.deinit();
-            self.variables.deinit();
-            self.variable_discards.deinit();
-            self.* = undefined;
-        }
-
-        fn complete(self: *Block, c: *Context) !ZigNode {
-            if (self.base.parent.?.id == .do_loop) {
-                // We reserve 1 extra statement if the parent is a do_loop. This is in case of
-                // do while, we want to put `if (cond) break;` at the end.
-                const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .do_loop);
-                var stmts = try c.arena.alloc(ZigNode, alloc_len);
-                stmts.len = self.statements.items.len;
-                @memcpy(stmts[0..self.statements.items.len], self.statements.items);
-                return ZigTag.block.create(c.arena, .{
-                    .label = self.label,
-                    .stmts = stmts,
-                });
-            }
-            if (self.statements.items.len == 0) return ZigTag.empty_block.init();
-            return ZigTag.block.create(c.arena, .{
-                .label = self.label,
-                .stmts = try c.arena.dupe(ZigNode, self.statements.items),
-            });
-        }
-
-        /// Given the desired name, return a name that does not shadow anything from outer scopes.
-        /// Inserts the returned name into the scope.
-        /// The name will not be visible to callers of getAlias.
-        fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
-            return scope.createMangledName(c, name, true);
-        }
-
-        /// Same as reserveMangledName, but enables the alias immediately.
-        fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
-            return scope.createMangledName(c, name, false);
-        }
-
-        fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 {
-            const name_copy = try c.arena.dupe(u8, name);
-            var proposed_name = name_copy;
-            while (scope.contains(proposed_name)) {
-                scope.mangle_count += 1;
-                proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count });
-            }
-            const new_mangle = try scope.variables.addOne();
-            if (reservation) {
-                new_mangle.* = .{ .name = name_copy, .alias = name_copy };
-            } else {
-                new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
-            }
-            return proposed_name;
-        }
-
-        fn getAlias(scope: *Block, name: []const u8) []const u8 {
-            for (scope.variables.items) |p| {
-                if (mem.eql(u8, p.name, name))
-                    return p.alias;
-            }
-            return scope.base.parent.?.getAlias(name);
-        }
-
-        fn localContains(scope: *Block, name: []const u8) bool {
-            for (scope.variables.items) |p| {
-                if (mem.eql(u8, p.alias, name))
-                    return true;
-            }
-            return false;
-        }
-
-        fn contains(scope: *Block, name: []const u8) bool {
-            if (scope.localContains(name))
-                return true;
-            return scope.base.parent.?.contains(name);
-        }
-
-        fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void {
-            const name_node = try ZigTag.identifier.create(c.arena, name);
-            const discard = try ZigTag.discard.create(c.arena, .{ .should_skip = false, .value = name_node });
-            try scope.statements.append(discard);
-            try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?);
-        }
-    };
-
-    const Root = struct {
-        base: Scope,
-        sym_table: SymbolTable,
-        macro_table: SymbolTable,
-        context: *Context,
-        nodes: std.ArrayList(ZigNode),
-
-        fn init(c: *Context) Root {
-            return .{
-                .base = .{
-                    .id = .root,
-                    .parent = null,
-                },
-                .sym_table = SymbolTable.init(c.gpa),
-                .macro_table = SymbolTable.init(c.gpa),
-                .context = c,
-                .nodes = std.ArrayList(ZigNode).init(c.gpa),
-            };
-        }
-
-        fn deinit(scope: *Root) void {
-            scope.sym_table.deinit();
-            scope.macro_table.deinit();
-            scope.nodes.deinit();
-        }
-
-        /// Check if the global scope contains this name, without looking into the "future", e.g.
-        /// ignore the preprocessed decl and macro names.
-        fn containsNow(scope: *Root, name: []const u8) bool {
-            return scope.sym_table.contains(name) or scope.macro_table.contains(name);
-        }
-
-        /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
-        fn contains(scope: *Root, name: []const u8) bool {
-            return scope.containsNow(name) or scope.context.global_names.contains(name);
-        }
-    };
-
-    fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => unreachable,
-                .block => return @fieldParentPtr(Block, "base", scope),
-                .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
-                else => scope = scope.parent.?,
-            }
-        }
-    }
-
-    fn findBlockReturnType(inner: *Scope) Type {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => unreachable,
-                .block => {
-                    const block = @fieldParentPtr(Block, "base", scope);
-                    if (block.return_type) |qt| return qt;
-                    scope = scope.parent.?;
-                },
-                else => scope = scope.parent.?,
-            }
-        }
-    }
-
-    fn getAlias(scope: *Scope, name: []const u8) []const u8 {
-        return switch (scope.id) {
-            .root => return name,
-            .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
-            .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
-        };
-    }
-
-    fn contains(scope: *Scope, name: []const u8) bool {
-        return switch (scope.id) {
-            .root => @fieldParentPtr(Root, "base", scope).contains(name),
-            .block => @fieldParentPtr(Block, "base", scope).contains(name),
-            .loop, .do_loop, .condition => scope.parent.?.contains(name),
-        };
-    }
-
-    fn getBreakableScope(inner: *Scope) *Scope {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => unreachable,
-                .loop, .do_loop => return scope,
-                else => scope = scope.parent.?,
-            }
-        }
-    }
-
-    /// Appends a node to the first block scope if inside a function, or to the root tree if not.
-    fn appendNode(inner: *Scope, node: ZigNode) !void {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => {
-                    const root = @fieldParentPtr(Root, "base", scope);
-                    return root.nodes.append(node);
-                },
-                .block => {
-                    const block = @fieldParentPtr(Block, "base", scope);
-                    return block.statements.append(node);
-                },
-                else => scope = scope.parent.?,
-            }
-        }
-    }
-
-    fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => return,
-                .block => {
-                    const block = @fieldParentPtr(Block, "base", scope);
-                    if (block.variable_discards.get(name)) |discard| {
-                        discard.data.should_skip = true;
-                        return;
-                    }
-                },
-                else => {},
-            }
-            scope = scope.parent.?;
-        }
-    }
-};
-
 const Context = struct {
     gpa: mem.Allocator,
     arena: mem.Allocator,
     decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{},
-    alias_list: translate_c.AliasList,
+    alias_list: AliasList,
     global_scope: *Scope.Root,
     mangle_count: u32 = 0,
     /// Table of record decls that have been demoted to opaques.
@@ -429,7 +147,7 @@ pub fn translate(
     var context = Context{
         .gpa = gpa,
         .arena = arena,
-        .alias_list = translate_c.AliasList.init(gpa),
+        .alias_list = AliasList.init(gpa),
         .global_scope = try arena.create(Scope.Root),
         .pattern_list = try translate_c.PatternList.init(gpa),
         .comp = comp,
src/translate_c.zig
@@ -7,312 +7,24 @@ const CToken = std.c.Token;
 const mem = std.mem;
 const math = std.math;
 const meta = std.meta;
+const CallingConvention = std.builtin.CallingConvention;
 const ast = @import("translate_c/ast.zig");
 const Node = ast.Node;
 const Tag = Node.Tag;
-
-const CallingConvention = std.builtin.CallingConvention;
-
-pub const Error = std.mem.Allocator.Error;
-pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
-pub const TypeError = Error || error{UnsupportedType};
-pub const TransError = TypeError || error{UnsupportedTranslation};
-
-pub const SymbolTable = std.StringArrayHashMap(Node);
-pub const AliasList = std.ArrayList(struct {
-    alias: []const u8,
-    name: []const u8,
-});
+const common = @import("translate_c/common.zig");
+const Error = common.Error;
+const MacroProcessingError = common.MacroProcessingError;
+const TypeError = common.TypeError;
+const TransError = common.TransError;
+const SymbolTable = common.SymbolTable;
+const AliasList = common.AliasList;
+const ResultUsed = common.ResultUsed;
+const Scope = common.ScopeExtra(Context, clang.QualType);
 
 // Maps macro parameter names to token position, for determining if different
 // identifiers refer to the same positional argument in different macros.
 const ArgsPositionMap = std.StringArrayHashMapUnmanaged(usize);
 
-const Scope = struct {
-    id: Id,
-    parent: ?*Scope,
-
-    const Id = enum {
-        block,
-        root,
-        condition,
-        loop,
-        do_loop,
-    };
-
-    /// Used for the scope of condition expressions, for example `if (cond)`.
-    /// The block is lazily initialised because it is only needed for rare
-    /// cases of comma operators being used.
-    const Condition = struct {
-        base: Scope,
-        block: ?Block = null,
-
-        fn getBlockScope(self: *Condition, c: *Context) !*Block {
-            if (self.block) |*b| return b;
-            self.block = try Block.init(c, &self.base, true);
-            return &self.block.?;
-        }
-
-        fn deinit(self: *Condition) void {
-            if (self.block) |*b| b.deinit();
-        }
-    };
-
-    /// Represents an in-progress Node.Block. This struct is stack-allocated.
-    /// When it is deinitialized, it produces an Node.Block which is allocated
-    /// into the main arena.
-    const Block = struct {
-        base: Scope,
-        statements: std.ArrayList(Node),
-        variables: AliasList,
-        mangle_count: u32 = 0,
-        label: ?[]const u8 = null,
-
-        /// By default all variables are discarded, since we do not know in advance if they
-        /// will be used. This maps the variable's name to the Discard payload, so that if
-        /// the variable is subsequently referenced we can indicate that the discard should
-        /// be skipped during the intermediate AST -> Zig AST render step.
-        variable_discards: std.StringArrayHashMap(*ast.Payload.Discard),
-
-        /// When the block corresponds to a function, keep track of the return type
-        /// so that the return expression can be cast, if necessary
-        return_type: ?clang.QualType = null,
-
-        /// C static local variables are wrapped in a block-local struct. The struct
-        /// is named after the (mangled) variable name, the Zig variable within the
-        /// struct itself is given this name.
-        const StaticInnerName = "static";
-
-        fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
-            var blk = Block{
-                .base = .{
-                    .id = .block,
-                    .parent = parent,
-                },
-                .statements = std.ArrayList(Node).init(c.gpa),
-                .variables = AliasList.init(c.gpa),
-                .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa),
-            };
-            if (labeled) {
-                blk.label = try blk.makeMangledName(c, "blk");
-            }
-            return blk;
-        }
-
-        fn deinit(self: *Block) void {
-            self.statements.deinit();
-            self.variables.deinit();
-            self.variable_discards.deinit();
-            self.* = undefined;
-        }
-
-        fn complete(self: *Block, c: *Context) !Node {
-            if (self.base.parent.?.id == .do_loop) {
-                // We reserve 1 extra statement if the parent is a do_loop. This is in case of
-                // do while, we want to put `if (cond) break;` at the end.
-                const alloc_len = self.statements.items.len + @intFromBool(self.base.parent.?.id == .do_loop);
-                var stmts = try c.arena.alloc(Node, alloc_len);
-                stmts.len = self.statements.items.len;
-                @memcpy(stmts[0..self.statements.items.len], self.statements.items);
-                return Tag.block.create(c.arena, .{
-                    .label = self.label,
-                    .stmts = stmts,
-                });
-            }
-            if (self.statements.items.len == 0) return Tag.empty_block.init();
-            return Tag.block.create(c.arena, .{
-                .label = self.label,
-                .stmts = try c.arena.dupe(Node, self.statements.items),
-            });
-        }
-
-        /// Given the desired name, return a name that does not shadow anything from outer scopes.
-        /// Inserts the returned name into the scope.
-        /// The name will not be visible to callers of getAlias.
-        fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
-            return scope.createMangledName(c, name, true);
-        }
-
-        /// Same as reserveMangledName, but enables the alias immediately.
-        fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
-            return scope.createMangledName(c, name, false);
-        }
-
-        fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 {
-            const name_copy = try c.arena.dupe(u8, name);
-            var proposed_name = name_copy;
-            while (scope.contains(proposed_name)) {
-                scope.mangle_count += 1;
-                proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count });
-            }
-            const new_mangle = try scope.variables.addOne();
-            if (reservation) {
-                new_mangle.* = .{ .name = name_copy, .alias = name_copy };
-            } else {
-                new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
-            }
-            return proposed_name;
-        }
-
-        fn getAlias(scope: *Block, name: []const u8) []const u8 {
-            for (scope.variables.items) |p| {
-                if (mem.eql(u8, p.name, name))
-                    return p.alias;
-            }
-            return scope.base.parent.?.getAlias(name);
-        }
-
-        fn localContains(scope: *Block, name: []const u8) bool {
-            for (scope.variables.items) |p| {
-                if (mem.eql(u8, p.alias, name))
-                    return true;
-            }
-            return false;
-        }
-
-        fn contains(scope: *Block, name: []const u8) bool {
-            if (scope.localContains(name))
-                return true;
-            return scope.base.parent.?.contains(name);
-        }
-
-        fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void {
-            const name_node = try Tag.identifier.create(c.arena, name);
-            const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = name_node });
-            try scope.statements.append(discard);
-            try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?);
-        }
-    };
-
-    const Root = struct {
-        base: Scope,
-        sym_table: SymbolTable,
-        macro_table: SymbolTable,
-        context: *Context,
-        nodes: std.ArrayList(Node),
-
-        fn init(c: *Context) Root {
-            return .{
-                .base = .{
-                    .id = .root,
-                    .parent = null,
-                },
-                .sym_table = SymbolTable.init(c.gpa),
-                .macro_table = SymbolTable.init(c.gpa),
-                .context = c,
-                .nodes = std.ArrayList(Node).init(c.gpa),
-            };
-        }
-
-        fn deinit(scope: *Root) void {
-            scope.sym_table.deinit();
-            scope.macro_table.deinit();
-            scope.nodes.deinit();
-        }
-
-        /// Check if the global scope contains this name, without looking into the "future", e.g.
-        /// ignore the preprocessed decl and macro names.
-        fn containsNow(scope: *Root, name: []const u8) bool {
-            return scope.sym_table.contains(name) or scope.macro_table.contains(name);
-        }
-
-        /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
-        fn contains(scope: *Root, name: []const u8) bool {
-            return scope.containsNow(name) or scope.context.global_names.contains(name) or scope.context.weak_global_names.contains(name);
-        }
-    };
-
-    fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => unreachable,
-                .block => return @fieldParentPtr(Block, "base", scope),
-                .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
-                else => scope = scope.parent.?,
-            }
-        }
-    }
-
-    fn findBlockReturnType(inner: *Scope) clang.QualType {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => unreachable,
-                .block => {
-                    const block = @fieldParentPtr(Block, "base", scope);
-                    if (block.return_type) |qt| return qt;
-                    scope = scope.parent.?;
-                },
-                else => scope = scope.parent.?,
-            }
-        }
-    }
-
-    fn getAlias(scope: *Scope, name: []const u8) []const u8 {
-        return switch (scope.id) {
-            .root => return name,
-            .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
-            .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
-        };
-    }
-
-    fn contains(scope: *Scope, name: []const u8) bool {
-        return switch (scope.id) {
-            .root => @fieldParentPtr(Root, "base", scope).contains(name),
-            .block => @fieldParentPtr(Block, "base", scope).contains(name),
-            .loop, .do_loop, .condition => scope.parent.?.contains(name),
-        };
-    }
-
-    fn getBreakableScope(inner: *Scope) *Scope {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => unreachable,
-                .loop, .do_loop => return scope,
-                else => scope = scope.parent.?,
-            }
-        }
-    }
-
-    /// Appends a node to the first block scope if inside a function, or to the root tree if not.
-    fn appendNode(inner: *Scope, node: Node) !void {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => {
-                    const root = @fieldParentPtr(Root, "base", scope);
-                    return root.nodes.append(node);
-                },
-                .block => {
-                    const block = @fieldParentPtr(Block, "base", scope);
-                    return block.statements.append(node);
-                },
-                else => scope = scope.parent.?,
-            }
-        }
-    }
-
-    fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
-        var scope = inner;
-        while (true) {
-            switch (scope.id) {
-                .root => return,
-                .block => {
-                    const block = @fieldParentPtr(Block, "base", scope);
-                    if (block.variable_discards.get(name)) |discard| {
-                        discard.data.should_skip = true;
-                        return;
-                    }
-                },
-                else => {},
-            }
-            scope = scope.parent.?;
-        }
-    }
-};
-
 pub const Context = struct {
     gpa: mem.Allocator,
     arena: mem.Allocator,
@@ -829,7 +541,7 @@ fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType,
 ///     var static = S.*;
 /// }).static;
 fn stringLiteralToCharStar(c: *Context, str: Node) Error!Node {
-    const var_name = Scope.Block.StaticInnerName;
+    const var_name = Scope.Block.static_inner_name;
 
     const variables = try c.arena.alloc(Node, 1);
     variables[0] = try Tag.mut_str.create(c.arena, .{ .name = var_name, .init = str });
@@ -1423,11 +1135,6 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E
     }
 }
 
-pub const ResultUsed = enum {
-    used,
-    unused,
-};
-
 fn transStmt(
     c: *Context,
     scope: *Scope,
@@ -2070,7 +1777,7 @@ fn transDeclStmtOne(
                 init_node = try removeCVQualifiers(c, dst_type_node, init_node);
             }
 
-            const var_name: []const u8 = if (is_static_local) Scope.Block.StaticInnerName else mangled_name;
+            const var_name: []const u8 = if (is_static_local) Scope.Block.static_inner_name else mangled_name;
             var node = try Tag.var_decl.create(c.arena, .{
                 .is_pub = false,
                 .is_const = is_const,
@@ -2153,7 +1860,7 @@ fn transDeclRefExpr(
         if (var_decl.isStaticLocal()) {
             ref_expr = try Tag.field_access.create(c.arena, .{
                 .lhs = ref_expr,
-                .field_name = Scope.Block.StaticInnerName,
+                .field_name = Scope.Block.static_inner_name,
             });
         }
     }