| author | |
| committer | |
| log | 687bd92f9c3d9f521c8fe5884627ef1b00320364 |
| tree | 298cff9330e4c4d4914d0cb4d047805c5b15153d |
| parent | ce11d6d16cf388ec7abff9680ee3a263185a9986 |
no tests for this yet. I think the quickest path to testing will be
creating the .o files and linking with libc, executing, and then
comparing output.11 files changed, 1555 insertions(+), 315 deletions(-)
src-self-hosted/decl.zig created+96| ... | ... | @@ -0,0 +1,96 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Allocator = mem.Allocator; | |
| 3 | const mem = std.mem; | |
| 4 | const ast = std.zig.ast; | |
| 5 | const Visib = @import("visib.zig").Visib; | |
| 6 | const ParsedFile = @import("parsed_file.zig").ParsedFile; | |
| 7 | const event = std.event; | |
| 8 | const Value = @import("value.zig").Value; | |
| 9 | const Token = std.zig.Token; | |
| 10 | const errmsg = @import("errmsg.zig"); | |
| 11 | const Scope = @import("scope.zig").Scope; | |
| 12 | const Module = @import("module.zig").Module; | |
| 13 | ||
| 14 | pub const Decl = struct { | |
| 15 | id: Id, | |
| 16 | name: []const u8, | |
| 17 | visib: Visib, | |
| 18 | resolution: event.Future(Module.BuildError!void), | |
| 19 | resolution_in_progress: u8, | |
| 20 | parsed_file: *ParsedFile, | |
| 21 | parent_scope: *Scope, | |
| 22 | ||
| 23 | pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); | |
| 24 | ||
| 25 | pub fn isExported(base: *const Decl, tree: *ast.Tree) bool { | |
| 26 | switch (base.id) { | |
| 27 | Id.Fn => { | |
| 28 | const fn_decl = @fieldParentPtr(Fn, "base", base); | |
| 29 | return fn_decl.isExported(tree); | |
| 30 | }, | |
| 31 | else => return false, | |
| 32 | } | |
| 33 | } | |
| 34 | ||
| 35 | pub fn getSpan(base: *const Decl) errmsg.Span { | |
| 36 | switch (base.id) { | |
| 37 | Id.Fn => { | |
| 38 | const fn_decl = @fieldParentPtr(Fn, "base", base); | |
| 39 | const fn_proto = fn_decl.fn_proto; | |
| 40 | const start = fn_proto.fn_token; | |
| 41 | const end = fn_proto.name_token orelse start; | |
| 42 | return errmsg.Span{ | |
| 43 | .first = start, | |
| 44 | .last = end + 1, | |
| 45 | }; | |
| 46 | }, | |
| 47 | else => @panic("TODO"), | |
| 48 | } | |
| 49 | } | |
| 50 | ||
| 51 | pub const Id = enum { | |
| 52 | Var, | |
| 53 | Fn, | |
| 54 | CompTime, | |
| 55 | }; | |
| 56 | ||
| 57 | pub const Var = struct { | |
| 58 | base: Decl, | |
| 59 | }; | |
| 60 | ||
| 61 | pub const Fn = struct { | |
| 62 | base: Decl, | |
| 63 | value: Val, | |
| 64 | fn_proto: *const ast.Node.FnProto, | |
| 65 | ||
| 66 | // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous | |
| 67 | pub const Val = union { | |
| 68 | Unresolved: void, | |
| 69 | Ok: *Value.Fn, | |
| 70 | }; | |
| 71 | ||
| 72 | pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { | |
| 73 | return if (self.fn_proto.extern_export_inline_token) |tok_index| x: { | |
| 74 | const token = tree.tokens.at(tok_index); | |
| 75 | break :x switch (token.id) { | |
| 76 | Token.Id.Extern => tree.tokenSlicePtr(token), | |
| 77 | else => null, | |
| 78 | }; | |
| 79 | } else null; | |
| 80 | } | |
| 81 | ||
| 82 | pub fn isExported(self: Fn, tree: *ast.Tree) bool { | |
| 83 | if (self.fn_proto.extern_export_inline_token) |tok_index| { | |
| 84 | const token = tree.tokens.at(tok_index); | |
| 85 | return token.id == Token.Id.Keyword_export; | |
| 86 | } else { | |
| 87 | return false; | |
| 88 | } | |
| 89 | } | |
| 90 | }; | |
| 91 | ||
| 92 | pub const CompTime = struct { | |
| 93 | base: Decl, | |
| 94 | }; | |
| 95 | }; | |
| 96 |
src-self-hosted/ir.zig+645-100| ... | ... | @@ -1,111 +1,656 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const Module = @import("module.zig").Module; | |
| 1 | 4 | const Scope = @import("scope.zig").Scope; |
| 5 | const ast = std.zig.ast; | |
| 6 | const Allocator = std.mem.Allocator; | |
| 7 | const Value = @import("value.zig").Value; | |
| 8 | const Type = Value.Type; | |
| 9 | const assert = std.debug.assert; | |
| 10 | const Token = std.zig.Token; | |
| 11 | const ParsedFile = @import("parsed_file.zig").ParsedFile; | |
| 12 | ||
| 13 | pub const LVal = enum { | |
| 14 | None, | |
| 15 | Ptr, | |
| 16 | }; | |
| 17 | ||
| 18 | pub const Mut = enum { | |
| 19 | Mut, | |
| 20 | Const, | |
| 21 | }; | |
| 22 | ||
| 23 | pub const Volatility = enum { | |
| 24 | NonVolatile, | |
| 25 | Volatile, | |
| 26 | }; | |
| 27 | ||
| 28 | pub const IrVal = union(enum) { | |
| 29 | Unknown, | |
| 30 | Known: *Value, | |
| 31 | ||
| 32 | pub fn dump(self: IrVal) void { | |
| 33 | switch (self) { | |
| 34 | IrVal.Unknown => std.debug.warn("Unknown"), | |
| 35 | IrVal.Known => |value| { | |
| 36 | std.debug.warn("Known("); | |
| 37 | value.dump(); | |
| 38 | std.debug.warn(")"); | |
| 39 | }, | |
| 40 | } | |
| 41 | } | |
| 42 | }; | |
| 2 | 43 | |
| 3 | 44 | pub const Instruction = struct { |
| 4 | 45 | id: Id, |
| 5 | 46 | scope: *Scope, |
| 47 | debug_id: usize, | |
| 48 | val: IrVal, | |
| 49 | ||
| 50 | /// true if this instruction was generated by zig and not from user code | |
| 51 | is_generated: bool, | |
| 52 | ||
| 53 | pub fn cast(base: *Instruction, comptime T: type) ?*T { | |
| 54 | if (base.id == comptime typeToId(T)) { | |
| 55 | return @fieldParentPtr(T, "base", base); | |
| 56 | } | |
| 57 | return null; | |
| 58 | } | |
| 59 | ||
| 60 | pub fn typeToId(comptime T: type) Id { | |
| 61 | comptime var i = 0; | |
| 62 | inline while (i < @memberCount(Id)) : (i += 1) { | |
| 63 | if (T == @field(Instruction, @memberName(Id, i))) { | |
| 64 | return @field(Id, @memberName(Id, i)); | |
| 65 | } | |
| 66 | } | |
| 67 | unreachable; | |
| 68 | } | |
| 69 | ||
| 70 | pub fn dump(base: *const Instruction) void { | |
| 71 | comptime var i = 0; | |
| 72 | inline while (i < @memberCount(Id)) : (i += 1) { | |
| 73 | if (base.id == @field(Id, @memberName(Id, i))) { | |
| 74 | const T = @field(Instruction, @memberName(Id, i)); | |
| 75 | std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id)); | |
| 76 | @fieldParentPtr(T, "base", base).dump(); | |
| 77 | std.debug.warn(")"); | |
| 78 | return; | |
| 79 | } | |
| 80 | } | |
| 81 | unreachable; | |
| 82 | } | |
| 83 | ||
| 84 | pub fn setGenerated(base: *Instruction) void { | |
| 85 | base.is_generated = true; | |
| 86 | } | |
| 87 | ||
| 88 | pub fn isNoReturn(base: *const Instruction) bool { | |
| 89 | switch (base.val) { | |
| 90 | IrVal.Unknown => return false, | |
| 91 | IrVal.Known => |x| return x.typeof.id == Type.Id.NoReturn, | |
| 92 | } | |
| 93 | } | |
| 6 | 94 | |
| 7 | 95 | pub const Id = enum { |
| 8 | Br, | |
| 9 | CondBr, | |
| 10 | SwitchBr, | |
| 11 | SwitchVar, | |
| 12 | SwitchTarget, | |
| 13 | Phi, | |
| 14 | UnOp, | |
| 15 | BinOp, | |
| 16 | DeclVar, | |
| 17 | LoadPtr, | |
| 18 | StorePtr, | |
| 19 | FieldPtr, | |
| 20 | StructFieldPtr, | |
| 21 | UnionFieldPtr, | |
| 22 | ElemPtr, | |
| 23 | VarPtr, | |
| 24 | Call, | |
| 25 | Const, | |
| 26 | 96 | Return, |
| 27 | Cast, | |
| 28 | ContainerInitList, | |
| 29 | ContainerInitFields, | |
| 30 | StructInit, | |
| 31 | UnionInit, | |
| 32 | Unreachable, | |
| 33 | TypeOf, | |
| 34 | ToPtrType, | |
| 35 | PtrTypeChild, | |
| 36 | SetRuntimeSafety, | |
| 37 | SetFloatMode, | |
| 38 | ArrayType, | |
| 39 | SliceType, | |
| 40 | Asm, | |
| 41 | SizeOf, | |
| 42 | TestNonNull, | |
| 43 | UnwrapMaybe, | |
| 44 | MaybeWrap, | |
| 45 | UnionTag, | |
| 46 | Clz, | |
| 47 | Ctz, | |
| 48 | Import, | |
| 49 | CImport, | |
| 50 | CInclude, | |
| 51 | CDefine, | |
| 52 | CUndef, | |
| 53 | ArrayLen, | |
| 97 | Const, | |
| 54 | 98 | Ref, |
| 55 | MinValue, | |
| 56 | MaxValue, | |
| 57 | CompileErr, | |
| 58 | CompileLog, | |
| 59 | ErrName, | |
| 60 | EmbedFile, | |
| 61 | Cmpxchg, | |
| 62 | Fence, | |
| 63 | Truncate, | |
| 64 | IntType, | |
| 65 | BoolNot, | |
| 66 | Memset, | |
| 67 | Memcpy, | |
| 68 | Slice, | |
| 69 | MemberCount, | |
| 70 | MemberType, | |
| 71 | MemberName, | |
| 72 | Breakpoint, | |
| 73 | ReturnAddress, | |
| 74 | FrameAddress, | |
| 75 | AlignOf, | |
| 76 | OverflowOp, | |
| 77 | TestErr, | |
| 78 | UnwrapErrCode, | |
| 79 | UnwrapErrPayload, | |
| 80 | ErrWrapCode, | |
| 81 | ErrWrapPayload, | |
| 82 | FnProto, | |
| 83 | TestComptime, | |
| 84 | PtrCast, | |
| 85 | BitCast, | |
| 86 | WidenOrShorten, | |
| 87 | IntToPtr, | |
| 88 | PtrToInt, | |
| 89 | IntToEnum, | |
| 90 | IntToErr, | |
| 91 | ErrToInt, | |
| 92 | CheckSwitchProngs, | |
| 93 | CheckStatementIsVoid, | |
| 94 | TypeName, | |
| 95 | CanImplicitCast, | |
| 96 | DeclRef, | |
| 97 | Panic, | |
| 98 | TagName, | |
| 99 | TagType, | |
| 100 | FieldParentPtr, | |
| 101 | OffsetOf, | |
| 102 | TypeId, | |
| 103 | SetEvalBranchQuota, | |
| 104 | PtrTypeOf, | |
| 105 | AlignCast, | |
| 106 | OpaqueType, | |
| 107 | SetAlignStack, | |
| 108 | ArgType, | |
| 109 | Export, | |
| 99 | DeclVar, | |
| 100 | CheckVoidStmt, | |
| 101 | Phi, | |
| 102 | Br, | |
| 103 | }; | |
| 104 | ||
| 105 | pub const Const = struct { | |
| 106 | base: Instruction, | |
| 107 | ||
| 108 | pub fn buildBool(irb: *Builder, scope: *Scope, val: bool) !*Instruction { | |
| 109 | const inst = try irb.arena().create(Const{ | |
| 110 | .base = Instruction{ | |
| 111 | .id = Instruction.Id.Const, | |
| 112 | .is_generated = false, | |
| 113 | .scope = scope, | |
| 114 | .debug_id = irb.next_debug_id, | |
| 115 | .val = IrVal{ .Known = &Value.Bool.get(irb.module, val).base }, | |
| 116 | }, | |
| 117 | }); | |
| 118 | irb.next_debug_id += 1; | |
| 119 | try irb.current_basic_block.instruction_list.append(&inst.base); | |
| 120 | return &inst.base; | |
| 121 | } | |
| 122 | ||
| 123 | pub fn buildVoid(irb: *Builder, scope: *Scope, is_generated: bool) !*Instruction { | |
| 124 | const inst = try irb.arena().create(Const{ | |
| 125 | .base = Instruction{ | |
| 126 | .id = Instruction.Id.Const, | |
| 127 | .is_generated = is_generated, | |
| 128 | .scope = scope, | |
| 129 | .debug_id = irb.next_debug_id, | |
| 130 | .val = IrVal{ .Known = &Value.Void.get(irb.module).base }, | |
| 131 | }, | |
| 132 | }); | |
| 133 | irb.next_debug_id += 1; | |
| 134 | try irb.current_basic_block.instruction_list.append(&inst.base); | |
| 135 | return &inst.base; | |
| 136 | } | |
| 137 | ||
| 138 | pub fn dump(inst: *const Const) void { | |
| 139 | inst.base.val.Known.dump(); | |
| 140 | } | |
| 141 | }; | |
| 142 | ||
| 143 | pub const Return = struct { | |
| 144 | base: Instruction, | |
| 145 | return_value: *Instruction, | |
| 146 | ||
| 147 | pub fn build(irb: *Builder, scope: *Scope, return_value: *Instruction) !*Instruction { | |
| 148 | const inst = try irb.arena().create(Return{ | |
| 149 | .base = Instruction{ | |
| 150 | .id = Instruction.Id.Return, | |
| 151 | .is_generated = false, | |
| 152 | .scope = scope, | |
| 153 | .debug_id = irb.next_debug_id, | |
| 154 | .val = IrVal{ .Known = &Value.Void.get(irb.module).base }, | |
| 155 | }, | |
| 156 | .return_value = return_value, | |
| 157 | }); | |
| 158 | irb.next_debug_id += 1; | |
| 159 | try irb.current_basic_block.instruction_list.append(&inst.base); | |
| 160 | return &inst.base; | |
| 161 | } | |
| 162 | ||
| 163 | pub fn dump(inst: *const Return) void { | |
| 164 | std.debug.warn("#{}", inst.return_value.debug_id); | |
| 165 | } | |
| 166 | }; | |
| 167 | ||
| 168 | pub const Ref = struct { | |
| 169 | base: Instruction, | |
| 170 | target: *Instruction, | |
| 171 | mut: Mut, | |
| 172 | volatility: Volatility, | |
| 173 | ||
| 174 | pub fn build( | |
| 175 | irb: *Builder, | |
| 176 | scope: *Scope, | |
| 177 | target: *Instruction, | |
| 178 | mut: Mut, | |
| 179 | volatility: Volatility, | |
| 180 | ) !*Instruction { | |
| 181 | const inst = try irb.arena().create(Ref{ | |
| 182 | .base = Instruction{ | |
| 183 | .id = Instruction.Id.Ref, | |
| 184 | .is_generated = false, | |
| 185 | .scope = scope, | |
| 186 | .debug_id = irb.next_debug_id, | |
| 187 | .val = IrVal.Unknown, | |
| 188 | }, | |
| 189 | .target = target, | |
| 190 | .mut = mut, | |
| 191 | .volatility = volatility, | |
| 192 | }); | |
| 193 | irb.next_debug_id += 1; | |
| 194 | try irb.current_basic_block.instruction_list.append(&inst.base); | |
| 195 | return &inst.base; | |
| 196 | } | |
| 197 | ||
| 198 | pub fn dump(inst: *const Ref) void {} | |
| 199 | }; | |
| 200 | ||
| 201 | pub const DeclVar = struct { | |
| 202 | base: Instruction, | |
| 203 | variable: *Variable, | |
| 204 | ||
| 205 | pub fn dump(inst: *const DeclVar) void {} | |
| 206 | }; | |
| 207 | ||
| 208 | pub const CheckVoidStmt = struct { | |
| 209 | base: Instruction, | |
| 210 | target: *Instruction, | |
| 211 | ||
| 212 | pub fn build( | |
| 213 | irb: *Builder, | |
| 214 | scope: *Scope, | |
| 215 | target: *Instruction, | |
| 216 | ) !*Instruction { | |
| 217 | const inst = try irb.arena().create(CheckVoidStmt{ | |
| 218 | .base = Instruction{ | |
| 219 | .id = Instruction.Id.CheckVoidStmt, | |
| 220 | .is_generated = true, | |
| 221 | .scope = scope, | |
| 222 | .debug_id = irb.next_debug_id, | |
| 223 | .val = IrVal{ .Known = &Value.Void.get(irb.module).base }, | |
| 224 | }, | |
| 225 | .target = target, | |
| 226 | }); | |
| 227 | irb.next_debug_id += 1; | |
| 228 | try irb.current_basic_block.instruction_list.append(&inst.base); | |
| 229 | return &inst.base; | |
| 230 | } | |
| 231 | ||
| 232 | pub fn dump(inst: *const CheckVoidStmt) void {} | |
| 233 | }; | |
| 234 | ||
| 235 | pub const Phi = struct { | |
| 236 | base: Instruction, | |
| 237 | incoming_blocks: []*BasicBlock, | |
| 238 | incoming_values: []*Instruction, | |
| 239 | ||
| 240 | pub fn build( | |
| 241 | irb: *Builder, | |
| 242 | scope: *Scope, | |
| 243 | incoming_blocks: []*BasicBlock, | |
| 244 | incoming_values: []*Instruction, | |
| 245 | ) !*Instruction { | |
| 246 | const inst = try irb.arena().create(Phi{ | |
| 247 | .base = Instruction{ | |
| 248 | .id = Instruction.Id.Phi, | |
| 249 | .is_generated = false, | |
| 250 | .scope = scope, | |
| 251 | .debug_id = irb.next_debug_id, | |
| 252 | .val = IrVal.Unknown, | |
| 253 | }, | |
| 254 | .incoming_blocks = incoming_blocks, | |
| 255 | .incoming_values = incoming_values, | |
| 256 | }); | |
| 257 | irb.next_debug_id += 1; | |
| 258 | try irb.current_basic_block.instruction_list.append(&inst.base); | |
| 259 | return &inst.base; | |
| 260 | } | |
| 261 | ||
| 262 | pub fn dump(inst: *const Phi) void {} | |
| 263 | }; | |
| 264 | ||
| 265 | pub const Br = struct { | |
| 266 | base: Instruction, | |
| 267 | dest_block: *BasicBlock, | |
| 268 | is_comptime: *Instruction, | |
| 269 | ||
| 270 | pub fn build( | |
| 271 | irb: *Builder, | |
| 272 | scope: *Scope, | |
| 273 | dest_block: *BasicBlock, | |
| 274 | is_comptime: *Instruction, | |
| 275 | ) !*Instruction { | |
| 276 | const inst = try irb.arena().create(Br{ | |
| 277 | .base = Instruction{ | |
| 278 | .id = Instruction.Id.Br, | |
| 279 | .is_generated = false, | |
| 280 | .scope = scope, | |
| 281 | .debug_id = irb.next_debug_id, | |
| 282 | .val = IrVal{ .Known = &Value.NoReturn.get(irb.module).base }, | |
| 283 | }, | |
| 284 | .dest_block = dest_block, | |
| 285 | .is_comptime = is_comptime, | |
| 286 | }); | |
| 287 | irb.next_debug_id += 1; | |
| 288 | try irb.current_basic_block.instruction_list.append(&inst.base); | |
| 289 | return &inst.base; | |
| 290 | } | |
| 291 | ||
| 292 | pub fn dump(inst: *const Br) void {} | |
| 110 | 293 | }; |
| 111 | 294 | }; |
| 295 | ||
| 296 | pub const Variable = struct { | |
| 297 | child_scope: *Scope, | |
| 298 | }; | |
| 299 | ||
| 300 | pub const BasicBlock = struct { | |
| 301 | ref_count: usize, | |
| 302 | name_hint: []const u8, | |
| 303 | debug_id: usize, | |
| 304 | scope: *Scope, | |
| 305 | instruction_list: std.ArrayList(*Instruction), | |
| 306 | ||
| 307 | pub fn ref(self: *BasicBlock) void { | |
| 308 | self.ref_count += 1; | |
| 309 | } | |
| 310 | }; | |
| 311 | ||
| 312 | /// Stuff that survives longer than Builder | |
| 313 | pub const Code = struct { | |
| 314 | basic_block_list: std.ArrayList(*BasicBlock), | |
| 315 | arena: std.heap.ArenaAllocator, | |
| 316 | ||
| 317 | /// allocator is module.a() | |
| 318 | pub fn destroy(self: *Code, allocator: *Allocator) void { | |
| 319 | self.arena.deinit(); | |
| 320 | allocator.destroy(self); | |
| 321 | } | |
| 322 | ||
| 323 | pub fn dump(self: *Code) void { | |
| 324 | var bb_i: usize = 0; | |
| 325 | for (self.basic_block_list.toSliceConst()) |bb| { | |
| 326 | std.debug.warn("{}_{}:\n", bb.name_hint, bb.debug_id); | |
| 327 | for (bb.instruction_list.toSliceConst()) |instr| { | |
| 328 | std.debug.warn(" "); | |
| 329 | instr.dump(); | |
| 330 | std.debug.warn("\n"); | |
| 331 | } | |
| 332 | } | |
| 333 | } | |
| 334 | }; | |
| 335 | ||
| 336 | pub const Builder = struct { | |
| 337 | module: *Module, | |
| 338 | code: *Code, | |
| 339 | current_basic_block: *BasicBlock, | |
| 340 | next_debug_id: usize, | |
| 341 | parsed_file: *ParsedFile, | |
| 342 | is_comptime: bool, | |
| 343 | ||
| 344 | pub const Error = error{ | |
| 345 | OutOfMemory, | |
| 346 | Unimplemented, | |
| 347 | }; | |
| 348 | ||
| 349 | pub fn init(module: *Module, parsed_file: *ParsedFile) !Builder { | |
| 350 | const code = try module.a().create(Code{ | |
| 351 | .basic_block_list = undefined, | |
| 352 | .arena = std.heap.ArenaAllocator.init(module.a()), | |
| 353 | }); | |
| 354 | code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); | |
| 355 | errdefer code.destroy(module.a()); | |
| 356 | ||
| 357 | return Builder{ | |
| 358 | .module = module, | |
| 359 | .parsed_file = parsed_file, | |
| 360 | .current_basic_block = undefined, | |
| 361 | .code = code, | |
| 362 | .next_debug_id = 0, | |
| 363 | .is_comptime = false, | |
| 364 | }; | |
| 365 | } | |
| 366 | ||
| 367 | pub fn abort(self: *Builder) void { | |
| 368 | self.code.destroy(self.module.a()); | |
| 369 | } | |
| 370 | ||
| 371 | /// Call code.destroy() when done | |
| 372 | pub fn finish(self: *Builder) *Code { | |
| 373 | return self.code; | |
| 374 | } | |
| 375 | ||
| 376 | /// No need to clean up resources thanks to the arena allocator. | |
| 377 | pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: []const u8) !*BasicBlock { | |
| 378 | const basic_block = try self.arena().create(BasicBlock{ | |
| 379 | .ref_count = 0, | |
| 380 | .name_hint = name_hint, | |
| 381 | .debug_id = self.next_debug_id, | |
| 382 | .scope = scope, | |
| 383 | .instruction_list = std.ArrayList(*Instruction).init(self.arena()), | |
| 384 | }); | |
| 385 | self.next_debug_id += 1; | |
| 386 | return basic_block; | |
| 387 | } | |
| 388 | ||
| 389 | pub fn setCursorAtEndAndAppendBlock(self: *Builder, basic_block: *BasicBlock) !void { | |
| 390 | try self.code.basic_block_list.append(basic_block); | |
| 391 | self.setCursorAtEnd(basic_block); | |
| 392 | } | |
| 393 | ||
| 394 | pub fn setCursorAtEnd(self: *Builder, basic_block: *BasicBlock) void { | |
| 395 | self.current_basic_block = basic_block; | |
| 396 | } | |
| 397 | ||
| 398 | pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Instruction { | |
| 399 | switch (node.id) { | |
| 400 | ast.Node.Id.Root => unreachable, | |
| 401 | ast.Node.Id.Use => unreachable, | |
| 402 | ast.Node.Id.TestDecl => unreachable, | |
| 403 | ast.Node.Id.VarDecl => @panic("TODO"), | |
| 404 | ast.Node.Id.Defer => @panic("TODO"), | |
| 405 | ast.Node.Id.InfixOp => @panic("TODO"), | |
| 406 | ast.Node.Id.PrefixOp => @panic("TODO"), | |
| 407 | ast.Node.Id.SuffixOp => @panic("TODO"), | |
| 408 | ast.Node.Id.Switch => @panic("TODO"), | |
| 409 | ast.Node.Id.While => @panic("TODO"), | |
| 410 | ast.Node.Id.For => @panic("TODO"), | |
| 411 | ast.Node.Id.If => @panic("TODO"), | |
| 412 | ast.Node.Id.ControlFlowExpression => return error.Unimplemented, | |
| 413 | ast.Node.Id.Suspend => @panic("TODO"), | |
| 414 | ast.Node.Id.VarType => @panic("TODO"), | |
| 415 | ast.Node.Id.ErrorType => @panic("TODO"), | |
| 416 | ast.Node.Id.FnProto => @panic("TODO"), | |
| 417 | ast.Node.Id.PromiseType => @panic("TODO"), | |
| 418 | ast.Node.Id.IntegerLiteral => @panic("TODO"), | |
| 419 | ast.Node.Id.FloatLiteral => @panic("TODO"), | |
| 420 | ast.Node.Id.StringLiteral => @panic("TODO"), | |
| 421 | ast.Node.Id.MultilineStringLiteral => @panic("TODO"), | |
| 422 | ast.Node.Id.CharLiteral => @panic("TODO"), | |
| 423 | ast.Node.Id.BoolLiteral => @panic("TODO"), | |
| 424 | ast.Node.Id.NullLiteral => @panic("TODO"), | |
| 425 | ast.Node.Id.UndefinedLiteral => @panic("TODO"), | |
| 426 | ast.Node.Id.ThisLiteral => @panic("TODO"), | |
| 427 | ast.Node.Id.Unreachable => @panic("TODO"), | |
| 428 | ast.Node.Id.Identifier => @panic("TODO"), | |
| 429 | ast.Node.Id.GroupedExpression => { | |
| 430 | const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); | |
| 431 | return irb.genNode(grouped_expr.expr, scope, lval); | |
| 432 | }, | |
| 433 | ast.Node.Id.BuiltinCall => @panic("TODO"), | |
| 434 | ast.Node.Id.ErrorSetDecl => @panic("TODO"), | |
| 435 | ast.Node.Id.ContainerDecl => @panic("TODO"), | |
| 436 | ast.Node.Id.Asm => @panic("TODO"), | |
| 437 | ast.Node.Id.Comptime => @panic("TODO"), | |
| 438 | ast.Node.Id.Block => { | |
| 439 | const block = @fieldParentPtr(ast.Node.Block, "base", node); | |
| 440 | return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval); | |
| 441 | }, | |
| 442 | ast.Node.Id.DocComment => @panic("TODO"), | |
| 443 | ast.Node.Id.SwitchCase => @panic("TODO"), | |
| 444 | ast.Node.Id.SwitchElse => @panic("TODO"), | |
| 445 | ast.Node.Id.Else => @panic("TODO"), | |
| 446 | ast.Node.Id.Payload => @panic("TODO"), | |
| 447 | ast.Node.Id.PointerPayload => @panic("TODO"), | |
| 448 | ast.Node.Id.PointerIndexPayload => @panic("TODO"), | |
| 449 | ast.Node.Id.StructField => @panic("TODO"), | |
| 450 | ast.Node.Id.UnionTag => @panic("TODO"), | |
| 451 | ast.Node.Id.EnumTag => @panic("TODO"), | |
| 452 | ast.Node.Id.ErrorTag => @panic("TODO"), | |
| 453 | ast.Node.Id.AsmInput => @panic("TODO"), | |
| 454 | ast.Node.Id.AsmOutput => @panic("TODO"), | |
| 455 | ast.Node.Id.AsyncAttribute => @panic("TODO"), | |
| 456 | ast.Node.Id.ParamDecl => @panic("TODO"), | |
| 457 | ast.Node.Id.FieldInitializer => @panic("TODO"), | |
| 458 | } | |
| 459 | } | |
| 460 | ||
| 461 | fn isCompTime(irb: *Builder, target_scope: *Scope) bool { | |
| 462 | if (irb.is_comptime) | |
| 463 | return true; | |
| 464 | ||
| 465 | var scope = target_scope; | |
| 466 | while (true) { | |
| 467 | switch (scope.id) { | |
| 468 | Scope.Id.CompTime => return true, | |
| 469 | Scope.Id.FnDef => return false, | |
| 470 | Scope.Id.Decls => unreachable, | |
| 471 | Scope.Id.Block, | |
| 472 | Scope.Id.Defer, | |
| 473 | Scope.Id.DeferExpr, | |
| 474 | => scope = scope.parent orelse return false, | |
| 475 | } | |
| 476 | } | |
| 477 | } | |
| 478 | ||
| 479 | pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction { | |
| 480 | const block_scope = try Scope.Block.create(irb.module, parent_scope); | |
| 481 | ||
| 482 | const outer_block_scope = &block_scope.base; | |
| 483 | var child_scope = outer_block_scope; | |
| 484 | ||
| 485 | if (parent_scope.findFnDef()) |fndef_scope| { | |
| 486 | if (fndef_scope.fn_val.child_scope == parent_scope) { | |
| 487 | fndef_scope.fn_val.block_scope = block_scope; | |
| 488 | } | |
| 489 | } | |
| 490 | ||
| 491 | if (block.statements.len == 0) { | |
| 492 | // {} | |
| 493 | return Instruction.Const.buildVoid(irb, child_scope, false); | |
| 494 | } | |
| 495 | ||
| 496 | if (block.label) |label| { | |
| 497 | block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena()); | |
| 498 | block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); | |
| 499 | block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd"); | |
| 500 | block_scope.is_comptime = try Instruction.Const.buildBool(irb, parent_scope, irb.isCompTime(parent_scope)); | |
| 501 | } | |
| 502 | ||
| 503 | var is_continuation_unreachable = false; | |
| 504 | var noreturn_return_value: ?*Instruction = null; | |
| 505 | ||
| 506 | var stmt_it = block.statements.iterator(0); | |
| 507 | while (stmt_it.next()) |statement_node_ptr| { | |
| 508 | const statement_node = statement_node_ptr.*; | |
| 509 | ||
| 510 | if (statement_node.cast(ast.Node.Defer)) |defer_node| { | |
| 511 | // defer starts a new scope | |
| 512 | const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token); | |
| 513 | const kind = switch (defer_token.id) { | |
| 514 | Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit, | |
| 515 | Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit, | |
| 516 | else => unreachable, | |
| 517 | }; | |
| 518 | const defer_expr_scope = try Scope.DeferExpr.create(irb.module, parent_scope, defer_node.expr); | |
| 519 | const defer_child_scope = try Scope.Defer.create(irb.module, parent_scope, kind, defer_expr_scope); | |
| 520 | child_scope = &defer_child_scope.base; | |
| 521 | continue; | |
| 522 | } | |
| 523 | const statement_value = try irb.genNode(statement_node, child_scope, LVal.None); | |
| 524 | ||
| 525 | is_continuation_unreachable = statement_value.isNoReturn(); | |
| 526 | if (is_continuation_unreachable) { | |
| 527 | // keep the last noreturn statement value around in case we need to return it | |
| 528 | noreturn_return_value = statement_value; | |
| 529 | } | |
| 530 | ||
| 531 | if (statement_value.cast(Instruction.DeclVar)) |decl_var| { | |
| 532 | // variable declarations start a new scope | |
| 533 | child_scope = decl_var.variable.child_scope; | |
| 534 | } else if (!is_continuation_unreachable) { | |
| 535 | // this statement's value must be void | |
| 536 | _ = Instruction.CheckVoidStmt.build(irb, child_scope, statement_value); | |
| 537 | } | |
| 538 | } | |
| 539 | ||
| 540 | if (is_continuation_unreachable) { | |
| 541 | assert(noreturn_return_value != null); | |
| 542 | if (block.label == null or block_scope.incoming_blocks.len == 0) { | |
| 543 | return noreturn_return_value.?; | |
| 544 | } | |
| 545 | ||
| 546 | try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); | |
| 547 | return Instruction.Phi.build( | |
| 548 | irb, | |
| 549 | parent_scope, | |
| 550 | block_scope.incoming_blocks.toOwnedSlice(), | |
| 551 | block_scope.incoming_values.toOwnedSlice(), | |
| 552 | ); | |
| 553 | } | |
| 554 | ||
| 555 | if (block.label) |label| { | |
| 556 | try block_scope.incoming_blocks.append(irb.current_basic_block); | |
| 557 | try block_scope.incoming_values.append( | |
| 558 | try Instruction.Const.buildVoid(irb, parent_scope, true), | |
| 559 | ); | |
| 560 | _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); | |
| 561 | (try Instruction.Br.build( | |
| 562 | irb, | |
| 563 | parent_scope, | |
| 564 | block_scope.end_block, | |
| 565 | block_scope.is_comptime, | |
| 566 | )).setGenerated(); | |
| 567 | try irb.setCursorAtEndAndAppendBlock(block_scope.end_block); | |
| 568 | return Instruction.Phi.build( | |
| 569 | irb, | |
| 570 | parent_scope, | |
| 571 | block_scope.incoming_blocks.toOwnedSlice(), | |
| 572 | block_scope.incoming_values.toOwnedSlice(), | |
| 573 | ); | |
| 574 | } | |
| 575 | ||
| 576 | _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit); | |
| 577 | const result = try Instruction.Const.buildVoid(irb, child_scope, false); | |
| 578 | result.setGenerated(); | |
| 579 | return result; | |
| 580 | } | |
| 581 | ||
| 582 | fn genDefersForBlock( | |
| 583 | irb: *Builder, | |
| 584 | inner_scope: *Scope, | |
| 585 | outer_scope: *Scope, | |
| 586 | gen_kind: Scope.Defer.Kind, | |
| 587 | ) !bool { | |
| 588 | var scope = inner_scope; | |
| 589 | var is_noreturn = false; | |
| 590 | while (true) { | |
| 591 | switch (scope.id) { | |
| 592 | Scope.Id.Defer => { | |
| 593 | const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope); | |
| 594 | const generate = switch (defer_scope.kind) { | |
| 595 | Scope.Defer.Kind.ScopeExit => true, | |
| 596 | Scope.Defer.Kind.ErrorExit => gen_kind == Scope.Defer.Kind.ErrorExit, | |
| 597 | }; | |
| 598 | if (generate) { | |
| 599 | const defer_expr_scope = defer_scope.defer_expr_scope; | |
| 600 | const instruction = try irb.genNode( | |
| 601 | defer_expr_scope.expr_node, | |
| 602 | &defer_expr_scope.base, | |
| 603 | LVal.None, | |
| 604 | ); | |
| 605 | if (instruction.isNoReturn()) { | |
| 606 | is_noreturn = true; | |
| 607 | } else { | |
| 608 | _ = Instruction.CheckVoidStmt.build(irb, &defer_expr_scope.base, instruction); | |
| 609 | } | |
| 610 | } | |
| 611 | }, | |
| 612 | Scope.Id.FnDef, | |
| 613 | Scope.Id.Decls, | |
| 614 | => return is_noreturn, | |
| 615 | ||
| 616 | Scope.Id.CompTime, | |
| 617 | Scope.Id.Block, | |
| 618 | => scope = scope.parent orelse return is_noreturn, | |
| 619 | ||
| 620 | Scope.Id.DeferExpr => unreachable, | |
| 621 | } | |
| 622 | } | |
| 623 | } | |
| 624 | ||
| 625 | pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Instruction, lval: LVal) !*Instruction { | |
| 626 | switch (lval) { | |
| 627 | LVal.None => return instruction, | |
| 628 | LVal.Ptr => { | |
| 629 | // We needed a pointer to a value, but we got a value. So we create | |
| 630 | // an instruction which just makes a const pointer of it. | |
| 631 | return Instruction.Ref.build(irb, scope, instruction, Mut.Const, Volatility.NonVolatile); | |
| 632 | }, | |
| 633 | } | |
| 634 | } | |
| 635 | ||
| 636 | fn arena(self: *Builder) *Allocator { | |
| 637 | return &self.code.arena.allocator; | |
| 638 | } | |
| 639 | }; | |
| 640 | ||
| 641 | pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_file: *ParsedFile) !*Code { | |
| 642 | var irb = try Builder.init(module, parsed_file); | |
| 643 | errdefer irb.abort(); | |
| 644 | ||
| 645 | const entry_block = try irb.createBasicBlock(scope, "Entry"); | |
| 646 | entry_block.ref(); // Entry block gets a reference because we enter it to begin. | |
| 647 | try irb.setCursorAtEndAndAppendBlock(entry_block); | |
| 648 | ||
| 649 | const result = try irb.genNode(body_node, scope, LVal.None); | |
| 650 | if (!result.isNoReturn()) { | |
| 651 | const void_inst = try Instruction.Const.buildVoid(&irb, scope, false); | |
| 652 | (try Instruction.Return.build(&irb, scope, void_inst)).setGenerated(); | |
| 653 | } | |
| 654 | ||
| 655 | return irb.finish(); | |
| 656 | } |
src-self-hosted/module.zig+186-197| ... | ... | @@ -15,12 +15,21 @@ const errmsg = @import("errmsg.zig"); |
| 15 | 15 | const ast = std.zig.ast; |
| 16 | 16 | const event = std.event; |
| 17 | 17 | const assert = std.debug.assert; |
| 18 | const AtomicRmwOp = builtin.AtomicRmwOp; | |
| 19 | const AtomicOrder = builtin.AtomicOrder; | |
| 20 | const Scope = @import("scope.zig").Scope; | |
| 21 | const Decl = @import("decl.zig").Decl; | |
| 22 | const ir = @import("ir.zig"); | |
| 23 | const Visib = @import("visib.zig").Visib; | |
| 24 | const ParsedFile = @import("parsed_file.zig").ParsedFile; | |
| 25 | const Value = @import("value.zig").Value; | |
| 26 | const Type = Value.Type; | |
| 18 | 27 | |
| 19 | 28 | pub const Module = struct { |
| 20 | 29 | loop: *event.Loop, |
| 21 | 30 | name: Buffer, |
| 22 | 31 | root_src_path: ?[]const u8, |
| 23 | module: llvm.ModuleRef, | |
| 32 | llvm_module: llvm.ModuleRef, | |
| 24 | 33 | context: llvm.ContextRef, |
| 25 | 34 | builder: llvm.BuilderRef, |
| 26 | 35 | target: Target, |
| ... | ... | @@ -91,6 +100,16 @@ pub const Module = struct { |
| 91 | 100 | |
| 92 | 101 | compile_errors: event.Locked(CompileErrList), |
| 93 | 102 | |
| 103 | meta_type: *Type.MetaType, | |
| 104 | void_type: *Type.Void, | |
| 105 | bool_type: *Type.Bool, | |
| 106 | noreturn_type: *Type.NoReturn, | |
| 107 | ||
| 108 | void_value: *Value.Void, | |
| 109 | true_value: *Value.Bool, | |
| 110 | false_value: *Value.Bool, | |
| 111 | noreturn_value: *Value.NoReturn, | |
| 112 | ||
| 94 | 113 | const CompileErrList = std.ArrayList(*errmsg.Msg); |
| 95 | 114 | |
| 96 | 115 | // TODO handle some of these earlier and report them in a way other than error codes |
| ... | ... | @@ -129,6 +148,7 @@ pub const Module = struct { |
| 129 | 148 | Overflow, |
| 130 | 149 | NotSupported, |
| 131 | 150 | BufferTooSmall, |
| 151 | Unimplemented, | |
| 132 | 152 | }; |
| 133 | 153 | |
| 134 | 154 | pub const Event = union(enum) { |
| ... | ... | @@ -180,8 +200,8 @@ pub const Module = struct { |
| 180 | 200 | const context = c.LLVMContextCreate() orelse return error.OutOfMemory; |
| 181 | 201 | errdefer c.LLVMContextDispose(context); |
| 182 | 202 | |
| 183 | const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory; | |
| 184 | errdefer c.LLVMDisposeModule(module); | |
| 203 | const llvm_module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory; | |
| 204 | errdefer c.LLVMDisposeModule(llvm_module); | |
| 185 | 205 | |
| 186 | 206 | const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory; |
| 187 | 207 | errdefer c.LLVMDisposeBuilder(builder); |
| ... | ... | @@ -189,12 +209,12 @@ pub const Module = struct { |
| 189 | 209 | const events = try event.Channel(Event).create(loop, 0); |
| 190 | 210 | errdefer events.destroy(); |
| 191 | 211 | |
| 192 | return loop.allocator.create(Module{ | |
| 212 | const module = try loop.allocator.create(Module{ | |
| 193 | 213 | .loop = loop, |
| 194 | 214 | .events = events, |
| 195 | 215 | .name = name_buffer, |
| 196 | 216 | .root_src_path = root_src_path, |
| 197 | .module = module, | |
| 217 | .llvm_module = llvm_module, | |
| 198 | 218 | .context = context, |
| 199 | 219 | .builder = builder, |
| 200 | 220 | .target = target.*, |
| ... | ... | @@ -248,7 +268,109 @@ pub const Module = struct { |
| 248 | 268 | .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), |
| 249 | 269 | .build_group = event.Group(BuildError!void).init(loop), |
| 250 | 270 | .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), |
| 271 | ||
| 272 | .meta_type = undefined, | |
| 273 | .void_type = undefined, | |
| 274 | .void_value = undefined, | |
| 275 | .bool_type = undefined, | |
| 276 | .true_value = undefined, | |
| 277 | .false_value = undefined, | |
| 278 | .noreturn_type = undefined, | |
| 279 | .noreturn_value = undefined, | |
| 280 | }); | |
| 281 | try module.initTypes(); | |
| 282 | return module; | |
| 283 | } | |
| 284 | ||
| 285 | fn initTypes(module: *Module) !void { | |
| 286 | module.meta_type = try module.a().create(Type.MetaType{ | |
| 287 | .base = Type{ | |
| 288 | .base = Value{ | |
| 289 | .id = Value.Id.Type, | |
| 290 | .typeof = undefined, | |
| 291 | .ref_count = 3, // 3 because it references itself twice | |
| 292 | }, | |
| 293 | .id = builtin.TypeId.Type, | |
| 294 | }, | |
| 295 | .value = undefined, | |
| 296 | }); | |
| 297 | module.meta_type.value = &module.meta_type.base; | |
| 298 | module.meta_type.base.base.typeof = &module.meta_type.base; | |
| 299 | errdefer module.a().destroy(module.meta_type); | |
| 300 | ||
| 301 | module.void_type = try module.a().create(Type.Void{ | |
| 302 | .base = Type{ | |
| 303 | .base = Value{ | |
| 304 | .id = Value.Id.Type, | |
| 305 | .typeof = &Type.MetaType.get(module).base, | |
| 306 | .ref_count = 1, | |
| 307 | }, | |
| 308 | .id = builtin.TypeId.Void, | |
| 309 | }, | |
| 310 | }); | |
| 311 | errdefer module.a().destroy(module.void_type); | |
| 312 | ||
| 313 | module.noreturn_type = try module.a().create(Type.NoReturn{ | |
| 314 | .base = Type{ | |
| 315 | .base = Value{ | |
| 316 | .id = Value.Id.Type, | |
| 317 | .typeof = &Type.MetaType.get(module).base, | |
| 318 | .ref_count = 1, | |
| 319 | }, | |
| 320 | .id = builtin.TypeId.NoReturn, | |
| 321 | }, | |
| 322 | }); | |
| 323 | errdefer module.a().destroy(module.noreturn_type); | |
| 324 | ||
| 325 | module.bool_type = try module.a().create(Type.Bool{ | |
| 326 | .base = Type{ | |
| 327 | .base = Value{ | |
| 328 | .id = Value.Id.Type, | |
| 329 | .typeof = &Type.MetaType.get(module).base, | |
| 330 | .ref_count = 1, | |
| 331 | }, | |
| 332 | .id = builtin.TypeId.Bool, | |
| 333 | }, | |
| 334 | }); | |
| 335 | errdefer module.a().destroy(module.bool_type); | |
| 336 | ||
| 337 | module.void_value = try module.a().create(Value.Void{ | |
| 338 | .base = Value{ | |
| 339 | .id = Value.Id.Void, | |
| 340 | .typeof = &Type.Void.get(module).base, | |
| 341 | .ref_count = 1, | |
| 342 | }, | |
| 343 | }); | |
| 344 | errdefer module.a().destroy(module.void_value); | |
| 345 | ||
| 346 | module.true_value = try module.a().create(Value.Bool{ | |
| 347 | .base = Value{ | |
| 348 | .id = Value.Id.Bool, | |
| 349 | .typeof = &Type.Bool.get(module).base, | |
| 350 | .ref_count = 1, | |
| 351 | }, | |
| 352 | .x = true, | |
| 353 | }); | |
| 354 | errdefer module.a().destroy(module.true_value); | |
| 355 | ||
| 356 | module.false_value = try module.a().create(Value.Bool{ | |
| 357 | .base = Value{ | |
| 358 | .id = Value.Id.Bool, | |
| 359 | .typeof = &Type.Bool.get(module).base, | |
| 360 | .ref_count = 1, | |
| 361 | }, | |
| 362 | .x = false, | |
| 251 | 363 | }); |
| 364 | errdefer module.a().destroy(module.false_value); | |
| 365 | ||
| 366 | module.noreturn_value = try module.a().create(Value.NoReturn{ | |
| 367 | .base = Value{ | |
| 368 | .id = Value.Id.NoReturn, | |
| 369 | .typeof = &Type.NoReturn.get(module).base, | |
| 370 | .ref_count = 1, | |
| 371 | }, | |
| 372 | }); | |
| 373 | errdefer module.a().destroy(module.noreturn_value); | |
| 252 | 374 | } |
| 253 | 375 | |
| 254 | 376 | fn dump(self: *Module) void { |
| ... | ... | @@ -256,9 +378,17 @@ pub const Module = struct { |
| 256 | 378 | } |
| 257 | 379 | |
| 258 | 380 | pub fn destroy(self: *Module) void { |
| 381 | self.noreturn_value.base.deref(self); | |
| 382 | self.void_value.base.deref(self); | |
| 383 | self.false_value.base.deref(self); | |
| 384 | self.true_value.base.deref(self); | |
| 385 | self.noreturn_type.base.base.deref(self); | |
| 386 | self.void_type.base.base.deref(self); | |
| 387 | self.meta_type.base.base.deref(self); | |
| 388 | ||
| 259 | 389 | self.events.destroy(); |
| 260 | 390 | c.LLVMDisposeBuilder(self.builder); |
| 261 | c.LLVMDisposeModule(self.module); | |
| 391 | c.LLVMDisposeModule(self.llvm_module); | |
| 262 | 392 | c.LLVMContextDispose(self.context); |
| 263 | 393 | self.name.deinit(); |
| 264 | 394 | |
| ... | ... | @@ -331,8 +461,8 @@ pub const Module = struct { |
| 331 | 461 | const tree = &parsed_file.tree; |
| 332 | 462 | |
| 333 | 463 | // create empty struct for it |
| 334 | const decls = try Scope.Decls.create(self.a(), null); | |
| 335 | errdefer decls.destroy(); | |
| 464 | const decls = try Scope.Decls.create(self, null); | |
| 465 | defer decls.base.deref(self); | |
| 336 | 466 | |
| 337 | 467 | var decl_group = event.Group(BuildError!void).init(self.loop); |
| 338 | 468 | errdefer decl_group.cancelAll(); |
| ... | ... | @@ -359,14 +489,17 @@ pub const Module = struct { |
| 359 | 489 | .id = Decl.Id.Fn, |
| 360 | 490 | .name = name, |
| 361 | 491 | .visib = parseVisibToken(tree, fn_proto.visib_token), |
| 362 | .resolution = Decl.Resolution.Unresolved, | |
| 492 | .resolution = event.Future(BuildError!void).init(self.loop), | |
| 493 | .resolution_in_progress = 0, | |
| 494 | .parsed_file = parsed_file, | |
| 495 | .parent_scope = &decls.base, | |
| 363 | 496 | }, |
| 364 | 497 | .value = Decl.Fn.Val{ .Unresolved = {} }, |
| 365 | 498 | .fn_proto = fn_proto, |
| 366 | 499 | }); |
| 367 | 500 | errdefer self.a().destroy(fn_decl); |
| 368 | 501 | |
| 369 | try decl_group.call(addTopLevelDecl, self, parsed_file, &fn_decl.base); | |
| 502 | try decl_group.call(addTopLevelDecl, self, &fn_decl.base); | |
| 370 | 503 | }, |
| 371 | 504 | ast.Node.Id.TestDecl => @panic("TODO"), |
| 372 | 505 | else => unreachable, |
| ... | ... | @@ -376,12 +509,12 @@ pub const Module = struct { |
| 376 | 509 | try await (async self.build_group.wait() catch unreachable); |
| 377 | 510 | } |
| 378 | 511 | |
| 379 | async fn addTopLevelDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { | |
| 380 | const is_export = decl.isExported(&parsed_file.tree); | |
| 512 | async fn addTopLevelDecl(self: *Module, decl: *Decl) !void { | |
| 513 | const is_export = decl.isExported(&decl.parsed_file.tree); | |
| 381 | 514 | |
| 382 | 515 | if (is_export) { |
| 383 | try self.build_group.call(verifyUniqueSymbol, self, parsed_file, decl); | |
| 384 | try self.build_group.call(generateDecl, self, parsed_file, decl); | |
| 516 | try self.build_group.call(verifyUniqueSymbol, self, decl); | |
| 517 | try self.build_group.call(resolveDecl, self, decl); | |
| 385 | 518 | } |
| 386 | 519 | } |
| 387 | 520 | |
| ... | ... | @@ -416,36 +549,21 @@ pub const Module = struct { |
| 416 | 549 | try compile_errors.value.append(msg); |
| 417 | 550 | } |
| 418 | 551 | |
| 419 | async fn verifyUniqueSymbol(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { | |
| 552 | async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void { | |
| 420 | 553 | const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable); |
| 421 | 554 | defer exported_symbol_names.release(); |
| 422 | 555 | |
| 423 | 556 | if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { |
| 424 | 557 | try self.addCompileError( |
| 425 | parsed_file, | |
| 558 | decl.parsed_file, | |
| 426 | 559 | decl.getSpan(), |
| 427 | 560 | "exported symbol collision: '{}'", |
| 428 | 561 | decl.name, |
| 429 | 562 | ); |
| 563 | // TODO add error note showing location of other symbol | |
| 430 | 564 | } |
| 431 | 565 | } |
| 432 | 566 | |
| 433 | /// This declaration has been blessed as going into the final code generation. | |
| 434 | async fn generateDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) void { | |
| 435 | switch (decl.id) { | |
| 436 | Decl.Id.Var => @panic("TODO"), | |
| 437 | Decl.Id.Fn => { | |
| 438 | const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl); | |
| 439 | return await (async self.generateDeclFn(parsed_file, fn_decl) catch unreachable); | |
| 440 | }, | |
| 441 | Decl.Id.CompTime => @panic("TODO"), | |
| 442 | } | |
| 443 | } | |
| 444 | ||
| 445 | async fn generateDeclFn(self: *Module, parsed_file: *ParsedFile, fn_decl: *Decl.Fn) void { | |
| 446 | fn_decl.value = Decl.Fn.Val{ .Ok = Value.Fn{} }; | |
| 447 | } | |
| 448 | ||
| 449 | 567 | pub fn link(self: *Module, out_file: ?[]const u8) !void { |
| 450 | 568 | warn("TODO link"); |
| 451 | 569 | return error.Todo; |
| ... | ... | @@ -501,177 +619,48 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib |
| 501 | 619 | } |
| 502 | 620 | } |
| 503 | 621 | |
| 504 | pub const Scope = struct { | |
| 505 | id: Id, | |
| 506 | parent: ?*Scope, | |
| 507 | ||
| 508 | pub const Id = enum { | |
| 509 | Decls, | |
| 510 | Block, | |
| 511 | }; | |
| 512 | ||
| 513 | pub const Decls = struct { | |
| 514 | base: Scope, | |
| 515 | table: Decl.Table, | |
| 516 | ||
| 517 | pub fn create(a: *Allocator, parent: ?*Scope) !*Decls { | |
| 518 | const self = try a.create(Decls{ | |
| 519 | .base = Scope{ | |
| 520 | .id = Id.Decls, | |
| 521 | .parent = parent, | |
| 522 | }, | |
| 523 | .table = undefined, | |
| 524 | }); | |
| 525 | errdefer a.destroy(self); | |
| 526 | ||
| 527 | self.table = Decl.Table.init(a); | |
| 528 | errdefer self.table.deinit(); | |
| 529 | ||
| 530 | return self; | |
| 531 | } | |
| 532 | ||
| 533 | pub fn destroy(self: *Decls) void { | |
| 534 | self.table.deinit(); | |
| 535 | self.table.allocator.destroy(self); | |
| 536 | self.* = undefined; | |
| 537 | } | |
| 538 | }; | |
| 539 | ||
| 540 | pub const Block = struct { | |
| 541 | base: Scope, | |
| 542 | }; | |
| 543 | }; | |
| 544 | ||
| 545 | pub const Visib = enum { | |
| 546 | Private, | |
| 547 | Pub, | |
| 548 | }; | |
| 549 | ||
| 550 | pub const Decl = struct { | |
| 551 | id: Id, | |
| 552 | name: []const u8, | |
| 553 | visib: Visib, | |
| 554 | resolution: Resolution, | |
| 555 | ||
| 556 | pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); | |
| 557 | ||
| 558 | pub fn isExported(base: *const Decl, tree: *ast.Tree) bool { | |
| 559 | switch (base.id) { | |
| 560 | Id.Fn => { | |
| 561 | const fn_decl = @fieldParentPtr(Fn, "base", base); | |
| 562 | return fn_decl.isExported(tree); | |
| 563 | }, | |
| 564 | else => return false, | |
| 565 | } | |
| 622 | /// This declaration has been blessed as going into the final code generation. | |
| 623 | pub async fn resolveDecl(module: *Module, decl: *Decl) !void { | |
| 624 | if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { | |
| 625 | decl.resolution.data = await (async generateDecl(module, decl) catch unreachable); | |
| 626 | decl.resolution.resolve(); | |
| 627 | } else { | |
| 628 | return (await (async decl.resolution.get() catch unreachable)).*; | |
| 566 | 629 | } |
| 630 | } | |
| 567 | 631 | |
| 568 | pub fn getSpan(base: *const Decl) errmsg.Span { | |
| 569 | switch (base.id) { | |
| 570 | Id.Fn => { | |
| 571 | const fn_decl = @fieldParentPtr(Fn, "base", base); | |
| 572 | const fn_proto = fn_decl.fn_proto; | |
| 573 | const start = fn_proto.fn_token; | |
| 574 | const end = fn_proto.name_token orelse start; | |
| 575 | return errmsg.Span{ | |
| 576 | .first = start, | |
| 577 | .last = end + 1, | |
| 578 | }; | |
| 579 | }, | |
| 580 | else => @panic("TODO"), | |
| 581 | } | |
| 632 | /// The function that actually does the generation. | |
| 633 | async fn generateDecl(module: *Module, decl: *Decl) !void { | |
| 634 | switch (decl.id) { | |
| 635 | Decl.Id.Var => @panic("TODO"), | |
| 636 | Decl.Id.Fn => { | |
| 637 | const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl); | |
| 638 | return await (async generateDeclFn(module, fn_decl) catch unreachable); | |
| 639 | }, | |
| 640 | Decl.Id.CompTime => @panic("TODO"), | |
| 582 | 641 | } |
| 642 | } | |
| 583 | 643 | |
| 584 | pub const Resolution = enum { | |
| 585 | Unresolved, | |
| 586 | InProgress, | |
| 587 | Invalid, | |
| 588 | Ok, | |
| 589 | }; | |
| 590 | ||
| 591 | pub const Id = enum { | |
| 592 | Var, | |
| 593 | Fn, | |
| 594 | CompTime, | |
| 595 | }; | |
| 596 | ||
| 597 | pub const Var = struct { | |
| 598 | base: Decl, | |
| 599 | }; | |
| 644 | async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void { | |
| 645 | const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl"); | |
| 600 | 646 | |
| 601 | pub const Fn = struct { | |
| 602 | base: Decl, | |
| 603 | value: Val, | |
| 604 | fn_proto: *const ast.Node.FnProto, | |
| 647 | const fndef_scope = try Scope.FnDef.create(module, fn_decl.base.parent_scope); | |
| 648 | defer fndef_scope.base.deref(module); | |
| 605 | 649 | |
| 606 | // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous | |
| 607 | pub const Val = union { | |
| 608 | Unresolved: void, | |
| 609 | Ok: Value.Fn, | |
| 610 | }; | |
| 650 | const fn_type = try Type.Fn.create(module); | |
| 651 | defer fn_type.base.base.deref(module); | |
| 611 | 652 | |
| 612 | pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { | |
| 613 | return if (self.fn_proto.extern_export_inline_token) |tok_index| x: { | |
| 614 | const token = tree.tokens.at(tok_index); | |
| 615 | break :x switch (token.id) { | |
| 616 | Token.Id.Extern => tree.tokenSlicePtr(token), | |
| 617 | else => null, | |
| 618 | }; | |
| 619 | } else null; | |
| 620 | } | |
| 653 | const fn_val = try Value.Fn.create(module, fn_type, fndef_scope); | |
| 654 | defer fn_val.base.deref(module); | |
| 621 | 655 | |
| 622 | pub fn isExported(self: Fn, tree: *ast.Tree) bool { | |
| 623 | if (self.fn_proto.extern_export_inline_token) |tok_index| { | |
| 624 | const token = tree.tokens.at(tok_index); | |
| 625 | return token.id == Token.Id.Keyword_export; | |
| 626 | } else { | |
| 627 | return false; | |
| 628 | } | |
| 629 | } | |
| 630 | }; | |
| 656 | fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; | |
| 631 | 657 | |
| 632 | pub const CompTime = struct { | |
| 633 | base: Decl, | |
| 634 | }; | |
| 635 | }; | |
| 636 | ||
| 637 | pub const Value = struct { | |
| 638 | pub const Fn = struct {}; | |
| 639 | }; | |
| 640 | ||
| 641 | pub const Type = struct { | |
| 642 | id: Id, | |
| 643 | ||
| 644 | pub const Id = enum { | |
| 645 | Type, | |
| 646 | Void, | |
| 647 | Bool, | |
| 648 | NoReturn, | |
| 649 | Int, | |
| 650 | Float, | |
| 651 | Pointer, | |
| 652 | Array, | |
| 653 | Struct, | |
| 654 | ComptimeFloat, | |
| 655 | ComptimeInt, | |
| 656 | Undefined, | |
| 657 | Null, | |
| 658 | Optional, | |
| 659 | ErrorUnion, | |
| 660 | ErrorSet, | |
| 661 | Enum, | |
| 662 | Union, | |
| 663 | Fn, | |
| 664 | Opaque, | |
| 665 | Promise, | |
| 666 | }; | |
| 667 | ||
| 668 | pub const Struct = struct { | |
| 669 | base: Type, | |
| 670 | decls: *Scope.Decls, | |
| 671 | }; | |
| 672 | }; | |
| 673 | ||
| 674 | pub const ParsedFile = struct { | |
| 675 | tree: ast.Tree, | |
| 676 | realpath: []const u8, | |
| 677 | }; | |
| 658 | const code = try await (async ir.gen( | |
| 659 | module, | |
| 660 | body_node, | |
| 661 | &fndef_scope.base, | |
| 662 | fn_decl.base.parsed_file, | |
| 663 | ) catch unreachable); | |
| 664 | //code.dump(); | |
| 665 | //try await (async irAnalyze(module, func) catch unreachable); | |
| 666 | } |
src-self-hosted/parsed_file.zig created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | const ast = @import("std").zig.ast; | |
| 2 | ||
| 3 | pub const ParsedFile = struct { | |
| 4 | tree: ast.Tree, | |
| 5 | realpath: []const u8, | |
| 6 | }; |
src-self-hosted/scope.zig+224-6| ... | ... | @@ -1,16 +1,234 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Allocator = mem.Allocator; | |
| 3 | const Decl = @import("decl.zig").Decl; | |
| 4 | const Module = @import("module.zig").Module; | |
| 5 | const mem = std.mem; | |
| 6 | const ast = std.zig.ast; | |
| 7 | const Value = @import("value.zig").Value; | |
| 8 | const ir = @import("ir.zig"); | |
| 9 | ||
| 1 | 10 | pub const Scope = struct { |
| 2 | 11 | id: Id, |
| 3 | parent: *Scope, | |
| 12 | parent: ?*Scope, | |
| 13 | ref_count: usize, | |
| 14 | ||
| 15 | pub fn ref(base: *Scope) void { | |
| 16 | base.ref_count += 1; | |
| 17 | } | |
| 18 | ||
| 19 | pub fn deref(base: *Scope, module: *Module) void { | |
| 20 | base.ref_count -= 1; | |
| 21 | if (base.ref_count == 0) { | |
| 22 | if (base.parent) |parent| parent.deref(module); | |
| 23 | switch (base.id) { | |
| 24 | Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(), | |
| 25 | Id.Block => @fieldParentPtr(Block, "base", base).destroy(module), | |
| 26 | Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(module), | |
| 27 | Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(module), | |
| 28 | Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(module), | |
| 29 | Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(module), | |
| 30 | } | |
| 31 | } | |
| 32 | } | |
| 33 | ||
| 34 | pub fn findFnDef(base: *Scope) ?*FnDef { | |
| 35 | var scope = base; | |
| 36 | while (true) { | |
| 37 | switch (scope.id) { | |
| 38 | Id.FnDef => return @fieldParentPtr(FnDef, "base", base), | |
| 39 | Id.Decls => return null, | |
| 40 | ||
| 41 | Id.Block, | |
| 42 | Id.Defer, | |
| 43 | Id.DeferExpr, | |
| 44 | Id.CompTime, | |
| 45 | => scope = scope.parent orelse return null, | |
| 46 | } | |
| 47 | } | |
| 48 | } | |
| 4 | 49 | |
| 5 | 50 | pub const Id = enum { |
| 6 | 51 | Decls, |
| 7 | 52 | Block, |
| 8 | Defer, | |
| 9 | DeferExpr, | |
| 10 | VarDecl, | |
| 11 | CImport, | |
| 12 | Loop, | |
| 13 | 53 | FnDef, |
| 14 | 54 | CompTime, |
| 55 | Defer, | |
| 56 | DeferExpr, | |
| 57 | }; | |
| 58 | ||
| 59 | pub const Decls = struct { | |
| 60 | base: Scope, | |
| 61 | table: Decl.Table, | |
| 62 | ||
| 63 | /// Creates a Decls scope with 1 reference | |
| 64 | pub fn create(module: *Module, parent: ?*Scope) !*Decls { | |
| 65 | const self = try module.a().create(Decls{ | |
| 66 | .base = Scope{ | |
| 67 | .id = Id.Decls, | |
| 68 | .parent = parent, | |
| 69 | .ref_count = 1, | |
| 70 | }, | |
| 71 | .table = undefined, | |
| 72 | }); | |
| 73 | errdefer module.a().destroy(self); | |
| 74 | ||
| 75 | self.table = Decl.Table.init(module.a()); | |
| 76 | errdefer self.table.deinit(); | |
| 77 | ||
| 78 | if (parent) |p| p.ref(); | |
| 79 | ||
| 80 | return self; | |
| 81 | } | |
| 82 | ||
| 83 | pub fn destroy(self: *Decls) void { | |
| 84 | self.table.deinit(); | |
| 85 | self.table.allocator.destroy(self); | |
| 86 | } | |
| 87 | }; | |
| 88 | ||
| 89 | pub const Block = struct { | |
| 90 | base: Scope, | |
| 91 | incoming_values: std.ArrayList(*ir.Instruction), | |
| 92 | incoming_blocks: std.ArrayList(*ir.BasicBlock), | |
| 93 | end_block: *ir.BasicBlock, | |
| 94 | is_comptime: *ir.Instruction, | |
| 95 | ||
| 96 | /// Creates a Block scope with 1 reference | |
| 97 | pub fn create(module: *Module, parent: ?*Scope) !*Block { | |
| 98 | const self = try module.a().create(Block{ | |
| 99 | .base = Scope{ | |
| 100 | .id = Id.Block, | |
| 101 | .parent = parent, | |
| 102 | .ref_count = 1, | |
| 103 | }, | |
| 104 | .incoming_values = undefined, | |
| 105 | .incoming_blocks = undefined, | |
| 106 | .end_block = undefined, | |
| 107 | .is_comptime = undefined, | |
| 108 | }); | |
| 109 | errdefer module.a().destroy(self); | |
| 110 | ||
| 111 | if (parent) |p| p.ref(); | |
| 112 | return self; | |
| 113 | } | |
| 114 | ||
| 115 | pub fn destroy(self: *Block, module: *Module) void { | |
| 116 | module.a().destroy(self); | |
| 117 | } | |
| 118 | }; | |
| 119 | ||
| 120 | pub const FnDef = struct { | |
| 121 | base: Scope, | |
| 122 | ||
| 123 | /// This reference is not counted so that the scope can get destroyed with the function | |
| 124 | fn_val: *Value.Fn, | |
| 125 | ||
| 126 | /// Creates a FnDef scope with 1 reference | |
| 127 | /// Must set the fn_val later | |
| 128 | pub fn create(module: *Module, parent: ?*Scope) !*FnDef { | |
| 129 | const self = try module.a().create(FnDef{ | |
| 130 | .base = Scope{ | |
| 131 | .id = Id.FnDef, | |
| 132 | .parent = parent, | |
| 133 | .ref_count = 1, | |
| 134 | }, | |
| 135 | .fn_val = undefined, | |
| 136 | }); | |
| 137 | ||
| 138 | if (parent) |p| p.ref(); | |
| 139 | ||
| 140 | return self; | |
| 141 | } | |
| 142 | ||
| 143 | pub fn destroy(self: *FnDef, module: *Module) void { | |
| 144 | module.a().destroy(self); | |
| 145 | } | |
| 146 | }; | |
| 147 | ||
| 148 | pub const CompTime = struct { | |
| 149 | base: Scope, | |
| 150 | ||
| 151 | /// Creates a CompTime scope with 1 reference | |
| 152 | pub fn create(module: *Module, parent: ?*Scope) !*CompTime { | |
| 153 | const self = try module.a().create(CompTime{ | |
| 154 | .base = Scope{ | |
| 155 | .id = Id.CompTime, | |
| 156 | .parent = parent, | |
| 157 | .ref_count = 1, | |
| 158 | }, | |
| 159 | }); | |
| 160 | ||
| 161 | if (parent) |p| p.ref(); | |
| 162 | return self; | |
| 163 | } | |
| 164 | ||
| 165 | pub fn destroy(self: *CompTime, module: *Module) void { | |
| 166 | module.a().destroy(self); | |
| 167 | } | |
| 168 | }; | |
| 169 | ||
| 170 | pub const Defer = struct { | |
| 171 | base: Scope, | |
| 172 | defer_expr_scope: *DeferExpr, | |
| 173 | kind: Kind, | |
| 174 | ||
| 175 | pub const Kind = enum { | |
| 176 | ScopeExit, | |
| 177 | ErrorExit, | |
| 178 | }; | |
| 179 | ||
| 180 | /// Creates a Defer scope with 1 reference | |
| 181 | pub fn create( | |
| 182 | module: *Module, | |
| 183 | parent: ?*Scope, | |
| 184 | kind: Kind, | |
| 185 | defer_expr_scope: *DeferExpr, | |
| 186 | ) !*Defer { | |
| 187 | const self = try module.a().create(Defer{ | |
| 188 | .base = Scope{ | |
| 189 | .id = Id.Defer, | |
| 190 | .parent = parent, | |
| 191 | .ref_count = 1, | |
| 192 | }, | |
| 193 | .defer_expr_scope = defer_expr_scope, | |
| 194 | .kind = kind, | |
| 195 | }); | |
| 196 | errdefer module.a().destroy(self); | |
| 197 | ||
| 198 | defer_expr_scope.base.ref(); | |
| 199 | ||
| 200 | if (parent) |p| p.ref(); | |
| 201 | return self; | |
| 202 | } | |
| 203 | ||
| 204 | pub fn destroy(self: *Defer, module: *Module) void { | |
| 205 | self.defer_expr_scope.base.deref(module); | |
| 206 | module.a().destroy(self); | |
| 207 | } | |
| 208 | }; | |
| 209 | ||
| 210 | pub const DeferExpr = struct { | |
| 211 | base: Scope, | |
| 212 | expr_node: *ast.Node, | |
| 213 | ||
| 214 | /// Creates a DeferExpr scope with 1 reference | |
| 215 | pub fn create(module: *Module, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { | |
| 216 | const self = try module.a().create(DeferExpr{ | |
| 217 | .base = Scope{ | |
| 218 | .id = Id.DeferExpr, | |
| 219 | .parent = parent, | |
| 220 | .ref_count = 1, | |
| 221 | }, | |
| 222 | .expr_node = expr_node, | |
| 223 | }); | |
| 224 | errdefer module.a().destroy(self); | |
| 225 | ||
| 226 | if (parent) |p| p.ref(); | |
| 227 | return self; | |
| 228 | } | |
| 229 | ||
| 230 | pub fn destroy(self: *DeferExpr, module: *Module) void { | |
| 231 | module.a().destroy(self); | |
| 232 | } | |
| 15 | 233 | }; |
| 16 | 234 | }; |
src-self-hosted/type.zig created+268| ... | ... | @@ -0,0 +1,268 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const Scope = @import("scope.zig").Scope; | |
| 3 | const Module = @import("module.zig").Module; | |
| 4 | const Value = @import("value.zig").Value; | |
| 5 | ||
| 6 | pub const Type = struct { | |
| 7 | base: Value, | |
| 8 | id: Id, | |
| 9 | ||
| 10 | pub const Id = builtin.TypeId; | |
| 11 | ||
| 12 | pub fn destroy(base: *Type, module: *Module) void { | |
| 13 | switch (base.id) { | |
| 14 | Id.Struct => @fieldParentPtr(Struct, "base", base).destroy(module), | |
| 15 | Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module), | |
| 16 | Id.Type => @fieldParentPtr(MetaType, "base", base).destroy(module), | |
| 17 | Id.Void => @fieldParentPtr(Void, "base", base).destroy(module), | |
| 18 | Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module), | |
| 19 | Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module), | |
| 20 | Id.Int => @fieldParentPtr(Int, "base", base).destroy(module), | |
| 21 | Id.Float => @fieldParentPtr(Float, "base", base).destroy(module), | |
| 22 | Id.Pointer => @fieldParentPtr(Pointer, "base", base).destroy(module), | |
| 23 | Id.Array => @fieldParentPtr(Array, "base", base).destroy(module), | |
| 24 | Id.ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(module), | |
| 25 | Id.ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(module), | |
| 26 | Id.Undefined => @fieldParentPtr(Undefined, "base", base).destroy(module), | |
| 27 | Id.Null => @fieldParentPtr(Null, "base", base).destroy(module), | |
| 28 | Id.Optional => @fieldParentPtr(Optional, "base", base).destroy(module), | |
| 29 | Id.ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(module), | |
| 30 | Id.ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(module), | |
| 31 | Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(module), | |
| 32 | Id.Union => @fieldParentPtr(Union, "base", base).destroy(module), | |
| 33 | Id.Namespace => @fieldParentPtr(Namespace, "base", base).destroy(module), | |
| 34 | Id.Block => @fieldParentPtr(Block, "base", base).destroy(module), | |
| 35 | Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(module), | |
| 36 | Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(module), | |
| 37 | Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(module), | |
| 38 | Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(module), | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | pub const Struct = struct { | |
| 43 | base: Type, | |
| 44 | decls: *Scope.Decls, | |
| 45 | ||
| 46 | pub fn destroy(self: *Struct, module: *Module) void { | |
| 47 | module.a().destroy(self); | |
| 48 | } | |
| 49 | }; | |
| 50 | ||
| 51 | pub const Fn = struct { | |
| 52 | base: Type, | |
| 53 | ||
| 54 | pub fn create(module: *Module) !*Fn { | |
| 55 | return module.a().create(Fn{ | |
| 56 | .base = Type{ | |
| 57 | .base = Value{ | |
| 58 | .id = Value.Id.Type, | |
| 59 | .typeof = &MetaType.get(module).base, | |
| 60 | .ref_count = 1, | |
| 61 | }, | |
| 62 | .id = builtin.TypeId.Fn, | |
| 63 | }, | |
| 64 | }); | |
| 65 | } | |
| 66 | ||
| 67 | pub fn destroy(self: *Fn, module: *Module) void { | |
| 68 | module.a().destroy(self); | |
| 69 | } | |
| 70 | }; | |
| 71 | ||
| 72 | pub const MetaType = struct { | |
| 73 | base: Type, | |
| 74 | value: *Type, | |
| 75 | ||
| 76 | /// Adds 1 reference to the resulting type | |
| 77 | pub fn get(module: *Module) *MetaType { | |
| 78 | module.meta_type.base.base.ref(); | |
| 79 | return module.meta_type; | |
| 80 | } | |
| 81 | ||
| 82 | pub fn destroy(self: *MetaType, module: *Module) void { | |
| 83 | module.a().destroy(self); | |
| 84 | } | |
| 85 | }; | |
| 86 | ||
| 87 | pub const Void = struct { | |
| 88 | base: Type, | |
| 89 | ||
| 90 | /// Adds 1 reference to the resulting type | |
| 91 | pub fn get(module: *Module) *Void { | |
| 92 | module.void_type.base.base.ref(); | |
| 93 | return module.void_type; | |
| 94 | } | |
| 95 | ||
| 96 | pub fn destroy(self: *Void, module: *Module) void { | |
| 97 | module.a().destroy(self); | |
| 98 | } | |
| 99 | }; | |
| 100 | ||
| 101 | pub const Bool = struct { | |
| 102 | base: Type, | |
| 103 | ||
| 104 | /// Adds 1 reference to the resulting type | |
| 105 | pub fn get(module: *Module) *Bool { | |
| 106 | module.bool_type.base.base.ref(); | |
| 107 | return module.bool_type; | |
| 108 | } | |
| 109 | ||
| 110 | pub fn destroy(self: *Bool, module: *Module) void { | |
| 111 | module.a().destroy(self); | |
| 112 | } | |
| 113 | }; | |
| 114 | ||
| 115 | pub const NoReturn = struct { | |
| 116 | base: Type, | |
| 117 | ||
| 118 | /// Adds 1 reference to the resulting type | |
| 119 | pub fn get(module: *Module) *NoReturn { | |
| 120 | module.noreturn_type.base.base.ref(); | |
| 121 | return module.noreturn_type; | |
| 122 | } | |
| 123 | ||
| 124 | pub fn destroy(self: *NoReturn, module: *Module) void { | |
| 125 | module.a().destroy(self); | |
| 126 | } | |
| 127 | }; | |
| 128 | ||
| 129 | pub const Int = struct { | |
| 130 | base: Type, | |
| 131 | ||
| 132 | pub fn destroy(self: *Int, module: *Module) void { | |
| 133 | module.a().destroy(self); | |
| 134 | } | |
| 135 | }; | |
| 136 | ||
| 137 | pub const Float = struct { | |
| 138 | base: Type, | |
| 139 | ||
| 140 | pub fn destroy(self: *Float, module: *Module) void { | |
| 141 | module.a().destroy(self); | |
| 142 | } | |
| 143 | }; | |
| 144 | pub const Pointer = struct { | |
| 145 | base: Type, | |
| 146 | ||
| 147 | pub fn destroy(self: *Pointer, module: *Module) void { | |
| 148 | module.a().destroy(self); | |
| 149 | } | |
| 150 | }; | |
| 151 | pub const Array = struct { | |
| 152 | base: Type, | |
| 153 | ||
| 154 | pub fn destroy(self: *Array, module: *Module) void { | |
| 155 | module.a().destroy(self); | |
| 156 | } | |
| 157 | }; | |
| 158 | pub const ComptimeFloat = struct { | |
| 159 | base: Type, | |
| 160 | ||
| 161 | pub fn destroy(self: *ComptimeFloat, module: *Module) void { | |
| 162 | module.a().destroy(self); | |
| 163 | } | |
| 164 | }; | |
| 165 | pub const ComptimeInt = struct { | |
| 166 | base: Type, | |
| 167 | ||
| 168 | pub fn destroy(self: *ComptimeInt, module: *Module) void { | |
| 169 | module.a().destroy(self); | |
| 170 | } | |
| 171 | }; | |
| 172 | pub const Undefined = struct { | |
| 173 | base: Type, | |
| 174 | ||
| 175 | pub fn destroy(self: *Undefined, module: *Module) void { | |
| 176 | module.a().destroy(self); | |
| 177 | } | |
| 178 | }; | |
| 179 | pub const Null = struct { | |
| 180 | base: Type, | |
| 181 | ||
| 182 | pub fn destroy(self: *Null, module: *Module) void { | |
| 183 | module.a().destroy(self); | |
| 184 | } | |
| 185 | }; | |
| 186 | pub const Optional = struct { | |
| 187 | base: Type, | |
| 188 | ||
| 189 | pub fn destroy(self: *Optional, module: *Module) void { | |
| 190 | module.a().destroy(self); | |
| 191 | } | |
| 192 | }; | |
| 193 | pub const ErrorUnion = struct { | |
| 194 | base: Type, | |
| 195 | ||
| 196 | pub fn destroy(self: *ErrorUnion, module: *Module) void { | |
| 197 | module.a().destroy(self); | |
| 198 | } | |
| 199 | }; | |
| 200 | pub const ErrorSet = struct { | |
| 201 | base: Type, | |
| 202 | ||
| 203 | pub fn destroy(self: *ErrorSet, module: *Module) void { | |
| 204 | module.a().destroy(self); | |
| 205 | } | |
| 206 | }; | |
| 207 | pub const Enum = struct { | |
| 208 | base: Type, | |
| 209 | ||
| 210 | pub fn destroy(self: *Enum, module: *Module) void { | |
| 211 | module.a().destroy(self); | |
| 212 | } | |
| 213 | }; | |
| 214 | pub const Union = struct { | |
| 215 | base: Type, | |
| 216 | ||
| 217 | pub fn destroy(self: *Union, module: *Module) void { | |
| 218 | module.a().destroy(self); | |
| 219 | } | |
| 220 | }; | |
| 221 | pub const Namespace = struct { | |
| 222 | base: Type, | |
| 223 | ||
| 224 | pub fn destroy(self: *Namespace, module: *Module) void { | |
| 225 | module.a().destroy(self); | |
| 226 | } | |
| 227 | }; | |
| 228 | ||
| 229 | pub const Block = struct { | |
| 230 | base: Type, | |
| 231 | ||
| 232 | pub fn destroy(self: *Block, module: *Module) void { | |
| 233 | module.a().destroy(self); | |
| 234 | } | |
| 235 | }; | |
| 236 | ||
| 237 | pub const BoundFn = struct { | |
| 238 | base: Type, | |
| 239 | ||
| 240 | pub fn destroy(self: *BoundFn, module: *Module) void { | |
| 241 | module.a().destroy(self); | |
| 242 | } | |
| 243 | }; | |
| 244 | ||
| 245 | pub const ArgTuple = struct { | |
| 246 | base: Type, | |
| 247 | ||
| 248 | pub fn destroy(self: *ArgTuple, module: *Module) void { | |
| 249 | module.a().destroy(self); | |
| 250 | } | |
| 251 | }; | |
| 252 | ||
| 253 | pub const Opaque = struct { | |
| 254 | base: Type, | |
| 255 | ||
| 256 | pub fn destroy(self: *Opaque, module: *Module) void { | |
| 257 | module.a().destroy(self); | |
| 258 | } | |
| 259 | }; | |
| 260 | ||
| 261 | pub const Promise = struct { | |
| 262 | base: Type, | |
| 263 | ||
| 264 | pub fn destroy(self: *Promise, module: *Module) void { | |
| 265 | module.a().destroy(self); | |
| 266 | } | |
| 267 | }; | |
| 268 | }; |
src-self-hosted/value.zig created+125| ... | ... | @@ -0,0 +1,125 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const Scope = @import("scope.zig").Scope; | |
| 4 | const Module = @import("module.zig").Module; | |
| 5 | ||
| 6 | /// Values are ref-counted, heap-allocated, and copy-on-write | |
| 7 | /// If there is only 1 ref then write need not copy | |
| 8 | pub const Value = struct { | |
| 9 | id: Id, | |
| 10 | typeof: *Type, | |
| 11 | ref_count: usize, | |
| 12 | ||
| 13 | pub fn ref(base: *Value) void { | |
| 14 | base.ref_count += 1; | |
| 15 | } | |
| 16 | ||
| 17 | pub fn deref(base: *Value, module: *Module) void { | |
| 18 | base.ref_count -= 1; | |
| 19 | if (base.ref_count == 0) { | |
| 20 | base.typeof.base.deref(module); | |
| 21 | switch (base.id) { | |
| 22 | Id.Type => @fieldParentPtr(Type, "base", base).destroy(module), | |
| 23 | Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module), | |
| 24 | Id.Void => @fieldParentPtr(Void, "base", base).destroy(module), | |
| 25 | Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module), | |
| 26 | Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module), | |
| 27 | } | |
| 28 | } | |
| 29 | } | |
| 30 | ||
| 31 | pub fn dump(base: *const Value) void { | |
| 32 | std.debug.warn("{}", @tagName(base.id)); | |
| 33 | } | |
| 34 | ||
| 35 | pub const Id = enum { | |
| 36 | Type, | |
| 37 | Fn, | |
| 38 | Void, | |
| 39 | Bool, | |
| 40 | NoReturn, | |
| 41 | }; | |
| 42 | ||
| 43 | pub const Type = @import("type.zig").Type; | |
| 44 | ||
| 45 | pub const Fn = struct { | |
| 46 | base: Value, | |
| 47 | ||
| 48 | /// parent should be the top level decls or container decls | |
| 49 | fndef_scope: *Scope.FnDef, | |
| 50 | ||
| 51 | /// parent is scope for last parameter | |
| 52 | child_scope: *Scope, | |
| 53 | ||
| 54 | /// parent is child_scope | |
| 55 | block_scope: *Scope.Block, | |
| 56 | ||
| 57 | /// Creates a Fn value with 1 ref | |
| 58 | pub fn create(module: *Module, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef) !*Fn { | |
| 59 | const self = try module.a().create(Fn{ | |
| 60 | .base = Value{ | |
| 61 | .id = Value.Id.Fn, | |
| 62 | .typeof = &fn_type.base, | |
| 63 | .ref_count = 1, | |
| 64 | }, | |
| 65 | .fndef_scope = fndef_scope, | |
| 66 | .child_scope = &fndef_scope.base, | |
| 67 | .block_scope = undefined, | |
| 68 | }); | |
| 69 | fn_type.base.base.ref(); | |
| 70 | fndef_scope.fn_val = self; | |
| 71 | fndef_scope.base.ref(); | |
| 72 | return self; | |
| 73 | } | |
| 74 | ||
| 75 | pub fn destroy(self: *Fn, module: *Module) void { | |
| 76 | self.fndef_scope.base.deref(module); | |
| 77 | module.a().destroy(self); | |
| 78 | } | |
| 79 | }; | |
| 80 | ||
| 81 | pub const Void = struct { | |
| 82 | base: Value, | |
| 83 | ||
| 84 | pub fn get(module: *Module) *Void { | |
| 85 | module.void_value.base.ref(); | |
| 86 | return module.void_value; | |
| 87 | } | |
| 88 | ||
| 89 | pub fn destroy(self: *Void, module: *Module) void { | |
| 90 | module.a().destroy(self); | |
| 91 | } | |
| 92 | }; | |
| 93 | ||
| 94 | pub const Bool = struct { | |
| 95 | base: Value, | |
| 96 | x: bool, | |
| 97 | ||
| 98 | pub fn get(module: *Module, x: bool) *Bool { | |
| 99 | if (x) { | |
| 100 | module.true_value.base.ref(); | |
| 101 | return module.true_value; | |
| 102 | } else { | |
| 103 | module.false_value.base.ref(); | |
| 104 | return module.false_value; | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | pub fn destroy(self: *Bool, module: *Module) void { | |
| 109 | module.a().destroy(self); | |
| 110 | } | |
| 111 | }; | |
| 112 | ||
| 113 | pub const NoReturn = struct { | |
| 114 | base: Value, | |
| 115 | ||
| 116 | pub fn get(module: *Module) *NoReturn { | |
| 117 | module.noreturn_value.base.ref(); | |
| 118 | return module.noreturn_value; | |
| 119 | } | |
| 120 | ||
| 121 | pub fn destroy(self: *NoReturn, module: *Module) void { | |
| 122 | module.a().destroy(self); | |
| 123 | } | |
| 124 | }; | |
| 125 | }; |
src-self-hosted/visib.zig created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | pub const Visib = enum { | |
| 2 | Private, | |
| 3 | Pub, | |
| 4 | }; |
std/event/future.zig+1-1| ... | ... | @@ -57,7 +57,7 @@ test "std.event.Future" { |
| 57 | 57 | const allocator = &da.allocator; |
| 58 | 58 | |
| 59 | 59 | var loop: Loop = undefined; |
| 60 | try loop.initSingleThreaded(allocator); | |
| 60 | try loop.initMultiThreaded(allocator); | |
| 61 | 61 | defer loop.deinit(); |
| 62 | 62 | |
| 63 | 63 | const handle = try async<allocator> testFuture(&loop); |
std/zig/ast.zig-6| ... | ... | @@ -970,14 +970,8 @@ pub const Node = struct { |
| 970 | 970 | pub const Defer = struct { |
| 971 | 971 | base: Node, |
| 972 | 972 | defer_token: TokenIndex, |
| 973 | kind: Kind, | |
| 974 | 973 | expr: *Node, |
| 975 | 974 | |
| 976 | const Kind = enum { | |
| 977 | Error, | |
| 978 | Unconditional, | |
| 979 | }; | |
| 980 | ||
| 981 | 975 | pub fn iterate(self: *Defer, index: usize) ?*Node { |
| 982 | 976 | var i = index; |
| 983 | 977 |
std/zig/parse.zig-5| ... | ... | @@ -1041,11 +1041,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 1041 | 1041 | const node = try arena.create(ast.Node.Defer{ |
| 1042 | 1042 | .base = ast.Node{ .id = ast.Node.Id.Defer }, |
| 1043 | 1043 | .defer_token = token_index, |
| 1044 | .kind = switch (token_ptr.id) { | |
| 1045 | Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, | |
| 1046 | Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, | |
| 1047 | else => unreachable, | |
| 1048 | }, | |
| 1049 | 1044 | .expr = undefined, |
| 1050 | 1045 | }); |
| 1051 | 1046 | const node_ptr = try block.statements.addOne(); |