| ... | @@ -1,26 +1,35 @@ | ... | @@ -1,26 +1,35 @@ |
| 1 | //! Transform pass converts an Ast into a high-level/untyped instruction format. | 1 | //! Transform pass converts an Ast into a high-level/untyped instruction format. |
| 2 | //! See Teg.zig for more details about the IR. Transforming handles: | 2 | //! See Teg.zig for more details about the IR. Transforming handles: |
| 3 | //! | 3 | //! |
| | 4 | //! - Flattening nested expressions into instruction sequences. |
| 4 | //! - Resolving all local variables into reference or declaration names. | 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 | //! - Annotating expression statements with `.discard` and `.ensure_void` | 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 | //! Some trivial diagnostics such as unused variables, unknown result types, | 10 | //! Some trivial diagnostics such as unused variables, unknown result types, |
| 10 | //! invalid assignment targets, and other style issues are also reported here. | 11 | //! invalid assignment targets, and other style issues are also reported here. |
| | 12 | |
| 11 | gpa: Allocator, | 13 | gpa: Allocator, |
| | 14 | arena: Allocator, |
| | 15 | |
| 12 | ast: Ast, | 16 | ast: Ast, |
| | 17 | |
| | 18 | // these values are allocated through `gpa`, so that resulting IR can be |
| | 19 | // trivially shrunk before returning. |
| | 20 | |
| 13 | insts: Inst.List = .{}, | 21 | insts: Inst.List = .{}, |
| 14 | string_bytes: std.ArrayListUnmanaged(u8) = .{}, | | |
| 15 | extra: ExtraStore = .{}, | 22 | extra: ExtraStore = .{}, |
| | 23 | string_bytes: std.ArrayListUnmanaged(u8) = .{}, |
| | 24 | report: Report.Builder, |
| 16 | | 25 | |
| 17 | scratch: std.ArrayListUnmanaged(u32) = .{}, | 26 | // however, these are allocated through `arena`, since they are always freed at |
| 18 | id_decode_buf: std.ArrayList(u8), | 27 | // the end of the pass. |
| 19 | | 28 | |
| | 29 | scratch: core.U32Stack = .{}, |
| | 30 | id_decode_buf: std.ArrayList(u8), |
| 20 | scopes: std.ArrayListUnmanaged(Scope) = .{}, | 31 | scopes: std.ArrayListUnmanaged(Scope) = .{}, |
| 21 | | 32 | |
| 22 | report: Report.Builder, | | |
| 23 | | | |
| 24 | pub const Error = error{ | 33 | pub const Error = error{ |
| 25 | /// A `Report` will contain at least one error. | 34 | /// A `Report` will contain at least one error. |
| 26 | TransformFail, | 35 | TransformFail, |
| ... | @@ -40,6 +49,8 @@ pub fn transform( | ... | @@ -40,6 +49,8 @@ pub fn transform( |
| 40 | var t: Transform = .{ | 49 | var t: Transform = .{ |
| 41 | .ast = ast, | 50 | .ast = ast, |
| 42 | .gpa = gpa, | 51 | .gpa = gpa, |
| | 52 | // TODO: Currently this is illusion of free will |
| | 53 | .arena = gpa, |
| 43 | .report = .{ .gpa = gpa, .current_file = file }, | 54 | .report = .{ .gpa = gpa, .current_file = file }, |
| 44 | .id_decode_buf = std.ArrayList(u8).initCapacity(sfa.get(), 4096) catch unreachable, | 55 | .id_decode_buf = std.ArrayList(u8).initCapacity(sfa.get(), 4096) catch unreachable, |
| 45 | }; | 56 | }; |
| ... | @@ -57,9 +68,7 @@ pub fn transform( | ... | @@ -57,9 +68,7 @@ pub fn transform( |
| 57 | return error.TransformFail; | 68 | return error.TransformFail; |
| 58 | } | 69 | } |
| 59 | | 70 | |
| 60 | assert(@intFromEnum(module) == 0); | 71 | assert(module == .main_module_inst); |
| 61 | | | |
| 62 | try t.insts.setCapacity(t.gpa, t.insts.len); | | |
| 63 | | 72 | |
| 64 | return .{ | 73 | return .{ |
| 65 | .insts = t.insts.toOwnedSlice(), | 74 | .insts = t.insts.toOwnedSlice(), |
| ... | @@ -69,84 +78,82 @@ pub fn transform( | ... | @@ -69,84 +78,82 @@ pub fn transform( |
| 69 | }; | 78 | }; |
| 70 | } | 79 | } |
| 71 | | 80 | |
| 72 | pub fn deinit(t: *Transform) void { | 81 | fn deinit(t: *Transform) void { |
| 73 | t.scratch.deinit(t.gpa); | | |
| 74 | assert(t.scopes.items.len == 0); | 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 | t.string_bytes.deinit(t.gpa); | 87 | t.string_bytes.deinit(t.gpa); |
| 77 | t.insts.deinit(t.gpa); | 88 | t.insts.deinit(t.gpa); |
| 78 | t.extra.deinit(t.gpa); | 89 | t.extra.deinit(t.gpa); |
| 79 | t.id_decode_buf.deinit(); | | |
| 80 | t.* = undefined; | 90 | t.* = undefined; |
| 81 | } | 91 | } |
| 82 | | 92 | |
| 83 | const Scope = struct { | 93 | /// Represents how the parent node is going to implicitly cast this expression. |
| 84 | symbols: std.StringArrayHashMapUnmanaged(Symbol) = .{}, | 94 | /// |
| 85 | | 95 | /// Consider an expression like `let x: S = .{ y: 1 }`. The type from the |
| 86 | const Symbol = struct { | 96 | /// declaration must impact the visited struct initialization expression. This |
| 87 | node: NodeIndex, | 97 | /// behavior is recursive, so something more complex can properly infer: |
| 88 | data: Symbol.Data, | 98 | /// |
| 89 | // TODO: optimize this structure | 99 | /// struct S { a: int } |
| 90 | used: bool = false, | 100 | /// struct Y {} |
| 91 | mutated: bool = false, | 101 | /// let x: [](S, []Y) = [ .{ .{ a: 1 }, [ .{} ] } ]; |
| 92 | discarded: ?NodeIndex = null, | 102 | /// |
| 93 | | 103 | /// The innermost `.{}` must eventually analyze to `Y{}`, which can be unwrapped |
| 94 | const Data = union(enum) { | 104 | /// from the type expression via specialized instructions for this purpose. |
| 95 | declaration, | 105 | const ResultType = union(enum) { |
| 96 | param, | 106 | /// There is no result type available |
| 97 | mutable: Inst.Index, | 107 | none, |
| 98 | immutable: Inst.Ref, | 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 | const BlockContext = struct { | 147 | const BlockContext = struct { |
| 146 | node_base: i32, | 148 | decl_node_offset: i32, |
| 147 | return_target: ?*?Inst.Index = null, | 149 | return_target: ?*ReturnTarget = null, |
| 148 | break_target: ?*BreakTarget = null, | 150 | break_target: ?*BreakTarget = null, |
| 149 | | 151 | |
| | 152 | const ReturnTarget = struct { |
| | 153 | generated_block: ?Inst.Index = null, |
| | 154 | result_type: ResultType, |
| | 155 | }; |
| | 156 | |
| 150 | /// This struct is always stored on the stack; the idea is to pass mutable | 157 | /// This struct is always stored on the stack; the idea is to pass mutable |
| 151 | /// pointers to it. When `break :brk` is encountered, it will generate the | 158 | /// pointers to it. When `break :brk` is encountered, it will generate the |
| 152 | /// block instruction. This way, redundant blocks are never created. | 159 | /// block instruction. This way, redundant blocks are never created. |
| ... | @@ -157,6 +164,7 @@ const BlockContext = struct { | ... | @@ -157,6 +164,7 @@ const BlockContext = struct { |
| 157 | /// If `null`, this label was unused. | 164 | /// If `null`, this label was unused. |
| 158 | block_index: ?Inst.Index = null, | 165 | block_index: ?Inst.Index = null, |
| 159 | parent: ?*BreakTarget = null, | 166 | parent: ?*BreakTarget = null, |
| | 167 | result_type: *ResultType, |
| 160 | | 168 | |
| 161 | fn addChild(parent: *BreakTarget, label: ?[]const u8, is_loop: bool) BreakTarget { | 169 | fn addChild(parent: *BreakTarget, label: ?[]const u8, is_loop: bool) BreakTarget { |
| 162 | return .{ | 170 | return .{ |
| ... | @@ -168,10 +176,7 @@ const BlockContext = struct { | ... | @@ -168,10 +176,7 @@ const BlockContext = struct { |
| 168 | | 176 | |
| 169 | fn getInst(target: *BreakTarget, t: *Transform) !Inst.Index { | 177 | fn getInst(target: *BreakTarget, t: *Transform) !Inst.Index { |
| 170 | return target.block_index orelse { | 178 | return target.block_index orelse { |
| 171 | const inst = try t.addRootInst(.{ | 179 | const inst = try t.addUntracked(.block, undefined); |
| 172 | .tag = .block, | | |
| 173 | .data = undefined, | | |
| 174 | }); | | |
| 175 | target.block_index = inst; | 180 | target.block_index = inst; |
| 176 | return inst; | 181 | return inst; |
| 177 | }; | 182 | }; |
| ... | @@ -180,41 +185,41 @@ const BlockContext = struct { | ... | @@ -180,41 +185,41 @@ const BlockContext = struct { |
| 180 | | 185 | |
| 181 | fn init(root_node: NodeIndex) BlockContext { | 186 | fn init(root_node: NodeIndex) BlockContext { |
| 182 | return .{ | 187 | return .{ |
| 183 | .node_base = @intCast(root_node), | 188 | .decl_node_offset = @intCast(root_node), |
| 184 | }; | 189 | }; |
| 185 | } | 190 | } |
| 186 | | 191 | |
| 187 | fn nodeOffset(self: BlockContext, node: NodeIndex) i32 { | 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 | fn declOffset(self: BlockContext, node: NodeIndex) BlockContext { | 196 | fn declOffset(self: BlockContext, node: NodeIndex) BlockContext { |
| 192 | return .{ | 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 | return .{ | 203 | return .{ |
| 199 | .node_base = self.node_base, | 204 | .decl_node_offset = self.decl_node_offset, |
| 200 | .return_target = return_target, | 205 | .return_target = return_target, |
| 201 | }; | 206 | }; |
| 202 | } | 207 | } |
| 203 | | 208 | |
| 204 | fn withBreak(self: BlockContext, break_target: *BreakTarget) BlockContext { | 209 | fn withBreak(self: BlockContext, break_target: *BreakTarget) BlockContext { |
| 205 | return .{ | 210 | return .{ |
| 206 | .node_base = self.node_base, | 211 | .decl_node_offset = self.decl_node_offset, |
| 207 | .return_target = self.return_target, | 212 | .return_target = self.return_target, |
| 208 | .break_target = break_target, | 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 | var break_target = self.break_target; | 218 | var break_target = self.break_target; |
| 214 | while (break_target) |target| : (break_target = target.parent) { | 219 | while (break_target) |target| : (break_target = target.parent) { |
| 215 | if (target.label) |target_label| { | 220 | if (target.label) |target_label| { |
| 216 | if (mem.eql(u8, label, target_label)) { | 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,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 | const Liveness = enum { alive, dead }; | 308 | const Liveness = enum { alive, dead }; |
| 226 | | 309 | |
| 227 | const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{ | 310 | const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{ |
| ... | @@ -242,10 +325,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I | ... | @@ -242,10 +325,7 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I |
| 242 | const trace = tracer.trace(@src()); | 325 | const trace = tracer.trace(@src()); |
| 243 | defer trace.end(); | 326 | defer trace.end(); |
| 244 | | 327 | |
| 245 | const module = try t.addRootInst(.{ | 328 | const module = try t.addUntracked(.module, undefined); |
| 246 | .tag = .module, | | |
| 247 | .data = undefined, | | |
| 248 | }); | | |
| 249 | | 329 | |
| 250 | const decl = BlockContext.init(module_node); | 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,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(.{ | 354 | const top_level_fn = try t.addUntracked(.declaration, undefined); |
| 275 | .tag = .declaration, | | |
| 276 | .data = undefined, | | |
| 277 | }); | | |
| 278 | | 355 | |
| 279 | temp[temp.len - 1] = top_level_fn; | 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,42 +362,39 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I |
| 285 | try t.extra.list.appendSlice(t.gpa, @ptrCast(temp)); | 362 | try t.extra.list.appendSlice(t.gpa, @ptrCast(temp)); |
| 286 | | 363 | |
| 287 | const top_level_code_ref: Inst.Ref = ref: { | 364 | const top_level_code_ref: Inst.Ref = ref: { |
| 288 | const scratch_top = t.scratch.items.len; | 365 | const scratch = t.scratch.mark(); |
| 289 | defer t.scratch.shrinkRetainingCapacity(scratch_top); | 366 | defer scratch.deinit(); |
| 290 | | 367 | |
| 291 | for (statements) |statement| { | 368 | for (statements) |statement| { |
| 292 | try t.transformModuleStatement(decl, statement); | 369 | try t.transformModuleStatement(decl, statement); |
| 293 | } | 370 | } |
| 294 | | 371 | |
| 295 | // if there are no new instructions, we dont have to create a block context | 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 | break :ref .void; | 374 | break :ref .void; |
| 298 | } | 375 | } |
| 299 | | 376 | |
| 300 | const block = try t.addRootInst(.{ | 377 | const block = try t.addUntracked(.block, undefined); |
| 301 | .tag = .block, | | |
| 302 | .data = undefined, | | |
| 303 | }); | | |
| 304 | | 378 | |
| 305 | t.getInstData(block).* = EncodedData.encode(.block, .{ | 379 | t.getInstData(block).* = EncodedData.encode(.block, .{ |
| 306 | .src_node = decl.nodeOffset(module_node), | 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 | break :ref block.toRef(); | 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 | .kind = .function, | 388 | .kind = .function, |
| 315 | .name = NullTerminatedString.empty, | 389 | .name = NullTerminatedString.empty, |
| 316 | .hash = .{0} ** 16, | 390 | .hash = .{0} ** 16, |
| 317 | .instruction_count = @as(u32, @intCast(t.insts.len)) - @intFromEnum(top_level_fn), | 391 | .instruction_count = @as(u32, @intCast(t.insts.len)) - @intFromEnum(top_level_fn), |
| 318 | .body = top_level_code_ref, | 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 | .params = .{ .start = 0, .len = 0 }, | 396 | .params = .{ .start = 0, .len = 0 }, |
| 323 | .return_type = Inst.Ref.void, | 397 | .return_type = Inst.Ref.void.toOptional(), |
| 324 | }); | 398 | }); |
| 325 | | 399 | |
| 326 | t.getInstData(top_level_fn).* = EncodedData.encode(.declaration, .{ | 400 | t.getInstData(top_level_fn).* = EncodedData.encode(.declaration, .{ |
| ... | @@ -361,10 +435,14 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void | ... | @@ -361,10 +435,14 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void |
| 361 | gop.value_ptr.* = .{ | 435 | gop.value_ptr.* = .{ |
| 362 | .node = node, | 436 | .node = node, |
| 363 | .data = .declaration, | 437 | .data = .declaration, |
| | 438 | .type = .{ .present = .void }, |
| 364 | }; | 439 | }; |
| 365 | }, | 440 | }, |
| 366 | .struct_decl => { | 441 | .struct_decl => { |
| 367 | const decl = t.ast.fullStructDecl(node); | 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 | const gop = try t.scope().symbols.getOrPut(t.gpa, decl.id); | 446 | const gop = try t.scope().symbols.getOrPut(t.gpa, decl.id); |
| 369 | if (gop.found_existing) { | 447 | if (gop.found_existing) { |
| 370 | try t.report.err(.duplicate_function, .{ | 448 | try t.report.err(.duplicate_function, .{ |
| ... | @@ -377,6 +455,7 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void | ... | @@ -377,6 +455,7 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void |
| 377 | gop.value_ptr.* = .{ | 455 | gop.value_ptr.* = .{ |
| 378 | .node = node, | 456 | .node = node, |
| 379 | .data = .declaration, | 457 | .data = .declaration, |
| | 458 | .type = .{ .present = .void }, |
| 380 | }; | 459 | }; |
| 381 | }, | 460 | }, |
| 382 | else => {}, | 461 | else => {}, |
| ... | @@ -397,14 +476,14 @@ fn transformModuleStatement(t: *Transform, decl: BlockContext, node: NodeIndex) | ... | @@ -397,14 +476,14 @@ fn transformModuleStatement(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 397 | .struct_decl, | 476 | .struct_decl, |
| 398 | => {}, // declarations are analyzed separately | 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 | fn transformDecl(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index { | 483 | fn transformDecl(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index { |
| 405 | return switch (t.ast.nodeTag(node)) { | 484 | return switch (t.ast.nodeTag(node)) { |
| 406 | .fn_decl => t.transformFunction(outer_decl, node), | 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 | else => unreachable, // not a declaration | 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,33 +495,40 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A |
| 416 | const decl = outer_decl.declOffset(node); | 495 | const decl = outer_decl.declOffset(node); |
| 417 | const fn_decl = t.ast.fullFnDecl(node); | 496 | const fn_decl = t.ast.fullFnDecl(node); |
| 418 | | 497 | |
| 419 | const decl_inst = try t.addRootInst(.{ | 498 | const decl_inst = try t.addUntracked(.declaration, undefined); |
| 420 | .tag = .declaration, | | |
| 421 | .data = undefined, | | |
| 422 | }); | | |
| 423 | | 499 | |
| 424 | try t.pushScope(); | 500 | try t.pushScope(); |
| 425 | defer t.popScope(); | 501 | defer t.popScope(); |
| 426 | | 502 | |
| 427 | const param_list = brk: { | 503 | const param_list = brk: { |
| 428 | const scratch_top = t.scratch.items.len; | 504 | const scratch = t.scratch.mark(); |
| 429 | defer t.scratch.shrinkRetainingCapacity(scratch_top); | 505 | defer scratch.deinit(); |
| 430 | | 506 | |
| 431 | for (fn_decl.params) |param| { | 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 | const return_type = if (fn_decl.return_type) |return_type_node| | 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 | else | 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 | .kind = .function, | 532 | .kind = .function, |
| 447 | .name = try t.addNullTerminatedString(fn_decl.id), | 533 | .name = try t.addNullTerminatedString(fn_decl.id), |
| 448 | .hash = .{0} ** 16, | 534 | .hash = .{0} ** 16, |
| ... | @@ -450,9 +536,9 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A | ... | @@ -450,9 +536,9 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A |
| 450 | .body = body, | 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 | .params = param_list, | 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 | t.getInstData(decl_inst).* = EncodedData.encode(.declaration, .{ | 544 | t.getInstData(decl_inst).* = EncodedData.encode(.declaration, .{ |
| ... | @@ -465,21 +551,18 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A | ... | @@ -465,21 +551,18 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A |
| 465 | return decl_inst; | 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 | const trace = tracer.trace(@src()); | 555 | const trace = tracer.trace(@src()); |
| 470 | defer trace.end(); | 556 | defer trace.end(); |
| 471 | | 557 | |
| 472 | const decl = outer_decl.declOffset(node); | 558 | const decl = outer_decl.declOffset(node); |
| 473 | const decl_data = t.ast.fullStructDecl(node); | 559 | const decl_data = t.ast.fullStructDecl(node); |
| 474 | | 560 | |
| 475 | const decl_inst = try t.addRootInst(.{ | 561 | const decl_inst = try t.addUntracked(.declaration, undefined); |
| 476 | .tag = .declaration, | | |
| 477 | .data = undefined, | | |
| 478 | }); | | |
| 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 | .kind = .immutable, | 566 | .kind = .immutable, |
| 484 | .name = try t.addNullTerminatedString(decl_data.id), | 567 | .name = try t.addNullTerminatedString(decl_data.id), |
| 485 | .hash = .{0} ** 16, | 568 | .hash = .{0} ** 16, |
| ... | @@ -495,76 +578,83 @@ fn transformStruct(t: *Transform, outer_decl: BlockContext, node: NodeIndex) All | ... | @@ -495,76 +578,83 @@ fn transformStruct(t: *Transform, outer_decl: BlockContext, node: NodeIndex) All |
| 495 | return decl_inst; | 578 | return decl_inst; |
| 496 | } | 579 | } |
| 497 | | 580 | |
| 498 | const TransformFunctionCtx = struct { | 581 | const TransformToBlockMode = enum { |
| 499 | param_list: RefList, | 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 | 595 | /// guh |
| 503 | /// for places where a block is not allowed, such as a parameter list. | 596 | fn transformToBlock( |
| 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( | | |
| 507 | t: *Transform, | 597 | t: *Transform, |
| 508 | decl: BlockContext, | 598 | ctx: BlockContext, |
| 509 | node: NodeIndex, | 599 | node: NodeIndex, |
| 510 | comptime is_function: bool, | 600 | comptime mode: TransformToBlockMode, |
| | 601 | result_type: mode.ResultType(), |
| 511 | ) Allocator.Error!Inst.Ref { | 602 | ) Allocator.Error!Inst.Ref { |
| 512 | const trace = tracer.trace(@src()); | 603 | const trace = tracer.trace(@src()); |
| 513 | defer trace.end(); | 604 | defer trace.end(); |
| 514 | | 605 | |
| 515 | const node_tag = t.ast.nodes.items(.tag)[node]; | 606 | const scratch = t.scratch.mark(); |
| 516 | switch (node_tag) { | 607 | defer scratch.deinit(); |
| 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; | | |
| 529 | | 608 | |
| 530 | const scratch_top = t.scratch.items.len; | 609 | // For function blocks, add .param instructions for all of the parameters |
| 531 | defer t.scratch.shrinkRetainingCapacity(scratch_top); | 610 | if (mode == .function_body) { |
| 532 | | | |
| 533 | if (is_function) { | | |
| 534 | const s = t.scopes.getLast(); | 611 | const s = t.scopes.getLast(); |
| 535 | for (s.symbols.values(), 0..) |*param, i| { | 612 | for (s.symbols.values(), 0..) |*param, i| { |
| 536 | assert(param.data == .param); | 613 | assert(param.data == .param); |
| 537 | const ref = try t.addInstRef(.{ | 614 | const ref = try t.addRef(.param, .{ |
| 538 | .tag = .param, | 615 | .src_node = ctx.nodeOffset(node), |
| 539 | .data = EncodedData.encode(.param, .{ | 616 | .data = @intCast(i), |
| 540 | .src_node = decl.nodeOffset(node), | | |
| 541 | .data = @intCast(i), | | |
| 542 | }), | | |
| 543 | }); | 617 | }); |
| 544 | param.data = .{ .immutable = ref }; | 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 | const expr = try t.transformExpression( | 629 | const expr = try t.transformExpression( |
| 549 | if (is_function) | 630 | // Function blocks are allowed to return, in which |
| 550 | decl.withReturn(&block) | 631 | // it will return to this block. |
| | 632 | if (mode == .function_body) |
| | 633 | ctx.withReturn(&ret) |
| 551 | else | 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 | node, | 641 | node, |
| 554 | ); | 642 | ); |
| 555 | | 643 | |
| 556 | // if there are no new instructions, we dont have to create a block context | 644 | // if there are no new instructions, we dont have to create a block context |
| 557 | if (scratch_top == t.scratch.items.len) { | 645 | if (scratch.len() == 0) { |
| 558 | const result = expr orelse unreachable; // null means we *added* break or return | 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 | if (result.value() != null) { | 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 | return result; | 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 | if (expr) |result| { | 658 | if (expr) |result| { |
| 569 | if (result.index()) |index| { | 659 | if (result.index()) |index| { |
| 570 | if (t.insts.items(.tag)[@intFromEnum(index)] == .block) { | 660 | if (t.insts.items(.tag)[@intFromEnum(index)] == .block) { |
| ... | @@ -573,32 +663,26 @@ fn transformExpressionToBlock( | ... | @@ -573,32 +663,26 @@ fn transformExpressionToBlock( |
| 573 | } | 663 | } |
| 574 | } | 664 | } |
| 575 | | 665 | |
| 576 | const ret_block = if (block) |b| | 666 | // Otherwise try using the function return block, or create a new one |
| 577 | b | 667 | const maybe_generated_block = if (mode == .function_body) ret.generated_block else null; |
| 578 | else | 668 | const ret_block = maybe_generated_block orelse try t.addUntracked(.block, undefined); |
| 579 | try t.addRootInst(.{ | | |
| 580 | .tag = .block, | | |
| 581 | .data = undefined, | | |
| 582 | }); | | |
| 583 | | 669 | |
| | 670 | // The block must end with a break |
| 584 | if (expr) |result| { | 671 | if (expr) |result| { |
| 585 | _ = try t.addInstRef(.{ | 672 | _ = try t.addIndex(.@"break", .{ |
| 586 | .tag = .@"break", | 673 | .src_node = ctx.nodeOffset(node), |
| 587 | .data = EncodedData.encode(.@"break", .{ | 674 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 588 | .src_node = decl.nodeOffset(node), | 675 | .block = ret_block, |
| 589 | .extra_index = try t.extra.append(t.gpa, Inst.Break, .{ | 676 | .operand = result, |
| 590 | .block = ret_block, | | |
| 591 | .operand = result, | | |
| 592 | }), | | |
| 593 | }), | 677 | }), |
| 594 | }); | 678 | }); |
| 595 | } | 679 | } |
| 596 | | 680 | |
| 597 | const content = try t.scratchToIndexList(scratch_top); | 681 | const content = try t.scratchToIndexList(scratch); |
| 598 | assert(content.len > 0); | 682 | assert(content.len > 0); |
| 599 | | 683 | |
| 600 | t.getInstData(ret_block).* = EncodedData.encode(.block, .{ | 684 | t.getInstData(ret_block).* = EncodedData.encode(.block, .{ |
| 601 | .src_node = decl.nodeOffset(node), | 685 | .src_node = ctx.nodeOffset(node), |
| 602 | .extra_index = try t.extra.append(t.gpa, IndexList, content), | 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,7 +694,13 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc |
| 610 | defer trace.end(); | 694 | defer trace.end(); |
| 611 | | 695 | |
| 612 | const param_decl = t.ast.fullParamDecl(param); | 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 | var id, const id_allocated = try t.decodeIdentifierExtra(param_decl.id); | 705 | var id, const id_allocated = try t.decodeIdentifierExtra(param_decl.id); |
| 616 | if (id_allocated) | 706 | if (id_allocated) |
| ... | @@ -631,9 +721,10 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc | ... | @@ -631,9 +721,10 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc |
| 631 | sym.value_ptr.* = .{ | 721 | sym.value_ptr.* = .{ |
| 632 | .node = param, | 722 | .node = param, |
| 633 | .data = .param, | 723 | .data = .param, |
| | 724 | .type = .{ .present = ty }, |
| 634 | }; | 725 | }; |
| 635 | | 726 | |
| 636 | return value; | 727 | return ty; |
| 637 | } | 728 | } |
| 638 | | 729 | |
| 639 | pub const Builtin = enum { | 730 | pub const Builtin = enum { |
| ... | @@ -664,14 +755,12 @@ pub const Builtin = enum { | ... | @@ -664,14 +755,12 @@ pub const Builtin = enum { |
| 664 | return .void; | 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(.{ | 761 | return try t.addRef(.js, .{ |
| 670 | .tag = .js, | 762 | .src_node = decl.nodeOffset(args[0]), |
| 671 | .data = EncodedData.encode(.js, .{ | 763 | .operand = source, |
| 672 | .src_node = decl.nodeOffset(args[0]), | | |
| 673 | .operand = source, | | |
| 674 | }), | | |
| 675 | }); | 764 | }); |
| 676 | } | 765 | } |
| 677 | | 766 | |
| ... | @@ -684,17 +773,15 @@ pub const Builtin = enum { | ... | @@ -684,17 +773,15 @@ pub const Builtin = enum { |
| 684 | return .void; | 773 | return .void; |
| 685 | } | 774 | } |
| 686 | | 775 | |
| 687 | const base = try t.transformExpression(decl, args[0]) orelse return null; | 776 | var opaque_type: ResultType = .{ .ref = .opaque_type }; |
| 688 | const args_list = try t.transformExpressionList(decl, args[1..]) orelse return null; | 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(.{ | 780 | return try t.addRef(.js_new, .{ |
| 691 | .tag = .js_new, | 781 | .src_node = decl.nodeOffset(args[0]), |
| 692 | .data = EncodedData.encode(.js_new, .{ | 782 | .extra_index = try t.extra.append(t.gpa, Teg.JsNew, .{ |
| 693 | .src_node = decl.nodeOffset(args[0]), | 783 | .base = base, |
| 694 | .extra_index = try t.extra.append(t.gpa, Inst.JsNew, .{ | 784 | .args = args_list, |
| 695 | .base = base, | | |
| 696 | .args = args_list, | | |
| 697 | }), | | |
| 698 | }), | 785 | }), |
| 699 | }); | 786 | }); |
| 700 | } | 787 | } |
| ... | @@ -708,15 +795,13 @@ pub const Builtin = enum { | ... | @@ -708,15 +795,13 @@ pub const Builtin = enum { |
| 708 | return .void; | 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 | return null; | 800 | return null; |
| 713 | | 801 | |
| 714 | return try t.addInstRef(.{ | 802 | return try t.addRef(.compile_error, .{ |
| 715 | .tag = .compile_error, | 803 | .src_node = decl.nodeOffset(node), |
| 716 | .data = EncodedData.encode(.compile_error, .{ | 804 | .operand = source, |
| 717 | .src_node = decl.nodeOffset(node), | | |
| 718 | .operand = source, | | |
| 719 | }), | | |
| 720 | }); | 805 | }); |
| 721 | } | 806 | } |
| 722 | | 807 | |
| ... | @@ -729,22 +814,25 @@ pub const Builtin = enum { | ... | @@ -729,22 +814,25 @@ pub const Builtin = enum { |
| 729 | return .void; | 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 | return null; | 819 | return null; |
| 734 | | 820 | |
| 735 | return try t.addInstRef(.{ | 821 | return try t.addRef(.type_of, .{ |
| 736 | .tag = .type_of, | 822 | .src_node = decl.nodeOffset(node), |
| 737 | .data = EncodedData.encode(.type_of, .{ | 823 | .operand = source, |
| 738 | .src_node = decl.nodeOffset(node), | | |
| 739 | .operand = source, | | |
| 740 | }), | | |
| 741 | }); | 824 | }); |
| 742 | } | 825 | } |
| 743 | }; | 826 | }; |
| 744 | | 827 | |
| 745 | /// This is the primary transformation function: converting an AST expression into instructions. | 828 | /// This is the primary transformation function: converting an AST expression into instructions. |
| 746 | /// `null` represents a `return` or `break`, aka this branch of code is dead. | 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 | const node_tag = t.ast.nodes.items(.tag)[node]; | 836 | const node_tag = t.ast.nodes.items(.tag)[node]; |
| 749 | | 837 | |
| 750 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); | 838 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); |
| ... | @@ -775,12 +863,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -775,12 +863,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 775 | lookup.used = true; | 863 | lookup.used = true; |
| 776 | | 864 | |
| 777 | return switch (lookup.data) { | 865 | return switch (lookup.data) { |
| 778 | .declaration => try t.addInstRef(.{ | 866 | .declaration => try t.addRef(.load_decl, .{ |
| 779 | .tag = .load_decl, | 867 | .src_node = ctx.nodeOffset(node), |
| 780 | .data = EncodedData.encode(.load_decl, .{ | 868 | .string = try t.addNullTerminatedString(id), |
| 781 | .src_node = decl.nodeOffset(node), | | |
| 782 | .string = try t.addNullTerminatedString(id), | | |
| 783 | }), | | |
| 784 | }), | 869 | }), |
| 785 | .mutable => |idx| idx.toRef(), | 870 | .mutable => |idx| idx.toRef(), |
| 786 | .immutable => |ref| ref, | 871 | .immutable => |ref| ref, |
| ... | @@ -800,41 +885,32 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -800,41 +885,32 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 800 | try t.report.warn(.zero_prefixed_literal, .{ .token = t.ast.nodeToken(node) }); | 885 | try t.report.warn(.zero_prefixed_literal, .{ .token = t.ast.nodeToken(node) }); |
| 801 | } | 886 | } |
| 802 | | 887 | |
| 803 | return try t.addInstRef(.{ | 888 | return try t.addRef(.number, .{ |
| 804 | .tag = .number, | 889 | .src_node = ctx.nodeOffset(node), |
| 805 | .data = EncodedData.encode(.number, .{ | 890 | .extra_index = try t.extra.append(t.gpa, Float, Float.encode(value)), |
| 806 | .src_node = decl.nodeOffset(node), | | |
| 807 | .extra_index = try t.extra.append(t.gpa, Float, Float.encode(value)), | | |
| 808 | }), | | |
| 809 | }); | 891 | }); |
| 810 | }, | 892 | }, |
| 811 | | 893 | |
| 812 | .string => { | 894 | .string => { |
| 813 | const str = t.ast.basicNodeSlice(node); | 895 | const str = t.ast.basicNodeSlice(node); |
| 814 | const sliced = try t.addDecodedSlicedString(str[1 .. str.len - 1]); | 896 | const sliced = try t.addDecodedSlicedString(str[1 .. str.len - 1]); |
| 815 | return try t.addInstRef(.{ | 897 | return try t.addRef(.string, .{ |
| 816 | .tag = .string, | 898 | .src_node = ctx.nodeOffset(node), |
| 817 | .data = EncodedData.encode(.string, .{ | 899 | .extra_index = try t.extra.append(t.gpa, SlicedString, sliced), |
| 818 | .src_node = decl.nodeOffset(node), | | |
| 819 | .extra_index = try t.extra.append(t.gpa, SlicedString, sliced), | | |
| 820 | }), | | |
| 821 | }); | 900 | }); |
| 822 | }, | 901 | }, |
| 823 | | 902 | |
| 824 | .assign => { | 903 | .assign => { |
| 825 | const ast_bin = t.ast.nodeData(node).bin; | 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 | return .void; | 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 | return null; | 908 | return null; |
| 830 | const assign_inst = try t.addInstRef(.{ | 909 | const assign_inst = try t.addRef(.store, .{ |
| 831 | .tag = .store, | 910 | .src_node = ctx.nodeOffset(node), |
| 832 | .data = EncodedData.encode(.store, .{ | 911 | .extra_index = try t.extra.append(t.gpa, Teg.Store, .{ |
| 833 | .src_node = decl.nodeOffset(node), | 912 | .memory = lhs, |
| 834 | .extra_index = try t.extra.append(t.gpa, Inst.Store, .{ | 913 | .operand = rhs, |
| 835 | .memory = lhs, | | |
| 836 | .operand = rhs, | | |
| 837 | }), | | |
| 838 | }), | 914 | }), |
| 839 | }); | 915 | }); |
| 840 | return assign_inst; | 916 | return assign_inst; |
| ... | @@ -877,41 +953,39 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -877,41 +953,39 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 877 | // compound compute + assign operation. | 953 | // compound compute + assign operation. |
| 878 | const is_assign = comptime tag.isAssignBin(); | 954 | const is_assign = comptime tag.isAssignBin(); |
| 879 | const teg_tag = @field( | 955 | const teg_tag = @field( |
| 880 | Teg.Inst.Tag, | 956 | Teg.Tag, |
| 881 | if (is_assign) | 957 | if (is_assign) |
| 882 | @tagName(tag)[0 .. @tagName(tag).len - "_assign".len] | 958 | @tagName(tag)[0 .. @tagName(tag).len - "_assign".len] |
| 883 | else | 959 | else |
| 884 | @tagName(tag), | 960 | @tagName(tag), |
| 885 | ); | 961 | ); |
| 886 | const ast_bin = t.ast.nodeData(node).bin; | 962 | const ast_bin = t.ast.nodeData(node).bin; |
| 887 | const lhs = if (is_assign) | 963 | var lhs_result_type: ResultType = .none; |
| 888 | try t.transformAssignmentTarget(decl, ast_bin.lhs) orelse | 964 | const lhs, var assign_result_type: ResultType = if (is_assign) |
| | 965 | try t.transformAssignmentTarget(ctx, ast_bin.lhs) orelse |
| 889 | return .void | 966 | return .void |
| 890 | else | 967 | else |
| 891 | try t.transformExpression(decl, ast_bin.lhs) orelse | 968 | .{ |
| 892 | return null; | 969 | try t.transformExpression(ctx, &lhs_result_type, ast_bin.lhs) orelse |
| 893 | const rhs = try t.transformExpression(decl, ast_bin.rhs) orelse return null; | 970 | return null, |
| 894 | | 971 | .none, |
| 895 | const compute_inst = try t.addInstRef(.{ | 972 | }; |
| 896 | .tag = teg_tag, | 973 | const rhs = try t.transformExpression(ctx, &assign_result_type, ast_bin.rhs) orelse return null; |
| 897 | .data = EncodedData.encode(teg_tag, .{ | 974 | |
| 898 | .src_node = decl.nodeOffset(node), | 975 | const compute_inst = try t.addRef(teg_tag, .{ |
| 899 | .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{ | 976 | .src_node = ctx.nodeOffset(node), |
| 900 | .lhs = if (is_assign) lhs.toRef() else lhs, | 977 | .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ |
| 901 | .rhs = rhs, | 978 | .lhs = if (is_assign) lhs.toRef() else lhs, |
| 902 | }), | 979 | .rhs = rhs, |
| 903 | }), | 980 | }), |
| 904 | }); | 981 | }); |
| 905 | | 982 | |
| 906 | if (is_assign) { | 983 | if (is_assign) { |
| 907 | const assign_inst = try t.addInstRef(.{ | 984 | const assign_inst = try t.addRef(.store, .{ |
| 908 | .tag = .store, | 985 | .src_node = ctx.nodeOffset(node), |
| 909 | .data = EncodedData.encode(.store, .{ | 986 | .extra_index = try t.extra.append(t.gpa, Teg.Store, .{ |
| 910 | .src_node = decl.nodeOffset(node), | 987 | .memory = lhs, |
| 911 | .extra_index = try t.extra.append(t.gpa, Inst.Store, .{ | 988 | .operand = compute_inst, |
| 912 | .memory = lhs, | | |
| 913 | .operand = compute_inst, | | |
| 914 | }), | | |
| 915 | }), | 989 | }), |
| 916 | }); | 990 | }); |
| 917 | return assign_inst; | 991 | return assign_inst; |
| ... | @@ -926,29 +1000,31 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -926,29 +1000,31 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 926 | .array_type, | 1000 | .array_type, |
| 927 | => |tag| { | 1001 | => |tag| { |
| 928 | const ast_un = t.ast.nodeData(node).un; | 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 | return null; | 1014 | return null; |
| 933 | | 1015 | |
| 934 | return try t.addInstRef(.{ | 1016 | return try t.addRef(teg_tag, .{ |
| 935 | .tag = teg_tag, | 1017 | .src_node = ctx.nodeOffset(node), |
| 936 | .data = EncodedData.encode(teg_tag, .{ | 1018 | .operand = operand, |
| 937 | .src_node = decl.nodeOffset(node), | | |
| 938 | .operand = operand, | | |
| 939 | }), | | |
| 940 | }); | 1019 | }); |
| 941 | }, | 1020 | }, |
| 942 | | 1021 | |
| 943 | .@"comptime" => { | 1022 | .@"comptime" => { |
| 944 | const ast_un = t.ast.nodeData(node).un; | 1023 | const ast_un = t.ast.nodeData(node).un; |
| 945 | const operand = try t.transformExpressionToBlock(decl, ast_un.expr, false); | 1024 | const operand = try t.transformToBlock(ctx, ast_un.expr, .tracked_result, result); |
| 946 | return try t.addInstRef(.{ | 1025 | return try t.addRef(.@"comptime", .{ |
| 947 | .tag = .@"comptime", | 1026 | .src_node = ctx.nodeOffset(node), |
| 948 | .data = EncodedData.encode(.@"comptime", .{ | 1027 | .operand = operand, |
| 949 | .src_node = decl.nodeOffset(node), | | |
| 950 | .operand = operand, | | |
| 951 | }), | | |
| 952 | }); | 1028 | }); |
| 953 | }, | 1029 | }, |
| 954 | | 1030 | |
| ... | @@ -957,43 +1033,42 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -957,43 +1033,42 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 957 | const label = t.ast.tokenSlice(t.ast.nodeToken(node)); | 1033 | const label = t.ast.tokenSlice(t.ast.nodeToken(node)); |
| 958 | | 1034 | |
| 959 | var target: BlockContext.BreakTarget = .{ | 1035 | var target: BlockContext.BreakTarget = .{ |
| 960 | .parent = decl.break_target, | 1036 | .parent = ctx.break_target, |
| 961 | .label = label, | 1037 | .label = label, |
| 962 | .is_loop = false, | 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 | // if there are no new instructions, we dont have to create a block context | 1047 | // if there are no new instructions, we dont have to create a block context |
| 971 | if (scratch_top == t.scratch.items.len) | 1048 | if (scratch.len() == 0) |
| 972 | return inner_expr orelse unreachable; // null means we added break or return | 1049 | return inner_expr orelse |
| | 1050 | unreachable; // null means we added break or return |
| 973 | | 1051 | |
| 974 | if (target.block_index) |block| { | 1052 | if (target.block_index) |block| { |
| 975 | if (inner_expr) |result| { | 1053 | if (inner_expr) |out| { |
| 976 | _ = try t.addInstRef(.{ | 1054 | _ = try t.addRef(.@"break", .{ |
| 977 | .tag = .@"break", | 1055 | .src_node = ctx.nodeOffset(node), |
| 978 | .data = EncodedData.encode(.@"break", .{ | 1056 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 979 | .src_node = decl.nodeOffset(node), | 1057 | .block = block, |
| 980 | .extra_index = try t.extra.append(t.gpa, Inst.Break, .{ | 1058 | .operand = out, |
| 981 | .block = block, | | |
| 982 | .operand = result, | | |
| 983 | }), | | |
| 984 | }), | 1059 | }), |
| 985 | }); | 1060 | }); |
| 986 | } | 1061 | } |
| 987 | | 1062 | |
| 988 | const content = try t.scratchToIndexList(scratch_top); | 1063 | const content = try t.scratchToIndexList(scratch); |
| 989 | t.scratch.shrinkRetainingCapacity(scratch_top); | 1064 | scratch.deinit(); |
| 990 | | 1065 | |
| 991 | t.getInstData(block).* = EncodedData.encode(.block, .{ | 1066 | t.getInstData(block).* = EncodedData.encode(.block, .{ |
| 992 | .src_node = decl.nodeOffset(node), | 1067 | .src_node = ctx.nodeOffset(node), |
| 993 | .extra_index = try t.extra.append(t.gpa, IndexList, content), | 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 | return block.toRef(); | 1073 | return block.toRef(); |
| 999 | } else { | 1074 | } else { |
| ... | @@ -1012,9 +1087,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1012,9 +1087,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1012 | for (statements, 1..) |statement, i| { | 1087 | for (statements, 1..) |statement, i| { |
| 1013 | const is_last = i == statements.len; | 1088 | const is_last = i == statements.len; |
| 1014 | last = try t.transformBlockStatement( | 1089 | last = try t.transformBlockStatement( |
| 1015 | decl, | 1090 | ctx, |
| 1016 | statement, | 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 | ) orelse { | 1096 | ) orelse { |
| 1019 | return null; | 1097 | return null; |
| 1020 | }; | 1098 | }; |
| ... | @@ -1023,8 +1101,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1023,8 +1101,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1023 | return if (tag == .block_implicit_return) last else .void; | 1101 | return if (tag == .block_implicit_return) last else .void; |
| 1024 | }, | 1102 | }, |
| 1025 | | 1103 | |
| 1026 | .builtin_call, | 1104 | .builtin_call => { |
| 1027 | => { | | |
| 1028 | const builtin = Builtin.map.get(t.ast.basicNodeSlice(node)[1..]) orelse { | 1105 | const builtin = Builtin.map.get(t.ast.basicNodeSlice(node)[1..]) orelse { |
| 1029 | try t.report.err(.invalid_builtin, .{ .node = node }); | 1106 | try t.report.err(.invalid_builtin, .{ .node = node }); |
| 1030 | return .void; | 1107 | return .void; |
| ... | @@ -1034,12 +1111,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1034,12 +1111,12 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1034 | const args = base.slice(&t.ast); | 1111 | const args = base.slice(&t.ast); |
| 1035 | | 1112 | |
| 1036 | return switch (builtin) { | 1113 | return switch (builtin) { |
| 1037 | .js => Builtin.builtin_js(t, decl, node, args), | 1114 | .js => Builtin.builtin_js(t, ctx, node, args), |
| 1038 | .js_new => Builtin.builtin_jsNew(t, decl, node, args), | 1115 | .js_new => Builtin.builtin_jsNew(t, ctx, node, args), |
| 1039 | // .import => Builtin.import(t, decl, node, args), | 1116 | // .import => Builtin.import(t, decl, node, args), |
| 1040 | // .fetch => Builtin.fetch(t, decl, node, args), | 1117 | // .fetch => Builtin.fetch(t, decl, node, args), |
| 1041 | .compile_error => Builtin.builtin_compileError(t, decl, node, args), | 1118 | .compile_error => Builtin.builtin_compileError(t, ctx, node, args), |
| 1042 | .type_of => Builtin.builtin_typeOf(t, decl, node, args), | 1119 | .type_of => Builtin.builtin_typeOf(t, ctx, node, args), |
| 1043 | else => @panic("TODO"), | 1120 | else => @panic("TODO"), |
| 1044 | }; | 1121 | }; |
| 1045 | }, | 1122 | }, |
| ... | @@ -1048,17 +1125,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1048,17 +1125,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1048 | const call = t.ast.nodeData(node).call; | 1125 | const call = t.ast.nodeData(node).call; |
| 1049 | const args = t.ast.extraData(call.extra, Ast.Call).args.slice(&t.ast); | 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; | 1128 | var base_result: ResultType = .none; |
| 1052 | const args_list = try t.transformExpressionList(decl, args) orelse return null; | 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(.{ | 1146 | break :call_args .{ |
| 1055 | .tag = .call, | 1147 | .start = @intCast(extra_top), |
| 1056 | .data = EncodedData.encode(.call, .{ | 1148 | .len = @intCast(args.len), |
| 1057 | .src_node = decl.nodeOffset(node), | 1149 | }; |
| 1058 | .extra_index = try t.extra.append(t.gpa, Inst.Call, .{ | 1150 | }; |
| 1059 | .base = base, | 1151 | |
| 1060 | .args = args_list, | 1152 | const call_inst = try t.addRef(.call, .{ |
| 1061 | }), | 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,28 +1163,23 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1068 | .field => { | 1163 | .field => { |
| 1069 | const field = t.ast.nodeData(node).field; | 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 | // TODO: handle identifier escapes | 1169 | // TODO: handle identifier escapes |
| 1074 | const id = t.ast.tokenSlice(field.field_token); | 1170 | const id = t.ast.tokenSlice(field.field_token); |
| 1075 | const prop_name_str = try t.addSlicedString(id); | 1171 | const prop_name_str = try t.addSlicedString(id); |
| 1076 | | 1172 | |
| 1077 | const string_ref = try t.addInstRef(.{ | 1173 | const string_ref = try t.addRef(.string, .{ |
| 1078 | .tag = .string, | 1174 | .src_node = ctx.nodeOffset(node), |
| 1079 | .data = EncodedData.encode(.string, .{ | 1175 | .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str), |
| 1080 | .src_node = decl.nodeOffset(node), | | |
| 1081 | .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str), | | |
| 1082 | }), | | |
| 1083 | }); | 1176 | }); |
| 1084 | | 1177 | |
| 1085 | return try t.addInstRef(.{ | 1178 | return try t.addRef(.field, .{ |
| 1086 | .tag = .field, | 1179 | .src_node = ctx.nodeOffset(node), |
| 1087 | .data = EncodedData.encode(.field, .{ | 1180 | .extra_index = try t.extra.append(t.gpa, Teg.Bin, .{ |
| 1088 | .src_node = decl.nodeOffset(node), | 1181 | .lhs = base, |
| 1089 | .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{ | 1182 | .rhs = string_ref, |
| 1090 | .lhs = base, | | |
| 1091 | .rhs = string_ref, | | |
| 1092 | }), | | |
| 1093 | }), | 1183 | }), |
| 1094 | }); | 1184 | }); |
| 1095 | }, | 1185 | }, |
| ... | @@ -1098,45 +1188,36 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1098,45 +1188,36 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1098 | const if_node = t.ast.nodeData(node).@"if"; | 1188 | const if_node = t.ast.nodeData(node).@"if"; |
| 1099 | const if_data = t.ast.extraData(if_node.extra, Ast.If); | 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 | return null; | 1193 | return null; |
| 1103 | | 1194 | |
| 1104 | const yes = try t.transformExpressionToBlock( | 1195 | var yes_result = result.*; |
| 1105 | decl, | 1196 | const yes = try t.transformToBlock(ctx, if_data.yes, .tracked_result, &yes_result); |
| 1106 | if_data.yes, | 1197 | |
| 1107 | false, | 1198 | // TODO: can this re-use the resolved ref from above |
| 1108 | ); | 1199 | var no_result: ResultType = if (if_data.no == .none) .used else result.*; |
| 1109 | const no = if (if_data.no.unwrap()) |no| | 1200 | const no = if (if_data.no.unwrap()) |no| |
| 1110 | try t.transformExpressionToBlock( | 1201 | try t.transformToBlock( |
| 1111 | decl, | 1202 | ctx, |
| 1112 | no, | 1203 | no, |
| 1113 | false, | 1204 | .tracked_result, |
| | 1205 | &no_result, |
| 1114 | ) | 1206 | ) |
| 1115 | else | 1207 | else |
| 1116 | Inst.Ref.void; | 1208 | Inst.Ref.void; |
| 1117 | | 1209 | |
| 1118 | if (yes == .void and no == .void) { | 1210 | // Mark the incoming result type as used if both branches use the result type |
| 1119 | // when both branches do nothing, we can omit the branch, | 1211 | if (result.* != .none and yes_result == .none and no_result == .none) { |
| 1120 | // but we still need to type-check the condition operand | 1212 | result.* = .used; |
| 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; | | |
| 1129 | } | 1213 | } |
| 1130 | | 1214 | |
| 1131 | return try t.addInstRef(.{ | 1215 | return try t.addRef(.@"if", .{ |
| 1132 | .tag = .@"if", | 1216 | .src_node = ctx.nodeOffset(node), |
| 1133 | .data = EncodedData.encode(.@"if", .{ | 1217 | .extra_index = try t.extra.append(t.gpa, Teg.If, .{ |
| 1134 | .src_node = decl.nodeOffset(node), | 1218 | .cond = cond, |
| 1135 | .extra_index = try t.extra.append(t.gpa, Inst.If, .{ | 1219 | .yes = yes, |
| 1136 | .cond = cond, | 1220 | .no = no, |
| 1137 | .yes = yes, | | |
| 1138 | .no = no, | | |
| 1139 | }), | | |
| 1140 | }), | 1221 | }), |
| 1141 | }); | 1222 | }); |
| 1142 | }, | 1223 | }, |
| ... | @@ -1144,36 +1225,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1144,36 +1225,35 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1144 | .@"return" => { | 1225 | .@"return" => { |
| 1145 | const ret_node = t.ast.nodeData(node).@"return"; | 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 | try t.report.err(.invalid_return, .{ .node = node }); | 1229 | try t.report.err(.invalid_return, .{ .node = node }); |
| 1149 | return .void; | 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 | block | 1234 | block |
| 1154 | else append_block: { | 1235 | else append_block: { |
| 1155 | const block = try t.addRootInst(.{ | 1236 | const block = try t.addUntracked(.block, undefined); |
| 1156 | .tag = .block, | 1237 | target.generated_block = block; |
| 1157 | .data = undefined, | | |
| 1158 | }); | | |
| 1159 | ret_block_ptr.* = block; | | |
| 1160 | break :append_block block; | 1238 | break :append_block block; |
| 1161 | }; | 1239 | }; |
| 1162 | | 1240 | |
| | 1241 | var return_result = target.result_type; |
| | 1242 | |
| 1163 | const operand: Inst.Ref = if (ret_node.expr.unwrap()) |expr| | 1243 | const operand: Inst.Ref = if (ret_node.expr.unwrap()) |expr| |
| 1164 | try t.transformExpression(decl.withReturn(null), expr) orelse | 1244 | try t.transformExpression( |
| 1165 | return null | 1245 | ctx.withReturn(null), |
| | 1246 | &return_result, |
| | 1247 | expr, |
| | 1248 | ) orelse return null |
| 1166 | else | 1249 | else |
| 1167 | .void; | 1250 | .void; |
| 1168 | | 1251 | |
| 1169 | _ = try t.addInstRef(.{ | 1252 | _ = try t.addIndex(.@"break", .{ |
| 1170 | .tag = .@"break", | 1253 | .src_node = ctx.nodeOffset(node), |
| 1171 | .data = EncodedData.encode(.@"break", .{ | 1254 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 1172 | .src_node = decl.nodeOffset(node), | 1255 | .block = ret_block, |
| 1173 | .extra_index = try t.extra.append(t.gpa, Inst.Break, .{ | 1256 | .operand = operand, |
| 1174 | .block = ret_block, | | |
| 1175 | .operand = operand, | | |
| 1176 | }), | | |
| 1177 | }), | 1257 | }), |
| 1178 | }); | 1258 | }); |
| 1179 | | 1259 | |
| ... | @@ -1187,36 +1267,29 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1187,36 +1267,29 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1187 | @panic("TODO: unlabelled breaks"), | 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 | try t.report.err(.unresolved_label, .{ .node = node }); | 1271 | try t.report.err(.unresolved_label, .{ .node = node }); |
| 1192 | return null; | 1272 | return null; |
| 1193 | }; | 1273 | }; |
| 1194 | | 1274 | |
| | 1275 | var break_result = block.result_type.*; |
| 1195 | const operand: Inst.Ref = if (brk_node.value.unwrap()) |expr| | 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 | return null | 1278 | return null |
| 1198 | else | 1279 | else |
| 1199 | .void; | 1280 | .void; |
| 1200 | | 1281 | |
| 1201 | _ = try t.addInstRef(.{ | 1282 | _ = try t.addIndex(.@"break", .{ |
| 1202 | .tag = .@"break", | 1283 | .src_node = ctx.nodeOffset(node), |
| 1203 | .data = EncodedData.encode(.@"break", .{ | 1284 | .extra_index = try t.extra.append(t.gpa, Teg.Break, .{ |
| 1204 | .src_node = decl.nodeOffset(node), | 1285 | .block = try block.getInst(t), |
| 1205 | .extra_index = try t.extra.append(t.gpa, Inst.Break, .{ | 1286 | .operand = operand, |
| 1206 | .block = block, | | |
| 1207 | .operand = operand, | | |
| 1208 | }), | | |
| 1209 | }), | 1287 | }), |
| 1210 | }); | 1288 | }); |
| 1211 | | 1289 | |
| 1212 | return null; // control flow ends | 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 | .struct_decl => { | 1293 | .struct_decl => { |
| 1221 | const struct_node = t.ast.fullStructDecl(node); | 1294 | const struct_node = t.ast.fullStructDecl(node); |
| 1222 | const payload_index = try t.extra.alloc(t.gpa, struct_node.fields.len * 2); | 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,32 +1302,39 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1229 | const field = t.ast.fullStructField(field_node); | 1302 | const field = t.ast.fullStructField(field_node); |
| 1230 | | 1303 | |
| 1231 | const id = try t.decodeIdentifier(field.id); | 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))); | 1311 | t.extra.setU32(i, @intFromEnum(key)); |
| 1234 | t.extra.setU32(j, @intFromEnum(try t.transformExpressionToBlock(decl, field.type, false))); | 1312 | t.extra.setU32(j, @intFromEnum(value)); |
| 1235 | } | 1313 | } |
| 1236 | | 1314 | |
| 1237 | return try t.addInstRef(.{ | 1315 | return try t.addRef(.struct_decl, .{ |
| 1238 | .tag = .struct_decl, | 1316 | .src_node = ctx.nodeOffset(node), |
| 1239 | .data = EncodedData.encode(.struct_decl, .{ | 1317 | .extra_index = try t.extra.append(t.gpa, Teg.StructDecl, .{ |
| 1240 | .src_node = decl.nodeOffset(node), | 1318 | .payload_index = payload_index, |
| 1241 | .extra_index = try t.extra.append(t.gpa, Inst.StructDecl, .{ | 1319 | .field_len = @intCast(struct_node.fields.len), |
| 1242 | .payload_index = payload_index, | | |
| 1243 | .field_len = @intCast(struct_node.fields.len), | | |
| 1244 | }), | | |
| 1245 | }), | 1320 | }), |
| 1246 | }); | 1321 | }); |
| 1247 | }, | 1322 | }, |
| 1248 | | 1323 | |
| 1249 | .struct_init => { | 1324 | .struct_init => { |
| 1250 | const struct_node = t.ast.fullStructInit(node); | 1325 | const struct_node = t.ast.fullStructInit(node); |
| | 1326 | // TODO: use t.scratch for this |
| 1251 | const payload_index = try t.extra.alloc(t.gpa, struct_node.fields.len * 2); | 1327 | const payload_index = try t.extra.alloc(t.gpa, struct_node.fields.len * 2); |
| 1252 | | 1328 | |
| 1253 | const base_type = try t.transformExpression( | 1329 | var result_type: ResultType = .{ .ref = .type_type }; |
| 1254 | decl, | 1330 | const base_type = if (struct_node.type) |ty| |
| 1255 | struct_node.type orelse @panic("TODO: TEG implicit struct"), | 1331 | try t.transformExpression(ctx, &result_type, ty) orelse |
| 1256 | ) orelse | 1332 | return null |
| 1257 | @panic("TODO: print better error"); | 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 | for ( | 1339 | for ( |
| 1260 | struct_node.fields, | 1340 | struct_node.fields, |
| ... | @@ -1264,21 +1344,30 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1264,21 +1344,30 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1264 | const field = t.ast.fullStructField(field_node); | 1344 | const field = t.ast.fullStructField(field_node); |
| 1265 | | 1345 | |
| 1266 | const id = try t.decodeIdentifier(field.id); | 1346 | const id = try t.decodeIdentifier(field.id); |
| 1267 | | 1347 | const field_name = try t.addNullTerminatedString(id); |
| 1268 | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id))); | 1348 | |
| 1269 | t.extra.setU32(j, @intFromEnum((try t.transformExpression(decl, field.type)) orelse | 1349 | var field_result_type: ResultType = .{ .struct_field = .{ |
| 1270 | @panic("TODO: print better error"))); | 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(.{ | 1365 | return try t.addRef(.struct_init, .{ |
| 1274 | .tag = .struct_init, | 1366 | .src_node = ctx.nodeOffset(node), |
| 1275 | .data = EncodedData.encode(.struct_init, .{ | 1367 | .extra_index = try t.extra.append(t.gpa, Teg.StructInit, .{ |
| 1276 | .src_node = decl.nodeOffset(node), | 1368 | .base_type = base_type, |
| 1277 | .extra_index = try t.extra.append(t.gpa, Inst.StructInit, .{ | 1369 | .payload_index = payload_index, |
| 1278 | .base_type = base_type, | 1370 | .field_len = @intCast(struct_node.fields.len), |
| 1279 | .payload_index = payload_index, | | |
| 1280 | .field_len = @intCast(struct_node.fields.len), | | |
| 1281 | }), | | |
| 1282 | }), | 1371 | }), |
| 1283 | }); | 1372 | }); |
| 1284 | }, | 1373 | }, |
| ... | @@ -1286,17 +1375,19 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1286,17 +1375,19 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1286 | .array_init => { | 1375 | .array_init => { |
| 1287 | const array_init = t.ast.nodeData(node).list; | 1376 | const array_init = t.ast.nodeData(node).list; |
| 1288 | | 1377 | |
| 1289 | const items = try t.transformExpressionList(decl, array_init.slice(&t.ast)) orelse | 1378 | const array_type = try result.consume(t); |
| 1290 | return null; | | |
| 1291 | | 1379 | |
| 1292 | return try t.addInstRef(.{ | 1380 | const items = try t.transformExpressionList( |
| 1293 | .tag = .array_init, | 1381 | ctx, |
| 1294 | .data = EncodedData.encode(.array_init, .{ | 1382 | if (array_type) |ty| .{ .array_value = ty } else .none, |
| 1295 | .src_node = decl.nodeOffset(node), | 1383 | array_init.slice(&t.ast), |
| 1296 | .extra_index = try t.extra.append(t.gpa, Inst.ArrayInit, .{ | 1384 | ) orelse return null; |
| 1297 | .type_hint = .none, | 1385 | |
| 1298 | .list = items, | 1386 | return try t.addRef(.array_init, .{ |
| 1299 | }), | 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,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 | const extra_top = try t.extra.alloc(t.gpa, args.len); | 1400 | const extra_top = try t.extra.alloc(t.gpa, args.len); |
| 1310 | | 1401 | |
| 1311 | for (args, extra_top..) |arg, i| { | 1402 | for (args, extra_top..) |arg, i| { |
| 1312 | t.extra.setU32(i, @intFromEnum(try t.transformExpression(decl, arg) orelse | 1403 | var result_mut = result; |
| 1313 | return null)); | 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 | return .{ | 1411 | return .{ |
| ... | @@ -1319,7 +1414,25 @@ fn transformExpressionList(t: *Transform, decl: BlockContext, args: []const Node | ... | @@ -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 | const node_tag = t.ast.nodes.items(.tag)[node]; | 1436 | const node_tag = t.ast.nodes.items(.tag)[node]; |
| 1324 | | 1437 | |
| 1325 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); | 1438 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); |
| ... | @@ -1345,7 +1458,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) | ... | @@ -1345,7 +1458,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1345 | return null; | 1458 | return null; |
| 1346 | } | 1459 | } |
| 1347 | | 1460 | |
| 1348 | const lookup = t.lookupId(id) orelse { | 1461 | const lookup: *Scope.Symbol = t.lookupId(id) orelse { |
| 1349 | try t.report.err(.unknown_identifier, .{ .node = node }); | 1462 | try t.report.err(.unknown_identifier, .{ .node = node }); |
| 1350 | return null; | 1463 | return null; |
| 1351 | }; | 1464 | }; |
| ... | @@ -1353,7 +1466,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) | ... | @@ -1353,7 +1466,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1353 | return switch (lookup.data) { | 1466 | return switch (lookup.data) { |
| 1354 | .mutable => |inst| { | 1467 | .mutable => |inst| { |
| 1355 | lookup.mutated = true; | 1468 | lookup.mutated = true; |
| 1356 | return inst; | 1469 | return .{ inst, lookup.type.resolve() }; |
| 1357 | }, | 1470 | }, |
| 1358 | else => { | 1471 | else => { |
| 1359 | lookup.used = true; | 1472 | lookup.used = true; |
| ... | @@ -1371,6 +1484,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) | ... | @@ -1371,6 +1484,7 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1371 | }, | 1484 | }, |
| 1372 | }, | 1485 | }, |
| 1373 | }); | 1486 | }); |
| | 1487 | |
| 1374 | return null; | 1488 | return null; |
| 1375 | }, | 1489 | }, |
| 1376 | }; | 1490 | }; |
| ... | @@ -1379,10 +1493,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) | ... | @@ -1379,10 +1493,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1379 | // TODO: allow field expressions, but these need to emit a different store node | 1493 | // TODO: allow field expressions, but these need to emit a different store node |
| 1380 | | 1494 | |
| 1381 | else => { | 1495 | else => { |
| 1382 | _ = try t.transformExpression(decl, node); | 1496 | var result_type: ResultType = .none; |
| | 1497 | _ = try t.transformExpression(decl, &result_type, node); |
| | 1498 | |
| 1383 | try t.report.err(.invalid_assignment_target, .{ | 1499 | try t.report.err(.invalid_assignment_target, .{ |
| 1384 | .node = node, | 1500 | .node = node, |
| 1385 | }); | 1501 | }); |
| | 1502 | |
| 1386 | return null; | 1503 | return null; |
| 1387 | }, | 1504 | }, |
| 1388 | } | 1505 | } |
| ... | @@ -1390,10 +1507,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) | ... | @@ -1390,10 +1507,13 @@ fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 1390 | | 1507 | |
| 1391 | fn transformBlockStatement( | 1508 | fn transformBlockStatement( |
| 1392 | t: *Transform, | 1509 | t: *Transform, |
| 1393 | decl: BlockContext, | 1510 | ctx: BlockContext, |
| 1394 | node: NodeIndex, | 1511 | node: NodeIndex, |
| 1395 | is_implicit_return_expression: bool, | 1512 | maybe_result: ?*ResultType, |
| 1396 | ) !?Inst.Ref { | 1513 | ) !?Inst.Ref { |
| | 1514 | var void_result_storage: ResultType = .{ .ref = .void }; |
| | 1515 | const result = maybe_result orelse &void_result_storage; |
| | 1516 | |
| 1397 | const node_tag = t.ast.nodes.items(.tag)[node]; | 1517 | const node_tag = t.ast.nodes.items(.tag)[node]; |
| 1398 | | 1518 | |
| 1399 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); | 1519 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); |
| ... | @@ -1437,16 +1557,16 @@ fn transformBlockStatement( | ... | @@ -1437,16 +1557,16 @@ fn transformBlockStatement( |
| 1437 | | 1557 | |
| 1438 | return .void; | 1558 | return .void; |
| 1439 | }, | 1559 | }, |
| 1440 | else => try t.transformExpression(decl, var_decl.init) orelse | 1560 | else => ref: { |
| 1441 | return null, | 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(.{ | 1567 | return try t.addRef(.discard, .{ |
| 1445 | .tag = .discard, | 1568 | .src_node = ctx.nodeOffset(node), |
| 1446 | .data = EncodedData.encode(.discard, .{ | 1569 | .operand = ref, |
| 1447 | .src_node = decl.nodeOffset(node), | | |
| 1448 | .operand = ref, | | |
| 1449 | }), | | |
| 1450 | }); | 1570 | }); |
| 1451 | } | 1571 | } |
| 1452 | | 1572 | |
| ... | @@ -1464,36 +1584,37 @@ fn transformBlockStatement( | ... | @@ -1464,36 +1584,37 @@ fn transformBlockStatement( |
| 1464 | return .void; | 1584 | return .void; |
| 1465 | } | 1585 | } |
| 1466 | | 1586 | |
| | 1587 | var type_result: ResultType = .{ .ref = .type_type }; |
| 1467 | const type_ref = if (var_decl.type) |data_type| | 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 | else | 1590 | else |
| 1470 | null; | 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) | 1593 | var value_result: ResultType = if (type_ref) |ty| |
| 1488 | data_coerced_ref | 1594 | .{ .ref = ty } |
| 1489 | else | 1595 | else |
| 1490 | try t.addInstRef(.{ | 1596 | .none; |
| 1491 | .tag = .memory, | 1597 | |
| 1492 | .data = EncodedData.encode(.memory, .{ | 1598 | const data_ref = try t.transformExpression(ctx, &value_result, var_decl.init) orelse |
| 1493 | .src_node = decl.nodeOffset(node), | 1599 | return null; |
| 1494 | .operand = data_coerced_ref, | 1600 | |
| 1495 | }), | 1601 | // Add a .coerce instruction if there was a type declared. |
| 1496 | }); | 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 | const gop = try t.scope().symbols.getOrPut(t.gpa, id); | 1619 | const gop = try t.scope().symbols.getOrPut(t.gpa, id); |
| 1499 | | 1620 | |
| ... | @@ -1505,16 +1626,26 @@ fn transformBlockStatement( | ... | @@ -1505,16 +1626,26 @@ fn transformBlockStatement( |
| 1505 | | 1626 | |
| 1506 | gop.value_ptr.* = .{ | 1627 | gop.value_ptr.* = .{ |
| 1507 | .node = node, | 1628 | .node = node, |
| 1508 | .data = switch (var_decl.is_const) { | 1629 | .data = if (var_decl.is_const) |
| 1509 | true => .{ .immutable = mem_ref }, | 1630 | .{ .immutable = data_coerced_ref } |
| 1510 | false => .{ .mutable = mem_ref.index().? }, | 1631 | else |
| 1511 | }, | 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 | return .void; | 1644 | return .void; |
| 1515 | }, | 1645 | }, |
| 1516 | | 1646 | |
| 1517 | .fn_decl, | 1647 | .fn_decl, |
| | 1648 | .struct_decl, |
| 1518 | => |tag| { | 1649 | => |tag| { |
| 1519 | std.debug.panic("TODO: block statement {s}", .{@tagName(tag)}); | 1650 | std.debug.panic("TODO: block statement {s}", .{@tagName(tag)}); |
| 1520 | }, | 1651 | }, |
| ... | @@ -1530,18 +1661,17 @@ fn transformBlockStatement( | ... | @@ -1530,18 +1661,17 @@ fn transformBlockStatement( |
| 1530 | // discard, instead of appending an .ensure_void check, | 1661 | // discard, instead of appending an .ensure_void check, |
| 1531 | // we will add a .discard instruction instead. | 1662 | // we will add a .discard instruction instead. |
| 1532 | // this will emit a warning if given void. | 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 | return null; | 1666 | return null; |
| 1535 | return try t.addInstRef(.{ | 1667 | |
| 1536 | .tag = .discard, | 1668 | return try t.addRef(.discard, .{ |
| 1537 | .data = EncodedData.encode(.discard, .{ | 1669 | .src_node = ctx.nodeOffset(node), |
| 1538 | .src_node = decl.nodeOffset(node), | 1670 | .operand = value, |
| 1539 | .operand = result, | | |
| 1540 | }), | | |
| 1541 | }); | 1671 | }); |
| 1542 | } else { | 1672 | } else { |
| 1543 | // process this assignment as usual, do not append .ensure_void | 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,50 +1690,51 @@ fn transformBlockStatement( |
| 1560 | .shr_assign, | 1690 | .shr_assign, |
| 1561 | => { | 1691 | => { |
| 1562 | // do not append a .ensure_void check | 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 | else => { | 1696 | else => { |
| 1567 | const result = try t.transformExpression(decl, node) orelse | 1697 | const value = try t.transformExpression(ctx, result, node) orelse |
| 1568 | return null; | 1698 | return null; |
| 1569 | | 1699 | |
| 1570 | if (!is_implicit_return_expression and result != .void) { | 1700 | if (maybe_result == null and value != .void) { |
| 1571 | if (t.ast.nodeTag(node) == .id) { | 1701 | if (t.ast.nodeTag(node) == .id) { |
| 1572 | try t.report.warn(.dangling_identifier, .{ | 1702 | try t.report.warn(.dangling_identifier, .{ |
| 1573 | .node = node, | 1703 | .node = node, |
| 1574 | }); | 1704 | }); |
| 1575 | return result; | | |
| 1576 | } | 1705 | } |
| 1577 | | 1706 | |
| 1578 | return try t.addInstRef(.{ | 1707 | return try t.addRef(.ensure_void, .{ |
| 1579 | .tag = .ensure_void, | 1708 | .src_node = ctx.nodeOffset(node), |
| 1580 | .data = EncodedData.encode(.ensure_void, .{ | 1709 | .operand = value, |
| 1581 | .src_node = decl.nodeOffset(node), | | |
| 1582 | .operand = result, | | |
| 1583 | }), | | |
| 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 { | 1718 | fn addUntracked(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Index { |
| 1593 | try t.insts.append(t.gpa, inst); | 1719 | try t.insts.append(t.gpa, .{ |
| | 1720 | .tag = tag, |
| | 1721 | .data = EncodedData.encode(tag, data), |
| | 1722 | }); |
| 1594 | return @enumFromInt(t.insts.len - 1); | 1723 | return @enumFromInt(t.insts.len - 1); |
| 1595 | } | 1724 | } |
| 1596 | | 1725 | |
| 1597 | fn addInstRef(t: *Transform, inst: Inst) !Inst.Ref { | 1726 | fn addIndex(t: *Transform, comptime tag: Teg.Tag, data: tag.Data()) !Inst.Index { |
| 1598 | try t.scratch.ensureUnusedCapacity(t.gpa, 1); | 1727 | const index = try t.addUntracked(tag, data); |
| 1599 | try t.insts.append(t.gpa, inst); | 1728 | try t.scratch.add(t.arena, Inst.Index, index); |
| 1600 | const index: Inst.Index = @enumFromInt(t.insts.len - 1); | 1729 | return index; |
| 1601 | t.scratch.appendAssumeCapacity(@intFromEnum(index)); | 1730 | } |
| 1602 | return index.toRef(); | 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 | fn pushScope(t: *Transform) !void { | 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 | fn popScope(t: *Transform) void { | 1740 | fn popScope(t: *Transform) void { |
| ... | @@ -1620,22 +1751,18 @@ fn getInstData(t: *Transform, index: Inst.Index) *EncodedData { | ... | @@ -1620,22 +1751,18 @@ fn getInstData(t: *Transform, index: Inst.Index) *EncodedData { |
| 1620 | return &t.insts.items(.data)[@intFromEnum(index)]; | 1751 | return &t.insts.items(.data)[@intFromEnum(index)]; |
| 1621 | } | 1752 | } |
| 1622 | | 1753 | |
| 1623 | fn addScratch(t: *Transform, comptime T: type, value: T) !void { | 1754 | fn scratchToRefList(t: *Transform, scratch_top: core.U32Stack.Mark) !RefList { |
| 1624 | try t.scratch.append(t.gpa, @intFromEnum(value)); | 1755 | const items = scratch_top.slice(u32); |
| 1625 | } | | |
| 1626 | | | |
| 1627 | fn scratchToRefList(t: *Transform, scratch_top: usize) !RefList { | | |
| 1628 | const items = t.scratch.items[scratch_top..]; | | |
| 1629 | const extra_top = t.extra.list.items.len; | 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 | return .{ | 1758 | return .{ |
| 1632 | .start = @intCast(extra_top), | 1759 | .start = @intCast(extra_top), |
| 1633 | .len = @intCast(items.len), | 1760 | .len = @intCast(items.len), |
| 1634 | }; | 1761 | }; |
| 1635 | } | 1762 | } |
| 1636 | | 1763 | |
| 1637 | fn scratchToIndexList(t: *Transform, scratch_top: usize) !IndexList { | 1764 | fn scratchToIndexList(t: *Transform, scratch: core.U32Stack.Mark) !IndexList { |
| 1638 | const items = t.scratch.items[scratch_top..]; | 1765 | const items = scratch.slice(u32); |
| 1639 | const extra_top = t.extra.list.items.len; | 1766 | const extra_top = t.extra.list.items.len; |
| 1640 | try t.extra.list.appendSlice(t.gpa, items); | 1767 | try t.extra.list.appendSlice(t.gpa, items); |
| 1641 | return .{ | 1768 | return .{ |
| ... | @@ -1715,8 +1842,8 @@ fn addDecodedSlicedString(t: *Transform, escaped: []const u8) !SlicedString { | ... | @@ -1715,8 +1842,8 @@ fn addDecodedSlicedString(t: *Transform, escaped: []const u8) !SlicedString { |
| 1715 | // string. this is done so that duplicate strings will get de-duplicated | 1842 | // string. this is done so that duplicate strings will get de-duplicated |
| 1716 | // in string_bytes. It is more likely that there will be remaining bytes | 1843 | // in string_bytes. It is more likely that there will be remaining bytes |
| 1717 | // in `.scratch` vs `.id_decode_buf` | 1844 | // in `.scratch` vs `.id_decode_buf` |
| 1718 | try t.scratch.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1); | 1845 | try t.scratch.list.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1); |
| 1719 | const buf = mem.sliceAsBytes(t.scratch.unusedCapacitySlice()); | 1846 | const buf = mem.sliceAsBytes(t.scratch.list.unusedCapacitySlice()); |
| 1720 | | 1847 | |
| 1721 | break :brk Tokenizer.decodeStringBuf(escaped, buf); | 1848 | break :brk Tokenizer.decodeStringBuf(escaped, buf); |
| 1722 | } else escaped; | 1849 | } else escaped; |
| ... | @@ -1767,7 +1894,8 @@ const mem = std.mem; | ... | @@ -1767,7 +1894,8 @@ const mem = std.mem; |
| 1767 | const Allocator = mem.Allocator; | 1894 | const Allocator = mem.Allocator; |
| 1768 | const assert = std.debug.assert; | 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 | const Inst = Teg.Inst; | 1899 | const Inst = Teg.Inst; |
| 1772 | const NullTerminatedString = Teg.NullTerminatedString; | 1900 | const NullTerminatedString = Teg.NullTerminatedString; |
| 1773 | const SlicedString = Teg.SlicedString; | 1901 | const SlicedString = Teg.SlicedString; |
| ... | @@ -1776,15 +1904,15 @@ const IndexList = Teg.IndexList; | ... | @@ -1776,15 +1904,15 @@ const IndexList = Teg.IndexList; |
| 1776 | const EncodedData = Teg.EncodedData; | 1904 | const EncodedData = Teg.EncodedData; |
| 1777 | const ExtraIndex = Teg.ExtraIndex; | 1905 | const ExtraIndex = Teg.ExtraIndex; |
| 1778 | | 1906 | |
| 1779 | const dod = @import("array.zig"); | 1907 | const extra = core.array; |
| 1780 | const ExtraStore = dod.ExtraStore(.teg, .@"var"); | 1908 | const ExtraStore = extra.ExtraStore(.teg, .@"var"); |
| 1781 | const Float = dod.Float; | 1909 | const Float = extra.Float; |
| 1782 | | 1910 | |
| 1783 | const Tokenizer = @import("Tokenizer.zig"); | 1911 | const Tokenizer = core.Tokenizer; |
| 1784 | const Token = Tokenizer.Token; | 1912 | const Token = Tokenizer.Token; |
| 1785 | const ByteOffset = Tokenizer.ByteOffset; | 1913 | const ByteOffset = Tokenizer.ByteOffset; |
| 1786 | | 1914 | |
| 1787 | const Ast = @import("Ast.zig"); | 1915 | const Ast = core.Ast; |
| 1788 | const Unused = Ast.Unused; | 1916 | const Unused = Ast.Unused; |
| 1789 | const Node = Ast.Node; | 1917 | const Node = Ast.Node; |
| 1790 | const Data = Node.Data; | 1918 | const Data = Node.Data; |
| ... | @@ -1798,9 +1926,9 @@ const optionalTokenIndex = OptionalTokenIndex.init; | ... | @@ -1798,9 +1926,9 @@ const optionalTokenIndex = OptionalTokenIndex.init; |
| 1798 | const optionalNodeIndex = OptionalNodeIndex.init; | 1926 | const optionalNodeIndex = OptionalNodeIndex.init; |
| 1799 | const optionalExtraIndex = OptionalExtraIndex.init; | 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"); | 1931 | const Report = core.Report; |
| 1804 | const Comp = @import("Comp.zig"); | 1932 | const Comp = core.Comp; |
| 1805 | | 1933 | |
| 1806 | const tracer = @import("tracer"); | 1934 | const tracer = @import("tracer"); |