diff options
Diffstat (limited to 'src-self-hosted/scope.zig')
| -rw-r--r-- | src-self-hosted/scope.zig | 392 |
1 files changed, 386 insertions, 6 deletions
diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 05e586daae..a38e765c6e 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -1,16 +1,396 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Allocator = mem.Allocator; +const Decl = @import("decl.zig").Decl; +const Compilation = @import("compilation.zig").Compilation; +const mem = std.mem; +const ast = std.zig.ast; +const Value = @import("value.zig").Value; +const Type = @import("type.zig").Type; +const ir = @import("ir.zig"); +const Span = @import("errmsg.zig").Span; +const assert = std.debug.assert; +const event = std.event; +const llvm = @import("llvm.zig"); + pub const Scope = struct { id: Id, - parent: &Scope, + parent: ?*Scope, + ref_count: std.atomic.Int(usize), + + /// Thread-safe + pub fn ref(base: *Scope) void { + _ = base.ref_count.incr(); + } + + /// Thread-safe + pub fn deref(base: *Scope, comp: *Compilation) void { + if (base.ref_count.decr() == 1) { + if (base.parent) |parent| parent.deref(comp); + switch (base.id) { + Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp), + Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp), + Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp), + Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp), + Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), + Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp), + Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp), + Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp), + } + } + } + + pub fn findRoot(base: *Scope) *Root { + var scope = base; + while (scope.parent) |parent| { + scope = parent; + } + assert(scope.id == Id.Root); + return @fieldParentPtr(Root, "base", scope); + } + + pub fn findFnDef(base: *Scope) ?*FnDef { + var scope = base; + while (true) { + switch (scope.id) { + Id.FnDef => return @fieldParentPtr(FnDef, "base", scope), + Id.Root, Id.Decls => return null, + + Id.Block, + Id.Defer, + Id.DeferExpr, + Id.CompTime, + Id.Var, + => scope = scope.parent.?, + } + } + } + + pub fn findDeferExpr(base: *Scope) ?*DeferExpr { + var scope = base; + while (true) { + switch (scope.id) { + Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope), + + Id.FnDef, + Id.Decls, + => return null, + + Id.Block, + Id.Defer, + Id.CompTime, + Id.Root, + Id.Var, + => scope = scope.parent orelse return null, + } + } + } + + fn init(base: *Scope, id: Id, parent: *Scope) void { + base.* = Scope{ + .id = id, + .parent = parent, + .ref_count = std.atomic.Int(usize).init(1), + }; + parent.ref(); + } pub const Id = enum { + Root, Decls, Block, - Defer, - DeferExpr, - VarDecl, - CImport, - Loop, FnDef, CompTime, + Defer, + DeferExpr, + Var, + }; + + pub const Root = struct { + base: Scope, + tree: *ast.Tree, + realpath: []const u8, + + /// Creates a Root scope with 1 reference + /// Takes ownership of realpath + /// Takes ownership of tree, will deinit and destroy when done. + pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root { + const self = try comp.gpa().createOne(Root); + self.* = Root{ + .base = Scope{ + .id = Id.Root, + .parent = null, + .ref_count = std.atomic.Int(usize).init(1), + }, + .tree = tree, + .realpath = realpath, + }; + + return self; + } + + pub fn destroy(self: *Root, comp: *Compilation) void { + comp.gpa().free(self.tree.source); + self.tree.deinit(); + comp.gpa().destroy(self.tree); + comp.gpa().free(self.realpath); + comp.gpa().destroy(self); + } + }; + + pub const Decls = struct { + base: Scope, + + /// The lock must be respected for writing. However once name_future resolves, + /// readers can freely access it. + table: event.Locked(Decl.Table), + + /// Once this future is resolved, the table is complete and available for unlocked + /// read-only access. It does not mean all the decls are resolved; it means only that + /// the table has all the names. Each decl in the table has its own resolution state. + name_future: event.Future(void), + + /// Creates a Decls scope with 1 reference + pub fn create(comp: *Compilation, parent: *Scope) !*Decls { + const self = try comp.gpa().createOne(Decls); + self.* = Decls{ + .base = undefined, + .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), + .name_future = event.Future(void).init(comp.loop), + }; + self.base.init(Id.Decls, parent); + return self; + } + + pub fn destroy(self: *Decls, comp: *Compilation) void { + self.table.deinit(); + comp.gpa().destroy(self); + } + + pub async fn getTableReadOnly(self: *Decls) *Decl.Table { + _ = await (async self.name_future.get() catch unreachable); + return &self.table.private_data; + } + }; + + pub const Block = struct { + base: Scope, + incoming_values: std.ArrayList(*ir.Inst), + incoming_blocks: std.ArrayList(*ir.BasicBlock), + end_block: *ir.BasicBlock, + is_comptime: *ir.Inst, + + safety: Safety, + + const Safety = union(enum) { + Auto, + Manual: Manual, + + const Manual = struct { + /// the source span that disabled the safety value + span: Span, + + /// whether safety is enabled + enabled: bool, + }; + + fn get(self: Safety, comp: *Compilation) bool { + return switch (self) { + Safety.Auto => switch (comp.build_mode) { + builtin.Mode.Debug, + builtin.Mode.ReleaseSafe, + => true, + builtin.Mode.ReleaseFast, + builtin.Mode.ReleaseSmall, + => false, + }, + @TagType(Safety).Manual => |man| man.enabled, + }; + } + }; + + /// Creates a Block scope with 1 reference + pub fn create(comp: *Compilation, parent: *Scope) !*Block { + const self = try comp.gpa().createOne(Block); + self.* = Block{ + .base = undefined, + .incoming_values = undefined, + .incoming_blocks = undefined, + .end_block = undefined, + .is_comptime = undefined, + .safety = Safety.Auto, + }; + self.base.init(Id.Block, parent); + return self; + } + + pub fn destroy(self: *Block, comp: *Compilation) void { + comp.gpa().destroy(self); + } + }; + + pub const FnDef = struct { + base: Scope, + + /// This reference is not counted so that the scope can get destroyed with the function + fn_val: ?*Value.Fn, + + /// Creates a FnDef scope with 1 reference + /// Must set the fn_val later + pub fn create(comp: *Compilation, parent: *Scope) !*FnDef { + const self = try comp.gpa().createOne(FnDef); + self.* = FnDef{ + .base = undefined, + .fn_val = null, + }; + self.base.init(Id.FnDef, parent); + return self; + } + + pub fn destroy(self: *FnDef, comp: *Compilation) void { + comp.gpa().destroy(self); + } + }; + + pub const CompTime = struct { + base: Scope, + + /// Creates a CompTime scope with 1 reference + pub fn create(comp: *Compilation, parent: *Scope) !*CompTime { + const self = try comp.gpa().createOne(CompTime); + self.* = CompTime{ .base = undefined }; + self.base.init(Id.CompTime, parent); + return self; + } + + pub fn destroy(self: *CompTime, comp: *Compilation) void { + comp.gpa().destroy(self); + } + }; + + pub const Defer = struct { + base: Scope, + defer_expr_scope: *DeferExpr, + kind: Kind, + + pub const Kind = enum { + ScopeExit, + ErrorExit, + }; + + /// Creates a Defer scope with 1 reference + pub fn create( + comp: *Compilation, + parent: *Scope, + kind: Kind, + defer_expr_scope: *DeferExpr, + ) !*Defer { + const self = try comp.gpa().createOne(Defer); + self.* = Defer{ + .base = undefined, + .defer_expr_scope = defer_expr_scope, + .kind = kind, + }; + self.base.init(Id.Defer, parent); + defer_expr_scope.base.ref(); + return self; + } + + pub fn destroy(self: *Defer, comp: *Compilation) void { + self.defer_expr_scope.base.deref(comp); + comp.gpa().destroy(self); + } + }; + + pub const DeferExpr = struct { + base: Scope, + expr_node: *ast.Node, + reported_err: bool, + + /// Creates a DeferExpr scope with 1 reference + pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr { + const self = try comp.gpa().createOne(DeferExpr); + self.* = DeferExpr{ + .base = undefined, + .expr_node = expr_node, + .reported_err = false, + }; + self.base.init(Id.DeferExpr, parent); + return self; + } + + pub fn destroy(self: *DeferExpr, comp: *Compilation) void { + comp.gpa().destroy(self); + } + }; + + pub const Var = struct { + base: Scope, + name: []const u8, + src_node: *ast.Node, + data: Data, + + pub const Data = union(enum) { + Param: Param, + Const: *Value, + }; + + pub const Param = struct { + index: usize, + typ: *Type, + llvm_value: llvm.ValueRef, + }; + + pub fn createParam( + comp: *Compilation, + parent: *Scope, + name: []const u8, + src_node: *ast.Node, + param_index: usize, + param_type: *Type, + ) !*Var { + const self = try create(comp, parent, name, src_node); + self.data = Data{ + .Param = Param{ + .index = param_index, + .typ = param_type, + .llvm_value = undefined, + }, + }; + return self; + } + + pub fn createConst( + comp: *Compilation, + parent: *Scope, + name: []const u8, + src_node: *ast.Node, + value: *Value, + ) !*Var { + const self = try create(comp, parent, name, src_node); + self.data = Data{ .Const = value }; + value.ref(); + return self; + } + + fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var { + const self = try comp.gpa().createOne(Var); + self.* = Var{ + .base = undefined, + .name = name, + .src_node = src_node, + .data = undefined, + }; + self.base.init(Id.Var, parent); + return self; + } + + pub fn destroy(self: *Var, comp: *Compilation) void { + switch (self.data) { + Data.Param => {}, + Data.Const => |value| value.deref(comp), + } + comp.gpa().destroy(self); + } }; }; |
