diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 01f2314167..b11a3408cd 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -21,7 +21,6 @@ const Scope = @import("scope.zig").Scope; const Decl = @import("decl.zig").Decl; const ir = @import("ir.zig"); const Visib = @import("visib.zig").Visib; -const ParsedFile = @import("parsed_file.zig").ParsedFile; const Value = @import("value.zig").Value; const Type = Value.Type; const Span = errmsg.Span; @@ -470,6 +469,35 @@ pub const Compilation = struct { return comp; } + /// it does ref the result because it could be an arbitrary integer size + pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type { + if (name.len >= 2) { + switch (name[0]) { + 'i', 'u' => blk: { + for (name[1..]) |byte| + switch (byte) { + '0'...'9' => {}, + else => break :blk, + }; + const is_signed = name[0] == 'i'; + const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) { + error.Overflow => return error.Overflow, + error.InvalidCharacter => unreachable, // we just checked the characters above + }; + @panic("get int type - need to make everything async"); + }, + else => {}, + } + } + + if (comp.primitive_type_table.get(name)) |entry| { + entry.value.base.ref(); + return entry.value; + } + + return null; + } + fn initTypes(comp: *Compilation) !void { comp.meta_type = try comp.arena().create(Type.MetaType{ .base = Type{ @@ -671,82 +699,81 @@ pub const Compilation = struct { } async fn compileAndLink(self: *Compilation) !void { - const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); - // TODO async/await os.path.real - const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { - try printError("unable to get real path '{}': {}", root_src_path, err); - return err; - }; - errdefer self.gpa().free(root_src_real_path); + if (self.root_src_path) |root_src_path| { + // TODO async/await os.path.real + const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { + try printError("unable to get real path '{}': {}", root_src_path, err); + return err; + }; + const root_scope = blk: { + errdefer self.gpa().free(root_src_real_path); - // TODO async/await readFileAlloc() - const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| { - try printError("unable to open '{}': {}", root_src_real_path, err); - return err; - }; - errdefer self.gpa().free(source_code); + // TODO async/await readFileAlloc() + const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| { + try printError("unable to open '{}': {}", root_src_real_path, err); + return err; + }; + errdefer self.gpa().free(source_code); - const parsed_file = try self.gpa().create(ParsedFile{ - .tree = undefined, - .realpath = root_src_real_path, - }); - errdefer self.gpa().destroy(parsed_file); + var tree = try std.zig.parse(self.gpa(), source_code); + errdefer tree.deinit(); - parsed_file.tree = try std.zig.parse(self.gpa(), source_code); - errdefer parsed_file.tree.deinit(); + break :blk try Scope.Root.create(self, tree, root_src_real_path); + }; + defer root_scope.base.deref(self); - const tree = &parsed_file.tree; + const tree = &root_scope.tree; - // create empty struct for it - const decls = try Scope.Decls.create(self, null); - defer decls.base.deref(self); + const decls = try Scope.Decls.create(self, &root_scope.base); + defer decls.base.deref(self); - var decl_group = event.Group(BuildError!void).init(self.loop); - errdefer decl_group.cancelAll(); + var decl_group = event.Group(BuildError!void).init(self.loop); + errdefer decl_group.cancelAll(); - var it = tree.root_node.decls.iterator(0); - while (it.next()) |decl_ptr| { - const decl = decl_ptr.*; - switch (decl.id) { - ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); + var it = tree.root_node.decls.iterator(0); + while (it.next()) |decl_ptr| { + const decl = decl_ptr.*; + switch (decl.id) { + ast.Node.Id.Comptime => { + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl); - try decl_group.call(addCompTimeBlock, self, parsed_file, &decls.base, comptime_node); - }, - ast.Node.Id.VarDecl => @panic("TODO"), - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + try decl_group.call(addCompTimeBlock, self, &decls.base, comptime_node); + }, + ast.Node.Id.VarDecl => @panic("TODO"), + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { - try self.addCompileError(parsed_file, Span{ - .first = fn_proto.fn_token, - .last = fn_proto.fn_token + 1, - }, "missing function name"); - continue; - }; + const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { + try self.addCompileError(root_scope, Span{ + .first = fn_proto.fn_token, + .last = fn_proto.fn_token + 1, + }, "missing function name"); + continue; + }; - const fn_decl = try self.gpa().create(Decl.Fn{ - .base = Decl{ - .id = Decl.Id.Fn, - .name = name, - .visib = parseVisibToken(tree, fn_proto.visib_token), - .resolution = event.Future(BuildError!void).init(self.loop), - .resolution_in_progress = 0, - .parsed_file = parsed_file, - .parent_scope = &decls.base, - }, - .value = Decl.Fn.Val{ .Unresolved = {} }, - .fn_proto = fn_proto, - }); - errdefer self.gpa().destroy(fn_decl); + const fn_decl = try self.gpa().create(Decl.Fn{ + .base = Decl{ + .id = Decl.Id.Fn, + .name = name, + .visib = parseVisibToken(tree, fn_proto.visib_token), + .resolution = event.Future(BuildError!void).init(self.loop), + .resolution_in_progress = 0, + .parent_scope = &decls.base, + }, + .value = Decl.Fn.Val{ .Unresolved = {} }, + .fn_proto = fn_proto, + }); + errdefer self.gpa().destroy(fn_decl); - try decl_group.call(addTopLevelDecl, self, &fn_decl.base); - }, - ast.Node.Id.TestDecl => @panic("TODO"), - else => unreachable, + try decl_group.call(addTopLevelDecl, self, &fn_decl.base); + }, + ast.Node.Id.TestDecl => @panic("TODO"), + else => unreachable, + } } + try await (async decl_group.wait() catch unreachable); } - try await (async decl_group.wait() catch unreachable); + try await (async self.prelink_group.wait() catch unreachable); const any_prelink_errors = blk: { @@ -764,23 +791,15 @@ pub const Compilation = struct { /// caller takes ownership of resulting Code async fn genAndAnalyzeCode( comp: *Compilation, - parsed_file: *ParsedFile, scope: *Scope, node: *ast.Node, expected_type: ?*Type, - ) !?*ir.Code { - const unanalyzed_code = (await (async ir.gen( + ) !*ir.Code { + const unanalyzed_code = try await (async ir.gen( comp, node, scope, - parsed_file, - ) catch unreachable)) catch |err| switch (err) { - // This poison value should not cause the errdefers to run. It simply means - // that self.compile_errors is populated. - // TODO https://github.com/ziglang/zig/issues/769 - error.SemanticAnalysisFailed => return null, - else => return err, - }; + ) catch unreachable); defer unanalyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { @@ -788,44 +807,46 @@ pub const Compilation = struct { unanalyzed_code.dump(); } - const analyzed_code = (await (async ir.analyze( + const analyzed_code = try await (async ir.analyze( comp, - parsed_file, unanalyzed_code, expected_type, - ) catch unreachable)) catch |err| switch (err) { - // This poison value should not cause the errdefers to run. It simply means - // that self.compile_errors is populated. - // TODO https://github.com/ziglang/zig/issues/769 - error.SemanticAnalysisFailed => return null, - else => return err, - }; + ) catch unreachable); errdefer analyzed_code.destroy(comp.gpa()); + if (comp.verbose_ir) { + std.debug.warn("analyzed:\n"); + analyzed_code.dump(); + } + return analyzed_code; } async fn addCompTimeBlock( comp: *Compilation, - parsed_file: *ParsedFile, scope: *Scope, comptime_node: *ast.Node.Comptime, ) !void { const void_type = Type.Void.get(comp); defer void_type.base.base.deref(comp); - const analyzed_code = (try await (async genAndAnalyzeCode( + const analyzed_code = (await (async genAndAnalyzeCode( comp, - parsed_file, scope, comptime_node.expr, &void_type.base, - ) catch unreachable)) orelse return; + ) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that comp.compile_errors is populated. + error.SemanticAnalysisFailed => return {}, + else => return err, + }; analyzed_code.destroy(comp.gpa()); } async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { - const is_export = decl.isExported(&decl.parsed_file.tree); + const tree = &decl.findRootScope().tree; + const is_export = decl.isExported(tree); if (is_export) { try self.prelink_group.call(verifyUniqueSymbol, self, decl); @@ -833,24 +854,24 @@ pub const Compilation = struct { } } - fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void { - const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); - errdefer self.loop.allocator.free(text); + fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void { + const text = try std.fmt.allocPrint(self.gpa(), fmt, args); + errdefer self.gpa().free(text); - try self.prelink_group.call(addCompileErrorAsync, self, parsed_file, span, text); + try self.prelink_group.call(addCompileErrorAsync, self, root, span, text); } async fn addCompileErrorAsync( self: *Compilation, - parsed_file: *ParsedFile, + root: *Scope.Root, span: Span, text: []u8, ) !void { const msg = try self.loop.allocator.create(errmsg.Msg{ - .path = parsed_file.realpath, + .path = root.realpath, .text = text, .span = span, - .tree = &parsed_file.tree, + .tree = &root.tree, }); errdefer self.loop.allocator.destroy(msg); @@ -866,7 +887,7 @@ pub const Compilation = struct { if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { try self.addCompileError( - decl.parsed_file, + decl.findRootScope(), decl.getSpan(), "exported symbol collision: '{}'", decl.name, @@ -988,6 +1009,24 @@ pub const Compilation = struct { fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void { // TODO put the garbage somewhere } + + /// Returns a value which has been ref()'d once + async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value { + const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable); + defer analyzed_code.destroy(comp.gpa()); + + return analyzed_code.getCompTimeResult(comp); + } + + async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type { + const meta_type = &Type.MetaType.get(comp).base; + defer meta_type.base.deref(comp); + + const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable); + errdefer result_val.base.deref(comp); + + return result_val.cast(Type).?; + } }; fn printError(comptime format: []const u8, args: ...) !void { @@ -1011,7 +1050,12 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*; - decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); + decl.resolution.data = (await (async generateDecl(comp, decl) catch unreachable)) catch |err| switch (err) { + // This poison value should not cause the errdefers to run. It simply means + // that comp.compile_errors is populated. + error.SemanticAnalysisFailed => {}, + else => err, + }; decl.resolution.resolve(); return decl.resolution.data; } @@ -1034,9 +1078,12 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope); defer fndef_scope.base.deref(comp); - // TODO actually look at the return type of the AST - const return_type = &Type.Void.get(comp).base; - defer return_type.base.deref(comp); + const return_type_node = switch (fn_decl.fn_proto.return_type) { + ast.Node.FnProto.ReturnType.Explicit => |n| n, + ast.Node.FnProto.ReturnType.InferErrorSet => |n| n, + }; + const return_type = try await (async comp.analyzeTypeExpr(&fndef_scope.base, return_type_node) catch unreachable); + return_type.base.deref(comp); const is_var_args = false; const params = ([*]Type.Fn.Param)(undefined)[0..0]; @@ -1050,19 +1097,13 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; - const analyzed_code = (try await (async comp.genAndAnalyzeCode( - fn_decl.base.parsed_file, + const analyzed_code = try await (async comp.genAndAnalyzeCode( &fndef_scope.base, body_node, return_type, - ) catch unreachable)) orelse return; + ) catch unreachable); errdefer analyzed_code.destroy(comp.gpa()); - if (comp.verbose_ir) { - std.debug.warn("analyzed:\n"); - analyzed_code.dump(); - } - // Kick off rendering to LLVM module, but it doesn't block the fn decl // analysis from being complete. try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); diff --git a/src-self-hosted/decl.zig b/src-self-hosted/decl.zig index c0173266ee..bb065640c2 100644 --- a/src-self-hosted/decl.zig +++ b/src-self-hosted/decl.zig @@ -3,7 +3,6 @@ const Allocator = mem.Allocator; const mem = std.mem; const ast = std.zig.ast; const Visib = @import("visib.zig").Visib; -const ParsedFile = @import("parsed_file.zig").ParsedFile; const event = std.event; const Value = @import("value.zig").Value; const Token = std.zig.Token; @@ -17,7 +16,6 @@ pub const Decl = struct { visib: Visib, resolution: event.Future(Compilation.BuildError!void), resolution_in_progress: u8, - parsed_file: *ParsedFile, parent_scope: *Scope, pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); @@ -48,6 +46,10 @@ pub const Decl = struct { } } + pub fn findRootScope(base: *const Decl) *Scope.Root { + return base.parent_scope.findRoot(); + } + pub const Id = enum { Var, Fn, diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 5a8a74ef54..bdfccd085a 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -8,7 +8,6 @@ const Value = @import("value.zig").Value; const Type = Value.Type; const assert = std.debug.assert; const Token = std.zig.Token; -const ParsedFile = @import("parsed_file.zig").ParsedFile; const Span = @import("errmsg.zig").Span; const llvm = @import("llvm.zig"); const ObjectFile = @import("codegen.zig").ObjectFile; @@ -611,6 +610,33 @@ pub const Code = struct { } } } + + /// returns a ref-incremented value, or adds a compile error + pub fn getCompTimeResult(self: *Code, comp: *Compilation) !*Value { + const bb = self.basic_block_list.at(0); + for (bb.instruction_list.toSliceConst()) |inst| { + if (inst.cast(Inst.Return)) |ret_inst| { + const ret_value = ret_inst.params.return_value; + if (ret_value.isCompTime()) { + return ret_value.val.KnownValue.getRef(); + } + try comp.addCompileError( + ret_value.scope.findRoot(), + ret_value.span, + "unable to evaluate constant expression", + ); + return error.SemanticAnalysisFailed; + } else if (inst.hasSideEffects()) { + try comp.addCompileError( + inst.scope.findRoot(), + inst.span, + "unable to evaluate constant expression", + ); + return error.SemanticAnalysisFailed; + } + } + unreachable; + } }; pub const Builder = struct { @@ -618,14 +644,14 @@ pub const Builder = struct { code: *Code, current_basic_block: *BasicBlock, next_debug_id: usize, - parsed_file: *ParsedFile, + root_scope: *Scope.Root, is_comptime: bool, is_async: bool, begin_scope: ?*Scope, pub const Error = Analyze.Error; - pub fn init(comp: *Compilation, parsed_file: *ParsedFile, begin_scope: ?*Scope) !Builder { + pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder { const code = try comp.gpa().create(Code{ .basic_block_list = undefined, .arena = std.heap.ArenaAllocator.init(comp.gpa()), @@ -636,7 +662,7 @@ pub const Builder = struct { return Builder{ .comp = comp, - .parsed_file = parsed_file, + .root_scope = root_scope, .current_basic_block = undefined, .code = code, .next_debug_id = 0, @@ -718,7 +744,10 @@ pub const Builder = struct { ast.Node.Id.UndefinedLiteral => return error.Unimplemented, ast.Node.Id.ThisLiteral => return error.Unimplemented, ast.Node.Id.Unreachable => return error.Unimplemented, - ast.Node.Id.Identifier => return error.Unimplemented, + ast.Node.Id.Identifier => { + const identifier = @fieldParentPtr(ast.Node.Identifier, "base", node); + return irb.genIdentifier(identifier, scope, lval); + }, ast.Node.Id.GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); return irb.genNode(grouped_expr.expr, scope, lval); @@ -761,16 +790,17 @@ pub const Builder = struct { Scope.Id.CompTime => return true, Scope.Id.FnDef => return false, Scope.Id.Decls => unreachable, + Scope.Id.Root => unreachable, Scope.Id.Block, Scope.Id.Defer, Scope.Id.DeferExpr, - => scope = scope.parent orelse return false, + => scope = scope.parent.?, } } } pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst { - const int_token = irb.parsed_file.tree.tokenSlice(int_lit.token); + const int_token = irb.root_scope.tree.tokenSlice(int_lit.token); var base: u8 = undefined; var rest: []const u8 = undefined; @@ -845,7 +875,7 @@ pub const Builder = struct { if (statement_node.cast(ast.Node.Defer)) |defer_node| { // defer starts a new scope - const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token); + const defer_token = irb.root_scope.tree.tokens.at(defer_node.defer_token); const kind = switch (defer_token.id) { Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit, Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit, @@ -928,7 +958,7 @@ pub const Builder = struct { const src_span = Span.token(control_flow_expr.ltoken); if (scope.findFnDef() == null) { try irb.comp.addCompileError( - irb.parsed_file, + irb.root_scope, src_span, "return expression outside function definition", ); @@ -938,7 +968,7 @@ pub const Builder = struct { if (scope.findDeferExpr()) |scope_defer_expr| { if (!scope_defer_expr.reported_err) { try irb.comp.addCompileError( - irb.parsed_file, + irb.root_scope, src_span, "cannot return from defer expression", ); @@ -1012,6 +1042,69 @@ pub const Builder = struct { } } + pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { + const src_span = Span.token(identifier.token); + const name = irb.root_scope.tree.tokenSlice(identifier.token); + + //if (buf_eql_str(variable_name, "_") && lval == LValPtr) { + // IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, node); + // const_instruction->base.value.type = get_pointer_to_type(irb->codegen, + // irb->codegen->builtin_types.entry_void, false); + // const_instruction->base.value.special = ConstValSpecialStatic; + // const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard; + // return &const_instruction->base; + //} + + if (irb.comp.getPrimitiveType(name)) |result| { + if (result) |primitive_type| { + defer primitive_type.base.deref(irb.comp); + switch (lval) { + LVal.Ptr => return error.Unimplemented, + LVal.None => return irb.buildConstValue(scope, src_span, &primitive_type.base), + } + } + } else |err| switch (err) { + error.Overflow => { + try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large"); + return error.SemanticAnalysisFailed; + }, + } + //TypeTableEntry *primitive_type = get_primitive_type(irb->codegen, variable_name); + //if (primitive_type != nullptr) { + // IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); + // if (lval == LValPtr) { + // return ir_build_ref(irb, scope, node, value, false, false); + // } else { + // return value; + // } + //} + + //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); + //if (var) { + // IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var); + // if (lval == LValPtr) + // return var_ptr; + // else + // return ir_build_load_ptr(irb, scope, node, var_ptr); + //} + + //Tld *tld = find_decl(irb->codegen, scope, variable_name); + //if (tld) + // return ir_build_decl_ref(irb, scope, node, tld, lval); + + //if (node->owner->any_imports_failed) { + // // skip the error message since we had a failing import in this file + // // if an import breaks we don't need redundant undeclared identifier errors + // return irb->codegen->invalid_instruction; + //} + + // TODO put a variable of same name with invalid type in global scope + // so that future references to this same name will find a variable with an invalid type + + try irb.comp.addCompileError(irb.root_scope, src_span, "unknown identifier '{}'", name); + return error.SemanticAnalysisFailed; + } + const DeferCounts = struct { scope_exit: usize, error_exit: usize, @@ -1035,10 +1128,11 @@ pub const Builder = struct { Scope.Id.CompTime, Scope.Id.Block, + Scope.Id.Decls, + Scope.Id.Root, => scope = scope.parent orelse break, Scope.Id.DeferExpr => unreachable, - Scope.Id.Decls => unreachable, } } return result; @@ -1081,6 +1175,7 @@ pub const Builder = struct { }, Scope.Id.FnDef, Scope.Id.Decls, + Scope.Id.Root, => return is_noreturn, Scope.Id.CompTime, @@ -1188,6 +1283,12 @@ pub const Builder = struct { return inst; } + fn buildConstValue(self: *Builder, scope: *Scope, span: Span, v: *Value) !*Inst { + const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{}); + inst.val = IrVal{ .KnownValue = v.getRef() }; + return inst; + } + /// If the code is explicitly set to be comptime, then builds a const bool, /// otherwise builds a TestCompTime instruction. fn buildTestCompTime(self: *Builder, scope: *Scope, span: Span, target: *Inst) !*Inst { @@ -1259,8 +1360,8 @@ const Analyze = struct { OutOfMemory, }; - pub fn init(comp: *Compilation, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze { - var irb = try Builder.init(comp, parsed_file, null); + pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze { + var irb = try Builder.init(comp, root_scope, null); errdefer irb.abort(); return Analyze{ @@ -1338,7 +1439,7 @@ const Analyze = struct { } fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void { - return self.irb.comp.addCompileError(self.irb.parsed_file, span, fmt, args); + return self.irb.comp.addCompileError(self.irb.root_scope, span, fmt, args); } fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type { @@ -1800,9 +1901,8 @@ pub async fn gen( comp: *Compilation, body_node: *ast.Node, scope: *Scope, - parsed_file: *ParsedFile, ) !*Code { - var irb = try Builder.init(comp, parsed_file, scope); + var irb = try Builder.init(comp, scope.findRoot(), scope); errdefer irb.abort(); const entry_block = try irb.createBasicBlock(scope, c"Entry"); @@ -1818,11 +1918,12 @@ pub async fn gen( return irb.finish(); } -pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code { - var ira = try Analyze.init(comp, parsed_file, expected_type); - errdefer ira.abort(); - +pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code { const old_entry_bb = old_code.basic_block_list.at(0); + const root_scope = old_entry_bb.scope.findRoot(); + + var ira = try Analyze.init(comp, root_scope, expected_type); + errdefer ira.abort(); const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null); new_entry_bb.ref(); diff --git a/src-self-hosted/parsed_file.zig b/src-self-hosted/parsed_file.zig deleted file mode 100644 index d728c2fd18..0000000000 --- a/src-self-hosted/parsed_file.zig +++ /dev/null @@ -1,6 +0,0 @@ -const ast = @import("std").zig.ast; - -pub const ParsedFile = struct { - tree: ast.Tree, - realpath: []const u8, -}; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 00be450479..7733e61600 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -8,6 +8,7 @@ const ast = std.zig.ast; const Value = @import("value.zig").Value; const ir = @import("ir.zig"); const Span = @import("errmsg.zig").Span; +const assert = std.debug.assert; pub const Scope = struct { id: Id, @@ -23,7 +24,8 @@ pub const Scope = struct { if (base.ref_count == 0) { if (base.parent) |parent| parent.deref(comp); switch (base.id) { - Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(), + 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), @@ -33,6 +35,15 @@ pub const Scope = struct { } } + 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) { @@ -44,6 +55,7 @@ pub const Scope = struct { Id.Defer, Id.DeferExpr, Id.CompTime, + Id.Root, => scope = scope.parent orelse return null, } } @@ -62,12 +74,14 @@ pub const Scope = struct { Id.Block, Id.Defer, Id.CompTime, + Id.Root, => scope = scope.parent orelse return null, } } } pub const Id = enum { + Root, Decls, Block, FnDef, @@ -76,12 +90,43 @@ pub const Scope = struct { DeferExpr, }; + pub const Root = struct { + base: Scope, + tree: ast.Tree, + realpath: []const u8, + + /// Creates a Root scope with 1 reference + /// Takes ownership of realpath + /// Caller must set tree + pub fn create(comp: *Compilation, tree: ast.Tree, realpath: []u8) !*Root { + const self = try comp.gpa().create(Root{ + .base = Scope{ + .id = Id.Root, + .parent = null, + .ref_count = 1, + }, + .tree = tree, + .realpath = realpath, + }); + errdefer comp.gpa().destroy(self); + + return self; + } + + pub fn destroy(self: *Root, comp: *Compilation) void { + comp.gpa().free(self.tree.source); + self.tree.deinit(); + comp.gpa().free(self.realpath); + comp.gpa().destroy(self); + } + }; + pub const Decls = struct { base: Scope, table: Decl.Table, /// Creates a Decls scope with 1 reference - pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls { + pub fn create(comp: *Compilation, parent: *Scope) !*Decls { const self = try comp.gpa().create(Decls{ .base = Scope{ .id = Id.Decls, @@ -95,14 +140,14 @@ pub const Scope = struct { self.table = Decl.Table.init(comp.gpa()); errdefer self.table.deinit(); - if (parent) |p| p.ref(); + parent.ref(); return self; } - pub fn destroy(self: *Decls) void { + pub fn destroy(self: *Decls, comp: *Compilation) void { self.table.deinit(); - self.table.allocator.destroy(self); + comp.gpa().destroy(self); } }; @@ -143,7 +188,7 @@ pub const Scope = struct { }; /// Creates a Block scope with 1 reference - pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { + pub fn create(comp: *Compilation, parent: *Scope) !*Block { const self = try comp.gpa().create(Block{ .base = Scope{ .id = Id.Block, @@ -158,7 +203,7 @@ pub const Scope = struct { }); errdefer comp.gpa().destroy(self); - if (parent) |p| p.ref(); + parent.ref(); return self; } @@ -175,7 +220,7 @@ pub const Scope = struct { /// Creates a FnDef scope with 1 reference /// Must set the fn_val later - pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef { + pub fn create(comp: *Compilation, parent: *Scope) !*FnDef { const self = try comp.gpa().create(FnDef{ .base = Scope{ .id = Id.FnDef, @@ -185,7 +230,7 @@ pub const Scope = struct { .fn_val = undefined, }); - if (parent) |p| p.ref(); + parent.ref(); return self; } @@ -199,7 +244,7 @@ pub const Scope = struct { base: Scope, /// Creates a CompTime scope with 1 reference - pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime { + pub fn create(comp: *Compilation, parent: *Scope) !*CompTime { const self = try comp.gpa().create(CompTime{ .base = Scope{ .id = Id.CompTime, @@ -208,7 +253,7 @@ pub const Scope = struct { }, }); - if (parent) |p| p.ref(); + parent.ref(); return self; } @@ -230,7 +275,7 @@ pub const Scope = struct { /// Creates a Defer scope with 1 reference pub fn create( comp: *Compilation, - parent: ?*Scope, + parent: *Scope, kind: Kind, defer_expr_scope: *DeferExpr, ) !*Defer { @@ -247,7 +292,7 @@ pub const Scope = struct { defer_expr_scope.base.ref(); - if (parent) |p| p.ref(); + parent.ref(); return self; } @@ -263,7 +308,7 @@ pub const Scope = struct { reported_err: bool, /// Creates a DeferExpr scope with 1 reference - pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { + pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr { const self = try comp.gpa().create(DeferExpr{ .base = Scope{ .id = Id.DeferExpr, @@ -275,7 +320,7 @@ pub const Scope = struct { }); errdefer comp.gpa().destroy(self); - if (parent) |p| p.ref(); + parent.ref(); return self; } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index e518cd4173..b8563b00b8 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -39,6 +39,11 @@ pub const Value = struct { return base; } + pub fn cast(base: *Value, comptime T: type) ?*T { + if (base.id != @field(Id, @typeName(T))) return null; + return @fieldParentPtr(T, "base", base); + } + pub fn dump(base: *const Value) void { std.debug.warn("{}", @tagName(base.id)); }