| ... | ... | @@ -1,26 +1,35 @@ |
| 1 | 1 | //! Transform pass converts an Ast into a high-level/untyped instruction format. |
| 2 | 2 | //! See Teg.zig for more details about the IR. Transforming handles: |
| 3 | 3 | //! |
| 4 | //! - Flattening nested expressions into instruction sequences. |
| 4 | 5 | //! - Resolving all local variables into reference or declaration names. |
| 5 | | //! - Resolving breaks and returns to their block id. |
| 6 | //! - Resolving breaks and returns to their generated block id. |
| 6 | 7 | //! - Annotating expression statements with `.discard` and `.ensure_void` |
| 7 | | //! - Flattening nested expressions into instruction sequences. |
| 8 | //! - Resolving implicit expression with their corresponding result type |
| 8 | 9 | //! |
| 9 | 10 | //! Some trivial diagnostics such as unused variables, unknown result types, |
| 10 | 11 | //! invalid assignment targets, and other style issues are also reported here. |
| 12 | |
| 11 | 13 | gpa: Allocator, |
| 14 | arena: Allocator, |
| 15 | |
| 12 | 16 | ast: Ast, |
| 17 | |
| 18 | // these values are allocated through `gpa`, so that resulting IR can be |
| 19 | // trivially shrunk before returning. |
| 20 | |
| 13 | 21 | insts: Inst.List = .{}, |
| 14 | | string_bytes: std.ArrayListUnmanaged(u8) = .{}, |
| 15 | 22 | extra: ExtraStore = .{}, |
| 23 | string_bytes: std.ArrayListUnmanaged(u8) = .{}, |
| 24 | report: Report.Builder, |
| 16 | 25 | |
| 17 | | scratch: std.ArrayListUnmanaged(u32) = .{}, |
| 18 | | id_decode_buf: std.ArrayList(u8), |
| 26 | // however, these are allocated through `arena`, since they are always freed at |
| 27 | // the end of the pass. |
| 19 | 28 | |
| 29 | scratch: core.U32Stack = .{}, |
| 30 | id_decode_buf: std.ArrayList(u8), |
| 20 | 31 | scopes: std.ArrayListUnmanaged(Scope) = .{}, |
| 21 | 32 | |
| 22 | | report: Report.Builder, |
| 23 | | |
| 24 | 33 | pub const Error = error{ |
| 25 | 34 | /// A `Report` will contain at least one error. |
| 26 | 35 | TransformFail, |
| ... | ... | @@ -40,6 +49,8 @@ pub fn transform( |
| 40 | 49 | var t: Transform = .{ |
| 41 | 50 | .ast = ast, |
| 42 | 51 | .gpa = gpa, |
| 52 | // TODO: Currently this is illusion of free will |
| 53 | .arena = gpa, |
| 43 | 54 | .report = .{ .gpa = gpa, .current_file = file }, |
| 44 | 55 | .id_decode_buf = std.ArrayList(u8).initCapacity(sfa.get(), 4096) catch unreachable, |
| 45 | 56 | }; |
| ... | ... | @@ -57,9 +68,7 @@ pub fn transform( |
| 57 | 68 | return error.TransformFail; |
| 58 | 69 | } |
| 59 | 70 | |
| 60 | | assert(@intFromEnum(module) == 0); |
| 61 | | |
| 62 | | try t.insts.setCapacity(t.gpa, t.insts.len); |
| 71 | assert(module == .main_module_inst); |
| 63 | 72 | |
| 64 | 73 | return .{ |
| 65 | 74 | .insts = t.insts.toOwnedSlice(), |
| ... | ... | @@ -69,84 +78,82 @@ pub fn transform( |
| 69 | 78 | }; |
| 70 | 79 | } |
| 71 | 80 | |
| 72 | | pub fn deinit(t: *Transform) void { |
| 73 | | t.scratch.deinit(t.gpa); |
| 81 | fn deinit(t: *Transform) void { |
| 74 | 82 | assert(t.scopes.items.len == 0); |
| 75 | | t.scopes.deinit(t.gpa); |
| 83 | t.id_decode_buf.deinit(); |
| 84 | t.scratch.list.deinit(t.arena); |
| 85 | t.scopes.deinit(t.arena); |
| 86 | t.id_decode_buf.deinit(); |
| 76 | 87 | t.string_bytes.deinit(t.gpa); |
| 77 | 88 | t.insts.deinit(t.gpa); |
| 78 | 89 | t.extra.deinit(t.gpa); |
| 79 | | t.id_decode_buf.deinit(); |
| 80 | 90 | t.* = undefined; |
| 81 | 91 | } |
| 82 | 92 | |
| 83 | | const Scope = struct { |
| 84 | | symbols: std.StringArrayHashMapUnmanaged(Symbol) = .{}, |
| 85 | | |
| 86 | | const Symbol = struct { |
| 87 | | node: NodeIndex, |
| 88 | | data: Symbol.Data, |
| 89 | | // TODO: optimize this structure |
| 90 | | used: bool = false, |
| 91 | | mutated: bool = false, |
| 92 | | discarded: ?NodeIndex = null, |
| 93 | | |
| 94 | | const Data = union(enum) { |
| 95 | | declaration, |
| 96 | | param, |
| 97 | | mutable: Inst.Index, |
| 98 | | immutable: Inst.Ref, |
| 93 | /// Represents how the parent node is going to implicitly cast this expression. |
| 94 | /// |
| 95 | /// Consider an expression like `let x: S = .{ y: 1 }`. The type from the |
| 96 | /// declaration must impact the visited struct initialization expression. This |
| 97 | /// behavior is recursive, so something more complex can properly infer: |
| 98 | /// |
| 99 | /// struct S { a: int } |
| 100 | /// struct Y {} |
| 101 | /// let x: [](S, []Y) = [ .{ .{ a: 1 }, [ .{} ] } ]; |
| 102 | /// |
| 103 | /// The innermost `.{}` must eventually analyze to `Y{}`, which can be unwrapped |
| 104 | /// from the type expression via specialized instructions for this purpose. |
| 105 | const ResultType = union(enum) { |
| 106 | /// There is no result type available |
| 107 | none, |
| 108 | /// The result type was consumed |
| 109 | used, |
| 110 | /// The expression will be cast to this ref. |
| 111 | ref: Inst.Ref, |
| 112 | /// The expression will be cast to the type of this ref |
| 113 | type_of: Inst.Ref, |
| 114 | /// The expression is an argument to a function. |
| 115 | argument: Teg.ResultTypeArgument, |
| 116 | /// The expression is a field in a struct initialization |
| 117 | struct_field: Teg.ResultTypeStructField, |
| 118 | /// The expression is a field in a tuple initialization |
| 119 | tuple_field: Teg.ResultTypeTupleField, |
| 120 | /// The expression is a field in an array initialization |
| 121 | array_value: Inst.Ref, |
| 122 | |
| 123 | /// This will return an instruction pointing to the the type |
| 124 | pub fn consume(result: *ResultType, t: *Transform) !?Inst.Ref { |
| 125 | if (result.* == .none) return null; |
| 126 | defer result.* = .used; |
| 127 | return switch (result.*) { |
| 128 | .none => unreachable, |
| 129 | .used => unreachable, // Use after free |
| 130 | .ref => |r| r, |
| 131 | .type_of => |r| try t.addRef(.type_of, .{ |
| 132 | // Result types don't have associated nodes, which is ok because |
| 133 | // type_of cannot fail in the context we are using it. Instead, |
| 134 | // we will use a predictable poison value, so that an attempt |
| 135 | // to print this will crash. |
| 136 | .src_node = std.math.maxInt(i32), |
| 137 | .operand = r, |
| 138 | }), |
| 139 | .argument => |r| try t.addRef(.result_type_argument, r), |
| 140 | .struct_field => |r| try t.addRef(.result_type_struct_field, r), |
| 141 | .tuple_field => |r| try t.addRef(.result_type_tuple_field, r), |
| 142 | .array_value => |r| try t.addRef(.result_type_array_value, .{ .type = r }), |
| 99 | 143 | }; |
| 100 | | |
| 101 | | pub fn addWarnings(symbol: Symbol, t: *Transform) !void { |
| 102 | | if (!symbol.mutated and symbol.data == .mutable) { |
| 103 | | try t.report.warn(.not_mutated_var, .{ |
| 104 | | .node = symbol.node, |
| 105 | | }); |
| 106 | | } |
| 107 | | if (!symbol.used) { |
| 108 | | try t.report.warn(.unused, .{ |
| 109 | | .node = symbol.node, |
| 110 | | .kind = switch (symbol.data) { |
| 111 | | .declaration => .function, |
| 112 | | .param => .param, |
| 113 | | .mutable => .variable, |
| 114 | | .immutable => switch (t.ast.nodeTag(symbol.node)) { |
| 115 | | .param_decl => .param, |
| 116 | | else => .constant, |
| 117 | | }, |
| 118 | | }, |
| 119 | | }); |
| 120 | | } else if (symbol.discarded) |discard| { |
| 121 | | try t.report.warn(.unnecessary_discard, .{ |
| 122 | | .node = discard, |
| 123 | | }); |
| 124 | | } |
| 125 | | } |
| 126 | | }; |
| 127 | | |
| 128 | | fn addWarnings(s: Scope, t: *Transform) !void { |
| 129 | | for (s.symbols.values()) |value| { |
| 130 | | try value.addWarnings(t); |
| 131 | | } |
| 132 | | } |
| 133 | | |
| 134 | | fn deinit(s: *Scope, t: *Transform) void { |
| 135 | | for (s.symbols.keys()) |k| { |
| 136 | | if (!isSliceInBuffer(u8, t.ast.utf8_source, k)) { |
| 137 | | t.gpa.free(k); |
| 138 | | } |
| 139 | | } |
| 140 | | s.symbols.deinit(t.gpa); |
| 141 | | s.* = undefined; |
| 142 | 144 | } |
| 143 | 145 | }; |
| 144 | 146 | |
| 145 | 147 | const BlockContext = struct { |
| 146 | | node_base: i32, |
| 147 | | return_target: ?*?Inst.Index = null, |
| 148 | decl_node_offset: i32, |
| 149 | return_target: ?*ReturnTarget = null, |
| 148 | 150 | break_target: ?*BreakTarget = null, |
| 149 | 151 | |
| 152 | const ReturnTarget = struct { |
| 153 | generated_block: ?Inst.Index = null, |
| 154 | result_type: ResultType, |
| 155 | }; |
| 156 | |
| 150 | 157 | /// This struct is always stored on the stack; the idea is to pass mutable |
| 151 | 158 | /// pointers to it. When `break :brk` is encountered, it will generate the |
| 152 | 159 | /// block instruction. This way, redundant blocks are never created. |
| ... | ... | @@ -157,6 +164,7 @@ const BlockContext = struct { |
| 157 | 164 | /// If `null`, this label was unused. |
| 158 | 165 | block_index: ?Inst.Index = null, |
| 159 | 166 | parent: ?*BreakTarget = null, |
| 167 | result_type: *ResultType, |
| 160 | 168 | |
| 161 | 169 | fn addChild(parent: *BreakTarget, label: ?[]const u8, is_loop: bool) BreakTarget { |
| 162 | 170 | return .{ |
| ... | ... | @@ -168,10 +176,7 @@ const BlockContext = struct { |
| 168 | 176 | |
| 169 | 177 | fn getInst(target: *BreakTarget, t: *Transform) !Inst.Index { |
| 170 | 178 | return target.block_index orelse { |
| 171 | | const inst = try t.addRootInst(.{ |
| 172 | | .tag = .block, |
| 173 | | .data = undefined, |
| 174 | | }); |
| 179 | const inst = try t.addUntracked(.block, undefined); |
| 175 | 180 | target.block_index = inst; |
| 176 | 181 | return inst; |
| 177 | 182 | }; |
| ... | ... | @@ -180,41 +185,41 @@ const BlockContext = struct { |
| 180 | 185 | |
| 181 | 186 | fn init(root_node: NodeIndex) BlockContext { |
| 182 | 187 | return .{ |
| 183 | | .node_base = @intCast(root_node), |
| 188 | .decl_node_offset = @intCast(root_node), |
| 184 | 189 | }; |
| 185 | 190 | } |
| 186 | 191 | |
| 187 | 192 | fn nodeOffset(self: BlockContext, node: NodeIndex) i32 { |
| 188 | | return @as(i32, @intCast(node)) - self.node_base; |
| 193 | return @as(i32, @intCast(node)) - self.decl_node_offset; |
| 189 | 194 | } |
| 190 | 195 | |
| 191 | 196 | fn declOffset(self: BlockContext, node: NodeIndex) BlockContext { |
| 192 | 197 | return .{ |
| 193 | | .node_base = self.nodeOffset(node), |
| 198 | .decl_node_offset = self.nodeOffset(node), |
| 194 | 199 | }; |
| 195 | 200 | } |
| 196 | 201 | |
| 197 | | fn withReturn(self: BlockContext, return_target: ?*?Inst.Index) BlockContext { |
| 202 | fn withReturn(self: BlockContext, return_target: ?*ReturnTarget) BlockContext { |
| 198 | 203 | return .{ |
| 199 | | .node_base = self.node_base, |
| 204 | .decl_node_offset = self.decl_node_offset, |
| 200 | 205 | .return_target = return_target, |
| 201 | 206 | }; |
| 202 | 207 | } |
| 203 | 208 | |
| 204 | 209 | fn withBreak(self: BlockContext, break_target: *BreakTarget) BlockContext { |
| 205 | 210 | return .{ |
| 206 | | .node_base = self.node_base, |
| 211 | .decl_node_offset = self.decl_node_offset, |
| 207 | 212 | .return_target = self.return_target, |
| 208 | 213 | .break_target = break_target, |
| 209 | 214 | }; |
| 210 | 215 | } |
| 211 | 216 | |
| 212 | | fn findAndMarkBreak(self: BlockContext, t: *Transform, label: []const u8) !?Inst.Index { |
| 217 | fn lookupNamedBreak(self: BlockContext, label: []const u8) ?*BreakTarget { |
| 213 | 218 | var break_target = self.break_target; |
| 214 | 219 | while (break_target) |target| : (break_target = target.parent) { |
| 215 | 220 | if (target.label) |target_label| { |
| 216 | 221 | if (mem.eql(u8, label, target_label)) { |
| 217 | | return try target.getInst(t); |
| 222 | return target; |
| 218 | 223 | } |
| 219 | 224 | } |
| 220 | 225 | } |
| ... | ... | @@ -222,6 +227,84 @@ const BlockContext = struct { |
| 222 | 227 | } |
| 223 | 228 | }; |
| 224 | 229 | |
| 230 | const Scope = struct { |
| 231 | symbols: std.StringArrayHashMapUnmanaged(Symbol) = .{}, |
| 232 | |
| 233 | // TODO: optimize this structure's size. |
| 234 | const Symbol = struct { |
| 235 | node: NodeIndex, |
| 236 | data: Symbol.Data, |
| 237 | used: bool = false, |
| 238 | mutated: bool = false, |
| 239 | discarded: ?NodeIndex = null, |
| 240 | type: SymbolResultType, |
| 241 | |
| 242 | const Data = union(enum) { |
| 243 | declaration, |
| 244 | param, |
| 245 | mutable: Inst.Index, |
| 246 | immutable: Inst.Ref, |
| 247 | }; |
| 248 | |
| 249 | const SymbolResultType = union(enum) { |
| 250 | /// Points to the resolved result type (.ref), as is. |
| 251 | present: Inst.Ref, |
| 252 | /// This points to the value. Which means a `type_of` instruction |
| 253 | /// must be inserted before it may be used. |
| 254 | missing: Inst.Ref, |
| 255 | |
| 256 | pub fn resolve(srt: SymbolResultType) ResultType { |
| 257 | return switch (srt) { |
| 258 | .present => |type_ref| .{ .ref = type_ref }, |
| 259 | .missing => |value_ref| .{ .type_of = value_ref }, |
| 260 | }; |
| 261 | } |
| 262 | }; |
| 263 | |
| 264 | pub fn addWarnings(symbol: Symbol, t: *Transform) !void { |
| 265 | if (!symbol.mutated and symbol.data == .mutable) { |
| 266 | try t.report.warn(.not_mutated_var, .{ |
| 267 | .node = symbol.node, |
| 268 | }); |
| 269 | } |
| 270 | if (!symbol.used) { |
| 271 | try t.report.warn(.unused, .{ |
| 272 | .node = symbol.node, |
| 273 | .kind = switch (symbol.data) { |
| 274 | .declaration => .function, |
| 275 | .param => .param, |
| 276 | .mutable => .variable, |
| 277 | .immutable => switch (t.ast.nodeTag(symbol.node)) { |
| 278 | .param_decl => .param, |
| 279 | else => .constant, |
| 280 | }, |
| 281 | }, |
| 282 | }); |
| 283 | } else if (symbol.discarded) |discard| { |
| 284 | try t.report.warn(.unnecessary_discard, .{ |
| 285 | .node = discard, |
| 286 | }); |
| 287 | } |
| 288 | } |
| 289 | }; |
| 290 | |
| 291 | fn addWarnings(s: Scope, t: *Transform) !void { |
| 292 | for (s.symbols.values()) |value| { |
| 293 | try value.addWarnings(t); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | fn deinit(s: *Scope, t: *Transform) void { |
| 298 | for (s.symbols.keys()) |k| { |
| 299 | if (!isSliceInBuffer(u8, t.ast.utf8_source, k)) { |
| 300 | t.gpa.free(k); |
| 301 | } |
| 302 | } |
| 303 | s.symbols.deinit(t.gpa); |
| 304 | s.* = undefined; |
| 305 | } |
| 306 | }; |
| 307 | |
| 225 | 308 | const Liveness = enum { alive, dead }; |
| 226 | 309 | |
| 227 | 310 | const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{ |
| ... | ... | @@ -242,10 +325,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I |
| 242 | 325 | const trace = tracer.trace(@src()); |
| 243 | 326 | defer trace.end(); |
| 244 | 327 | |
| 245 | | const module = try t.addRootInst(.{ |
| 246 | | .tag = .module, |
| 247 | | .data = undefined, |
| 248 | | }); |
| 328 | const module = try t.addUntracked(.module, undefined); |
| 249 | 329 | |
| 250 | 330 | const decl = BlockContext.init(module_node); |
| 251 | 331 | |
| ... | ... | @@ -271,10 +351,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I |
| 271 | 351 | } |
| 272 | 352 | } |
| 273 | 353 | |
| 274 | | const top_level_fn = try t.addRootInst(.{ |
| 275 | | .tag = .declaration, |
| 276 | | .data = undefined, |
| 277 | | }); |
| 354 | const top_level_fn = try t.addUntracked(.declaration, undefined); |
| 278 | 355 | |
| 279 | 356 | temp[temp.len - 1] = top_level_fn; |
| 280 | 357 | |
| ... | ... | @@ -285,42 +362,39 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I |
| 285 | 362 | try t.extra.list.appendSlice(t.gpa, @ptrCast(temp)); |
| 286 | 363 | |
| 287 | 364 | const top_level_code_ref: Inst.Ref = ref: { |
| 288 | | const scratch_top = t.scratch.items.len; |
| 289 | | defer t.scratch.shrinkRetainingCapacity(scratch_top); |
| 365 | const scratch = t.scratch.mark(); |
| 366 | defer scratch.deinit(); |
| 290 | 367 | |
| 291 | 368 | for (statements) |statement| { |
| 292 | 369 | try t.transformModuleStatement(decl, statement); |
| 293 | 370 | } |
| 294 | 371 | |
| 295 | 372 | // if there are no new instructions, we dont have to create a block context |
| 296 | | if (scratch_top == t.scratch.items.len) { |
| 373 | if (scratch.len() == 0) { |
| 297 | 374 | break :ref .void; |
| 298 | 375 | } |
| 299 | 376 | |
| 300 | | const block = try t.addRootInst(.{ |
| 301 | | .tag = .block, |
| 302 | | .data = undefined, |
| 303 | | }); |
| 377 | const block = try t.addUntracked(.block, undefined); |
| 304 | 378 | |
| 305 | 379 | t.getInstData(block).* = EncodedData.encode(.block, .{ |
| 306 | 380 | .src_node = decl.nodeOffset(module_node), |
| 307 | | .extra_index = try t.extra.append(t.gpa, IndexList, try t.scratchToIndexList(scratch_top)), |
| 381 | .extra_index = try t.extra.append(t.gpa, IndexList, try t.scratchToIndexList(scratch)), |
| 308 | 382 | }); |
| 309 | 383 | |
| 310 | 384 | break :ref block.toRef(); |
| 311 | 385 | }; |
| 312 | 386 | |
| 313 | | const decl_extra, var trail = try t.extra.appendTrailing(t.gpa, Inst.Decl, .{ |
| 387 | const decl_extra, var trail = try t.extra.appendTrailing(t.gpa, Teg.Decl, .{ |
| 314 | 388 | .kind = .function, |
| 315 | 389 | .name = NullTerminatedString.empty, |
| 316 | 390 | .hash = .{0} ** 16, |
| 317 | 391 | .instruction_count = @as(u32, @intCast(t.insts.len)) - @intFromEnum(top_level_fn), |
| 318 | 392 | .body = top_level_code_ref, |
| 319 | | }, dod.extraSizeOf(Inst.DeclFn)); |
| 393 | }, extra.extraSizeOf(Teg.DeclFn)); |
| 320 | 394 | |
| 321 | | trail.writeStruct(Inst.DeclFn, .{ |
| 395 | trail.writeStruct(Teg.DeclFn, .{ |
| 322 | 396 | .params = .{ .start = 0, .len = 0 }, |
| 323 | | .return_type = Inst.Ref.void, |
| 397 | .return_type = Inst.Ref.void.toOptional(), |
| 324 | 398 | }); |
| 325 | 399 | |
| 326 | 400 | t.getInstData(top_level_fn).* = EncodedData.encode(.declaration, .{ |
| ... | ... | @@ -361,10 +435,14 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void |
| 361 | 435 | gop.value_ptr.* = .{ |
| 362 | 436 | .node = node, |
| 363 | 437 | .data = .declaration, |
| 438 | .type = .{ .present = .void }, |
| 364 | 439 | }; |
| 365 | 440 | }, |
| 366 | 441 | .struct_decl => { |
| 367 | 442 | const decl = t.ast.fullStructDecl(node); |
| 443 | var id, const id_allocated = try t.decodeIdentifierExtra(decl.id); |
| 444 | if (id_allocated) |
| 445 | id = try t.gpa.dupe(u8, id); |
| 368 | 446 | const gop = try t.scope().symbols.getOrPut(t.gpa, decl.id); |
| 369 | 447 | if (gop.found_existing) { |
| 370 | 448 | try t.report.err(.duplicate_function, .{ |
| ... | ... | @@ -377,6 +455,7 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void |
| 377 | 455 | gop.value_ptr.* = .{ |
| 378 | 456 | .node = node, |
| 379 | 457 | .data = .declaration, |
| 458 | .type = .{ .present = .void }, |
| 380 | 459 | }; |
| 381 | 460 | }, |
| 382 | 461 | else => {}, |
| ... | ... | @@ -397,14 +476,14 @@ fn transformModuleStatement(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 397 | 476 | .struct_decl, |
| 398 | 477 | => {}, // declarations are analyzed separately |
| 399 | 478 | |
| 400 | | else => _ = try t.transformBlockStatement(decl, node, false), |
| 479 | else => _ = try t.transformBlockStatement(decl, node, null), |
| 401 | 480 | } |
| 402 | 481 | } |
| 403 | 482 | |
| 404 | 483 | fn transformDecl(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index { |
| 405 | 484 | return switch (t.ast.nodeTag(node)) { |
| 406 | 485 | .fn_decl => t.transformFunction(outer_decl, node), |
| 407 | | .struct_decl => t.transformStruct(outer_decl, node), |
| 486 | .struct_decl => t.transformStructDeclaration(outer_decl, node), |
| 408 | 487 | else => unreachable, // not a declaration |
| 409 | 488 | }; |
| 410 | 489 | } |
| ... | ... | @@ -416,33 +495,40 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A |
| 416 | 495 | const decl = outer_decl.declOffset(node); |
| 417 | 496 | const fn_decl = t.ast.fullFnDecl(node); |
| 418 | 497 | |
| 419 | | const decl_inst = try t.addRootInst(.{ |
| 420 | | .tag = .declaration, |
| 421 | | .data = undefined, |
| 422 | | }); |
| 498 | const decl_inst = try t.addUntracked(.declaration, undefined); |
| 423 | 499 | |
| 424 | 500 | try t.pushScope(); |
| 425 | 501 | defer t.popScope(); |
| 426 | 502 | |
| 427 | 503 | const param_list = brk: { |
| 428 | | const scratch_top = t.scratch.items.len; |
| 429 | | defer t.scratch.shrinkRetainingCapacity(scratch_top); |
| 504 | const scratch = t.scratch.mark(); |
| 505 | defer scratch.deinit(); |
| 430 | 506 | |
| 431 | 507 | for (fn_decl.params) |param| { |
| 432 | | try t.addScratch(Inst.Ref, try t.transformParamDecl(decl, param)); |
| 508 | try t.scratch.add(t.arena, Inst.Ref, try t.transformParamDecl(decl, param)); |
| 433 | 509 | } |
| 434 | 510 | |
| 435 | | break :brk try t.scratchToRefList(scratch_top); |
| 511 | break :brk try t.scratchToRefList(scratch); |
| 436 | 512 | }; |
| 437 | 513 | |
| 438 | 514 | const return_type = if (fn_decl.return_type) |return_type_node| |
| 439 | | try t.transformExpressionToBlock(decl, return_type_node, false) |
| 515 | try t.transformToBlock( |
| 516 | decl, |
| 517 | return_type_node, |
| 518 | .result_without_coercion, |
| 519 | .{ .ref = .type_type }, |
| 520 | ) |
| 440 | 521 | else |
| 441 | | Inst.Ref.void; |
| 522 | null; |
| 442 | 523 | |
| 443 | | const body = try t.transformExpressionToBlock(decl, fn_decl.body, true); |
| 524 | const body = try t.transformToBlock( |
| 525 | decl, |
| 526 | fn_decl.body, |
| 527 | .function_body, |
| 528 | if (return_type) |ref| .{ .ref = ref } else .none, |
| 529 | ); |
| 444 | 530 | |
| 445 | | const decl_extra = try t.extra.append(t.gpa, Inst.Decl, .{ |
| 531 | const decl_extra = try t.extra.append(t.gpa, Teg.Decl, .{ |
| 446 | 532 | .kind = .function, |
| 447 | 533 | .name = try t.addNullTerminatedString(fn_decl.id), |
| 448 | 534 | .hash = .{0} ** 16, |
| ... | ... | @@ -450,9 +536,9 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A |
| 450 | 536 | .body = body, |
| 451 | 537 | }); |
| 452 | 538 | |
| 453 | | _ = try t.extra.append(t.gpa, Inst.DeclFn, .{ |
| 539 | _ = try t.extra.append(t.gpa, Teg.DeclFn, .{ |
| 454 | 540 | .params = param_list, |
| 455 | | .return_type = return_type, |
| 541 | .return_type = if (return_type) |r| r.toOptional() else .none, |
| 456 | 542 | }); |
| 457 | 543 | |
| 458 | 544 | t.getInstData(decl_inst).* = EncodedData.encode(.declaration, .{ |
| ... | ... | @@ -465,21 +551,18 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A |
| 465 | 551 | return decl_inst; |
| 466 | 552 | } |
| 467 | 553 | |
| 468 | | fn transformStruct(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index { |
| 554 | fn transformStructDeclaration(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index { |
| 469 | 555 | const trace = tracer.trace(@src()); |
| 470 | 556 | defer trace.end(); |
| 471 | 557 | |
| 472 | 558 | const decl = outer_decl.declOffset(node); |
| 473 | 559 | const decl_data = t.ast.fullStructDecl(node); |
| 474 | 560 | |
| 475 | | const decl_inst = try t.addRootInst(.{ |
| 476 | | .tag = .declaration, |
| 477 | | .data = undefined, |
| 478 | | }); |
| 561 | const decl_inst = try t.addUntracked(.declaration, undefined); |
| 479 | 562 | |
| 480 | | const body = try t.transformExpressionToBlock(decl, node, false); |
| 563 | const body = try t.transformToBlock(decl, node, .result_without_coercion, .none); |
| 481 | 564 | |
| 482 | | const decl_extra = try t.extra.append(t.gpa, Inst.Decl, .{ |
| 565 | const decl_extra = try t.extra.append(t.gpa, Teg.Decl, .{ |
| 483 | 566 | .kind = .immutable, |
| 484 | 567 | .name = try t.addNullTerminatedString(decl_data.id), |
| 485 | 568 | .hash = .{0} ** 16, |
| ... | ... | @@ -495,76 +578,83 @@ fn transformStruct(t: *Transform, outer_decl: BlockContext, node: NodeIndex) All |
| 495 | 578 | return decl_inst; |
| 496 | 579 | } |
| 497 | 580 | |
| 498 | | const TransformFunctionCtx = struct { |
| 499 | | param_list: RefList, |
| 581 | const TransformToBlockMode = enum { |
| 582 | tracked_result, |
| 583 | result_without_coercion, |
| 584 | coerced_result, |
| 585 | function_body, |
| 586 | |
| 587 | fn ResultType(mode: TransformToBlockMode) type { |
| 588 | return switch (mode) { |
| 589 | .tracked_result => *Transform.ResultType, |
| 590 | else => Transform.ResultType, |
| 591 | }; |
| 592 | } |
| 500 | 593 | }; |
| 501 | 594 | |
| 502 | | /// Converts expressions into blocks with a break. This is important |
| 503 | | /// for places where a block is not allowed, such as a parameter list. |
| 504 | | /// This is also used for the function body, even though it is a block. |
| 505 | | /// Simple refs turn into just the ref |
| 506 | | fn transformExpressionToBlock( |
| 595 | /// guh |
| 596 | fn transformToBlock( |
| 507 | 597 | t: *Transform, |
| 508 | | decl: BlockContext, |
| 598 | ctx: BlockContext, |
| 509 | 599 | node: NodeIndex, |
| 510 | | comptime is_function: bool, |
| 600 | comptime mode: TransformToBlockMode, |
| 601 | result_type: mode.ResultType(), |
| 511 | 602 | ) Allocator.Error!Inst.Ref { |
| 512 | 603 | const trace = tracer.trace(@src()); |
| 513 | 604 | defer trace.end(); |
| 514 | 605 | |
| 515 | | const node_tag = t.ast.nodes.items(.tag)[node]; |
| 516 | | switch (node_tag) { |
| 517 | | .id => { |
| 518 | | // Lower builtin ids without using a block |
| 519 | | const id = try t.decodeIdentifier(t.ast.basicNodeSlice(node)); |
| 520 | | if (BuiltInIds.get(id)) |ref| { |
| 521 | | return ref; |
| 522 | | } |
| 523 | | }, |
| 524 | | |
| 525 | | else => {}, |
| 526 | | } |
| 527 | | |
| 528 | | var block: ?Inst.Index = null; |
| 606 | const scratch = t.scratch.mark(); |
| 607 | defer scratch.deinit(); |
| 529 | 608 | |
| 530 | | const scratch_top = t.scratch.items.len; |
| 531 | | defer t.scratch.shrinkRetainingCapacity(scratch_top); |
| 532 | | |
| 533 | | if (is_function) { |
| 609 | // For function blocks, add .param instructions for all of the parameters |
| 610 | if (mode == .function_body) { |
| 534 | 611 | const s = t.scopes.getLast(); |
| 535 | 612 | for (s.symbols.values(), 0..) |*param, i| { |
| 536 | 613 | assert(param.data == .param); |
| 537 | | const ref = try t.addInstRef(.{ |
| 538 | | .tag = .param, |
| 539 | | .data = EncodedData.encode(.param, .{ |
| 540 | | .src_node = decl.nodeOffset(node), |
| 541 | | .data = @intCast(i), |
| 542 | | }), |
| 614 | const ref = try t.addRef(.param, .{ |
| 615 | .src_node = ctx.nodeOffset(node), |
| 616 | .data = @intCast(i), |
| 543 | 617 | }); |
| 544 | 618 | param.data = .{ .immutable = ref }; |
| 545 | 619 | } |
| 546 | 620 | } |
| 547 | 621 | |
| 622 | // The result type is only passed by reference if mode is tracked_result. |
| 623 | const copies_result_type = mode.ResultType() == ResultType; |
| 624 | var result_type_storage = if (copies_result_type) result_type; |
| 625 | |
| 626 | var ret = if (mode == .function_body) |
| 627 | BlockContext.ReturnTarget{ .result_type = result_type_storage }; |
| 628 | |
| 548 | 629 | const expr = try t.transformExpression( |
| 549 | | if (is_function) |
| 550 | | decl.withReturn(&block) |
| 630 | // Function blocks are allowed to return, in which |
| 631 | // it will return to this block. |
| 632 | if (mode == .function_body) |
| 633 | ctx.withReturn(&ret) |
| 551 | 634 | else |
| 552 | | decl, |
| 635 | ctx, |
| 636 | // Pass result type pointer |
| 637 | if (copies_result_type) |
| 638 | &result_type_storage |
| 639 | else |
| 640 | result_type, |
| 553 | 641 | node, |
| 554 | 642 | ); |
| 555 | 643 | |
| 556 | 644 | // if there are no new instructions, we dont have to create a block context |
| 557 | | if (scratch_top == t.scratch.items.len) { |
| 558 | | const result = expr orelse unreachable; // null means we *added* break or return |
| 645 | if (scratch.len() == 0) { |
| 646 | const result = expr orelse |
| 647 | unreachable; // null means we *added* break or return |
| 559 | 648 | |
| 560 | | // TODO: evaluate if `if` blocks would be fine in sema with instruction refs |
| 561 | | // with this extra if check here, we filter out non-blocks. |
| 562 | 649 | if (result.value() != null) { |
| 563 | | assert(block == null); |
| 650 | if (mode == .function_body) |
| 651 | assert(ret.generated_block == null); // return was called without an instruction |
| 652 | |
| 564 | 653 | return result; |
| 565 | 654 | } |
| 566 | 655 | } |
| 567 | 656 | |
| 657 | // If we analyzed a block, then just use that. No need for an extra wrapper block. |
| 568 | 658 | if (expr) |result| { |
| 569 | 659 | if (result.index()) |index| { |
| 570 | 660 | if (t.insts.items(.tag)[@intFromEnum(index)] == .block) { |
| ... | ... | @@ -573,32 +663,26 @@ fn transformExpressionToBlock( |
| 573 | 663 | } |
| 574 | 664 | } |
| 575 | 665 | |
| 576 | | const ret_block = if (block) |b| |
| 577 | | b |
| 578 | | else |
| 579 | | try t.addRootInst(.{ |
| 580 | | .tag = .block, |
| 581 | | .data = undefined, |
| 582 | | }); |
| 666 | // Otherwise try using the function return block, or create a new one |
| 667 | const maybe_generated_block = if (mode == .function_body) ret.generated_block else null; |
| 668 | const ret_block = maybe_generated_block orelse try t.addUntracked(.block, undefined); |
| 583 | 669 | |
| 670 | // The block must end with a break |
| 584 | 671 | if (expr) |result| { |
| 585 | | _ = try t.addInstRef(.{ |
| 586 | | .tag = .@"break", |
| 587 | | .data = EncodedData.encode(.@"break", .{ |
| 588 | | .src_node = decl.nodeOffset(node), |
| 589 | | .extra_index = try t.extra.append(t.gpa, Inst.Break, .{ |
| 590 | | .block = ret_block, |
| 591 | | .operand = result, |
| 592 | | }), |
| 672 | _ = try t.addIndex(.@"break", .{ |
| 673 | .src_node = ctx.nodeOffset(node), |
| 674 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 675 | .block = ret_block, |
| 676 | .operand = result, |
| 593 | 677 | }), |
| 594 | 678 | }); |
| 595 | 679 | } |
| 596 | 680 | |
| 597 | | const content = try t.scratchToIndexList(scratch_top); |
| 681 | const content = try t.scratchToIndexList(scratch); |
| 598 | 682 | assert(content.len > 0); |
| 599 | 683 | |
| 600 | 684 | t.getInstData(ret_block).* = EncodedData.encode(.block, .{ |
| 601 | | .src_node = decl.nodeOffset(node), |
| 685 | .src_node = ctx.nodeOffset(node), |
| 602 | 686 | .extra_index = try t.extra.append(t.gpa, IndexList, content), |
| 603 | 687 | }); |
| 604 | 688 | |
| ... | ... | @@ -610,7 +694,13 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc |
| 610 | 694 | defer trace.end(); |
| 611 | 695 | |
| 612 | 696 | const param_decl = t.ast.fullParamDecl(param); |
| 613 | | const value = try t.transformExpressionToBlock(decl, param_decl.type orelse @panic("TODO: inferred param types"), false); |
| 697 | const ty = try t.transformToBlock( |
| 698 | decl, |
| 699 | param_decl.type orelse |
| 700 | @panic("TODO: inferred param types"), |
| 701 | .result_without_coercion, |
| 702 | .{ .ref = .type_type }, |
| 703 | ); |
| 614 | 704 | |
| 615 | 705 | var id, const id_allocated = try t.decodeIdentifierExtra(param_decl.id); |
| 616 | 706 | if (id_allocated) |
| ... | ... | @@ -631,9 +721,10 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc |
| 631 | 721 | sym.value_ptr.* = .{ |
| 632 | 722 | .node = param, |
| 633 | 723 | .data = .param, |
| 724 | .type = .{ .present = ty }, |
| 634 | 725 | }; |
| 635 | 726 | |
| 636 | | return value; |
| 727 | return ty; |
| 637 | 728 | } |
| 638 | 729 | |
| 639 | 730 | pub const Builtin = enum { |
| ... | ... | @@ -664,14 +755,12 @@ pub const Builtin = enum { |
| 664 | 755 | return .void; |
| 665 | 756 | } |
| 666 | 757 | |
| 667 | | const source = try t.transformExpression(decl, args[0]) orelse return null; |
| 758 | var result: ResultType = .{ .ref = .string_type }; |
| 759 | const source = try t.transformExpression(decl, &result, args[0]) orelse return null; |
| 668 | 760 | |
| 669 | | return try t.addInstRef(.{ |
| 670 | | .tag = .js, |
| 671 | | .data = EncodedData.encode(.js, .{ |
| 672 | | .src_node = decl.nodeOffset(args[0]), |
| 673 | | .operand = source, |
| 674 | | }), |
| 761 | return try t.addRef(.js, .{ |
| 762 | .src_node = decl.nodeOffset(args[0]), |
| 763 | .operand = source, |
| 675 | 764 | }); |
| 676 | 765 | } |
| 677 | 766 | |
| ... | ... | @@ -684,17 +773,15 @@ pub const Builtin = enum { |
| 684 | 773 | return .void; |
| 685 | 774 | } |
| 686 | 775 | |
| 687 | | const base = try t.transformExpression(decl, args[0]) orelse return null; |
| 688 | | const args_list = try t.transformExpressionList(decl, args[1..]) orelse return null; |
| 776 | var opaque_type: ResultType = .{ .ref = .opaque_type }; |
| 777 | const base = try t.transformExpression(decl, &opaque_type, args[0]) orelse return null; |
| 778 | const args_list = try t.transformExpressionList(decl, .{ .ref = .opaque_type }, args[1..]) orelse return null; |
| 689 | 779 | |
| 690 | | return try t.addInstRef(.{ |
| 691 | | .tag = .js_new, |
| 692 | | .data = EncodedData.encode(.js_new, .{ |
| 693 | | .src_node = decl.nodeOffset(args[0]), |
| 694 | | .extra_index = try t.extra.append(t.gpa, Inst.JsNew, .{ |
| 695 | | .base = base, |
| 696 | | .args = args_list, |
| 697 | | }), |
| 780 | return try t.addRef(.js_new, .{ |
| 781 | .src_node = decl.nodeOffset(args[0]), |
| 782 | .extra_index = try t.extra.append(t.gpa, Teg.JsNew, .{ |
| 783 | .base = base, |
| 784 | .args = args_list, |
| 698 | 785 | }), |
| 699 | 786 | }); |
| 700 | 787 | } |
| ... | ... | @@ -708,15 +795,13 @@ pub const Builtin = enum { |
| 708 | 795 | return .void; |
| 709 | 796 | } |
| 710 | 797 | |
| 711 | | const source = try t.transformExpression(decl, args[0]) orelse |
| 798 | var string_type: ResultType = .{ .ref = .string_type }; |
| 799 | const source = try t.transformExpression(decl, &string_type, args[0]) orelse |
| 712 | 800 | return null; |
| 713 | 801 | |
| 714 | | return try t.addInstRef(.{ |
| 715 | | .tag = .compile_error, |
| 716 | | .data = EncodedData.encode(.compile_error, .{ |
| 717 | | .src_node = decl.nodeOffset(node), |
| 718 | | .operand = source, |
| 719 | | }), |
| 802 | return try t.addRef(.compile_error, .{ |
| 803 | .src_node = decl.nodeOffset(node), |
| 804 | .operand = source, |
| 720 | 805 | }); |
| 721 | 806 | } |
| 722 | 807 | |
| ... | ... | @@ -729,22 +814,25 @@ pub const Builtin = enum { |
| 729 | 814 | return .void; |
| 730 | 815 | } |
| 731 | 816 | |
| 732 | | const source = try t.transformExpression(decl, args[0]) orelse |
| 817 | var result: ResultType = .none; |
| 818 | const source = try t.transformExpression(decl, &result, args[0]) orelse |
| 733 | 819 | return null; |
| 734 | 820 | |
| 735 | | return try t.addInstRef(.{ |
| 736 | | .tag = .type_of, |
| 737 | | .data = EncodedData.encode(.type_of, .{ |
| 738 | | .src_node = decl.nodeOffset(node), |
| 739 | | .operand = source, |
| 740 | | }), |
| 821 | return try t.addRef(.type_of, .{ |
| 822 | .src_node = decl.nodeOffset(node), |
| 823 | .operand = source, |
| 741 | 824 | }); |
| 742 | 825 | } |
| 743 | 826 | }; |
| 744 | 827 | |
| 745 | 828 | /// This is the primary transformation function: converting an AST expression into instructions. |
| 746 | 829 | /// `null` represents a `return` or `break`, aka this branch of code is dead. |
| 747 | | fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Allocator.Error!?Inst.Ref { |
| 830 | fn transformExpression( |
| 831 | t: *Transform, |
| 832 | ctx: BlockContext, |
| 833 | result: *ResultType, |
| 834 | node: NodeIndex, |
| 835 | ) Allocator.Error!?Inst.Ref { |
| 748 | 836 | const node_tag = t.ast.nodes.items(.tag)[node]; |
| 749 | 837 | |
| 750 | 838 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); |
| ... | ... | @@ -775,12 +863,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 775 | 863 | lookup.used = true; |
| 776 | 864 | |
| 777 | 865 | return switch (lookup.data) { |
| 778 | | .declaration => try t.addInstRef(.{ |
| 779 | | .tag = .load_decl, |
| 780 | | .data = EncodedData.encode(.load_decl, .{ |
| 781 | | .src_node = decl.nodeOffset(node), |
| 782 | | .string = try t.addNullTerminatedString(id), |
| 783 | | }), |
| 866 | .declaration => try t.addRef(.load_decl, .{ |
| 867 | .src_node = ctx.nodeOffset(node), |
| 868 | .string = try t.addNullTerminatedString(id), |
| 784 | 869 | }), |
| 785 | 870 | .mutable => |idx| idx.toRef(), |
| 786 | 871 | .immutable => |ref| ref, |
| ... | ... | @@ -800,41 +885,32 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 800 | 885 | try t.report.warn(.zero_prefixed_literal, .{ .token = t.ast.nodeToken(node) }); |
| 801 | 886 | } |
| 802 | 887 | |
| 803 | | return try t.addInstRef(.{ |
| 804 | | .tag = .number, |
| 805 | | .data = EncodedData.encode(.number, .{ |
| 806 | | .src_node = decl.nodeOffset(node), |
| 807 | | .extra_index = try t.extra.append(t.gpa, Float, Float.encode(value)), |
| 808 | | }), |
| 888 | return try t.addRef(.number, .{ |
| 889 | .src_node = ctx.nodeOffset(node), |
| 890 | .extra_index = try t.extra.append(t.gpa, Float, Float.encode(value)), |
| 809 | 891 | }); |
| 810 | 892 | }, |
| 811 | 893 | |
| 812 | 894 | .string => { |
| 813 | 895 | const str = t.ast.basicNodeSlice(node); |
| 814 | 896 | const sliced = try t.addDecodedSlicedString(str[1 .. str.len - 1]); |
| 815 | | return try t.addInstRef(.{ |
| 816 | | .tag = .string, |
| 817 | | .data = EncodedData.encode(.string, .{ |
| 818 | | .src_node = decl.nodeOffset(node), |
| 819 | | .extra_index = try t.extra.append(t.gpa, SlicedString, sliced), |
| 820 | | }), |
| 897 | return try t.addRef(.string, .{ |
| 898 | .src_node = ctx.nodeOffset(node), |
| 899 | .extra_index = try t.extra.append(t.gpa, SlicedString, sliced), |
| 821 | 900 | }); |
| 822 | 901 | }, |
| 823 | 902 | |
| 824 | 903 | .assign => { |
| 825 | 904 | const ast_bin = t.ast.nodeData(node).bin; |
| 826 | | const lhs = try t.transformAssignmentTarget(decl, ast_bin.lhs) orelse |
| 905 | const lhs, var result_type = try t.transformAssignmentTarget(ctx, ast_bin.lhs) orelse |
| 827 | 906 | return .void; |
| 828 | | const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse |
| 907 | const rhs = try t.transformExpression(ctx, &result_type, ast_bin.rhs) orelse |
| 829 | 908 | return null; |
| 830 | | const assign_inst = try t.addInstRef(.{ |
| 831 | | .tag = .store, |
| 832 | | .data = EncodedData.encode(.store, .{ |
| 833 | | .src_node = decl.nodeOffset(node), |
| 834 | | .extra_index = try t.extra.append(t.gpa, Inst.Store, .{ |
| 835 | | .memory = lhs, |
| 836 | | .operand = rhs, |
| 837 | | }), |
| 909 | const assign_inst = try t.addRef(.store, .{ |
| 910 | .src_node = ctx.nodeOffset(node), |
| 911 | .extra_index = try t.extra.append(t.gpa, Teg.Store, .{ |
| 912 | .memory = lhs, |
| 913 | .operand = rhs, |
| 838 | 914 | }), |
| 839 | 915 | }); |
| 840 | 916 | return assign_inst; |
| ... | ... | @@ -877,41 +953,39 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 877 | 953 | // compound compute + assign operation. |
| 878 | 954 | const is_assign = comptime tag.isAssignBin(); |
| 879 | 955 | const teg_tag = @field( |
| 880 | | Teg.Inst.Tag, |
| 956 | Teg.Tag, |
| 881 | 957 | if (is_assign) |
| 882 | 958 | @tagName(tag)[0 .. @tagName(tag).len - "_assign".len] |
| 883 | 959 | else |
| 884 | 960 | @tagName(tag), |
| 885 | 961 | ); |
| 886 | 962 | const ast_bin = t.ast.nodeData(node).bin; |
| 887 | | const lhs = if (is_assign) |
| 888 | | try t.transformAssignmentTarget(decl, ast_bin.lhs) orelse |
| 963 | var lhs_result_type: ResultType = .none; |
| 964 | const lhs, var assign_result_type: ResultType = if (is_assign) |
| 965 | try t.transformAssignmentTarget(ctx, ast_bin.lhs) orelse |
| 889 | 966 | return .void |
| 890 | 967 | else |
| 891 | | try t.transformExpression(decl, ast_bin.lhs) orelse |
| 892 | | return null; |
| 893 | | const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse return null; |
| 894 | | |
| 895 | | const compute_inst = try t.addInstRef(.{ |
| 896 | | .tag = teg_tag, |
| 897 | | .data = EncodedData.encode(teg_tag, .{ |
| 898 | | .src_node = decl.nodeOffset(node), |
| 899 | | .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{ |
| 900 | | .lhs = if (is_assign) lhs.toRef() else lhs, |
| 901 | | .rhs = rhs, |
| 902 | | }), |
| 968 | .{ |
| 969 | try t.transformExpression(ctx, &lhs_result_type, ast_bin.lhs) orelse |
| 970 | return null, |
| 971 | .none, |
| 972 | }; |
| 973 | const rhs = try t.transformExpression(ctx, &assign_result_type, ast_bin.rhs) orelse return null; |
| 974 | |
| 975 | const compute_inst = try t.addRef(teg_tag, .{ |
| 976 | .src_node = ctx.nodeOffset(node), |
| 977 | .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ |
| 978 | .lhs = if (is_assign) lhs.toRef() else lhs, |
| 979 | .rhs = rhs, |
| 903 | 980 | }), |
| 904 | 981 | }); |
| 905 | 982 | |
| 906 | 983 | if (is_assign) { |
| 907 | | const assign_inst = try t.addInstRef(.{ |
| 908 | | .tag = .store, |
| 909 | | .data = EncodedData.encode(.store, .{ |
| 910 | | .src_node = decl.nodeOffset(node), |
| 911 | | .extra_index = try t.extra.append(t.gpa, Inst.Store, .{ |
| 912 | | .memory = lhs, |
| 913 | | .operand = compute_inst, |
| 914 | | }), |
| 984 | const assign_inst = try t.addRef(.store, .{ |
| 985 | .src_node = ctx.nodeOffset(node), |
| 986 | .extra_index = try t.extra.append(t.gpa, Teg.Store, .{ |
| 987 | .memory = lhs, |
| 988 | .operand = compute_inst, |
| 915 | 989 | }), |
| 916 | 990 | }); |
| 917 | 991 | return assign_inst; |
| ... | ... | @@ -926,29 +1000,31 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 926 | 1000 | .array_type, |
| 927 | 1001 | => |tag| { |
| 928 | 1002 | const ast_un = t.ast.nodeData(node).un; |
| 929 | | const teg_tag = @field(Teg.Inst.Tag, @tagName(tag)); |
| 1003 | const teg_tag = @field(Teg.Tag, @tagName(tag)); |
| 930 | 1004 | |
| 931 | | const operand = try t.transformExpression(decl, ast_un.expr) orelse |
| 1005 | var operand_type: ResultType = switch (tag) { |
| 1006 | .negate => .none, // any numeric type allowed |
| 1007 | .bool_not => .{ .ref = .bool_type }, |
| 1008 | .bit_not => .{ .ref = .int32_type }, |
| 1009 | .array_type => .{ .ref = .type_type }, |
| 1010 | else => @compileError(unreachable), |
| 1011 | }; |
| 1012 | |
| 1013 | const operand = try t.transformExpression(ctx, &operand_type, ast_un.expr) orelse |
| 932 | 1014 | return null; |
| 933 | 1015 | |
| 934 | | return try t.addInstRef(.{ |
| 935 | | .tag = teg_tag, |
| 936 | | .data = EncodedData.encode(teg_tag, .{ |
| 937 | | .src_node = decl.nodeOffset(node), |
| 938 | | .operand = operand, |
| 939 | | }), |
| 1016 | return try t.addRef(teg_tag, .{ |
| 1017 | .src_node = ctx.nodeOffset(node), |
| 1018 | .operand = operand, |
| 940 | 1019 | }); |
| 941 | 1020 | }, |
| 942 | 1021 | |
| 943 | 1022 | .@"comptime" => { |
| 944 | 1023 | const ast_un = t.ast.nodeData(node).un; |
| 945 | | const operand = try t.transformExpressionToBlock(decl, ast_un.expr, false); |
| 946 | | return try t.addInstRef(.{ |
| 947 | | .tag = .@"comptime", |
| 948 | | .data = EncodedData.encode(.@"comptime", .{ |
| 949 | | .src_node = decl.nodeOffset(node), |
| 950 | | .operand = operand, |
| 951 | | }), |
| 1024 | const operand = try t.transformToBlock(ctx, ast_un.expr, .tracked_result, result); |
| 1025 | return try t.addRef(.@"comptime", .{ |
| 1026 | .src_node = ctx.nodeOffset(node), |
| 1027 | .operand = operand, |
| 952 | 1028 | }); |
| 953 | 1029 | }, |
| 954 | 1030 | |
| ... | ... | @@ -957,43 +1033,42 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 957 | 1033 | const label = t.ast.tokenSlice(t.ast.nodeToken(node)); |
| 958 | 1034 | |
| 959 | 1035 | var target: BlockContext.BreakTarget = .{ |
| 960 | | .parent = decl.break_target, |
| 1036 | .parent = ctx.break_target, |
| 961 | 1037 | .label = label, |
| 962 | 1038 | .is_loop = false, |
| 1039 | .result_type = result, |
| 963 | 1040 | }; |
| 964 | | const inner_ctx = decl.withBreak(&target); |
| 1041 | const inner_ctx = ctx.withBreak(&target); |
| 965 | 1042 | |
| 966 | | const scratch_top = t.scratch.items.len; |
| 1043 | const scratch = t.scratch.mark(); |
| 967 | 1044 | |
| 968 | | const inner_expr = try t.transformExpression(inner_ctx, expr); |
| 1045 | const inner_expr = try t.transformExpression(inner_ctx, result, expr); |
| 969 | 1046 | |
| 970 | 1047 | // if there are no new instructions, we dont have to create a block context |
| 971 | | if (scratch_top == t.scratch.items.len) |
| 972 | | return inner_expr orelse unreachable; // null means we added break or return |
| 1048 | if (scratch.len() == 0) |
| 1049 | return inner_expr orelse |
| 1050 | unreachable; // null means we added break or return |
| 973 | 1051 | |
| 974 | 1052 | if (target.block_index) |block| { |
| 975 | | if (inner_expr) |result| { |
| 976 | | _ = try t.addInstRef(.{ |
| 977 | | .tag = .@"break", |
| 978 | | .data = EncodedData.encode(.@"break", .{ |
| 979 | | .src_node = decl.nodeOffset(node), |
| 980 | | .extra_index = try t.extra.append(t.gpa, Inst.Break, .{ |
| 981 | | .block = block, |
| 982 | | .operand = result, |
| 983 | | }), |
| 1053 | if (inner_expr) |out| { |
| 1054 | _ = try t.addRef(.@"break", .{ |
| 1055 | .src_node = ctx.nodeOffset(node), |
| 1056 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 1057 | .block = block, |
| 1058 | .operand = out, |
| 984 | 1059 | }), |
| 985 | 1060 | }); |
| 986 | 1061 | } |
| 987 | 1062 | |
| 988 | | const content = try t.scratchToIndexList(scratch_top); |
| 989 | | t.scratch.shrinkRetainingCapacity(scratch_top); |
| 1063 | const content = try t.scratchToIndexList(scratch); |
| 1064 | scratch.deinit(); |
| 990 | 1065 | |
| 991 | 1066 | t.getInstData(block).* = EncodedData.encode(.block, .{ |
| 992 | | .src_node = decl.nodeOffset(node), |
| 1067 | .src_node = ctx.nodeOffset(node), |
| 993 | 1068 | .extra_index = try t.extra.append(t.gpa, IndexList, content), |
| 994 | 1069 | }); |
| 995 | 1070 | |
| 996 | | try t.addScratch(Inst.Index, block); |
| 1071 | try t.scratch.add(t.arena, Inst.Index, block); |
| 997 | 1072 | |
| 998 | 1073 | return block.toRef(); |
| 999 | 1074 | } else { |
| ... | ... | @@ -1012,9 +1087,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1012 | 1087 | for (statements, 1..) |statement, i| { |
| 1013 | 1088 | const is_last = i == statements.len; |
| 1014 | 1089 | last = try t.transformBlockStatement( |
| 1015 | | decl, |
| 1090 | ctx, |
| 1016 | 1091 | statement, |
| 1017 | | tag == .block_implicit_return and is_last, |
| 1092 | if (tag == .block_implicit_return and is_last) |
| 1093 | result |
| 1094 | else |
| 1095 | null, |
| 1018 | 1096 | ) orelse { |
| 1019 | 1097 | return null; |
| 1020 | 1098 | }; |
| ... | ... | @@ -1023,8 +1101,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1023 | 1101 | return if (tag == .block_implicit_return) last else .void; |
| 1024 | 1102 | }, |
| 1025 | 1103 | |
| 1026 | | .builtin_call, |
| 1027 | | => { |
| 1104 | .builtin_call => { |
| 1028 | 1105 | const builtin = Builtin.map.get(t.ast.basicNodeSlice(node)[1..]) orelse { |
| 1029 | 1106 | try t.report.err(.invalid_builtin, .{ .node = node }); |
| 1030 | 1107 | return .void; |
| ... | ... | @@ -1034,12 +1111,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1034 | 1111 | const args = base.slice(&t.ast); |
| 1035 | 1112 | |
| 1036 | 1113 | return switch (builtin) { |
| 1037 | | .js => Builtin.builtin_js(t, decl, node, args), |
| 1038 | | .js_new => Builtin.builtin_jsNew(t, decl, node, args), |
| 1114 | .js => Builtin.builtin_js(t, ctx, node, args), |
| 1115 | .js_new => Builtin.builtin_jsNew(t, ctx, node, args), |
| 1039 | 1116 | // .import => Builtin.import(t, decl, node, args), |
| 1040 | 1117 | // .fetch => Builtin.fetch(t, decl, node, args), |
| 1041 | | .compile_error => Builtin.builtin_compileError(t, decl, node, args), |
| 1042 | | .type_of => Builtin.builtin_typeOf(t, decl, node, args), |
| 1118 | .compile_error => Builtin.builtin_compileError(t, ctx, node, args), |
| 1119 | .type_of => Builtin.builtin_typeOf(t, ctx, node, args), |
| 1043 | 1120 | else => @panic("TODO"), |
| 1044 | 1121 | }; |
| 1045 | 1122 | }, |
| ... | ... | @@ -1048,17 +1125,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1048 | 1125 | const call = t.ast.nodeData(node).call; |
| 1049 | 1126 | const args = t.ast.extraData(call.extra, Ast.Call).args.slice(&t.ast); |
| 1050 | 1127 | |
| 1051 | | const base = try t.transformExpression(decl, call.base) orelse return null; |
| 1052 | | const args_list = try t.transformExpressionList(decl, args) orelse return null; |
| 1128 | var base_result: ResultType = .none; |
| 1129 | const base = try t.transformExpression(ctx, &base_result, call.base) orelse return null; |
| 1130 | |
| 1131 | const args_list: RefList = call_args: { |
| 1132 | const extra_top = try t.extra.alloc(t.gpa, args.len); |
| 1133 | |
| 1134 | for (args, 0.., extra_top..) |arg, arg_id, index| { |
| 1135 | var arg_result: ResultType = .{ .argument = .{ |
| 1136 | .call_base = base, |
| 1137 | .argument = @intCast(arg_id), |
| 1138 | } }; |
| 1139 | t.extra.setU32( |
| 1140 | index, |
| 1141 | @intFromEnum(try t.transformExpression(ctx, &arg_result, arg) orelse |
| 1142 | return null), |
| 1143 | ); |
| 1144 | } |
| 1053 | 1145 | |
| 1054 | | const call_inst = try t.addInstRef(.{ |
| 1055 | | .tag = .call, |
| 1056 | | .data = EncodedData.encode(.call, .{ |
| 1057 | | .src_node = decl.nodeOffset(node), |
| 1058 | | .extra_index = try t.extra.append(t.gpa, Inst.Call, .{ |
| 1059 | | .base = base, |
| 1060 | | .args = args_list, |
| 1061 | | }), |
| 1146 | break :call_args .{ |
| 1147 | .start = @intCast(extra_top), |
| 1148 | .len = @intCast(args.len), |
| 1149 | }; |
| 1150 | }; |
| 1151 | |
| 1152 | const call_inst = try t.addRef(.call, .{ |
| 1153 | .src_node = ctx.nodeOffset(node), |
| 1154 | .extra_index = try t.extra.append(t.gpa, Teg.Call, .{ |
| 1155 | .base = base, |
| 1156 | .args = args_list, |
| 1062 | 1157 | }), |
| 1063 | 1158 | }); |
| 1064 | 1159 | |
| ... | ... | @@ -1068,28 +1163,23 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1068 | 1163 | .field => { |
| 1069 | 1164 | const field = t.ast.nodeData(node).field; |
| 1070 | 1165 | |
| 1071 | | const base = try t.transformExpression(decl, field.base) orelse return null; |
| 1166 | var base_result: ResultType = .none; |
| 1167 | const base = try t.transformExpression(ctx, &base_result, field.base) orelse return null; |
| 1072 | 1168 | |
| 1073 | 1169 | // TODO: handle identifier escapes |
| 1074 | 1170 | const id = t.ast.tokenSlice(field.field_token); |
| 1075 | 1171 | const prop_name_str = try t.addSlicedString(id); |
| 1076 | 1172 | |
| 1077 | | const string_ref = try t.addInstRef(.{ |
| 1078 | | .tag = .string, |
| 1079 | | .data = EncodedData.encode(.string, .{ |
| 1080 | | .src_node = decl.nodeOffset(node), |
| 1081 | | .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str), |
| 1082 | | }), |
| 1173 | const string_ref = try t.addRef(.string, .{ |
| 1174 | .src_node = ctx.nodeOffset(node), |
| 1175 | .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str), |
| 1083 | 1176 | }); |
| 1084 | 1177 | |
| 1085 | | return try t.addInstRef(.{ |
| 1086 | | .tag = .field, |
| 1087 | | .data = EncodedData.encode(.field, .{ |
| 1088 | | .src_node = decl.nodeOffset(node), |
| 1089 | | .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{ |
| 1090 | | .lhs = base, |
| 1091 | | .rhs = string_ref, |
| 1092 | | }), |
| 1178 | return try t.addRef(.field, .{ |
| 1179 | .src_node = ctx.nodeOffset(node), |
| 1180 | .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ |
| 1181 | .lhs = base, |
| 1182 | .rhs = string_ref, |
| 1093 | 1183 | }), |
| 1094 | 1184 | }); |
| 1095 | 1185 | }, |
| ... | ... | @@ -1098,45 +1188,36 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1098 | 1188 | const if_node = t.ast.nodeData(node).@"if"; |
| 1099 | 1189 | const if_data = t.ast.extraData(if_node.extra, Ast.If); |
| 1100 | 1190 | |
| 1101 | | const cond = try t.transformExpression(decl, if_node.cond) orelse |
| 1191 | var cond_result: ResultType = .{ .ref = .bool_type }; |
| 1192 | const cond = try t.transformExpression(ctx, &cond_result, if_node.cond) orelse |
| 1102 | 1193 | return null; |
| 1103 | 1194 | |
| 1104 | | const yes = try t.transformExpressionToBlock( |
| 1105 | | decl, |
| 1106 | | if_data.yes, |
| 1107 | | false, |
| 1108 | | ); |
| 1195 | var yes_result = result.*; |
| 1196 | const yes = try t.transformToBlock(ctx, if_data.yes, .tracked_result, &yes_result); |
| 1197 | |
| 1198 | // TODO: can this re-use the resolved ref from above |
| 1199 | var no_result: ResultType = if (if_data.no == .none) .used else result.*; |
| 1109 | 1200 | const no = if (if_data.no.unwrap()) |no| |
| 1110 | | try t.transformExpressionToBlock( |
| 1111 | | decl, |
| 1201 | try t.transformToBlock( |
| 1202 | ctx, |
| 1112 | 1203 | no, |
| 1113 | | false, |
| 1204 | .tracked_result, |
| 1205 | &no_result, |
| 1114 | 1206 | ) |
| 1115 | 1207 | else |
| 1116 | 1208 | Inst.Ref.void; |
| 1117 | 1209 | |
| 1118 | | if (yes == .void and no == .void) { |
| 1119 | | // when both branches do nothing, we can omit the branch, |
| 1120 | | // but we still need to type-check the condition operand |
| 1121 | | _ = try t.addInstRef(.{ |
| 1122 | | .tag = .ensure_bool, |
| 1123 | | .data = EncodedData.encode(.ensure_bool, .{ |
| 1124 | | .src_node = decl.nodeOffset(node), |
| 1125 | | .operand = cond, |
| 1126 | | }), |
| 1127 | | }); |
| 1128 | | return .void; |
| 1210 | // Mark the incoming result type as used if both branches use the result type |
| 1211 | if (result.* != .none and yes_result == .none and no_result == .none) { |
| 1212 | result.* = .used; |
| 1129 | 1213 | } |
| 1130 | 1214 | |
| 1131 | | return try t.addInstRef(.{ |
| 1132 | | .tag = .@"if", |
| 1133 | | .data = EncodedData.encode(.@"if", .{ |
| 1134 | | .src_node = decl.nodeOffset(node), |
| 1135 | | .extra_index = try t.extra.append(t.gpa, Inst.If, .{ |
| 1136 | | .cond = cond, |
| 1137 | | .yes = yes, |
| 1138 | | .no = no, |
| 1139 | | }), |
| 1215 | return try t.addRef(.@"if", .{ |
| 1216 | .src_node = ctx.nodeOffset(node), |
| 1217 | .extra_index = try t.extra.append(t.gpa, Teg.If, .{ |
| 1218 | .cond = cond, |
| 1219 | .yes = yes, |
| 1220 | .no = no, |
| 1140 | 1221 | }), |
| 1141 | 1222 | }); |
| 1142 | 1223 | }, |
| ... | ... | @@ -1144,36 +1225,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1144 | 1225 | .@"return" => { |
| 1145 | 1226 | const ret_node = t.ast.nodeData(node).@"return"; |
| 1146 | 1227 | |
| 1147 | | const ret_block_ptr = decl.return_target orelse { |
| 1228 | const target = ctx.return_target orelse { |
| 1148 | 1229 | try t.report.err(.invalid_return, .{ .node = node }); |
| 1149 | 1230 | return .void; |
| 1150 | 1231 | }; |
| 1151 | 1232 | |
| 1152 | | const ret_block = if (ret_block_ptr.*) |block| |
| 1233 | const ret_block = if (target.generated_block) |block| |
| 1153 | 1234 | block |
| 1154 | 1235 | else append_block: { |
| 1155 | | const block = try t.addRootInst(.{ |
| 1156 | | .tag = .block, |
| 1157 | | .data = undefined, |
| 1158 | | }); |
| 1159 | | ret_block_ptr.* = block; |
| 1236 | const block = try t.addUntracked(.block, undefined); |
| 1237 | target.generated_block = block; |
| 1160 | 1238 | break :append_block block; |
| 1161 | 1239 | }; |
| 1162 | 1240 | |
| 1241 | var return_result = target.result_type; |
| 1242 | |
| 1163 | 1243 | const operand: Inst.Ref = if (ret_node.expr.unwrap()) |expr| |
| 1164 | | try t.transformExpression(decl.withReturn(null), expr) orelse |
| 1165 | | return null |
| 1244 | try t.transformExpression( |
| 1245 | ctx.withReturn(null), |
| 1246 | &return_result, |
| 1247 | expr, |
| 1248 | ) orelse return null |
| 1166 | 1249 | else |
| 1167 | 1250 | .void; |
| 1168 | 1251 | |
| 1169 | | _ = try t.addInstRef(.{ |
| 1170 | | .tag = .@"break", |
| 1171 | | .data = EncodedData.encode(.@"break", .{ |
| 1172 | | .src_node = decl.nodeOffset(node), |
| 1173 | | .extra_index = try t.extra.append(t.gpa, Inst.Break, .{ |
| 1174 | | .block = ret_block, |
| 1175 | | .operand = operand, |
| 1176 | | }), |
| 1252 | _ = try t.addIndex(.@"break", .{ |
| 1253 | .src_node = ctx.nodeOffset(node), |
| 1254 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 1255 | .block = ret_block, |
| 1256 | .operand = operand, |
| 1177 | 1257 | }), |
| 1178 | 1258 | }); |
| 1179 | 1259 | |
| ... | ... | @@ -1187,36 +1267,29 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1187 | 1267 | @panic("TODO: unlabelled breaks"), |
| 1188 | 1268 | ); |
| 1189 | 1269 | |
| 1190 | | const block = try decl.findAndMarkBreak(t, label) orelse { |
| 1270 | const block: *BlockContext.BreakTarget = ctx.lookupNamedBreak(label) orelse { |
| 1191 | 1271 | try t.report.err(.unresolved_label, .{ .node = node }); |
| 1192 | 1272 | return null; |
| 1193 | 1273 | }; |
| 1194 | 1274 | |
| 1275 | var break_result = block.result_type.*; |
| 1195 | 1276 | const operand: Inst.Ref = if (brk_node.value.unwrap()) |expr| |
| 1196 | | try t.transformExpression(decl.withReturn(null), expr) orelse |
| 1277 | try t.transformExpression(ctx.withReturn(null), &break_result, expr) orelse |
| 1197 | 1278 | return null |
| 1198 | 1279 | else |
| 1199 | 1280 | .void; |
| 1200 | 1281 | |
| 1201 | | _ = try t.addInstRef(.{ |
| 1202 | | .tag = .@"break", |
| 1203 | | .data = EncodedData.encode(.@"break", .{ |
| 1204 | | .src_node = decl.nodeOffset(node), |
| 1205 | | .extra_index = try t.extra.append(t.gpa, Inst.Break, .{ |
| 1206 | | .block = block, |
| 1207 | | .operand = operand, |
| 1208 | | }), |
| 1282 | _ = try t.addIndex(.@"break", .{ |
| 1283 | .src_node = ctx.nodeOffset(node), |
| 1284 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 1285 | .block = try block.getInst(t), |
| 1286 | .operand = operand, |
| 1209 | 1287 | }), |
| 1210 | 1288 | }); |
| 1211 | 1289 | |
| 1212 | 1290 | return null; // control flow ends |
| 1213 | 1291 | }, |
| 1214 | 1292 | |
| 1215 | | .group => { |
| 1216 | | const group_node = t.ast.nodeData(node).un; |
| 1217 | | return try t.transformExpression(decl, group_node.expr); |
| 1218 | | }, |
| 1219 | | |
| 1220 | 1293 | .struct_decl => { |
| 1221 | 1294 | const struct_node = t.ast.fullStructDecl(node); |
| 1222 | 1295 | const payload_index = try t.extra.alloc(t.gpa, struct_node.fields.len * 2); |
| ... | ... | @@ -1229,32 +1302,39 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1229 | 1302 | const field = t.ast.fullStructField(field_node); |
| 1230 | 1303 | |
| 1231 | 1304 | const id = try t.decodeIdentifier(field.id); |
| 1305 | const key = try t.addNullTerminatedString(id); |
| 1306 | |
| 1307 | var type_result: ResultType = .{ .ref = .type_type }; |
| 1308 | const value = try t.transformExpression(ctx, &type_result, field.type) orelse |
| 1309 | return null; |
| 1232 | 1310 | |
| 1233 | | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id))); |
| 1234 | | t.extra.setU32(j, @intFromEnum(try t.transformExpressionToBlock(decl, field.type, false))); |
| 1311 | t.extra.setU32(i, @intFromEnum(key)); |
| 1312 | t.extra.setU32(j, @intFromEnum(value)); |
| 1235 | 1313 | } |
| 1236 | 1314 | |
| 1237 | | return try t.addInstRef(.{ |
| 1238 | | .tag = .struct_decl, |
| 1239 | | .data = EncodedData.encode(.struct_decl, .{ |
| 1240 | | .src_node = decl.nodeOffset(node), |
| 1241 | | .extra_index = try t.extra.append(t.gpa, Inst.StructDecl, .{ |
| 1242 | | .payload_index = payload_index, |
| 1243 | | .field_len = @intCast(struct_node.fields.len), |
| 1244 | | }), |
| 1315 | return try t.addRef(.struct_decl, .{ |
| 1316 | .src_node = ctx.nodeOffset(node), |
| 1317 | .extra_index = try t.extra.append(t.gpa, Teg.StructDecl, .{ |
| 1318 | .payload_index = payload_index, |
| 1319 | .field_len = @intCast(struct_node.fields.len), |
| 1245 | 1320 | }), |
| 1246 | 1321 | }); |
| 1247 | 1322 | }, |
| 1248 | 1323 | |
| 1249 | 1324 | .struct_init => { |
| 1250 | 1325 | const struct_node = t.ast.fullStructInit(node); |
| 1326 | // TODO: use t.scratch for this |
| 1251 | 1327 | const payload_index = try t.extra.alloc(t.gpa, struct_node.fields.len * 2); |
| 1252 | 1328 | |
| 1253 | | const base_type = try t.transformExpression( |
| 1254 | | decl, |
| 1255 | | struct_node.type orelse @panic("TODO: TEG implicit struct"), |
| 1256 | | ) orelse |
| 1257 | | @panic("TODO: print better error"); |
| 1329 | var result_type: ResultType = .{ .ref = .type_type }; |
| 1330 | const base_type = if (struct_node.type) |ty| |
| 1331 | try t.transformExpression(ctx, &result_type, ty) orelse |
| 1332 | return null |
| 1333 | else |
| 1334 | (try result.consume(t)) orelse { |
| 1335 | try t.report.err(.missing_result_type, .{ .node = node }); |
| 1336 | return .void; |
| 1337 | }; |
| 1258 | 1338 | |
| 1259 | 1339 | for ( |
| 1260 | 1340 | struct_node.fields, |
| ... | ... | @@ -1264,21 +1344,30 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1264 | 1344 | const field = t.ast.fullStructField(field_node); |
| 1265 | 1345 | |
| 1266 | 1346 | const id = try t.decodeIdentifier(field.id); |
| 1267 | | |
| 1268 | | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id))); |
| 1269 | | t.extra.setU32(j, @intFromEnum((try t.transformExpression(decl, field.type)) orelse |
| 1270 | | @panic("TODO: print better error"))); |
| 1347 | const field_name = try t.addNullTerminatedString(id); |
| 1348 | |
| 1349 | var field_result_type: ResultType = .{ .struct_field = .{ |
| 1350 | .type = base_type, |
| 1351 | .field = field_name, |
| 1352 | } }; |
| 1353 | const field_value = (try t.transformExpression( |
| 1354 | ctx, |
| 1355 | &field_result_type, |
| 1356 | field.type, |
| 1357 | )) orelse return null; |
| 1358 | |
| 1359 | t.extra.setU32(i, @intFromEnum(field_name)); |
| 1360 | t.extra.setU32(j, @intFromEnum( |
| 1361 | field_value, |
| 1362 | )); |
| 1271 | 1363 | } |
| 1272 | 1364 | |
| 1273 | | return try t.addInstRef(.{ |
| 1274 | | .tag = .struct_init, |
| 1275 | | .data = EncodedData.encode(.struct_init, .{ |
| 1276 | | .src_node = decl.nodeOffset(node), |
| 1277 | | .extra_index = try t.extra.append(t.gpa, Inst.StructInit, .{ |
| 1278 | | .base_type = base_type, |
| 1279 | | .payload_index = payload_index, |
| 1280 | | .field_len = @intCast(struct_node.fields.len), |
| 1281 | | }), |
| 1365 | return try t.addRef(.struct_init, .{ |
| 1366 | .src_node = ctx.nodeOffset(node), |
| 1367 | .extra_index = try t.extra.append(t.gpa, Teg.StructInit, .{ |
| 1368 | .base_type = base_type, |
| 1369 | .payload_index = payload_index, |
| 1370 | .field_len = @intCast(struct_node.fields.len), |
| 1282 | 1371 | }), |
| 1283 | 1372 | }); |
| 1284 | 1373 | }, |
| ... | ... | @@ -1286,17 +1375,19 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1286 | 1375 | .array_init => { |
| 1287 | 1376 | const array_init = t.ast.nodeData(node).list; |
| 1288 | 1377 | |
| 1289 | | const items = try t.transformExpressionList(decl, array_init.slice(&t.ast)) orelse |
| 1290 | | return null; |
| 1378 | const array_type = try result.consume(t); |
| 1291 | 1379 | |
| 1292 | | return try t.addInstRef(.{ |
| 1293 | | .tag = .array_init, |
| 1294 | | .data = EncodedData.encode(.array_init, .{ |
| 1295 | | .src_node = decl.nodeOffset(node), |
| 1296 | | .extra_index = try t.extra.append(t.gpa, Inst.ArrayInit, .{ |
| 1297 | | .type_hint = .none, |
| 1298 | | .list = items, |
| 1299 | | }), |
| 1380 | const items = try t.transformExpressionList( |
| 1381 | ctx, |
| 1382 | if (array_type) |ty| .{ .array_value = ty } else .none, |
| 1383 | array_init.slice(&t.ast), |
| 1384 | ) orelse return null; |
| 1385 | |
| 1386 | return try t.addRef(.array_init, .{ |
| 1387 | .src_node = ctx.nodeOffset(node), |
| 1388 | .extra_index = try t.extra.append(t.gpa, Teg.ArrayInit, .{ |
| 1389 | .type_hint = .none, |
| 1390 | .list = items, |
| 1300 | 1391 | }), |
| 1301 | 1392 | }); |
| 1302 | 1393 | }, |
| ... | ... | @@ -1305,12 +1396,16 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1305 | 1396 | } |
| 1306 | 1397 | } |
| 1307 | 1398 | |
| 1308 | | fn transformExpressionList(t: *Transform, decl: BlockContext, args: []const NodeIndex) !?RefList { |
| 1399 | fn transformExpressionList(t: *Transform, decl: BlockContext, result: ResultType, args: []const NodeIndex) !?RefList { |
| 1309 | 1400 | const extra_top = try t.extra.alloc(t.gpa, args.len); |
| 1310 | 1401 | |
| 1311 | 1402 | for (args, extra_top..) |arg, i| { |
| 1312 | | t.extra.setU32(i, @intFromEnum(try t.transformExpression(decl, arg) orelse |
| 1313 | | return null)); |
| 1403 | var result_mut = result; |
| 1404 | t.extra.setU32( |
| 1405 | i, |
| 1406 | @intFromEnum(try t.transformExpression(decl, &result_mut, arg) orelse |
| 1407 | return null), |
| 1408 | ); |
| 1314 | 1409 | } |
| 1315 | 1410 | |
| 1316 | 1411 | return .{ |
| ... | ... | @@ -1319,7 +1414,25 @@ fn transformExpressionList(t: *Transform, decl: BlockContext, args: []const Node |
| 1319 | 1414 | }; |
| 1320 | 1415 | } |
| 1321 | 1416 | |
| 1322 | | fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) Allocator.Error!?Inst.Index { |
| 1417 | /// The left-hand side of an assignment is known as the "assignment target". |
| 1418 | /// This returns the assignment target's Ref, as well as a result type to visit |
| 1419 | /// the right-hand side with. Note that only a few expressions are valid here: |
| 1420 | /// |
| 1421 | /// - A plain identifier, which is resolved directly to a `memory` instruction |
| 1422 | /// - A `field` node: TODO |
| 1423 | /// - An `index` node: TODO |
| 1424 | /// |
| 1425 | /// Note that these three are the root nodes, it is legal to have: |
| 1426 | /// |
| 1427 | /// get().x = 1; // `field` of a `call` node |
| 1428 | /// |
| 1429 | /// In the end, the assignment target must comprise either a ref to a `memory` |
| 1430 | /// or a value with a reference type; and optionally a sub property. |
| 1431 | fn transformAssignmentTarget( |
| 1432 | t: *Transform, |
| 1433 | decl: BlockContext, |
| 1434 | node: NodeIndex, |
| 1435 | ) Allocator.Error!?struct { Inst.Index, ResultType } { |
| 1323 | 1436 | const node_tag = t.ast.nodes.items(.tag)[node]; |
| 1324 | 1437 | |
| 1325 | 1438 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); |
| ... | ... | @@ -1345,7 +1458,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1345 | 1458 | return null; |
| 1346 | 1459 | } |
| 1347 | 1460 | |
| 1348 | | const lookup = t.lookupId(id) orelse { |
| 1461 | const lookup: *Scope.Symbol = t.lookupId(id) orelse { |
| 1349 | 1462 | try t.report.err(.unknown_identifier, .{ .node = node }); |
| 1350 | 1463 | return null; |
| 1351 | 1464 | }; |
| ... | ... | @@ -1353,7 +1466,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1353 | 1466 | return switch (lookup.data) { |
| 1354 | 1467 | .mutable => |inst| { |
| 1355 | 1468 | lookup.mutated = true; |
| 1356 | | return inst; |
| 1469 | return .{ inst, lookup.type.resolve() }; |
| 1357 | 1470 | }, |
| 1358 | 1471 | else => { |
| 1359 | 1472 | lookup.used = true; |
| ... | ... | @@ -1371,6 +1484,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1371 | 1484 | }, |
| 1372 | 1485 | }, |
| 1373 | 1486 | }); |
| 1487 | |
| 1374 | 1488 | return null; |
| 1375 | 1489 | }, |
| 1376 | 1490 | }; |
| ... | ... | @@ -1379,10 +1493,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1379 | 1493 | // TODO: allow field expressions, but these need to emit a different store node |
| 1380 | 1494 | |
| 1381 | 1495 | else => { |
| 1382 | | _ = try t.transformExpression(decl, node); |
| 1496 | var result_type: ResultType = .none; |
| 1497 | _ = try t.transformExpression(decl, &result_type, node); |
| 1498 | |
| 1383 | 1499 | try t.report.err(.invalid_assignment_target, .{ |
| 1384 | 1500 | .node = node, |
| 1385 | 1501 | }); |
| 1502 | |
| 1386 | 1503 | return null; |
| 1387 | 1504 | }, |
| 1388 | 1505 | } |
| ... | ... | @@ -1390,10 +1507,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1390 | 1507 | |
| 1391 | 1508 | fn transformBlockStatement( |
| 1392 | 1509 | t: *Transform, |
| 1393 | | decl: BlockContext, |
| 1510 | ctx: BlockContext, |
| 1394 | 1511 | node: NodeIndex, |
| 1395 | | is_implicit_return_expression: bool, |
| 1512 | maybe_result: ?*ResultType, |
| 1396 | 1513 | ) !?Inst.Ref { |
| 1514 | var void_result_storage: ResultType = .{ .ref = .void }; |
| 1515 | const result = maybe_result orelse &void_result_storage; |
| 1516 | |
| 1397 | 1517 | const node_tag = t.ast.nodes.items(.tag)[node]; |
| 1398 | 1518 | |
| 1399 | 1519 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); |
| ... | ... | @@ -1437,16 +1557,16 @@ fn transformBlockStatement( |
| 1437 | 1557 | |
| 1438 | 1558 | return .void; |
| 1439 | 1559 | }, |
| 1440 | | else => try t.transformExpression(decl, var_decl.init) orelse |
| 1441 | | return null, |
| 1560 | else => ref: { |
| 1561 | var empty_type: ResultType = .none; |
| 1562 | break :ref try t.transformExpression(ctx, &empty_type, var_decl.init) orelse |
| 1563 | return null; |
| 1564 | }, |
| 1442 | 1565 | }; |
| 1443 | 1566 | |
| 1444 | | return try t.addInstRef(.{ |
| 1445 | | .tag = .discard, |
| 1446 | | .data = EncodedData.encode(.discard, .{ |
| 1447 | | .src_node = decl.nodeOffset(node), |
| 1448 | | .operand = ref, |
| 1449 | | }), |
| 1567 | return try t.addRef(.discard, .{ |
| 1568 | .src_node = ctx.nodeOffset(node), |
| 1569 | .operand = ref, |
| 1450 | 1570 | }); |
| 1451 | 1571 | } |
| 1452 | 1572 | |
| ... | ... | @@ -1464,36 +1584,37 @@ fn transformBlockStatement( |
| 1464 | 1584 | return .void; |
| 1465 | 1585 | } |
| 1466 | 1586 | |
| 1587 | var type_result: ResultType = .{ .ref = .type_type }; |
| 1467 | 1588 | const type_ref = if (var_decl.type) |data_type| |
| 1468 | | try t.transformExpression(decl, data_type) |
| 1589 | try t.transformExpression(ctx, &type_result, data_type) |
| 1469 | 1590 | else |
| 1470 | 1591 | null; |
| 1471 | | const data_ref = try t.transformExpression(decl, var_decl.init) orelse |
| 1472 | | return null; |
| 1473 | | const data_coerced_ref = if (type_ref) |ref| |
| 1474 | | try t.addInstRef(.{ |
| 1475 | | .tag = .as, |
| 1476 | | .data = EncodedData.encode(.as, .{ |
| 1477 | | .src_node = decl.nodeOffset(node), |
| 1478 | | .extra_index = try t.extra.append(t.gpa, Inst.As, .{ |
| 1479 | | .type = ref, |
| 1480 | | .operand = data_ref, |
| 1481 | | }), |
| 1482 | | }), |
| 1483 | | }) |
| 1484 | | else |
| 1485 | | data_ref; |
| 1486 | 1592 | |
| 1487 | | const mem_ref = if (var_decl.is_const) |
| 1488 | | data_coerced_ref |
| 1593 | var value_result: ResultType = if (type_ref) |ty| |
| 1594 | .{ .ref = ty } |
| 1489 | 1595 | else |
| 1490 | | try t.addInstRef(.{ |
| 1491 | | .tag = .memory, |
| 1492 | | .data = EncodedData.encode(.memory, .{ |
| 1493 | | .src_node = decl.nodeOffset(node), |
| 1494 | | .operand = data_coerced_ref, |
| 1495 | | }), |
| 1496 | | }); |
| 1596 | .none; |
| 1597 | |
| 1598 | const data_ref = try t.transformExpression(ctx, &value_result, var_decl.init) orelse |
| 1599 | return null; |
| 1600 | |
| 1601 | // Add a .coerce instruction if there was a type declared. |
| 1602 | const data_coerced_ref = coerced: { |
| 1603 | if (type_ref) |ref| { |
| 1604 | // We can avoid the coercion instruction if the result type was used. |
| 1605 | if (value_result != .used) { |
| 1606 | break :coerced try t.addRef(.coercion, .{ |
| 1607 | .src_node = ctx.nodeOffset(node), |
| 1608 | .extra_index = try t.extra.append(t.gpa, Teg.Coercion, .{ |
| 1609 | .type = ref, |
| 1610 | .operand = data_ref, |
| 1611 | }), |
| 1612 | }); |
| 1613 | } |
| 1614 | } |
| 1615 | |
| 1616 | break :coerced data_ref; |
| 1617 | }; |
| 1497 | 1618 | |
| 1498 | 1619 | const gop = try t.scope().symbols.getOrPut(t.gpa, id); |
| 1499 | 1620 | |
| ... | ... | @@ -1505,16 +1626,26 @@ fn transformBlockStatement( |
| 1505 | 1626 | |
| 1506 | 1627 | gop.value_ptr.* = .{ |
| 1507 | 1628 | .node = node, |
| 1508 | | .data = switch (var_decl.is_const) { |
| 1509 | | true => .{ .immutable = mem_ref }, |
| 1510 | | false => .{ .mutable = mem_ref.index().? }, |
| 1511 | | }, |
| 1629 | .data = if (var_decl.is_const) |
| 1630 | .{ .immutable = data_coerced_ref } |
| 1631 | else |
| 1632 | .{ |
| 1633 | .mutable = try t.addIndex(.memory, .{ |
| 1634 | .src_node = ctx.nodeOffset(node), |
| 1635 | .operand = data_coerced_ref, |
| 1636 | }), |
| 1637 | }, |
| 1638 | .type = if (type_ref) |ty| |
| 1639 | .{ .present = ty } |
| 1640 | else |
| 1641 | .{ .missing = data_ref }, |
| 1512 | 1642 | }; |
| 1513 | 1643 | |
| 1514 | 1644 | return .void; |
| 1515 | 1645 | }, |
| 1516 | 1646 | |
| 1517 | 1647 | .fn_decl, |
| 1648 | .struct_decl, |
| 1518 | 1649 | => |tag| { |
| 1519 | 1650 | std.debug.panic("TODO: block statement {s}", .{@tagName(tag)}); |
| 1520 | 1651 | }, |
| ... | ... | @@ -1530,18 +1661,17 @@ fn transformBlockStatement( |
| 1530 | 1661 | // discard, instead of appending an .ensure_void check, |
| 1531 | 1662 | // we will add a .discard instruction instead. |
| 1532 | 1663 | // this will emit a warning if given void. |
| 1533 | | const result = try t.transformExpression(decl, bin.rhs) orelse |
| 1664 | var result_type: ResultType = .none; |
| 1665 | const value = try t.transformExpression(ctx, &result_type, bin.rhs) orelse |
| 1534 | 1666 | return null; |
| 1535 | | return try t.addInstRef(.{ |
| 1536 | | .tag = .discard, |
| 1537 | | .data = EncodedData.encode(.discard, .{ |
| 1538 | | .src_node = decl.nodeOffset(node), |
| 1539 | | .operand = result, |
| 1540 | | }), |
| 1667 | |
| 1668 | return try t.addRef(.discard, .{ |
| 1669 | .src_node = ctx.nodeOffset(node), |
| 1670 | .operand = value, |
| 1541 | 1671 | }); |
| 1542 | 1672 | } else { |
| 1543 | 1673 | // process this assignment as usual, do not append .ensure_void |
| 1544 | | return try t.transformExpression(decl, node); |
| 1674 | return try t.transformExpression(ctx, result, node); |
| 1545 | 1675 | } |
| 1546 | 1676 | }, |
| 1547 | 1677 | |
| ... | ... | @@ -1560,50 +1690,51 @@ fn transformBlockStatement( |
| 1560 | 1690 | .shr_assign, |
| 1561 | 1691 | => { |
| 1562 | 1692 | // do not append a .ensure_void check |
| 1563 | | return try t.transformExpression(decl, node); |
| 1693 | return try t.transformExpression(ctx, result, node); |
| 1564 | 1694 | }, |
| 1565 | 1695 | |
| 1566 | 1696 | else => { |
| 1567 | | const result = try t.transformExpression(decl, node) orelse |
| 1697 | const value = try t.transformExpression(ctx, result, node) orelse |
| 1568 | 1698 | return null; |
| 1569 | 1699 | |
| 1570 | | if (!is_implicit_return_expression and result != .void) { |
| 1700 | if (maybe_result == null and value != .void) { |
| 1571 | 1701 | if (t.ast.nodeTag(node) == .id) { |
| 1572 | 1702 | try t.report.warn(.dangling_identifier, .{ |
| 1573 | 1703 | .node = node, |
| 1574 | 1704 | }); |
| 1575 | | return result; |
| 1576 | 1705 | } |
| 1577 | 1706 | |
| 1578 | | return try t.addInstRef(.{ |
| 1579 | | .tag = .ensure_void, |
| 1580 | | .data = EncodedData.encode(.ensure_void, .{ |
| 1581 | | .src_node = decl.nodeOffset(node), |
| 1582 | | .operand = result, |
| 1583 | | }), |
| 1707 | return try t.addRef(.ensure_void, .{ |
| 1708 | .src_node = ctx.nodeOffset(node), |
| 1709 | .operand = value, |
| 1584 | 1710 | }); |
| 1585 | 1711 | } |
| 1586 | 1712 | |
| 1587 | | return result; |
| 1713 | return value; |
| 1588 | 1714 | }, |
| 1589 | 1715 | } |
| 1590 | 1716 | } |
| 1591 | 1717 | |
| 1592 | | fn addRootInst(t: *Transform, inst: Inst) !Inst.Index { |
| 1593 | | try t.insts.append(t.gpa, inst); |
| 1718 | fn addUntracked(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Index { |
| 1719 | try t.insts.append(t.gpa, .{ |
| 1720 | .tag = tag, |
| 1721 | .data = EncodedData.encode(tag, data), |
| 1722 | }); |
| 1594 | 1723 | return @enumFromInt(t.insts.len - 1); |
| 1595 | 1724 | } |
| 1596 | 1725 | |
| 1597 | | fn addInstRef(t: *Transform, inst: Inst) !Inst.Ref { |
| 1598 | | try t.scratch.ensureUnusedCapacity(t.gpa, 1); |
| 1599 | | try t.insts.append(t.gpa, inst); |
| 1600 | | const index: Inst.Index = @enumFromInt(t.insts.len - 1); |
| 1601 | | t.scratch.appendAssumeCapacity(@intFromEnum(index)); |
| 1602 | | return index.toRef(); |
| 1726 | fn addIndex(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Index { |
| 1727 | const index = try t.addUntracked(tag, data); |
| 1728 | try t.scratch.add(t.arena, Inst.Index, index); |
| 1729 | return index; |
| 1730 | } |
| 1731 | |
| 1732 | fn addRef(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Ref { |
| 1733 | return (try t.addIndex(tag, data)).toRef(); |
| 1603 | 1734 | } |
| 1604 | 1735 | |
| 1605 | 1736 | fn pushScope(t: *Transform) !void { |
| 1606 | | try t.scopes.append(t.gpa, .{}); |
| 1737 | try t.scopes.append(t.arena, .{}); |
| 1607 | 1738 | } |
| 1608 | 1739 | |
| 1609 | 1740 | fn popScope(t: *Transform) void { |
| ... | ... | @@ -1620,22 +1751,18 @@ fn getInstData(t: *Transform, index: Inst.Index) *EncodedData { |
| 1620 | 1751 | return &t.insts.items(.data)[@intFromEnum(index)]; |
| 1621 | 1752 | } |
| 1622 | 1753 | |
| 1623 | | fn addScratch(t: *Transform, comptime T: type, value: T) !void { |
| 1624 | | try t.scratch.append(t.gpa, @intFromEnum(value)); |
| 1625 | | } |
| 1626 | | |
| 1627 | | fn scratchToRefList(t: *Transform, scratch_top: usize) !RefList { |
| 1628 | | const items = t.scratch.items[scratch_top..]; |
| 1754 | fn scratchToRefList(t: *Transform, scratch_top: core.U32Stack.Mark) !RefList { |
| 1755 | const items = scratch_top.slice(u32); |
| 1629 | 1756 | const extra_top = t.extra.list.items.len; |
| 1630 | | try t.extra.list.appendSlice(t.gpa, t.scratch.items[scratch_top..]); |
| 1757 | try t.extra.list.appendSlice(t.gpa, items); |
| 1631 | 1758 | return .{ |
| 1632 | 1759 | .start = @intCast(extra_top), |
| 1633 | 1760 | .len = @intCast(items.len), |
| 1634 | 1761 | }; |
| 1635 | 1762 | } |
| 1636 | 1763 | |
| 1637 | | fn scratchToIndexList(t: *Transform, scratch_top: usize) !IndexList { |
| 1638 | | const items = t.scratch.items[scratch_top..]; |
| 1764 | fn scratchToIndexList(t: *Transform, scratch: core.U32Stack.Mark) !IndexList { |
| 1765 | const items = scratch.slice(u32); |
| 1639 | 1766 | const extra_top = t.extra.list.items.len; |
| 1640 | 1767 | try t.extra.list.appendSlice(t.gpa, items); |
| 1641 | 1768 | return .{ |
| ... | ... | @@ -1715,8 +1842,8 @@ fn addDecodedSlicedString(t: *Transform, escaped: []const u8) !SlicedString { |
| 1715 | 1842 | // string. this is done so that duplicate strings will get de-duplicated |
| 1716 | 1843 | // in string_bytes. It is more likely that there will be remaining bytes |
| 1717 | 1844 | // in `.scratch` vs `.id_decode_buf` |
| 1718 | | try t.scratch.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1); |
| 1719 | | const buf = mem.sliceAsBytes(t.scratch.unusedCapacitySlice()); |
| 1845 | try t.scratch.list.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1); |
| 1846 | const buf = mem.sliceAsBytes(t.scratch.list.unusedCapacitySlice()); |
| 1720 | 1847 | |
| 1721 | 1848 | break :brk Tokenizer.decodeStringBuf(escaped, buf); |
| 1722 | 1849 | } else escaped; |
| ... | ... | @@ -1767,7 +1894,8 @@ const mem = std.mem; |
| 1767 | 1894 | const Allocator = mem.Allocator; |
| 1768 | 1895 | const assert = std.debug.assert; |
| 1769 | 1896 | |
| 1770 | | const Teg = @import("Teg.zig"); |
| 1897 | const core = @import("mod.zig"); |
| 1898 | const Teg = core.Teg; |
| 1771 | 1899 | const Inst = Teg.Inst; |
| 1772 | 1900 | const NullTerminatedString = Teg.NullTerminatedString; |
| 1773 | 1901 | const SlicedString = Teg.SlicedString; |
| ... | ... | @@ -1776,15 +1904,15 @@ const IndexList = Teg.IndexList; |
| 1776 | 1904 | const EncodedData = Teg.EncodedData; |
| 1777 | 1905 | const ExtraIndex = Teg.ExtraIndex; |
| 1778 | 1906 | |
| 1779 | | const dod = @import("array.zig"); |
| 1780 | | const ExtraStore = dod.ExtraStore(.teg, .@"var"); |
| 1781 | | const Float = dod.Float; |
| 1907 | const extra = core.array; |
| 1908 | const ExtraStore = extra.ExtraStore(.teg, .@"var"); |
| 1909 | const Float = extra.Float; |
| 1782 | 1910 | |
| 1783 | | const Tokenizer = @import("Tokenizer.zig"); |
| 1911 | const Tokenizer = core.Tokenizer; |
| 1784 | 1912 | const Token = Tokenizer.Token; |
| 1785 | 1913 | const ByteOffset = Tokenizer.ByteOffset; |
| 1786 | 1914 | |
| 1787 | | const Ast = @import("Ast.zig"); |
| 1915 | const Ast = core.Ast; |
| 1788 | 1916 | const Unused = Ast.Unused; |
| 1789 | 1917 | const Node = Ast.Node; |
| 1790 | 1918 | const Data = Node.Data; |
| ... | ... | @@ -1798,9 +1926,9 @@ const optionalTokenIndex = OptionalTokenIndex.init; |
| 1798 | 1926 | const optionalNodeIndex = OptionalNodeIndex.init; |
| 1799 | 1927 | const optionalExtraIndex = OptionalExtraIndex.init; |
| 1800 | 1928 | |
| 1801 | | const InternPool = @import("InternPool.zig"); |
| 1929 | const InternPool = core.InternPool; |
| 1802 | 1930 | |
| 1803 | | const Report = @import("Report.zig"); |
| 1804 | | const Comp = @import("Comp.zig"); |
| 1931 | const Report = core.Report; |
| 1932 | const Comp = core.Comp; |
| 1805 | 1933 | |
| 1806 | 1934 | const tracer = @import("tracer"); |