aboutsummaryrefslogtreecommitdiff
path: root/src-self-hosted/scope.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src-self-hosted/scope.zig')
-rw-r--r--src-self-hosted/scope.zig392
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);
+ }
};
};