| author | |
| committer | |
| log | b1f58dc720475cbc004da8de483b2aeae4fdb1fc |
| tree | 8499f019e6984acda8b117f2074b2a0697316154 |
| parent | 5b5a3038aa08c430f60533153b0823267b3dbdff |
To make it easier to create unbounded temporary allocations, such as in
the case of creating a []Value for an array value, a new abstraction
for `s.scratch` is added: `core.Stack` and `core.U32Stack`. This
provides a safer interface for these operations.21 files changed, 946 insertions(+), 333 deletions(-)
CHANGELOG.md+2| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | ## 0.1.4 | 1 | ## 0.1.4 |
| 2 | 2 | ||
| 3 | - feat: implement basic struct types (#42) | 3 | - feat: implement basic struct types (#42) |
| 4 | - feat: implement basic array types (#67) | ||
| 4 | - feat: implement string identifiers (#54) | 5 | - feat: implement string identifiers (#54) |
| 5 | - feat: include a `README.html` alongside compiler with documentation and code samples. | 6 | - feat: include a `README.html` alongside compiler with documentation and code samples. |
| 6 | - feat(transform): include a warning for leading zero in numbers | 7 | - feat(transform): include a warning for leading zero in numbers |
| ... | @@ -9,6 +10,7 @@ | ... | @@ -9,6 +10,7 @@ |
| 9 | - fix(sema): handle recursive function calls | 10 | - fix(sema): handle recursive function calls |
| 10 | - fix(sema): quit block analysis when hitting `noreturn` | 11 | - fix(sema): quit block analysis when hitting `noreturn` |
| 11 | - fix(sema): binary type resolution with opaque and numeric literals no longer crash | 12 | - fix(sema): binary type resolution with opaque and numeric literals no longer crash |
| 13 | - fix(sema): false positive warning for numeric int ambiguity when it was always float | ||
| 12 | 14 | ||
| 13 | ## 0.1.3 | 15 | ## 0.1.3 |
| 14 | 16 |
build.zig+6-6| ... | @@ -117,12 +117,12 @@ pub fn build(b: *std.Build) void { | ... | @@ -117,12 +117,12 @@ pub fn build(b: *std.Build) void { |
| 117 | const win_version: OsVersion = .{ .windows = .win7 }; | 117 | const win_version: OsVersion = .{ .windows = .win7 }; |
| 118 | 118 | ||
| 119 | inline for (.{ | 119 | inline for (.{ |
| 120 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version }), | 120 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version, .cpu_model = .baseline }), |
| 121 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version }), | 121 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version, .cpu_model = .baseline }), |
| 122 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos, .os_version_min = mac_version }), | 122 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos, .os_version_min = mac_version, .cpu_model = .baseline }), |
| 123 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .macos, .os_version_min = mac_version }), | 123 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .macos, .os_version_min = mac_version, .cpu_model = .baseline }), |
| 124 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu }), | 124 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu, .cpu_model = .baseline }), |
| 125 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu }), | 125 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu, .cpu_model = .baseline }), |
| 126 | }) |cross_target| { | 126 | }) |cross_target| { |
| 127 | const name = std.fmt.allocPrint( | 127 | const name = std.fmt.allocPrint( |
| 128 | b.allocator, | 128 | b.allocator, |
src/core/Air.Dump.zig+39-6| ... | @@ -92,6 +92,7 @@ pub const State = struct { | ... | @@ -92,6 +92,7 @@ pub const State = struct { |
| 92 | .lt, | 92 | .lt, |
| 93 | .gt, | 93 | .gt, |
| 94 | .eq, | 94 | .eq, |
| 95 | .index, | ||
| 95 | => |comptime_tag| { | 96 | => |comptime_tag| { |
| 96 | const bin: Air.Bin = air.instData(comptime_tag, inst); | 97 | const bin: Air.Bin = air.instData(comptime_tag, inst); |
| 97 | try w.writeAll("("); | 98 | try w.writeAll("("); |
| ... | @@ -185,6 +186,15 @@ pub const State = struct { | ... | @@ -185,6 +186,15 @@ pub const State = struct { |
| 185 | try s.newline(w); | 186 | try s.newline(w); |
| 186 | try w.writeAll("})"); | 187 | try w.writeAll("})"); |
| 187 | }, | 188 | }, |
| 189 | .array_init => { | ||
| 190 | const array_init: Air.ArrayInit = air.instData(.array_init, inst); | ||
| 191 | const array_type = array_init.ty.toArrayType(s.ip).?; | ||
| 192 | |||
| 193 | try w.writeAll("("); | ||
| 194 | try s.dumpValue(w, array_type.item_type.value()); | ||
| 195 | try s.dumpRefList(w, array_init.list.slice(air), '[', ']'); | ||
| 196 | try w.writeAll(")"); | ||
| 197 | }, | ||
| 188 | } | 198 | } |
| 189 | } | 199 | } |
| 190 | 200 | ||
| ... | @@ -337,6 +347,31 @@ pub const State = struct { | ... | @@ -337,6 +347,31 @@ pub const State = struct { |
| 337 | try w.writeAll(">"); | 347 | try w.writeAll(">"); |
| 338 | try tty.setColor(w, .reset); | 348 | try tty.setColor(w, .reset); |
| 339 | }, | 349 | }, |
| 350 | .array_type => |array_type| { | ||
| 351 | try w.writeAll("[]"); | ||
| 352 | try s.dumpValue(w, array_type.item_type.value()); | ||
| 353 | }, | ||
| 354 | .array_value => |array_value| { | ||
| 355 | try tty.setColor(w, .bright_black); | ||
| 356 | try w.writeAll("<"); | ||
| 357 | try tty.setColor(w, .bright_magenta); | ||
| 358 | try s.dumpValue(w, array_value.type.value()); | ||
| 359 | try tty.setColor(w, .bright_black); | ||
| 360 | try w.writeAll(", "); | ||
| 361 | try tty.setColor(w, .reset); | ||
| 362 | try w.writeAll("["); | ||
| 363 | |||
| 364 | for (array_value.items, 0..) |value, i| { | ||
| 365 | if (i != 0) | ||
| 366 | try w.writeAll(", "); | ||
| 367 | try s.dumpValue(w, value); | ||
| 368 | } | ||
| 369 | |||
| 370 | try w.writeAll("]"); | ||
| 371 | try tty.setColor(w, .bright_black); | ||
| 372 | try w.writeAll(">"); | ||
| 373 | try tty.setColor(w, .reset); | ||
| 374 | }, | ||
| 340 | } | 375 | } |
| 341 | } | 376 | } |
| 342 | 377 | ||
| ... | @@ -388,7 +423,7 @@ pub fn format( | ... | @@ -388,7 +423,7 @@ pub fn format( |
| 388 | var state = State{ | 423 | var state = State{ |
| 389 | .air = &air, | 424 | .air = &air, |
| 390 | .ip = dump.ip, | 425 | .ip = dump.ip, |
| 391 | .tty = detectTTYFromWriter(writer), | 426 | .tty = core.detectTTYFromWriter(writer), |
| 392 | .arena = arena.allocator(), | 427 | .arena = arena.allocator(), |
| 393 | }; | 428 | }; |
| 394 | try state.dumpInst(writer, .main_block); | 429 | try state.dumpInst(writer, .main_block); |
| ... | @@ -399,10 +434,8 @@ pub fn format( | ... | @@ -399,10 +434,8 @@ pub fn format( |
| 399 | const std = @import("std"); | 434 | const std = @import("std"); |
| 400 | const assert = std.debug.assert; | 435 | const assert = std.debug.assert; |
| 401 | 436 | ||
| 402 | const Air = @import("Air.zig"); | 437 | const core = @import("mod.zig"); |
| 438 | const Air = core.Air; | ||
| 403 | const Inst = Air.Inst; | 439 | const Inst = Air.Inst; |
| 404 | 440 | const InternPool = core.InternPool; | |
| 405 | const InternPool = @import("InternPool.zig"); | ||
| 406 | const Value = InternPool.Value; | 441 | const Value = InternPool.Value; |
| 407 | |||
| 408 | const detectTTYFromWriter = @import("util.zig").detectTTYFromWriter; |
src/core/Air.zig+19| ... | @@ -25,10 +25,13 @@ pub const Inst = struct { | ... | @@ -25,10 +25,13 @@ pub const Inst = struct { |
| 25 | store, | 25 | store, |
| 26 | 26 | ||
| 27 | struct_init, | 27 | struct_init, |
| 28 | array_init, | ||
| 28 | 29 | ||
| 29 | field_opaque, | 30 | field_opaque, |
| 30 | field_struct, | 31 | field_struct, |
| 31 | 32 | ||
| 33 | index, | ||
| 34 | |||
| 32 | // bin | 35 | // bin |
| 33 | add, | 36 | add, |
| 34 | sub, | 37 | sub, |
| ... | @@ -55,8 +58,10 @@ pub const Inst = struct { | ... | @@ -55,8 +58,10 @@ pub const Inst = struct { |
| 55 | .memory => Memory, | 58 | .memory => Memory, |
| 56 | .store => Store, | 59 | .store => Store, |
| 57 | .struct_init => StructInit, | 60 | .struct_init => StructInit, |
| 61 | .array_init => ArrayInit, | ||
| 58 | .field_opaque => FieldOpaque, | 62 | .field_opaque => FieldOpaque, |
| 59 | .field_struct => FieldStruct, | 63 | .field_struct => FieldStruct, |
| 64 | .index, | ||
| 60 | .add, | 65 | .add, |
| 61 | .sub, | 66 | .sub, |
| 62 | .mul, | 67 | .mul, |
| ... | @@ -272,6 +277,11 @@ pub const StructInit = struct { | ... | @@ -272,6 +277,11 @@ pub const StructInit = struct { |
| 272 | } | 277 | } |
| 273 | }; | 278 | }; |
| 274 | 279 | ||
| 280 | pub const ArrayInit = struct { | ||
| 281 | ty: Type, | ||
| 282 | list: RefList, | ||
| 283 | }; | ||
| 284 | |||
| 275 | pub const FieldStruct = struct { | 285 | pub const FieldStruct = struct { |
| 276 | base: Inst.Index, | 286 | base: Inst.Index, |
| 277 | field_index: u32, | 287 | field_index: u32, |
| ... | @@ -326,6 +336,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In | ... | @@ -326,6 +336,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In |
| 326 | .store => air.instData(.memory, air.instData(.store, index).memory).ty, | 336 | .store => air.instData(.memory, air.instData(.store, index).memory).ty, |
| 327 | .@"if" => air.instData(.@"if", index).data.get(air).union_ty, | 337 | .@"if" => air.instData(.@"if", index).data.get(air).union_ty, |
| 328 | .struct_init => air.instData(.struct_init, index).ty, | 338 | .struct_init => air.instData(.struct_init, index).ty, |
| 339 | .array_init => air.instData(.array_init, index).ty, | ||
| 329 | .arg => air.instData(.arg, index).type, | 340 | .arg => air.instData(.arg, index).type, |
| 330 | 341 | ||
| 331 | // A handful of binary operators are lowered to a form where both sides | 342 | // A handful of binary operators are lowered to a form where both sides |
| ... | @@ -367,6 +378,14 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In | ... | @@ -367,6 +378,14 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In |
| 367 | .toStructType(ip) orelse unreachable; // not a struct | 378 | .toStructType(ip) orelse unreachable; // not a struct |
| 368 | return base_type.field_types[inst.field_index]; | 379 | return base_type.field_types[inst.field_index]; |
| 369 | }, | 380 | }, |
| 381 | |||
| 382 | // Array accesses use the array they are accessing into. | ||
| 383 | .index => { | ||
| 384 | const inst = air.instData(.index, index); | ||
| 385 | const base_type = air.typeOf(inst.lhs, ip) | ||
| 386 | .toArrayType(ip) orelse unreachable; // not an array | ||
| 387 | return base_type.item_type; | ||
| 388 | }, | ||
| 370 | }; | 389 | }; |
| 371 | } | 390 | } |
| 372 | 391 |
src/core/Ast.Dump.zig+14-8| ... | @@ -30,7 +30,7 @@ pub fn format( | ... | @@ -30,7 +30,7 @@ pub fn format( |
| 30 | .node_tags = ast.nodes.items(.tag), | 30 | .node_tags = ast.nodes.items(.tag), |
| 31 | .node_data = ast.nodes.items(.data), | 31 | .node_data = ast.nodes.items(.data), |
| 32 | .node_tokens = ast.nodes.items(.main_token), | 32 | .node_tokens = ast.nodes.items(.main_token), |
| 33 | .tty = detectTTYFromWriter(writer), | 33 | .tty = core.detectTTYFromWriter(writer), |
| 34 | }; | 34 | }; |
| 35 | try dumpNode(writer, &state, 0); | 35 | try dumpNode(writer, &state, 0); |
| 36 | try writer.writeAll("\n"); | 36 | try writer.writeAll("\n"); |
| ... | @@ -127,6 +127,8 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err | ... | @@ -127,6 +127,8 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err |
| 127 | => try dumpNodeList(writer, state, list, '{', '}'), | 127 | => try dumpNodeList(writer, state, list, '{', '}'), |
| 128 | .builtin_call, | 128 | .builtin_call, |
| 129 | => try dumpNodeList(writer, state, list, '(', ')'), | 129 | => try dumpNodeList(writer, state, list, '(', ')'), |
| 130 | .array_init, | ||
| 131 | => try dumpNodeList(writer, state, list, '[', ']'), | ||
| 130 | else => unreachable, | 132 | else => unreachable, |
| 131 | } | 133 | } |
| 132 | }, | 134 | }, |
| ... | @@ -145,14 +147,18 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err | ... | @@ -145,14 +147,18 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err |
| 145 | try tty.setColor(writer, .reset); | 147 | try tty.setColor(writer, .reset); |
| 146 | var indent = false; | 148 | var indent = false; |
| 147 | if (var_decl.type) |type_node| { | 149 | if (var_decl.type) |type_node| { |
| 150 | try writer.writeAll(" {"); | ||
| 148 | state.indent += 1; | 151 | state.indent += 1; |
| 149 | indent = true; | 152 | indent = true; |
| 150 | try state.newline(writer); | 153 | try state.newline(writer); |
| 151 | try writer.writeAll(": "); | 154 | try writer.writeAll("type: "); |
| 152 | try dumpNode(writer, state, type_node); | 155 | try dumpNode(writer, state, type_node); |
| 153 | try writer.writeAll(", "); | 156 | try writer.writeAll(","); |
| 157 | try state.newline(writer); | ||
| 158 | try writer.writeAll("init: "); | ||
| 159 | } else { | ||
| 160 | try writer.writeAll(" = "); | ||
| 154 | } | 161 | } |
| 155 | try writer.writeAll(" = "); | ||
| 156 | try dumpNode(writer, state, var_decl.init); | 162 | try dumpNode(writer, state, var_decl.init); |
| 157 | if (indent) { | 163 | if (indent) { |
| 158 | state.indent -= 1; | 164 | state.indent -= 1; |
| ... | @@ -312,7 +318,8 @@ fn dumpNodeList(writer: anytype, state: *State, list: []const NodeIndex, l: u8, | ... | @@ -312,7 +318,8 @@ fn dumpNodeList(writer: anytype, state: *State, list: []const NodeIndex, l: u8, |
| 312 | try dumpNode(writer, state, node); | 318 | try dumpNode(writer, state, node); |
| 313 | } | 319 | } |
| 314 | } | 320 | } |
| 315 | try state.newline(writer); | 321 | if (list.len > 0) |
| 322 | try state.newline(writer); | ||
| 316 | try writer.print("{c}", .{r}); | 323 | try writer.print("{c}", .{r}); |
| 317 | } | 324 | } |
| 318 | 325 | ||
| ... | @@ -321,10 +328,9 @@ const Dump = @This(); | ... | @@ -321,10 +328,9 @@ const Dump = @This(); |
| 321 | const std = @import("std"); | 328 | const std = @import("std"); |
| 322 | const assert = std.debug.assert; | 329 | const assert = std.debug.assert; |
| 323 | 330 | ||
| 324 | const Ast = @import("Ast.zig"); | 331 | const core = @import("mod.zig"); |
| 332 | const Ast = core.Ast; | ||
| 325 | const NodeIndex = Ast.NodeIndex; | 333 | const NodeIndex = Ast.NodeIndex; |
| 326 | const Node = Ast.Node; | 334 | const Node = Ast.Node; |
| 327 | const TokenIndex = Ast.TokenIndex; | 335 | const TokenIndex = Ast.TokenIndex; |
| 328 | const Call = Ast.Call; | 336 | const Call = Ast.Call; |
| 329 | |||
| 330 | const detectTTYFromWriter = @import("util.zig").detectTTYFromWriter; |
src/core/Ast.zig+42-4| ... | @@ -218,6 +218,10 @@ pub const Node = struct { | ... | @@ -218,6 +218,10 @@ pub const Node = struct { |
| 218 | struct_field, | 218 | struct_field, |
| 219 | /// Struct Init | 219 | /// Struct Init |
| 220 | struct_init, | 220 | struct_init, |
| 221 | /// Array type | ||
| 222 | array_type, | ||
| 223 | /// Array initialization | ||
| 224 | array_init, | ||
| 221 | 225 | ||
| 222 | pub fn isAssignBin(op: Tag) bool { | 226 | pub fn isAssignBin(op: Tag) bool { |
| 223 | return switch (op) { | 227 | return switch (op) { |
| ... | @@ -333,6 +337,8 @@ pub const Node = struct { | ... | @@ -333,6 +337,8 @@ pub const Node = struct { |
| 333 | .shr_assign = .bin, | 337 | .shr_assign = .bin, |
| 334 | .string = .none, | 338 | .string = .none, |
| 335 | .struct_decl = .list, | 339 | .struct_decl = .list, |
| 340 | .array_type = .un, | ||
| 341 | .array_init = .list, | ||
| 336 | .struct_field = .un, | 342 | .struct_field = .un, |
| 337 | .struct_init = .struct_init, | 343 | .struct_init = .struct_init, |
| 338 | .sub = .bin, | 344 | .sub = .bin, |
| ... | @@ -784,12 +790,44 @@ const Ast = @This(); | ... | @@ -784,12 +790,44 @@ const Ast = @This(); |
| 784 | const std = @import("std"); | 790 | const std = @import("std"); |
| 785 | const assert = std.debug.assert; | 791 | const assert = std.debug.assert; |
| 786 | 792 | ||
| 787 | const Tokenizer = @import("Tokenizer.zig"); | 793 | const core = @import("mod.zig"); |
| 794 | |||
| 795 | const Tokenizer = core.Tokenizer; | ||
| 788 | const Token = Tokenizer.Token; | 796 | const Token = Tokenizer.Token; |
| 789 | const TokenList = Tokenizer.Token.List; | 797 | const TokenList = Tokenizer.Token.List; |
| 790 | const ByteOffset = Tokenizer.ByteOffset; | 798 | const ByteOffset = Tokenizer.ByteOffset; |
| 791 | 799 | ||
| 792 | const Report = @import("Report.zig"); | 800 | const Report = core.Report; |
| 801 | |||
| 802 | /// For numbers that are used both as user-visible one based numbers and zero | ||
| 803 | /// based offsets, use this type to reduce mistakes. A common usage of this | ||
| 804 | /// would be line and column numbers for `Report.Item`s | ||
| 805 | pub fn Ordinal(Int: type) type { | ||
| 806 | comptime assert(@typeInfo(Int).Int.signedness == .unsigned); | ||
| 807 | return enum(Int) { | ||
| 808 | start = 0, | ||
| 809 | _, | ||
| 810 | |||
| 811 | pub fn fromZeroBased(int: Int) @This() { | ||
| 812 | assert(int != std.math.maxInt(Int)); | ||
| 813 | return @enumFromInt(int); | ||
| 814 | } | ||
| 793 | 815 | ||
| 794 | const util = @import("util.zig"); | 816 | pub fn fromOneBased(int: Int) @This() { |
| 795 | const Ordinal = util.Ordinal; | 817 | assert(int > 0); |
| 818 | return @enumFromInt(int - 1); | ||
| 819 | } | ||
| 820 | |||
| 821 | pub fn zeroBased(ord: @This()) Int { | ||
| 822 | return @intFromEnum(ord); | ||
| 823 | } | ||
| 824 | |||
| 825 | pub fn oneBased(ord: @This()) Int { | ||
| 826 | return @intFromEnum(ord) + 1; | ||
| 827 | } | ||
| 828 | |||
| 829 | pub fn add(ord: @This(), inc: Int) @This() { | ||
| 830 | return fromZeroBased(ord.zeroBased() + inc); | ||
| 831 | } | ||
| 832 | }; | ||
| 833 | } |
src/core/InternPool.zig+130-37| ... | @@ -69,6 +69,8 @@ pub const Tag = enum(u8) { | ... | @@ -69,6 +69,8 @@ pub const Tag = enum(u8) { |
| 69 | function_type, | 69 | function_type, |
| 70 | /// A struct type | 70 | /// A struct type |
| 71 | struct_type, | 71 | struct_type, |
| 72 | /// An array type | ||
| 73 | array_type, | ||
| 72 | /// A type that can be represented with only an enum tag. | 74 | /// A type that can be represented with only an enum tag. |
| 73 | simple_type, | 75 | simple_type, |
| 74 | 76 | ||
| ... | @@ -92,6 +94,8 @@ pub const Tag = enum(u8) { | ... | @@ -92,6 +94,8 @@ pub const Tag = enum(u8) { |
| 92 | 94 | ||
| 93 | struct_value, | 95 | struct_value, |
| 94 | 96 | ||
| 97 | array_value, | ||
| 98 | |||
| 95 | pub fn Data(comptime tag: Tag) type { | 99 | pub fn Data(comptime tag: Tag) type { |
| 96 | return switch (tag) { | 100 | return switch (tag) { |
| 97 | .removed, | 101 | .removed, |
| ... | @@ -118,7 +122,9 @@ pub const Tag = enum(u8) { | ... | @@ -118,7 +122,9 @@ pub const Tag = enum(u8) { |
| 118 | .function_type => TypeFunction, | 122 | .function_type => TypeFunction, |
| 119 | .string => String.WithLen, | 123 | .string => String.WithLen, |
| 120 | .struct_value => StructValue, | 124 | .struct_value => StructValue, |
| 121 | .struct_type => TypeStruct, | 125 | .struct_type => StructType, |
| 126 | .array_type => ArrayType, | ||
| 127 | .array_value => ArrayValue, | ||
| 122 | }; | 128 | }; |
| 123 | } | 129 | } |
| 124 | 130 | ||
| ... | @@ -140,10 +146,23 @@ pub const Tag = enum(u8) { | ... | @@ -140,10 +146,23 @@ pub const Tag = enum(u8) { |
| 140 | /// Trailing: | 146 | /// Trailing: |
| 141 | /// 0. field_key: NullTerminatedString. Repeated per field_len | 147 | /// 0. field_key: NullTerminatedString. Repeated per field_len |
| 142 | /// 1. field_type: Type. Repeated per field_len | 148 | /// 1. field_type: Type. Repeated per field_len |
| 143 | pub const TypeStruct = struct { | 149 | pub const StructType = struct { |
| 144 | fields_len: u32, | 150 | fields_len: u32, |
| 145 | }; | 151 | }; |
| 146 | 152 | ||
| 153 | /// Trailing: | ||
| 154 | pub const ArrayType = struct { | ||
| 155 | item_type: Type, | ||
| 156 | }; | ||
| 157 | |||
| 158 | /// Trailing: | ||
| 159 | /// 1. item: Type. Repeated per items_len | ||
| 160 | pub const ArrayValue = struct { | ||
| 161 | /// This is the array type, not the item type | ||
| 162 | type: Type, | ||
| 163 | items_len: u32, | ||
| 164 | }; | ||
| 165 | |||
| 147 | /// Trailing: | 166 | /// Trailing: |
| 148 | /// 0. value: Value. Repeated ty.structInfo().field_len | 167 | /// 0. value: Value. Repeated ty.structInfo().field_len |
| 149 | pub const StructValue = struct { | 168 | pub const StructValue = struct { |
| ... | @@ -155,9 +174,9 @@ pub const Tag = enum(u8) { | ... | @@ -155,9 +174,9 @@ pub const Tag = enum(u8) { |
| 155 | /// This is an index into .items | 174 | /// This is an index into .items |
| 156 | pub const Value = enum(u32) { | 175 | pub const Value = enum(u32) { |
| 157 | pub const first_type: Value = .opaque_type; | 176 | pub const first_type: Value = .opaque_type; |
| 158 | pub const last_type: Value = .noreturn_type; | 177 | pub const last_type: Value = .numeric_literal_array_type; |
| 159 | pub const first_value: Value = .void; | 178 | pub const first_value: Value = .void; |
| 160 | pub const last_value: Value = .empty_string; | 179 | pub const last_value: Value = .empty_array; |
| 161 | 180 | ||
| 162 | opaque_type, | 181 | opaque_type, |
| 163 | bool_type, | 182 | bool_type, |
| ... | @@ -167,6 +186,8 @@ pub const Value = enum(u32) { | ... | @@ -167,6 +186,8 @@ pub const Value = enum(u32) { |
| 167 | noreturn_type, | 186 | noreturn_type, |
| 168 | type_type, | 187 | type_type, |
| 169 | numeric_literal_type, | 188 | numeric_literal_type, |
| 189 | numeric_literal_array_type, | ||
| 190 | noreturn_array_type, | ||
| 170 | 191 | ||
| 171 | void, | 192 | void, |
| 172 | bool_true, | 193 | bool_true, |
| ... | @@ -175,7 +196,7 @@ pub const Value = enum(u32) { | ... | @@ -175,7 +196,7 @@ pub const Value = enum(u32) { |
| 175 | float_zero, | 196 | float_zero, |
| 176 | nan, | 197 | nan, |
| 177 | empty_string, | 198 | empty_string, |
| 178 | standard_library, | 199 | empty_array, |
| 179 | 200 | ||
| 180 | none = std.math.maxInt(u32), | 201 | none = std.math.maxInt(u32), |
| 181 | _, | 202 | _, |
| ... | @@ -183,7 +204,7 @@ pub const Value = enum(u32) { | ... | @@ -183,7 +204,7 @@ pub const Value = enum(u32) { |
| 183 | /// An array of `Value` existing within the `extra` array. | 204 | /// An array of `Value` existing within the `extra` array. |
| 184 | /// This type exists to provide a struct with lifetime that is | 205 | /// This type exists to provide a struct with lifetime that is |
| 185 | /// not invalidated when items are added to the `InternPool`. | 206 | /// not invalidated when items are added to the `InternPool`. |
| 186 | /// TODO: Replace with ExtraStore.Slice(Value) | 207 | // TODO: Replace with ExtraStore.Slice(Value) |
| 187 | pub const Slice = struct { | 208 | pub const Slice = struct { |
| 188 | start: u32, | 209 | start: u32, |
| 189 | len: u32, | 210 | len: u32, |
| ... | @@ -237,7 +258,7 @@ pub const Value = enum(u32) { | ... | @@ -237,7 +258,7 @@ pub const Value = enum(u32) { |
| 237 | // TODO: move the formatter out of Air.Dump | 258 | // TODO: move the formatter out of Air.Dump |
| 238 | var state: Air.Dump.State = undefined; | 259 | var state: Air.Dump.State = undefined; |
| 239 | state.ip = ctx.ip; | 260 | state.ip = ctx.ip; |
| 240 | state.tty = @import("util.zig").detectTTYFromWriter(writer); | 261 | state.tty = core.detectTTYFromWriter(writer); |
| 241 | try Air.Dump.State.dumpValue(&state, writer, ctx.val); | 262 | try Air.Dump.State.dumpValue(&state, writer, ctx.val); |
| 242 | } | 263 | } |
| 243 | 264 | ||
| ... | @@ -245,9 +266,11 @@ pub const Value = enum(u32) { | ... | @@ -245,9 +266,11 @@ pub const Value = enum(u32) { |
| 245 | return switch (value.valueTag(ip)) { | 266 | return switch (value.valueTag(ip)) { |
| 246 | .removed => unreachable, // UAF | 267 | .removed => unreachable, // UAF |
| 247 | 268 | ||
| 248 | .function_type => .type, | 269 | .function_type, |
| 249 | .simple_type => .type, | 270 | .simple_type, |
| 250 | .struct_type => .type, | 271 | .struct_type, |
| 272 | .array_type, | ||
| 273 | => .type, | ||
| 251 | 274 | ||
| 252 | .simple_value => switch (value.valueData(ip, .simple_value)) { | 275 | .simple_value => switch (value.valueData(ip, .simple_value)) { |
| 253 | .true => .bool, | 276 | .true => .bool, |
| ... | @@ -261,6 +284,7 @@ pub const Value = enum(u32) { | ... | @@ -261,6 +284,7 @@ pub const Value = enum(u32) { |
| 261 | .string => .string, | 284 | .string => .string, |
| 262 | .func => value.toKey(ip).func.ty, | 285 | .func => value.toKey(ip).func.ty, |
| 263 | .struct_value => value.toKey(ip).struct_value.ty, | 286 | .struct_value => value.toKey(ip).struct_value.ty, |
| 287 | .array_value => value.toKey(ip).array_value.type, | ||
| 264 | }; | 288 | }; |
| 265 | } | 289 | } |
| 266 | 290 | ||
| ... | @@ -273,6 +297,7 @@ pub const Value = enum(u32) { | ... | @@ -273,6 +297,7 @@ pub const Value = enum(u32) { |
| 273 | .function_type, | 297 | .function_type, |
| 274 | .struct_type, | 298 | .struct_type, |
| 275 | .simple_type, | 299 | .simple_type, |
| 300 | .array_type, | ||
| 276 | => @enumFromInt(@intFromEnum(value)), | 301 | => @enumFromInt(@intFromEnum(value)), |
| 277 | 302 | ||
| 278 | // exception: void is a value AND a type | 303 | // exception: void is a value AND a type |
| ... | @@ -321,6 +346,8 @@ pub const Type = enum(u32) { | ... | @@ -321,6 +346,8 @@ pub const Type = enum(u32) { |
| 321 | noreturn = @intFromEnum(Value.noreturn_type), | 346 | noreturn = @intFromEnum(Value.noreturn_type), |
| 322 | type = @intFromEnum(Value.type_type), | 347 | type = @intFromEnum(Value.type_type), |
| 323 | numeric_literal = @intFromEnum(Value.numeric_literal_type), | 348 | numeric_literal = @intFromEnum(Value.numeric_literal_type), |
| 349 | numeric_literal_array = @intFromEnum(Value.numeric_literal_array_type), | ||
| 350 | noreturn_array = @intFromEnum(Value.noreturn_array_type), | ||
| 324 | void = @intFromEnum(Value.void), | 351 | void = @intFromEnum(Value.void), |
| 325 | _, | 352 | _, |
| 326 | 353 | ||
| ... | @@ -328,6 +355,7 @@ pub const Type = enum(u32) { | ... | @@ -328,6 +355,7 @@ pub const Type = enum(u32) { |
| 328 | pub const Tag = enum(u32) { | 355 | pub const Tag = enum(u32) { |
| 329 | function_type = @intFromEnum(InternPool.Tag.function_type), | 356 | function_type = @intFromEnum(InternPool.Tag.function_type), |
| 330 | struct_type = @intFromEnum(InternPool.Tag.struct_type), | 357 | struct_type = @intFromEnum(InternPool.Tag.struct_type), |
| 358 | array_type = @intFromEnum(InternPool.Tag.array_type), | ||
| 331 | simple_type = @intFromEnum(InternPool.Tag.simple_type), | 359 | simple_type = @intFromEnum(InternPool.Tag.simple_type), |
| 332 | }; | 360 | }; |
| 333 | 361 | ||
| ... | @@ -362,6 +390,11 @@ pub const Type = enum(u32) { | ... | @@ -362,6 +390,11 @@ pub const Type = enum(u32) { |
| 362 | return ip.valueToKey(ty.value()).struct_type; | 390 | return ip.valueToKey(ty.value()).struct_type; |
| 363 | } | 391 | } |
| 364 | 392 | ||
| 393 | pub fn toArrayType(ty: Type, ip: *const InternPool) ?Key.ArrayType { | ||
| 394 | if (ty.tag(ip) != .array_type) return null; | ||
| 395 | return ip.valueToKey(ty.value()).array_type; | ||
| 396 | } | ||
| 397 | |||
| 365 | pub fn hasRuntimeRepresentation(ty: Type, ip: *const InternPool) bool { | 398 | pub fn hasRuntimeRepresentation(ty: Type, ip: *const InternPool) bool { |
| 366 | return switch (ty) { | 399 | return switch (ty) { |
| 367 | .bool, | 400 | .bool, |
| ... | @@ -377,8 +410,11 @@ pub const Type = enum(u32) { | ... | @@ -377,8 +410,11 @@ pub const Type = enum(u32) { |
| 377 | => false, | 410 | => false, |
| 378 | else => switch (ty.tag(ip)) { | 411 | else => switch (ty.tag(ip)) { |
| 379 | .simple_type => unreachable, // all simple types are already printed | 412 | .simple_type => unreachable, // all simple types are already printed |
| 380 | .function_type => true, | 413 | .struct_type, |
| 381 | .struct_type => true, | 414 | => false, |
| 415 | .function_type, | ||
| 416 | => true, | ||
| 417 | .array_type => ty.toArrayType(ip).?.item_type.hasRuntimeRepresentation(ip), | ||
| 382 | }, | 418 | }, |
| 383 | }; | 419 | }; |
| 384 | } | 420 | } |
| ... | @@ -428,6 +464,11 @@ pub const Type = enum(u32) { | ... | @@ -428,6 +464,11 @@ pub const Type = enum(u32) { |
| 428 | .struct_type => { | 464 | .struct_type => { |
| 429 | try writer.writeAll("[struct]"); | 465 | try writer.writeAll("[struct]"); |
| 430 | }, | 466 | }, |
| 467 | .array_type => { | ||
| 468 | try writer.writeAll("[]"); | ||
| 469 | const array_type = ty.toArrayType(ip).?; | ||
| 470 | try writer.print("{}", .{array_type.item_type.displayName(ip)}); | ||
| 471 | }, | ||
| 431 | } | 472 | } |
| 432 | } | 473 | } |
| 433 | }; | 474 | }; |
| ... | @@ -464,6 +505,8 @@ pub const static_keys = [_]Key{ | ... | @@ -464,6 +505,8 @@ pub const static_keys = [_]Key{ |
| 464 | SimpleType.noreturn.key(), | 505 | SimpleType.noreturn.key(), |
| 465 | SimpleType.type.key(), | 506 | SimpleType.type.key(), |
| 466 | SimpleType.numeric_literal.key(), | 507 | SimpleType.numeric_literal.key(), |
| 508 | .{ .array_type = .{ .item_type = Type.numeric_literal } }, | ||
| 509 | .{ .array_type = .{ .item_type = Type.noreturn } }, | ||
| 467 | 510 | ||
| 468 | SimpleValue.void.key(), | 511 | SimpleValue.void.key(), |
| 469 | SimpleValue.true.key(), | 512 | SimpleValue.true.key(), |
| ... | @@ -471,6 +514,10 @@ pub const static_keys = [_]Key{ | ... | @@ -471,6 +514,10 @@ pub const static_keys = [_]Key{ |
| 471 | .{ .int32 = 0 }, | 514 | .{ .int32 = 0 }, |
| 472 | .{ .float = 0.0 }, | 515 | .{ .float = 0.0 }, |
| 473 | .{ .float = Float.pure_nan.decode() }, | 516 | .{ .float = Float.pure_nan.decode() }, |
| 517 | .{ .array_value = .{ | ||
| 518 | .type = Type.noreturn_array, | ||
| 519 | .items = &.{}, | ||
| 520 | } }, | ||
| 474 | .{ .string = "" }, | 521 | .{ .string = "" }, |
| 475 | }; | 522 | }; |
| 476 | 523 | ||
| ... | @@ -782,10 +829,12 @@ pub const Key = union(enum) { | ... | @@ -782,10 +829,12 @@ pub const Key = union(enum) { |
| 782 | simple_value: SimpleValue, | 829 | simple_value: SimpleValue, |
| 783 | struct_value: StructValue, | 830 | struct_value: StructValue, |
| 784 | func: Func, | 831 | func: Func, |
| 832 | array_value: ArrayValue, | ||
| 785 | // Types | 833 | // Types |
| 786 | simple_type: SimpleType, | 834 | simple_type: SimpleType, |
| 787 | function_type: FunctionType, | 835 | function_type: FunctionType, |
| 788 | struct_type: StructType, | 836 | struct_type: StructType, |
| 837 | array_type: ArrayType, | ||
| 789 | 838 | ||
| 790 | const Adapter = struct { | 839 | const Adapter = struct { |
| 791 | intern_pool: *const InternPool, | 840 | intern_pool: *const InternPool, |
| ... | @@ -827,6 +876,7 @@ pub const Key = union(enum) { | ... | @@ -827,6 +876,7 @@ pub const Key = union(enum) { |
| 827 | 876 | ||
| 828 | pub fn hash(self: StructType, hasher: *Hash) void { | 877 | pub fn hash(self: StructType, hasher: *Hash) void { |
| 829 | // TODO: remove this function. structural typing is not used | 878 | // TODO: remove this function. structural typing is not used |
| 879 | // this should be hashing it's location / decl, exactly like `Func` | ||
| 830 | std.hash.autoHashStrat(hasher, self, .DeepRecursive); | 880 | std.hash.autoHashStrat(hasher, self, .DeepRecursive); |
| 831 | } | 881 | } |
| 832 | }; | 882 | }; |
| ... | @@ -841,6 +891,25 @@ pub const Key = union(enum) { | ... | @@ -841,6 +891,25 @@ pub const Key = union(enum) { |
| 841 | } | 891 | } |
| 842 | }; | 892 | }; |
| 843 | 893 | ||
| 894 | pub const ArrayType = struct { | ||
| 895 | item_type: Type, | ||
| 896 | |||
| 897 | pub fn hash(self: ArrayType, hasher: *Hash) void { | ||
| 898 | std.hash.autoHash(hasher, self.item_type); | ||
| 899 | } | ||
| 900 | }; | ||
| 901 | |||
| 902 | pub const ArrayValue = struct { | ||
| 903 | /// This is the array type, not the item type | ||
| 904 | type: Type, | ||
| 905 | items: []const Value, | ||
| 906 | |||
| 907 | pub fn hash(self: ArrayValue, hasher: *Hash) void { | ||
| 908 | std.hash.autoHash(hasher, self.type); | ||
| 909 | std.hash.autoHashStrat(hasher, self.items, .Deep); | ||
| 910 | } | ||
| 911 | }; | ||
| 912 | |||
| 844 | pub const Func = struct { | 913 | pub const Func = struct { |
| 845 | ty: Type, | 914 | ty: Type, |
| 846 | owner_decl: DeclIndex, | 915 | owner_decl: DeclIndex, |
| ... | @@ -874,22 +943,13 @@ pub const Key = union(enum) { | ... | @@ -874,22 +943,13 @@ pub const Key = union(enum) { |
| 874 | .string, | 943 | .string, |
| 875 | => |x| Hash.hash(seed, x), | 944 | => |x| Hash.hash(seed, x), |
| 876 | .float, .comptime_float => |x| Hash.hash(seed, asBytes(&Float.assertPure(x))), | 945 | .float, .comptime_float => |x| Hash.hash(seed, asBytes(&Float.assertPure(x))), |
| 877 | .function_type => |x| { | 946 | inline .function_type, |
| 878 | var hasher = Hash.init(seed); | 947 | .func, |
| 879 | x.hash(&hasher); | 948 | .struct_type, |
| 880 | return hasher.final(); | 949 | .struct_value, |
| 881 | }, | 950 | .array_value, |
| 882 | .func => |x| { | 951 | .array_type, |
| 883 | var hasher = Hash.init(seed); | 952 | => |x| { |
| 884 | x.hash(&hasher); | ||
| 885 | return hasher.final(); | ||
| 886 | }, | ||
| 887 | .struct_type => |x| { | ||
| 888 | var hasher = Hash.init(seed); | ||
| 889 | x.hash(&hasher); | ||
| 890 | return hasher.final(); | ||
| 891 | }, | ||
| 892 | .struct_value => |x| { | ||
| 893 | var hasher = Hash.init(seed); | 953 | var hasher = Hash.init(seed); |
| 894 | x.hash(&hasher); | 954 | x.hash(&hasher); |
| 895 | return hasher.final(); | 955 | return hasher.final(); |
| ... | @@ -934,6 +994,14 @@ pub const Key = union(enum) { | ... | @@ -934,6 +994,14 @@ pub const Key = union(enum) { |
| 934 | return a_info.ty == b_info.ty and | 994 | return a_info.ty == b_info.ty and |
| 935 | std.mem.eql(Value, a_info.field_values, b_info.field_values); | 995 | std.mem.eql(Value, a_info.field_values, b_info.field_values); |
| 936 | }, | 996 | }, |
| 997 | .array_type => |a_info| { | ||
| 998 | return a_info.item_type == b.array_type.item_type; | ||
| 999 | }, | ||
| 1000 | .array_value => |a_info| { | ||
| 1001 | const b_info = b.array_value; | ||
| 1002 | return a_info.type == b_info.type and | ||
| 1003 | std.mem.eql(Value, a_info.items, b_info.items); | ||
| 1004 | }, | ||
| 937 | }; | 1005 | }; |
| 938 | } | 1006 | } |
| 939 | }; | 1007 | }; |
| ... | @@ -1066,7 +1134,7 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { | ... | @@ -1066,7 +1134,7 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { |
| 1066 | .function_type = ip.extraTypeFunction(data.decode(.function_type, item.tag)), | 1134 | .function_type = ip.extraTypeFunction(data.decode(.function_type, item.tag)), |
| 1067 | }, | 1135 | }, |
| 1068 | .struct_type => { | 1136 | .struct_type => { |
| 1069 | const payload: Tag.TypeStruct, var trail = | 1137 | const payload: Tag.StructType, var trail = |
| 1070 | data.decode(.struct_type, item.tag).getTrail(ip); | 1138 | data.decode(.struct_type, item.tag).getTrail(ip); |
| 1071 | 1139 | ||
| 1072 | return .{ .struct_type = .{ | 1140 | return .{ .struct_type = .{ |
| ... | @@ -1085,6 +1153,19 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { | ... | @@ -1085,6 +1153,19 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { |
| 1085 | ).get(ip), | 1153 | ).get(ip), |
| 1086 | } }; | 1154 | } }; |
| 1087 | }, | 1155 | }, |
| 1156 | .array_type => { | ||
| 1157 | const payload: Tag.ArrayType = data.decode(.array_type, item.tag).get(ip); | ||
| 1158 | return .{ .array_type = .{ | ||
| 1159 | .item_type = payload.item_type, | ||
| 1160 | } }; | ||
| 1161 | }, | ||
| 1162 | .array_value => { | ||
| 1163 | const payload: Tag.ArrayValue, var trail = data.decode(.array_value, item.tag).getTrail(ip); | ||
| 1164 | return .{ .array_value = .{ | ||
| 1165 | .type = payload.type, | ||
| 1166 | .items = trail.readValueSlice(payload.items_len).get(ip), | ||
| 1167 | } }; | ||
| 1168 | }, | ||
| 1088 | }; | 1169 | }; |
| 1089 | } | 1170 | } |
| 1090 | 1171 | ||
| ... | @@ -1147,7 +1228,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { | ... | @@ -1147,7 +1228,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { |
| 1147 | }, | 1228 | }, |
| 1148 | .struct_type => |s| brk: { | 1229 | .struct_type => |s| brk: { |
| 1149 | assert(s.field_keys.len == s.field_types.len); | 1230 | assert(s.field_keys.len == s.field_types.len); |
| 1150 | const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.TypeStruct, .{ | 1231 | const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.StructType, .{ |
| 1151 | .fields_len = @intCast(s.field_keys.len), | 1232 | .fields_len = @intCast(s.field_keys.len), |
| 1152 | }, s.field_keys.len * 2); | 1233 | }, s.field_keys.len * 2); |
| 1153 | trail.writeSlice(s.field_keys); | 1234 | trail.writeSlice(s.field_keys); |
| ... | @@ -1163,6 +1244,19 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { | ... | @@ -1163,6 +1244,19 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { |
| 1163 | trail.writeSlice(s.field_values); | 1244 | trail.writeSlice(s.field_values); |
| 1164 | break :brk Item.encode(.struct_value, extra); | 1245 | break :brk Item.encode(.struct_value, extra); |
| 1165 | }, | 1246 | }, |
| 1247 | .array_type => |s| brk: { | ||
| 1248 | break :brk Item.encode(.array_type, try ip.extra.append(gpa, Tag.ArrayType, .{ | ||
| 1249 | .item_type = s.item_type, | ||
| 1250 | })); | ||
| 1251 | }, | ||
| 1252 | .array_value => |s| brk: { | ||
| 1253 | const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.ArrayValue, .{ | ||
| 1254 | .type = s.type, | ||
| 1255 | .items_len = @intCast(s.items.len), | ||
| 1256 | }, s.items.len); | ||
| 1257 | trail.writeSlice(s.items); | ||
| 1258 | break :brk Item.encode(.array_value, extra); | ||
| 1259 | }, | ||
| 1166 | }; | 1260 | }; |
| 1167 | ip.items.appendAssumeCapacity(item); | 1261 | ip.items.appendAssumeCapacity(item); |
| 1168 | const index: Value = @enumFromInt(ip.items.len - 1); | 1262 | const index: Value = @enumFromInt(ip.items.len - 1); |
| ... | @@ -1332,13 +1426,12 @@ const Allocator = std.mem.Allocator; | ... | @@ -1332,13 +1426,12 @@ const Allocator = std.mem.Allocator; |
| 1332 | const Hash = std.hash.Wyhash; | 1426 | const Hash = std.hash.Wyhash; |
| 1333 | const assert = std.debug.assert; | 1427 | const assert = std.debug.assert; |
| 1334 | 1428 | ||
| 1335 | const Comp = @import("Comp.zig"); | 1429 | const core = @import("mod.zig"); |
| 1336 | const Cache = @import("Cache.zig"); | 1430 | const Comp = core.Comp; |
| 1337 | 1431 | const Cache = core.Cache; | |
| 1338 | const Teg = @import("Teg.zig"); | 1432 | const Teg = core.Teg; |
| 1339 | const Air = @import("Air.zig"); | 1433 | const Air = core.Air; |
| 1340 | 1434 | const dod = core.array; | |
| 1341 | const dod = @import("array.zig"); | ||
| 1342 | const Float = dod.Float; | 1435 | const Float = dod.Float; |
| 1343 | 1436 | ||
| 1344 | const tracer = @import("tracer"); | 1437 | const tracer = @import("tracer"); |
src/core/Parse.zig+77-15| ... | @@ -141,6 +141,8 @@ pub const AstError = union(enum) { | ... | @@ -141,6 +141,8 @@ pub const AstError = union(enum) { |
| 141 | /// Went past depth maximum | 141 | /// Went past depth maximum |
| 142 | depth_limit: Token, | 142 | depth_limit: Token, |
| 143 | 143 | ||
| 144 | array_type_flipped_hint: Token, | ||
| 145 | |||
| 144 | pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: Report.DebugStoredTrace) !Report { | 146 | pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: Report.DebugStoredTrace) !Report { |
| 145 | return (Report.Err{ .ast = err }).toReport(comp, file, trace); | 147 | return (Report.Err{ .ast = err }).toReport(comp, file, trace); |
| 146 | } | 148 | } |
| ... | @@ -176,6 +178,13 @@ pub const AstError = union(enum) { | ... | @@ -176,6 +178,13 @@ pub const AstError = union(enum) { |
| 176 | data.got.tag.description(), | 178 | data.got.tag.description(), |
| 177 | }); | 179 | }); |
| 178 | }, | 180 | }, |
| 181 | .array_type_flipped_hint => |token| { | ||
| 182 | try ctx.msg(.{ .token_stored = token }, "expected {s}, but found {s}", .{ | ||
| 183 | ExpectedContext.expression.description(), | ||
| 184 | token.tag.description(), | ||
| 185 | }); | ||
| 186 | try ctx.msgAlone("the syntax for array types is \"[]T\", not \"T[]\"", .{}); | ||
| 187 | }, | ||
| 179 | .depth_limit => |data| { | 188 | .depth_limit => |data| { |
| 180 | try ctx.msg(.{ .token_stored = data }, "syntax tree is nested too deeply (limit: {d})", .{depth_limit}); | 189 | try ctx.msg(.{ .token_stored = data }, "syntax tree is nested too deeply (limit: {d})", .{depth_limit}); |
| 181 | }, | 190 | }, |
| ... | @@ -237,7 +246,7 @@ fn parseModuleMembers(p: *Parse) Error!SubList { | ... | @@ -237,7 +246,7 @@ fn parseModuleMembers(p: *Parse) Error!SubList { |
| 237 | => try p.scratch.append(p.gpa, try p.parseModuleDecl()), | 246 | => try p.scratch.append(p.gpa, try p.parseModuleDecl()), |
| 238 | 247 | ||
| 239 | .kw_struct, | 248 | .kw_struct, |
| 240 | => try p.scratch.append(p.gpa, try p.parseStructDecl(.declaration)), | 249 | => try p.scratch.append(p.gpa, try p.parseStructDecl()), |
| 241 | 250 | ||
| 242 | .kw_enum, | 251 | .kw_enum, |
| 243 | .kw_trait, | 252 | .kw_trait, |
| ... | @@ -383,13 +392,12 @@ fn parseParamDecl(p: *Parse, types: TypePresence) Error!NodeIndex { | ... | @@ -383,13 +392,12 @@ fn parseParamDecl(p: *Parse, types: TypePresence) Error!NodeIndex { |
| 383 | }); | 392 | }); |
| 384 | } | 393 | } |
| 385 | 394 | ||
| 386 | fn parseStructDecl(p: *Parse, ctx: ExprParseContext) Error!NodeIndex { | 395 | fn parseStructDecl(p: *Parse) Error!NodeIndex { |
| 387 | const trace = t.trace(@src()); | 396 | const trace = t.trace(@src()); |
| 388 | defer trace.end(); | 397 | defer trace.end(); |
| 389 | 398 | ||
| 390 | const main_token = p.assertToken(.kw_struct); | 399 | const main_token = p.assertToken(.kw_struct); |
| 391 | 400 | ||
| 392 | _ = ctx; // TODO: | ||
| 393 | _ = try p.expect(.id); | 401 | _ = try p.expect(.id); |
| 394 | 402 | ||
| 395 | _ = try p.expect(.start_brace); | 403 | _ = try p.expect(.start_brace); |
| ... | @@ -586,6 +594,7 @@ fn isExpressionStart(p: *Parse) bool { | ... | @@ -586,6 +594,7 @@ fn isExpressionStart(p: *Parse) bool { |
| 586 | .number_float, | 594 | .number_float, |
| 587 | .number_int, | 595 | .number_int, |
| 588 | .start_brace, | 596 | .start_brace, |
| 597 | .start_bracket, | ||
| 589 | .start_paren, | 598 | .start_paren, |
| 590 | .string, | 599 | .string, |
| 591 | .tilde, | 600 | .tilde, |
| ... | @@ -840,13 +849,14 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -840,13 +849,14 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 840 | 849 | ||
| 841 | // Complex expressions | 850 | // Complex expressions |
| 842 | .builtin_id => try p.parseBuiltinCall(), | 851 | .builtin_id => try p.parseBuiltinCall(), |
| 843 | .kw_return => try p.parseReturn(), | 852 | .dot => try p.parseAnonInstantiation(), |
| 844 | .kw_break => try p.parseBreak(), | 853 | .kw_break => try p.parseBreak(), |
| 854 | .kw_fn => try p.parseAnonFunction(), | ||
| 845 | .kw_if => try p.parseIf(), | 855 | .kw_if => try p.parseIf(), |
| 846 | .start_paren => try p.parseGroup(), | 856 | .kw_return => try p.parseReturn(), |
| 847 | .start_brace => try p.parseBlock(), | 857 | .start_brace => try p.parseBlock(), |
| 848 | .dot => try p.parseAnonInstantiation(), | 858 | .start_bracket => try p.parseArrayValueOrType(mode), |
| 849 | .kw_fn => try p.parseAnonFunction(), | 859 | .start_paren => try p.parseGroup(), |
| 850 | 860 | ||
| 851 | // Unary operators | 861 | // Unary operators |
| 852 | inline .bang, | 862 | inline .bang, |
| ... | @@ -900,7 +910,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -900,7 +910,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 900 | }, | 910 | }, |
| 901 | .start_paren => { | 911 | .start_paren => { |
| 902 | const main_token = p.nextToken(); | 912 | const main_token = p.nextToken(); |
| 903 | const args = try p.parseCallArgs(); | 913 | const args = try p.parseExpressionList(.end_paren, .argument); |
| 904 | 914 | ||
| 905 | const extra = try p.addExtra(Ast.Call, .{ | 915 | const extra = try p.addExtra(Ast.Call, .{ |
| 906 | .args = args, | 916 | .args = args, |
| ... | @@ -917,7 +927,12 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ... | @@ -917,7 +927,12 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 917 | }, | 927 | }, |
| 918 | .start_bracket => { | 928 | .start_bracket => { |
| 919 | const main_token = p.nextToken(); | 929 | const main_token = p.nextToken(); |
| 920 | const index = try p.parseExpression(.expression); | 930 | |
| 931 | if (p.currentTag() == .end_bracket) { | ||
| 932 | return p.fail(.{ .array_type_flipped_hint = p.fullToken(p.pos) }); | ||
| 933 | } | ||
| 934 | |||
| 935 | const index = try p.tryParseExpression(.expression); | ||
| 921 | 936 | ||
| 922 | expr = try p.addNode(.{ | 937 | expr = try p.addNode(.{ |
| 923 | .tag = .index, | 938 | .tag = .index, |
| ... | @@ -1034,7 +1049,7 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex { | ... | @@ -1034,7 +1049,7 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex { |
| 1034 | 1049 | ||
| 1035 | _ = try p.expect(.start_paren); | 1050 | _ = try p.expect(.start_paren); |
| 1036 | 1051 | ||
| 1037 | const args = try p.parseCallArgs(); | 1052 | const args = try p.parseExpressionList(.end_paren, .argument); |
| 1038 | 1053 | ||
| 1039 | return try p.addNode(.{ | 1054 | return try p.addNode(.{ |
| 1040 | .tag = .builtin_call, | 1055 | .tag = .builtin_call, |
| ... | @@ -1043,7 +1058,11 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex { | ... | @@ -1043,7 +1058,11 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex { |
| 1043 | }); | 1058 | }); |
| 1044 | } | 1059 | } |
| 1045 | 1060 | ||
| 1046 | fn parseCallArgs(p: *Parse) Error!SubList { | 1061 | fn parseExpressionList( |
| 1062 | p: *Parse, | ||
| 1063 | comptime end_token: Token.Tag, | ||
| 1064 | comptime fail_context: ExpectedContext, | ||
| 1065 | ) Error!SubList { | ||
| 1047 | const trace = t.trace(@src()); | 1066 | const trace = t.trace(@src()); |
| 1048 | defer trace.end(); | 1067 | defer trace.end(); |
| 1049 | 1068 | ||
| ... | @@ -1054,19 +1073,19 @@ fn parseCallArgs(p: *Parse) Error!SubList { | ... | @@ -1054,19 +1073,19 @@ fn parseCallArgs(p: *Parse) Error!SubList { |
| 1054 | try p.scratch.append(p.gpa, try p.parseExpression(.expression)); | 1073 | try p.scratch.append(p.gpa, try p.parseExpression(.expression)); |
| 1055 | 1074 | ||
| 1056 | switch (p.currentTag()) { | 1075 | switch (p.currentTag()) { |
| 1057 | .end_paren => { | 1076 | end_token => { |
| 1058 | break; | 1077 | break; |
| 1059 | }, | 1078 | }, |
| 1060 | .comma => { | 1079 | .comma => { |
| 1061 | _ = p.nextToken(); | 1080 | _ = p.nextToken(); |
| 1062 | if (p.currentTag() == .end_paren) | 1081 | if (p.currentTag() == end_token) |
| 1063 | break; | 1082 | break; |
| 1064 | }, | 1083 | }, |
| 1065 | else => return p.failUnexpectedToken(.argument), | 1084 | else => return p.failUnexpectedToken(fail_context), |
| 1066 | } | 1085 | } |
| 1067 | } | 1086 | } |
| 1068 | 1087 | ||
| 1069 | _ = try p.expect(.end_paren); | 1088 | _ = try p.expect(end_token); |
| 1070 | 1089 | ||
| 1071 | return p.scratchToSubList(scratch_top); | 1090 | return p.scratchToSubList(scratch_top); |
| 1072 | } | 1091 | } |
| ... | @@ -1204,6 +1223,49 @@ fn parseAnonFunction(p: *Parse) Error!NodeIndex { | ... | @@ -1204,6 +1223,49 @@ fn parseAnonFunction(p: *Parse) Error!NodeIndex { |
| 1204 | }); | 1223 | }); |
| 1205 | } | 1224 | } |
| 1206 | 1225 | ||
| 1226 | fn parseArrayValueOrType(p: *Parse, mode: ParseExprMode) Error!NodeIndex { | ||
| 1227 | const main_token = p.assertToken(.start_bracket); | ||
| 1228 | |||
| 1229 | if (p.eatToken(.end_bracket) != null) { | ||
| 1230 | // Either array type or an empty array. These are technically ambiguous forms: | ||
| 1231 | // | ||
| 1232 | // [] & x; | ||
| 1233 | // | ||
| 1234 | // This is either a binary expression with an empty array, or an array | ||
| 1235 | // of references. Since the latter makes more sense, we will defer to | ||
| 1236 | // that. The same applies to the other operators that are both prefix | ||
| 1237 | // and infix, however those will all lead to semantic analysis errors. | ||
| 1238 | if (!p.isExpressionStart() or | ||
| 1239 | (mode == .preceding_block and p.currentTag() == .start_brace)) | ||
| 1240 | { | ||
| 1241 | // [] | ||
| 1242 | return p.addNode(.{ | ||
| 1243 | .tag = .array_init, | ||
| 1244 | .main_token = main_token, | ||
| 1245 | .data = .{ .list = SubList.empty }, | ||
| 1246 | }); | ||
| 1247 | } else { | ||
| 1248 | // []T | ||
| 1249 | const operand = try p.parseExpressionPart(mode); | ||
| 1250 | return p.addNode(.{ | ||
| 1251 | .tag = .array_type, | ||
| 1252 | .main_token = main_token, | ||
| 1253 | .data = .{ .un = .{ | ||
| 1254 | .expr = operand, | ||
| 1255 | } }, | ||
| 1256 | }); | ||
| 1257 | } | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | // [x, y, z] | ||
| 1261 | const list = try p.parseExpressionList(.end_bracket, .expression); | ||
| 1262 | return p.addNode(.{ | ||
| 1263 | .tag = .array_init, | ||
| 1264 | .main_token = main_token, | ||
| 1265 | .data = .{ .list = list }, | ||
| 1266 | }); | ||
| 1267 | } | ||
| 1268 | |||
| 1207 | // helpers | 1269 | // helpers |
| 1208 | 1270 | ||
| 1209 | /// Append a node and return its index. | 1271 | /// Append a node and return its index. |
src/core/Report.zig+21-3| ... | @@ -490,10 +490,25 @@ pub const Warn = union(enum) { | ... | @@ -490,10 +490,25 @@ pub const Warn = union(enum) { |
| 490 | 490 | ||
| 491 | fn build(data: @This(), ctx: *MsgContext) !void { | 491 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 492 | const node = try data.src.get(ctx); | 492 | const node = try data.src.get(ctx); |
| 493 | try ctx.msgNode(node, "constant integer in a \"var\" has an unknown type, assuming \"float\"", .{}); | 493 | try ctx.msgNode(node, "constant integer cannot be represented as a runtime value, implicitly casting to \"float\"", .{}); |
| 494 | try ctx.msgAlone("add \": float\" or \": int\" after the variable name to set the type", .{}); | 494 | try ctx.msgAlone("add \": float\" or \": int\" after the variable name to set the type", .{}); |
| 495 | } | 495 | } |
| 496 | }, | 496 | }, |
| 497 | numeric_int_array_var_ambiguity: struct { | ||
| 498 | src: LazyNode, | ||
| 499 | |||
| 500 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 501 | const node = try data.src.get(ctx); | ||
| 502 | try ctx.msgNode( | ||
| 503 | node, | ||
| 504 | "\"{}\" cannot be represented as a runtime value, implicitly casting to \"[]float\"", | ||
| 505 | .{ | ||
| 506 | Type.numeric_literal_array.displayName(ctx.ip()), | ||
| 507 | }, | ||
| 508 | ); | ||
| 509 | try ctx.msgAlone("add \": []float\" or \": []int\" after the variable name to set the type", .{}); | ||
| 510 | } | ||
| 511 | }, | ||
| 497 | }; | 512 | }; |
| 498 | 513 | ||
| 499 | /// A variant of std.builtin.StackTrace that stores its data within itself | 514 | /// A variant of std.builtin.StackTrace that stores its data within itself |
| ... | @@ -1065,6 +1080,11 @@ pub const LazyNode = struct { | ... | @@ -1065,6 +1080,11 @@ pub const LazyNode = struct { |
| 1065 | }, | 1080 | }, |
| 1066 | } | 1081 | } |
| 1067 | }, | 1082 | }, |
| 1083 | .array_init => { | ||
| 1084 | assert(node_tag == .array_init); | ||
| 1085 | const items = data.list.slice(ctx.ast); | ||
| 1086 | return items[lazy.operand.getArgId()]; | ||
| 1087 | }, | ||
| 1068 | else => {}, | 1088 | else => {}, |
| 1069 | } | 1089 | } |
| 1070 | 1090 | ||
| ... | @@ -1318,5 +1338,3 @@ const File = Comp.File; | ... | @@ -1318,5 +1338,3 @@ const File = Comp.File; |
| 1318 | const InternPool = @import("InternPool.zig"); | 1338 | const InternPool = @import("InternPool.zig"); |
| 1319 | const Value = InternPool.Value; | 1339 | const Value = InternPool.Value; |
| 1320 | const Type = InternPool.Type; | 1340 | const Type = InternPool.Type; |
| 1321 | |||
| 1322 | const util = @import("util.zig"); |
src/core/Sema.zig+222-133| ... | @@ -36,9 +36,9 @@ namespace_index: Namespace.Index, | ... | @@ -36,9 +36,9 @@ namespace_index: Namespace.Index, |
| 36 | 36 | ||
| 37 | // temp data: | 37 | // temp data: |
| 38 | inst_map: InstMap = undefined, | 38 | inst_map: InstMap = undefined, |
| 39 | /// scratch memory. can be used in any place you might temporarily allocate an arraylist | 39 | /// Scratch memory for temporary allocations. |
| 40 | /// this is most notably used to store block contents before the block is finalized | 40 | /// Block contents are appended for marks. |
| 41 | scratch: std.ArrayListUnmanaged(u32) = .{}, | 41 | scratch: core.U32Stack = .{}, |
| 42 | 42 | ||
| 43 | /// This is only set for function declarations | 43 | /// This is only set for function declarations |
| 44 | params: []Type = undefined, | 44 | params: []Type = undefined, |
| ... | @@ -134,7 +134,7 @@ pub fn deinit(s: *Sema) void { | ... | @@ -134,7 +134,7 @@ pub fn deinit(s: *Sema) void { |
| 134 | 134 | ||
| 135 | defer s.* = undefined; | 135 | defer s.* = undefined; |
| 136 | s.inst_map.deinit(s.arena); | 136 | s.inst_map.deinit(s.arena); |
| 137 | s.scratch.deinit(s.arena); | 137 | s.scratch.list.deinit(s.arena); |
| 138 | s.air_extra.deinit(s.gpa); | 138 | s.air_extra.deinit(s.gpa); |
| 139 | s.air_instructions.deinit(s.gpa); | 139 | s.air_instructions.deinit(s.gpa); |
| 140 | s.temp_result_map.deinit(s.arena); | 140 | s.temp_result_map.deinit(s.arena); |
| ... | @@ -205,7 +205,7 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value { | ... | @@ -205,7 +205,7 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value { |
| 205 | defer s.gpa.free(s.params); | 205 | defer s.gpa.free(s.params); |
| 206 | 206 | ||
| 207 | for (fn_decl.params.slice(&s.code), 0..) |param, i| { | 207 | for (fn_decl.params.slice(&s.code), 0..) |param, i| { |
| 208 | const src = s.inst_map.start.op(Report.Operand.argument(@intCast(i))); | 208 | const src = s.inst_map.start.op(operandArgument(@intCast(i))); |
| 209 | const ty = try s.resolveType(src, param); | 209 | const ty = try s.resolveType(src, param); |
| 210 | if (!ty.hasRuntimeRepresentation(s.ip)) { | 210 | if (!ty.hasRuntimeRepresentation(s.ip)) { |
| 211 | return s.fail(.param_comptime_only, .{ | 211 | return s.fail(.param_comptime_only, .{ |
| ... | @@ -268,8 +268,8 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -268,8 +268,8 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 268 | const t = tracer.trace(@src()); | 268 | const t = tracer.trace(@src()); |
| 269 | defer t.end(); | 269 | defer t.end(); |
| 270 | 270 | ||
| 271 | const scratch_top = s.scratch.items.len; | 271 | const scratch = s.scratch.mark(); |
| 272 | defer s.scratch.shrinkRetainingCapacity(scratch_top); | 272 | defer scratch.deinit(); |
| 273 | 273 | ||
| 274 | const block = try s.addBlock(); | 274 | const block = try s.addBlock(); |
| 275 | try s.inst_map.add(in, block.block_index.toRef()); | 275 | try s.inst_map.add(in, block.block_index.toRef()); |
| ... | @@ -284,10 +284,14 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -284,10 +284,14 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 284 | }; | 284 | }; |
| 285 | } | 285 | } |
| 286 | 286 | ||
| 287 | if (scratch_top + 1 == s.scratch.items.len) { | 287 | if (scratch.len() == 1) { |
| 288 | const single_instruction = s.air_instructions.items(.tag)[@intFromEnum(block.block_index) + 1]; | 288 | const air_instructions = s.air_instructions.slice(); |
| 289 | const single_inst_id = @intFromEnum(block.block_index) + 1; | ||
| 290 | assert(@intFromEnum(scratch.slice(Air.Inst.Index)[0]) == single_inst_id); | ||
| 291 | |||
| 292 | const single_instruction = air_instructions.items(.tag)[single_inst_id]; | ||
| 289 | if (single_instruction == .@"break") { | 293 | if (single_instruction == .@"break") { |
| 290 | const data: Air.Break = s.air_instructions.items(.data)[@intFromEnum(block.block_index) + 1] | 294 | const data: Air.Break = air_instructions.items(.data)[single_inst_id] |
| 291 | .decode(.@"break", single_instruction); | 295 | .decode(.@"break", single_instruction); |
| 292 | if (data.block_ref == block.block_index) { | 296 | if (data.block_ref == block.block_index) { |
| 293 | // This is a block that resolved to just a single value ref with | 297 | // This is a block that resolved to just a single value ref with |
| ... | @@ -303,7 +307,7 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -303,7 +307,7 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 303 | } | 307 | } |
| 304 | } | 308 | } |
| 305 | 309 | ||
| 306 | return (try block.finish(s, scratch_top)).toRef(); | 310 | return (try block.finish(s, scratch)).toRef(); |
| 307 | } | 311 | } |
| 308 | 312 | ||
| 309 | fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { | 313 | fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| ... | @@ -534,7 +538,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -534,7 +538,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 534 | for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| { | 538 | for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| { |
| 535 | const ref = try s.resolveRef(arg); | 539 | const ref = try s.resolveRef(arg); |
| 536 | const coerced = try s.coerceRef( | 540 | const coerced = try s.coerceRef( |
| 537 | in.op(Report.Operand.argument(@intCast(i))), | 541 | in.op(operandArgument(@intCast(i))), |
| 538 | ref, | 542 | ref, |
| 539 | param, | 543 | param, |
| 540 | ); | 544 | ); |
| ... | @@ -543,7 +547,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -543,7 +547,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 543 | } else { | 547 | } else { |
| 544 | for (call.args.slice(&s.code), 0..) |arg, i| { | 548 | for (call.args.slice(&s.code), 0..) |arg, i| { |
| 545 | const ref = try s.ensureValueRuntime( | 549 | const ref = try s.ensureValueRuntime( |
| 546 | in.op(Report.Operand.argument(@intCast(i))), | 550 | in.op(operandArgument(@intCast(i))), |
| 547 | try s.resolveRef(arg), | 551 | try s.resolveRef(arg), |
| 548 | ); | 552 | ); |
| 549 | scratch[i] = ref; | 553 | scratch[i] = ref; |
| ... | @@ -571,7 +575,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -571,7 +575,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 571 | defer s.arena.free(scratch); | 575 | defer s.arena.free(scratch); |
| 572 | for (call.args.slice(&s.code), 0..) |arg, i| { | 576 | for (call.args.slice(&s.code), 0..) |arg, i| { |
| 573 | const ref = try s.ensureValueRuntime( | 577 | const ref = try s.ensureValueRuntime( |
| 574 | in.op(Report.Operand.argument(@intCast(i))), | 578 | in.op(operandArgument(@intCast(i))), |
| 575 | try s.resolveRef(arg), | 579 | try s.resolveRef(arg), |
| 576 | ); | 580 | ); |
| 577 | scratch[i] = ref; | 581 | scratch[i] = ref; |
| ... | @@ -635,7 +639,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -635,7 +639,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 635 | const block = try s.analyzeBlock(in); | 639 | const block = try s.analyzeBlock(in); |
| 636 | switch (block.decode()) { | 640 | switch (block.decode()) { |
| 637 | .index => |t| if (s.tempAir().instTag(t) == .block) { | 641 | .index => |t| if (s.tempAir().instTag(t) == .block) { |
| 638 | try s.scratch.append(s.gpa, @intFromEnum(t)); | 642 | try s.scratch.add(s.gpa, Air.Inst.Index, t); |
| 639 | }, | 643 | }, |
| 640 | else => {}, | 644 | else => {}, |
| 641 | } | 645 | } |
| ... | @@ -692,7 +696,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -692,7 +696,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 692 | .compile_error => { | 696 | .compile_error => { |
| 693 | const node: Teg.Inst.RefNode = s.code.instData(.compile_error, in); | 697 | const node: Teg.Inst.RefNode = s.code.instData(.compile_error, in); |
| 694 | const value = try s.resolveCoerceValue( | 698 | const value = try s.resolveCoerceValue( |
| 695 | in.op(Report.Operand.argument(0)), | 699 | in.op(operandArgument(0)), |
| 696 | node.operand, | 700 | node.operand, |
| 697 | .string, | 701 | .string, |
| 698 | ); | 702 | ); |
| ... | @@ -705,42 +709,26 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -705,42 +709,26 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 705 | .struct_decl => { | 709 | .struct_decl => { |
| 706 | const node: Teg.Inst.StructDecl = s.code.instPayload(.struct_decl, in); | 710 | const node: Teg.Inst.StructDecl = s.code.instPayload(.struct_decl, in); |
| 707 | 711 | ||
| 708 | const scratch_top = s.scratch.items.len; | 712 | const scratch = try s.scratch.reserveMulti( |
| 709 | try s.scratch.ensureUnusedCapacity(s.arena, node.field_len * 2); | 713 | s.arena, |
| 710 | s.scratch.items.len += node.field_len * 2; | 714 | .{ .keys = NullTerminatedString, .types = Type }, |
| 711 | defer s.scratch.shrinkRetainingCapacity(scratch_top); | 715 | .{ node.field_len, node.field_len }, |
| 712 | 716 | ); | |
| 713 | for ( | 717 | defer scratch.deinit(); |
| 714 | node.keys(&s.code), | ||
| 715 | // Keeping the slice pointer is fine for this loop, since scratch is not resized | ||
| 716 | s.scratch.items[scratch_top..][0..node.field_len], | ||
| 717 | ) |key, *dest| { | ||
| 718 | dest.* = @intFromEnum(try s.ip.getOrPutString( | ||
| 719 | s.gpa, | ||
| 720 | key.slice(s.code.string_bytes), | ||
| 721 | .no_embedded_nulls, | ||
| 722 | )); | ||
| 723 | } | ||
| 724 | 718 | ||
| 725 | for ( | 719 | // Fill the first half with keys |
| 726 | node.types(&s.code), | 720 | for (scratch.slice(.keys), node.keys(&s.code)) |*dest, src| { |
| 727 | // We cannot reuse the pointer from scratch between calls to sema | 721 | dest.* = try s.ip.getOrPutString(s.gpa, src.slice(s.code.string_bytes), .no_embedded_nulls); |
| 728 | scratch_top + node.field_len.., | ||
| 729 | 0.., | ||
| 730 | ) |ty, i, arg_i| { | ||
| 731 | s.scratch.items[i] = @intFromEnum(try s.resolveCoerceValue( | ||
| 732 | in.op(Report.Operand.argument(@intCast(arg_i))), | ||
| 733 | ty, | ||
| 734 | .type, | ||
| 735 | )); | ||
| 736 | } | 722 | } |
| 737 | 723 | ||
| 738 | const keys = s.scratch.items[scratch_top..][0..node.field_len]; | 724 | // Fill the second half with coerced values. |
| 739 | const types = s.scratch.items[scratch_top + node.field_len ..][0..node.field_len]; | 725 | for (node.types(&s.code), 0..) |ty, i| { |
| 726 | scratch.at(.types, i).* = try s.resolveType(in.op(operandArgument(@intCast(i))), ty); | ||
| 727 | } | ||
| 740 | 728 | ||
| 741 | return try s.makeValueAir(.{ .struct_type = .{ | 729 | return try s.makeValueAir(.{ .struct_type = .{ |
| 742 | .field_keys = @ptrCast(keys), | 730 | .field_keys = scratch.slice(.keys), |
| 743 | .field_types = @ptrCast(types), | 731 | .field_types = scratch.slice(.types), |
| 744 | } }); | 732 | } }); |
| 745 | }, | 733 | }, |
| 746 | .struct_init => { | 734 | .struct_init => { |
| ... | @@ -760,22 +748,13 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -760,22 +748,13 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 760 | var is_constant = true; | 748 | var is_constant = true; |
| 761 | 749 | ||
| 762 | // Allocate scratch space to store the values as they are being prepared. | 750 | // Allocate scratch space to store the values as they are being prepared. |
| 763 | // TODO: make this an abstraction | 751 | const scratch = try s.scratch.reserve( |
| 764 | const scratch_top = s.scratch.items.len; | 752 | s.arena, |
| 765 | 753 | Air.Inst.Ref.Optional, | |
| 766 | try s.scratch.ensureUnusedCapacity(s.arena, struct_type.field_types.len); | 754 | struct_type.field_types.len, |
| 767 | const storage: []Air.Inst.Ref.Optional = | 755 | ); |
| 768 | @ptrCast(s.scratch.unusedCapacitySlice()[0..struct_type.field_types.len]); | 756 | defer scratch.deinit(); |
| 769 | s.scratch.items.len += storage.len; | 757 | @memset(scratch.slice(), .none); |
| 770 | @memset(storage, .none); | ||
| 771 | |||
| 772 | const scratch_end = s.scratch.items.len; | ||
| 773 | defer { | ||
| 774 | const from = s.scratch.items[scratch_end..]; | ||
| 775 | const dest = s.scratch.items[scratch_top..][0..from.len]; | ||
| 776 | std.mem.copyForwards(u32, dest, from); | ||
| 777 | s.scratch.shrinkRetainingCapacity(scratch_top + from.len); | ||
| 778 | } | ||
| 779 | 758 | ||
| 780 | for (node.keys(&s.code), node.inits(&s.code), 0..) |init_key, init_value, i| { | 759 | for (node.keys(&s.code), node.inits(&s.code), 0..) |init_key, init_value, i| { |
| 781 | // The order of `storage` is as ordered by the type, but this | 760 | // The order of `storage` is as ordered by the type, but this |
| ... | @@ -789,21 +768,20 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -789,21 +768,20 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 789 | has_errors = true; | 768 | has_errors = true; |
| 790 | try s.addError(.struct_init_unknown_field, .{ | 769 | try s.addError(.struct_init_unknown_field, .{ |
| 791 | .ty = ty, | 770 | .ty = ty, |
| 792 | .src = in.op(Report.Operand.argument(@intCast(i))).lazyNode(s.decl_index), | 771 | .src = in.op(operandArgument(@intCast(i))).lazyNode(s.decl_index), |
| 793 | }); | 772 | }); |
| 794 | continue; | 773 | continue; |
| 795 | }; | 774 | }; |
| 796 | if (storage[found_index] != .none) { | 775 | if (scratch.at(found_index).* != .none) { |
| 797 | has_errors = true; | 776 | has_errors = true; |
| 798 | try s.addError(.struct_init_duplicate_field, .{ | 777 | try s.addError(.struct_init_duplicate_field, .{ |
| 799 | .ty = ty, | 778 | .ty = ty, |
| 800 | .src = in.op(Report.Operand.argument(@intCast(i))).lazyNode(s.decl_index), | 779 | .src = in.op(operandArgument(@intCast(i))).lazyNode(s.decl_index), |
| 801 | }); | 780 | }); |
| 802 | continue; | 781 | continue; |
| 803 | } | 782 | } |
| 804 | // TODO: calling coerceRef / resolveRefRuntime will clobber the `storage` pointer | ||
| 805 | const coerced = s.resolveCoerceRef( | 783 | const coerced = s.resolveCoerceRef( |
| 806 | in.op(Report.Operand.argument(@intCast(i))), | 784 | in.op(operandArgument(@intCast(i))), |
| 807 | init_value, | 785 | init_value, |
| 808 | struct_type.field_types[found_index], | 786 | struct_type.field_types[found_index], |
| 809 | ) catch |e| switch (e) { | 787 | ) catch |e| switch (e) { |
| ... | @@ -811,17 +789,18 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -811,17 +789,18 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 811 | // Instead of bailing out with `try`, we will continue the loop. | 789 | // Instead of bailing out with `try`, we will continue the loop. |
| 812 | has_errors = true; | 790 | has_errors = true; |
| 813 | // Mark something non-null so that duplicate field errors are emitted. | 791 | // Mark something non-null so that duplicate field errors are emitted. |
| 814 | storage[found_index] = comptime Value.void.toAir().toOptional(); | 792 | scratch.at(found_index).* = comptime Value.void.toAir().toOptional(); |
| 815 | continue; | 793 | continue; |
| 816 | }, | 794 | }, |
| 817 | else => return e, | 795 | else => return e, |
| 818 | }; | 796 | }; |
| 819 | if (coerced.kind == .index) is_constant = false; | 797 | if (coerced.kind == .index) is_constant = false; |
| 820 | storage[found_index] = coerced.toOptional(); | 798 | scratch.at(found_index).* = coerced.toOptional(); |
| 821 | } | 799 | } |
| 822 | 800 | ||
| 823 | // Check for missing fields | 801 | // Check for missing fields |
| 824 | { | 802 | { |
| 803 | const storage = scratch.slice(); | ||
| 825 | var i: usize = 0; | 804 | var i: usize = 0; |
| 826 | while (mem.indexOfScalarPos(Air.Inst.Ref.Optional, storage, i, .none)) |index| { | 805 | while (mem.indexOfScalarPos(Air.Inst.Ref.Optional, storage, i, .none)) |index| { |
| 827 | try s.addError(.struct_init_missing_field, .{ | 806 | try s.addError(.struct_init_missing_field, .{ |
| ... | @@ -839,8 +818,8 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -839,8 +818,8 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 839 | 818 | ||
| 840 | if (is_constant) { | 819 | if (is_constant) { |
| 841 | // Convert all items in storage into a Value type | 820 | // Convert all items in storage into a Value type |
| 842 | const result_storage: []Value = @ptrCast(storage); | 821 | const result_storage = scratch.sliceReinterpret(Value); |
| 843 | for (storage, result_storage) |ref, *value| { | 822 | for (scratch.slice(), scratch.sliceReinterpret(Value)) |ref, *value| { |
| 844 | value.* = ref.unwrap().?.decode().value; | 823 | value.* = ref.unwrap().?.decode().value; |
| 845 | } | 824 | } |
| 846 | return try s.makeValueAir(.{ .struct_value = .{ | 825 | return try s.makeValueAir(.{ .struct_value = .{ |
| ... | @@ -848,16 +827,80 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { | ... | @@ -848,16 +827,80 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 848 | .field_values = result_storage, | 827 | .field_values = result_storage, |
| 849 | } }); | 828 | } }); |
| 850 | } else { | 829 | } else { |
| 851 | // TODO: make this less error prone | 830 | // Create a runtime struct instantiation |
| 852 | const result_storage: []u32 = @ptrCast(storage); | 831 | const value_index = try s.air_extra.appendSlice(s.gpa, scratch.slice()); |
| 853 | const value_index = try s.air_extra.alloc(s.gpa, result_storage.len); | ||
| 854 | @memcpy(s.air_extra.list.items[value_index..].ptr, result_storage); | ||
| 855 | return try s.addInstRef(.struct_init, .{ | 832 | return try s.addInstRef(.struct_init, .{ |
| 856 | .ty = ty, | 833 | .ty = ty, |
| 857 | .field_values = value_index, | 834 | .field_values = value_index, |
| 858 | }); | 835 | }); |
| 859 | } | 836 | } |
| 860 | }, | 837 | }, |
| 838 | |||
| 839 | .array_init => { | ||
| 840 | const node: Teg.Inst.ArrayInit = s.code.instPayload(.array_init, in); | ||
| 841 | |||
| 842 | assert(node.list.len > 0); | ||
| 843 | |||
| 844 | const scratch = try s.scratch.reserve(s.arena, Air.Inst.Ref, node.list.len); | ||
| 845 | defer scratch.deinit(); | ||
| 846 | |||
| 847 | var item_type: ?Type = if (node.type_hint.unwrap()) |ty| | ||
| 848 | try s.resolveType(in.op(.operand), ty) | ||
| 849 | else | ||
| 850 | null; | ||
| 851 | |||
| 852 | var is_constant = true; | ||
| 853 | |||
| 854 | for (node.list.slice(&s.code), 0..) |item_teg_ref, i| { | ||
| 855 | const item = try s.resolveRef(item_teg_ref); | ||
| 856 | const coerced = if (item_type) |ty| | ||
| 857 | try s.coerceRef( | ||
| 858 | in.op(operandArgument(@intCast(i))), | ||
| 859 | item, | ||
| 860 | ty, | ||
| 861 | ) | ||
| 862 | else brk: { | ||
| 863 | item_type = s.typeOfRef(item); | ||
| 864 | break :brk item; | ||
| 865 | }; | ||
| 866 | |||
| 867 | if (coerced.kind == .index) is_constant = false; | ||
| 868 | |||
| 869 | scratch.at(i).* = coerced; | ||
| 870 | } | ||
| 871 | |||
| 872 | const ty = try s.makeType(.{ .array_type = .{ | ||
| 873 | .item_type = item_type orelse unreachable, | ||
| 874 | } }); | ||
| 875 | |||
| 876 | if (is_constant) { | ||
| 877 | // Convert all items in storage into a Value type | ||
| 878 | const result_storage = scratch.sliceReinterpret(Value); | ||
| 879 | for (scratch.slice(), scratch.sliceReinterpret(Value)) |ref, *value| { | ||
| 880 | value.* = ref.decode().value; | ||
| 881 | } | ||
| 882 | return try s.makeValueAir(.{ .array_value = .{ | ||
| 883 | .type = ty, | ||
| 884 | .items = result_storage, | ||
| 885 | } }); | ||
| 886 | } else { | ||
| 887 | // Create a runtime array instantiation | ||
| 888 | return try s.addInstRef(.array_init, .{ | ||
| 889 | .ty = ty, | ||
| 890 | .list = .{ | ||
| 891 | .start = try s.air_extra.appendSlice(s.gpa, scratch.slice()), | ||
| 892 | .len = node.list.len, | ||
| 893 | }, | ||
| 894 | }); | ||
| 895 | } | ||
| 896 | }, | ||
| 897 | |||
| 898 | .array_type => { | ||
| 899 | const un: Teg.Inst.RefNode = s.code.instData(.array_type, in); | ||
| 900 | const operand = try s.resolveType(in.op(.operand), un.operand); | ||
| 901 | return s.makeValueAir(.{ .array_type = .{ .item_type = operand } }); | ||
| 902 | }, | ||
| 903 | |||
| 861 | else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}), | 904 | else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}), |
| 862 | } | 905 | } |
| 863 | } | 906 | } |
| ... | @@ -871,15 +914,15 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) Error!Air.Inst.Ref { | ... | @@ -871,15 +914,15 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) Error!Air.Inst.Ref { |
| 871 | const t = tracer.trace(@src()); | 914 | const t = tracer.trace(@src()); |
| 872 | defer t.end(); | 915 | defer t.end(); |
| 873 | 916 | ||
| 874 | const scratch_top = s.scratch.items.len; | 917 | const scratch = s.scratch.mark(); |
| 875 | defer s.scratch.shrinkRetainingCapacity(scratch_top); | 918 | defer scratch.deinit(); |
| 876 | 919 | ||
| 877 | const visited = s.resolveRef(in) catch |err| switch (err) { | 920 | const visited = s.resolveRef(in) catch |err| switch (err) { |
| 878 | error.HitNoReturn => Value.void.toAir(), | 921 | error.HitNoReturn => Value.void.toAir(), |
| 879 | else => return err, | 922 | else => return err, |
| 880 | }; | 923 | }; |
| 881 | 924 | ||
| 882 | if (scratch_top == s.scratch.items.len and visited.kind == .value) { | 925 | if (scratch.len() == 0 and visited.kind == .value) { |
| 883 | return visited; | 926 | return visited; |
| 884 | } | 927 | } |
| 885 | 928 | ||
| ... | @@ -894,25 +937,27 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) Error!Air.Inst.Ref { | ... | @@ -894,25 +937,27 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) Error!Air.Inst.Ref { |
| 894 | .block_ref = block.block_index, | 937 | .block_ref = block.block_index, |
| 895 | .value = visited, | 938 | .value = visited, |
| 896 | }); | 939 | }); |
| 897 | return (try block.finish(s, scratch_top)).toRef(); | 940 | return (try block.finish(s, scratch)).toRef(); |
| 898 | } | 941 | } |
| 899 | 942 | ||
| 900 | fn resolveRefExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) Error!Air.Inst.Ref { | 943 | fn resolveRefExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) Error!Air.Inst.Ref { |
| 901 | const index = ref.toIndex() orelse { | 944 | return switch (ref.decode()) { |
| 902 | return ref.toValue().?.toAir(); | 945 | .value => |value| value.toAir(), |
| 903 | }; | 946 | .index => |index| { |
| 947 | if (s.inst_map.get(index)) |air| { | ||
| 948 | if (mark) { | ||
| 949 | switch (air.decode()) { | ||
| 950 | .index => |i| s.markInst(i), | ||
| 951 | else => {}, | ||
| 952 | } | ||
| 953 | } | ||
| 904 | 954 | ||
| 905 | if (s.inst_map.get(index)) |air| { | 955 | return air; |
| 906 | if (mark) { | ||
| 907 | switch (air.decode()) { | ||
| 908 | .index => |i| s.markInst(i), | ||
| 909 | else => {}, | ||
| 910 | } | 956 | } |
| 911 | } | ||
| 912 | return air; | ||
| 913 | } | ||
| 914 | 957 | ||
| 915 | return s.analyzeCode(index); | 958 | return s.analyzeCode(index); |
| 959 | }, | ||
| 960 | }; | ||
| 916 | } | 961 | } |
| 917 | 962 | ||
| 918 | fn resolveRef(s: *Sema, ref: Teg.Inst.Ref) Error!Air.Inst.Ref { | 963 | fn resolveRef(s: *Sema, ref: Teg.Inst.Ref) Error!Air.Inst.Ref { |
| ... | @@ -997,21 +1042,21 @@ fn resolveType(s: *Sema, src: Teg.InstOp, ref: Teg.Inst.Ref) !Type { | ... | @@ -997,21 +1042,21 @@ fn resolveType(s: *Sema, src: Teg.InstOp, ref: Teg.Inst.Ref) !Type { |
| 997 | } | 1042 | } |
| 998 | 1043 | ||
| 999 | /// Coerce `value` runtime ref to `coerce_type`, and emit an error at `src`'s location | 1044 | /// Coerce `value` runtime ref to `coerce_type`, and emit an error at `src`'s location |
| 1000 | fn coerceRef(s: *Sema, src: InstOp, value: Air.Inst.Ref, to: Type) !Air.Inst.Ref { | 1045 | fn coerceRef(s: *Sema, src: InstOp, ref: Air.Inst.Ref, to: Type) !Air.Inst.Ref { |
| 1001 | const t = tracer.traceFmt(@src(), "{} to {}", .{ value.dump(s.ip), to.dump(s.ip) }); | 1046 | const t = tracer.traceFmt(@src(), "{} to {}", .{ ref.dump(s.ip), to.dump(s.ip) }); |
| 1002 | defer t.end(); | 1047 | defer t.end(); |
| 1003 | 1048 | ||
| 1004 | const from = s.typeOfRef(value); | 1049 | const from = s.typeOfRef(ref); |
| 1005 | if (from == to) { | 1050 | if (from == to) { |
| 1006 | return value; | 1051 | return ref; |
| 1007 | } | 1052 | } |
| 1008 | if (from == .noreturn) { | 1053 | if (from == .noreturn) { |
| 1009 | return error.HitNoReturn; | 1054 | return error.HitNoReturn; |
| 1010 | } | 1055 | } |
| 1011 | if (from == .@"opaque") { | 1056 | if (from == .@"opaque") { |
| 1012 | return value; | 1057 | return ref; |
| 1013 | } | 1058 | } |
| 1014 | const index = switch (value.decode()) { | 1059 | const index = switch (ref.decode()) { |
| 1015 | .value => |v| return (try s.coerceValue(src, v, to)).toAir(), | 1060 | .value => |v| return (try s.coerceValue(src, v, to)).toAir(), |
| 1016 | .index => |index| index, | 1061 | .index => |index| index, |
| 1017 | }; | 1062 | }; |
| ... | @@ -1053,7 +1098,7 @@ fn coerceRef(s: *Sema, src: InstOp, value: Air.Inst.Ref, to: Type) !Air.Inst.Ref | ... | @@ -1053,7 +1098,7 @@ fn coerceRef(s: *Sema, src: InstOp, value: Air.Inst.Ref, to: Type) !Air.Inst.Ref |
| 1053 | } | 1098 | } |
| 1054 | 1099 | ||
| 1055 | /// Same as `coerceRef` but on a compile-time known value | 1100 | /// Same as `coerceRef` but on a compile-time known value |
| 1056 | fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value { | 1101 | fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) Error!Value { |
| 1057 | const from = value.typeOf(s.ip); | 1102 | const from = value.typeOf(s.ip); |
| 1058 | if (from == to) { | 1103 | if (from == to) { |
| 1059 | return value; | 1104 | return value; |
| ... | @@ -1069,11 +1114,7 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value { | ... | @@ -1069,11 +1114,7 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value { |
| 1069 | .int32 => unreachable, // same type handled | 1114 | .int32 => unreachable, // same type handled |
| 1070 | .numeric_literal, .float => switch (value.toKey(s.ip)) { | 1115 | .numeric_literal, .float => switch (value.toKey(s.ip)) { |
| 1071 | .comptime_float, .float => |val| { | 1116 | .comptime_float, .float => |val| { |
| 1072 | if (!math.isFinite(val) or | 1117 | if (!core.inInt32Range(val)) { |
| 1073 | val < math.minInt(i32) or | ||
| 1074 | val > math.maxInt(i32) or | ||
| 1075 | (@rem(val, 1) != 0)) | ||
| 1076 | { | ||
| 1077 | return s.fail(.comptime_float_to_int, .{ | 1118 | return s.fail(.comptime_float_to_int, .{ |
| 1078 | .val = val, | 1119 | .val = val, |
| 1079 | .src = s.lazyNode(src), | 1120 | .src = s.lazyNode(src), |
| ... | @@ -1115,24 +1156,49 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value { | ... | @@ -1115,24 +1156,49 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value { |
| 1115 | // non-types do not coerce to types | 1156 | // non-types do not coerce to types |
| 1116 | }, | 1157 | }, |
| 1117 | .void => if (value == .void) return .void, | 1158 | .void => if (value == .void) return .void, |
| 1118 | else => {}, | 1159 | else => { |
| 1160 | if (to.toArrayType(s.ip)) |to_array_type| { | ||
| 1161 | if (value.valueTag(s.ip) == .array_value) { | ||
| 1162 | return s.coerceArrayValue(src, value, to_array_type.item_type); | ||
| 1163 | } | ||
| 1164 | } | ||
| 1165 | }, | ||
| 1119 | } | 1166 | } |
| 1120 | return s.coercionError(src, from, to); | 1167 | return s.coercionError(src, from, to); |
| 1121 | } | 1168 | } |
| 1122 | 1169 | ||
| 1123 | fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref { | 1170 | fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) Error!Air.Inst.Ref { |
| 1124 | switch (ref.decode()) { | 1171 | switch (ref.decode()) { |
| 1125 | .value => |v| { | 1172 | .value => |v| { |
| 1126 | const ty = v.typeOf(s.ip); | 1173 | const ty = v.typeOf(s.ip); |
| 1127 | switch (ty) { | 1174 | switch (ty) { |
| 1128 | .numeric_literal => { | 1175 | .numeric_literal => { |
| 1129 | try s.report.warn(.numeric_int_var_ambiguity, .{ | 1176 | const float = v.toFloat(s.ip) orelse unreachable; |
| 1130 | .src = s.lazyNode(src), | 1177 | |
| 1131 | }); | 1178 | if (!core.inInt32Range(float)) { |
| 1179 | try s.report.warn(.numeric_int_var_ambiguity, .{ | ||
| 1180 | .src = s.lazyNode(src), | ||
| 1181 | }); | ||
| 1182 | } | ||
| 1183 | |||
| 1132 | return (try s.makeValue(.{ | 1184 | return (try s.makeValue(.{ |
| 1133 | .float = v.toFloat(s.ip) orelse unreachable, | 1185 | .float = float, |
| 1134 | })).toAir(); | 1186 | })).toAir(); |
| 1135 | }, | 1187 | }, |
| 1188 | .numeric_literal_array => { | ||
| 1189 | try s.report.warn(.numeric_int_array_var_ambiguity, .{ | ||
| 1190 | .src = s.lazyNode(src), | ||
| 1191 | }); | ||
| 1192 | return (try s.coerceArrayValue(src, v, .float)).toAir(); | ||
| 1193 | }, | ||
| 1194 | .noreturn_array => { | ||
| 1195 | if (v.valueData(s.ip, .array_value).get(s.ip).items_len != 0) { | ||
| 1196 | return s.fail(.runtime_incompatible_type, .{ | ||
| 1197 | .src = s.lazyNode(src), | ||
| 1198 | .type = ty, | ||
| 1199 | }); | ||
| 1200 | } | ||
| 1201 | }, | ||
| 1136 | .type, | 1202 | .type, |
| 1137 | .noreturn, | 1203 | .noreturn, |
| 1138 | => return s.fail(.runtime_incompatible_type, .{ | 1204 | => return s.fail(.runtime_incompatible_type, .{ |
| ... | @@ -1147,12 +1213,11 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref { | ... | @@ -1147,12 +1213,11 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref { |
| 1147 | .string, | 1213 | .string, |
| 1148 | .void, | 1214 | .void, |
| 1149 | => {}, | 1215 | => {}, |
| 1150 | _ => switch (ty.tag(s.ip)) { | 1216 | _ => if (!ty.hasRuntimeRepresentation(s.ip)) { |
| 1151 | .simple_type => unreachable, // handled above | 1217 | return s.fail(.runtime_incompatible_type, .{ |
| 1152 | 1218 | .src = s.lazyNode(src), | |
| 1153 | // Allowed at runtime | 1219 | .type = ty, |
| 1154 | .function_type => {}, | 1220 | }); |
| 1155 | .struct_type => {}, | ||
| 1156 | }, | 1221 | }, |
| 1157 | } | 1222 | } |
| 1158 | }, | 1223 | }, |
| ... | @@ -1161,6 +1226,24 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref { | ... | @@ -1161,6 +1226,24 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref { |
| 1161 | return ref; | 1226 | return ref; |
| 1162 | } | 1227 | } |
| 1163 | 1228 | ||
| 1229 | fn coerceArrayValue(s: *Sema, src: InstOp, value: Value, item_type: Type) !Value { | ||
| 1230 | const array: InternPool.Tag.ArrayValue, var trail = value.valueData(s.ip, .array_value).getTrail(s.ip); | ||
| 1231 | |||
| 1232 | const scratch = try s.scratch.reserve(s.arena, Value, array.items_len); | ||
| 1233 | defer scratch.deinit(); | ||
| 1234 | |||
| 1235 | const from = trail.readValueSlice(array.items_len); | ||
| 1236 | |||
| 1237 | for (0..from.len) |i| { | ||
| 1238 | scratch.at(i).* = try s.coerceValue(src, from.get(s.ip)[i], item_type); | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | return try s.makeValue(.{ .array_value = .{ | ||
| 1242 | .type = try s.makeType(.{ .array_type = .{ .item_type = item_type } }), | ||
| 1243 | .items = scratch.slice(), | ||
| 1244 | } }); | ||
| 1245 | } | ||
| 1246 | |||
| 1164 | fn resolveDecl(s: *Sema, src: InstOp, decl_index: Decl.Index) Error!Value { | 1247 | fn resolveDecl(s: *Sema, src: InstOp, decl_index: Decl.Index) Error!Value { |
| 1165 | const decl = s.ip.declPtr(decl_index); | 1248 | const decl = s.ip.declPtr(decl_index); |
| 1166 | switch (decl.analysis) { | 1249 | switch (decl.analysis) { |
| ... | @@ -1382,8 +1465,8 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { | ... | @@ -1382,8 +1465,8 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { |
| 1382 | const IncompleteBlock = struct { | 1465 | const IncompleteBlock = struct { |
| 1383 | block_index: Air.Inst.Index, | 1466 | block_index: Air.Inst.Index, |
| 1384 | 1467 | ||
| 1385 | pub fn finish(block: IncompleteBlock, s: *Sema, scratch_top: usize) !Air.Inst.Index { | 1468 | pub fn finish(block: IncompleteBlock, s: *Sema, scratch_mark: core.U32Stack.Mark) !Air.Inst.Index { |
| 1386 | s.setInstData(.block, block.block_index, try s.scratchToIndexList(scratch_top)); | 1469 | s.setInstData(.block, block.block_index, try s.scratchMarkToIndexList(scratch_mark)); |
| 1387 | return block.block_index; | 1470 | return block.block_index; |
| 1388 | } | 1471 | } |
| 1389 | }; | 1472 | }; |
| ... | @@ -1404,7 +1487,7 @@ fn addInstRef(s: *Sema, comptime tag: Air.Inst.Tag, data: tag.Data()) !Air.Inst. | ... | @@ -1404,7 +1487,7 @@ fn addInstRef(s: *Sema, comptime tag: Air.Inst.Tag, data: tag.Data()) !Air.Inst. |
| 1404 | .flags = .{}, | 1487 | .flags = .{}, |
| 1405 | }); | 1488 | }); |
| 1406 | const index: Air.Inst.Index = @enumFromInt(s.air_instructions.len - 1); | 1489 | const index: Air.Inst.Index = @enumFromInt(s.air_instructions.len - 1); |
| 1407 | try s.scratch.append(s.arena, @intFromEnum(index)); | 1490 | try s.scratch.add(s.arena, Air.Inst.Index, index); |
| 1408 | return index.toRef(); | 1491 | return index.toRef(); |
| 1409 | } | 1492 | } |
| 1410 | 1493 | ||
| ... | @@ -1423,27 +1506,30 @@ fn makeValue(s: *Sema, key: InternPool.Key) !Value { | ... | @@ -1423,27 +1506,30 @@ fn makeValue(s: *Sema, key: InternPool.Key) !Value { |
| 1423 | return try s.ip.get(s.gpa, key); | 1506 | return try s.ip.get(s.gpa, key); |
| 1424 | } | 1507 | } |
| 1425 | 1508 | ||
| 1509 | fn makeType(s: *Sema, key: InternPool.Key) !Type { | ||
| 1510 | return (try s.makeValue(key)).toTypeUnsafe(s.ip); | ||
| 1511 | } | ||
| 1512 | |||
| 1426 | fn makeValueAir(s: *Sema, key: InternPool.Key) !Air.Inst.Ref { | 1513 | fn makeValueAir(s: *Sema, key: InternPool.Key) !Air.Inst.Ref { |
| 1427 | return (try s.makeValue(key)).toAir(); | 1514 | return (try s.makeValue(key)).toAir(); |
| 1428 | } | 1515 | } |
| 1429 | 1516 | ||
| 1430 | fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref { | 1517 | fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref { |
| 1431 | if (inst.toValue()) |interned| { | 1518 | return switch (inst.decode()) { |
| 1432 | return interned.toAir(); | 1519 | .value => |value| value.toAir(), |
| 1433 | } | 1520 | .index => |index| s.inst_map.get(index) orelse { |
| 1434 | const index = inst.toIndex().?; | 1521 | if (std.debug.runtime_safety) { |
| 1435 | return s.inst_map.get(index) orelse { | 1522 | std.debug.panic("Unmapped operand %{d}", .{@intFromEnum(index)}); |
| 1436 | if (std.debug.runtime_safety) { | 1523 | } |
| 1437 | std.debug.panic("Unmapped operand %{d}", .{@intFromEnum(index)}); | 1524 | unreachable; |
| 1438 | } | 1525 | }, |
| 1439 | unreachable; | ||
| 1440 | }; | 1526 | }; |
| 1441 | } | 1527 | } |
| 1442 | 1528 | ||
| 1443 | fn scratchToIndexList(s: *Sema, scratch_top: usize) !Air.IndexList { | 1529 | fn scratchMarkToIndexList(s: *Sema, mark: core.U32Stack.Mark) !Air.IndexList { |
| 1444 | const items = s.scratch.items[scratch_top..]; | 1530 | const items = mark.slice(u32); |
| 1445 | const extra_top = s.air_extra.list.items.len; | 1531 | const extra_top = s.air_extra.list.items.len; |
| 1446 | try s.air_extra.list.appendSlice(s.gpa, s.scratch.items[scratch_top..]); | 1532 | try s.air_extra.list.appendSlice(s.gpa, items); |
| 1447 | return .{ | 1533 | return .{ |
| 1448 | .start = @intCast(extra_top), | 1534 | .start = @intCast(extra_top), |
| 1449 | .len = @intCast(items.len), | 1535 | .len = @intCast(items.len), |
| ... | @@ -1524,12 +1610,15 @@ const Teg = core.Teg; | ... | @@ -1524,12 +1610,15 @@ const Teg = core.Teg; |
| 1524 | const InstOp = Teg.InstOp; | 1610 | const InstOp = Teg.InstOp; |
| 1525 | 1611 | ||
| 1526 | const Air = core.Air; | 1612 | const Air = core.Air; |
| 1527 | const Report = core.Report; | ||
| 1528 | const Ast = core.Ast; | 1613 | const Ast = core.Ast; |
| 1529 | const store = core.array; | 1614 | const store = core.array; |
| 1530 | const tracer = @import("tracer"); | 1615 | |
| 1616 | const Report = core.Report; | ||
| 1617 | const operandArgument = Report.Operand.argument; | ||
| 1531 | 1618 | ||
| 1532 | const InternPool = core.InternPool; | 1619 | const InternPool = core.InternPool; |
| 1533 | const Value = InternPool.Value; | 1620 | const Value = InternPool.Value; |
| 1534 | const Type = InternPool.Type; | 1621 | const Type = InternPool.Type; |
| 1535 | const NullTerminatedString = InternPool.NullTerminatedString; | 1622 | const NullTerminatedString = InternPool.NullTerminatedString; |
| 1623 | |||
| 1624 | const tracer = @import("tracer"); |
src/core/Teg.Dump.zig+50-17| ... | @@ -101,6 +101,7 @@ const State = struct { | ... | @@ -101,6 +101,7 @@ const State = struct { |
| 101 | try tty.setColor(writer, .reset); | 101 | try tty.setColor(writer, .reset); |
| 102 | }, | 102 | }, |
| 103 | inline .field, | 103 | inline .field, |
| 104 | .index, | ||
| 104 | .add, | 105 | .add, |
| 105 | .sub, | 106 | .sub, |
| 106 | .mul, | 107 | .mul, |
| ... | @@ -172,6 +173,7 @@ const State = struct { | ... | @@ -172,6 +173,7 @@ const State = struct { |
| 172 | .fetch, | 173 | .fetch, |
| 173 | .compile_error, | 174 | .compile_error, |
| 174 | .type_of, | 175 | .type_of, |
| 176 | .array_type, | ||
| 175 | => |comptime_tag| { | 177 | => |comptime_tag| { |
| 176 | const data = teg.instData(comptime_tag, inst); | 178 | const data = teg.instData(comptime_tag, inst); |
| 177 | try writer.writeAll("("); | 179 | try writer.writeAll("("); |
| ... | @@ -255,28 +257,59 @@ const State = struct { | ... | @@ -255,28 +257,59 @@ const State = struct { |
| 255 | try s.newline(writer); | 257 | try s.newline(writer); |
| 256 | try writer.writeAll("}"); | 258 | try writer.writeAll("}"); |
| 257 | }, | 259 | }, |
| 260 | .array_init => { | ||
| 261 | const data: Teg.Inst.ArrayInit = teg.instPayload(.array_init, inst); | ||
| 262 | |||
| 263 | if (data.type_hint.unwrap()) |type_hint| { | ||
| 264 | try writer.writeAll("("); | ||
| 265 | try s.dumpRef(writer, type_hint); | ||
| 266 | try writer.writeAll(", "); | ||
| 267 | } | ||
| 268 | |||
| 269 | try writer.writeAll("["); | ||
| 270 | |||
| 271 | s.indent += 1; | ||
| 272 | |||
| 273 | for (data.list.slice(teg)) |item| { | ||
| 274 | try s.newline(writer); | ||
| 275 | try s.dumpRef(writer, item); | ||
| 276 | try writer.writeAll(","); | ||
| 277 | } | ||
| 278 | |||
| 279 | s.indent -= 1; | ||
| 280 | try s.newline(writer); | ||
| 281 | |||
| 282 | try writer.writeAll("]"); | ||
| 283 | |||
| 284 | if (data.type_hint != .none) | ||
| 285 | try writer.writeAll(")"); | ||
| 286 | }, | ||
| 258 | } | 287 | } |
| 259 | } | 288 | } |
| 260 | 289 | ||
| 261 | fn dumpRef(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void { | 290 | fn dumpRef(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void { |
| 262 | if (ref.toIndex()) |index| { | 291 | switch (ref.decode()) { |
| 263 | try s.dumpInst(writer, index); | 292 | .index => |index| try s.dumpInst(writer, index), |
| 264 | } else { | 293 | .value => { |
| 265 | try s.tty.setColor(writer, .blue); | 294 | try s.tty.setColor(writer, .blue); |
| 266 | try writer.print("%{s}", .{@tagName(ref)}); | 295 | try writer.print("%{s}", .{@tagName(ref)}); |
| 267 | try s.tty.setColor(writer, .reset); | 296 | try s.tty.setColor(writer, .reset); |
| 297 | }, | ||
| 268 | } | 298 | } |
| 269 | } | 299 | } |
| 270 | 300 | ||
| 271 | fn dumpRefMarker(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void { | 301 | fn dumpRefMarker(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void { |
| 272 | if (ref.toIndex()) |index| { | 302 | switch (ref.decode()) { |
| 273 | try s.tty.setColor(writer, .blue); | 303 | .index => |index| { |
| 274 | try writer.print("%{d}", .{@intFromEnum(index)}); | 304 | try s.tty.setColor(writer, .blue); |
| 275 | try s.tty.setColor(writer, .reset); | 305 | try writer.print("%{d}", .{@intFromEnum(index)}); |
| 276 | } else { | 306 | try s.tty.setColor(writer, .reset); |
| 277 | try s.tty.setColor(writer, .blue); | 307 | }, |
| 278 | try writer.print("%{s}", .{@tagName(ref)}); | 308 | .value => { |
| 279 | try s.tty.setColor(writer, .reset); | 309 | try s.tty.setColor(writer, .blue); |
| 310 | try writer.print("%{s}", .{@tagName(ref)}); | ||
| 311 | try s.tty.setColor(writer, .reset); | ||
| 312 | }, | ||
| 280 | } | 313 | } |
| 281 | } | 314 | } |
| 282 | 315 | ||
| ... | @@ -356,6 +389,6 @@ pub fn format( | ... | @@ -356,6 +389,6 @@ pub fn format( |
| 356 | const std = @import("std"); | 389 | const std = @import("std"); |
| 357 | const assert = std.debug.assert; | 390 | const assert = std.debug.assert; |
| 358 | 391 | ||
| 359 | const Teg = @import("Teg.zig"); | 392 | const core = @import("mod.zig"); |
| 360 | 393 | const Teg = core.Teg; | |
| 361 | const detectTTYFromWriter = @import("util.zig").detectTTYFromWriter; | 394 | const detectTTYFromWriter = core.detectTTYFromWriter; |
src/core/Teg.zig+40-22| ... | @@ -46,6 +46,9 @@ pub const Inst = struct { | ... | @@ -46,6 +46,9 @@ pub const Inst = struct { |
| 46 | struct_decl, | 46 | struct_decl, |
| 47 | struct_init, | 47 | struct_init, |
| 48 | 48 | ||
| 49 | array_type, | ||
| 50 | array_init, | ||
| 51 | |||
| 49 | add, | 52 | add, |
| 50 | sub, | 53 | sub, |
| 51 | mul, | 54 | mul, |
| ... | @@ -70,6 +73,7 @@ pub const Inst = struct { | ... | @@ -70,6 +73,7 @@ pub const Inst = struct { |
| 70 | bool_not, | 73 | bool_not, |
| 71 | bit_not, | 74 | bit_not, |
| 72 | field, | 75 | field, |
| 76 | index, | ||
| 73 | call, | 77 | call, |
| 74 | @"comptime", | 78 | @"comptime", |
| 75 | 79 | ||
| ... | @@ -103,6 +107,7 @@ pub const Inst = struct { | ... | @@ -103,6 +107,7 @@ pub const Inst = struct { |
| 103 | .js_new => PlNode(JsNew), | 107 | .js_new => PlNode(JsNew), |
| 104 | .struct_decl => PlNode(StructDecl), | 108 | .struct_decl => PlNode(StructDecl), |
| 105 | .struct_init => PlNode(StructInit), | 109 | .struct_init => PlNode(StructInit), |
| 110 | .array_init => PlNode(ArrayInit), | ||
| 106 | 111 | ||
| 107 | // Single instruction operand | 112 | // Single instruction operand |
| 108 | .memory, | 113 | .memory, |
| ... | @@ -118,10 +123,12 @@ pub const Inst = struct { | ... | @@ -118,10 +123,12 @@ pub const Inst = struct { |
| 118 | .compile_error, | 123 | .compile_error, |
| 119 | .@"comptime", | 124 | .@"comptime", |
| 120 | .type_of, | 125 | .type_of, |
| 126 | .array_type, | ||
| 121 | => RefNode, | 127 | => RefNode, |
| 122 | 128 | ||
| 123 | // Two instruction operands | 129 | // Two instruction operands |
| 124 | .field, | 130 | .field, |
| 131 | .index, | ||
| 125 | .add, | 132 | .add, |
| 126 | .sub, | 133 | .sub, |
| 127 | .mul, | 134 | .mul, |
| ... | @@ -150,7 +157,7 @@ pub const Inst = struct { | ... | @@ -150,7 +157,7 @@ pub const Inst = struct { |
| 150 | } | 157 | } |
| 151 | }; | 158 | }; |
| 152 | 159 | ||
| 153 | /// TODO: make enum? idk | 160 | // TODO: make this an enum |
| 154 | const NodeOffset = i32; | 161 | const NodeOffset = i32; |
| 155 | 162 | ||
| 156 | pub fn PlNode(comptime Payload: type) type { | 163 | pub fn PlNode(comptime Payload: type) type { |
| ... | @@ -184,7 +191,7 @@ pub const Inst = struct { | ... | @@ -184,7 +191,7 @@ pub const Inst = struct { |
| 184 | main_module_inst = 0, | 191 | main_module_inst = 0, |
| 185 | _, | 192 | _, |
| 186 | 193 | ||
| 187 | pub const static_len = @intFromEnum(Ref.empty_string) + 1; | 194 | pub const static_len = @intFromEnum(Ref.empty_array) + 1; |
| 188 | 195 | ||
| 189 | pub fn toRef(i: Index) Inst.Ref { | 196 | pub fn toRef(i: Index) Inst.Ref { |
| 190 | return @enumFromInt(static_len + @intFromEnum(i)); | 197 | return @enumFromInt(static_len + @intFromEnum(i)); |
| ... | @@ -229,6 +236,8 @@ pub const Inst = struct { | ... | @@ -229,6 +236,8 @@ pub const Inst = struct { |
| 229 | noreturn_type, | 236 | noreturn_type, |
| 230 | type_type, | 237 | type_type, |
| 231 | numeric_literal_type, | 238 | numeric_literal_type, |
| 239 | numeric_literal_array_type, | ||
| 240 | noreturn_array_type, | ||
| 232 | 241 | ||
| 233 | void, | 242 | void, |
| 234 | bool_true, | 243 | bool_true, |
| ... | @@ -237,11 +246,7 @@ pub const Inst = struct { | ... | @@ -237,11 +246,7 @@ pub const Inst = struct { |
| 237 | float_zero, | 246 | float_zero, |
| 238 | nan, | 247 | nan, |
| 239 | empty_string, | 248 | empty_string, |
| 240 | standard_library, | 249 | empty_array, |
| 241 | |||
| 242 | /// This Ref does not correspond to any instruction or constant | ||
| 243 | /// value and may instead be used as a sentinel to indicate null. | ||
| 244 | none = std.math.maxInt(u32), | ||
| 245 | 250 | ||
| 246 | _, | 251 | _, |
| 247 | 252 | ||
| ... | @@ -258,8 +263,7 @@ pub const Inst = struct { | ... | @@ -258,8 +263,7 @@ pub const Inst = struct { |
| 258 | .{ .value = @enumFromInt(ref_int) }; | 263 | .{ .value = @enumFromInt(ref_int) }; |
| 259 | } | 264 | } |
| 260 | 265 | ||
| 261 | pub fn toIndex(ref: Ref) ?Index { | 266 | pub fn index(ref: Ref) ?Index { |
| 262 | assert(ref != .none); | ||
| 263 | const ref_int = @intFromEnum(ref); | 267 | const ref_int = @intFromEnum(ref); |
| 264 | if (ref_int >= Index.static_len) { | 268 | if (ref_int >= Index.static_len) { |
| 265 | return @enumFromInt(ref_int - Index.static_len); | 269 | return @enumFromInt(ref_int - Index.static_len); |
| ... | @@ -268,12 +272,7 @@ pub const Inst = struct { | ... | @@ -268,12 +272,7 @@ pub const Inst = struct { |
| 268 | } | 272 | } |
| 269 | } | 273 | } |
| 270 | 274 | ||
| 271 | pub fn toIndexAllowNone(inst: Ref) ?Index { | 275 | pub fn value(inst: Ref) ?InternPool.Value { |
| 272 | if (inst == .none) return null; | ||
| 273 | return toIndex(inst); | ||
| 274 | } | ||
| 275 | |||
| 276 | pub fn toValue(inst: Ref) ?InternPool.Value { | ||
| 277 | const ref_int = @intFromEnum(inst); | 276 | const ref_int = @intFromEnum(inst); |
| 278 | 277 | ||
| 279 | // TODO: is this > or >= | 278 | // TODO: is this > or >= |
| ... | @@ -282,6 +281,15 @@ pub const Inst = struct { | ... | @@ -282,6 +281,15 @@ pub const Inst = struct { |
| 282 | 281 | ||
| 283 | return @enumFromInt(ref_int); | 282 | return @enumFromInt(ref_int); |
| 284 | } | 283 | } |
| 284 | |||
| 285 | pub const Optional = enum(u32) { | ||
| 286 | none = std.math.maxInt(u32), | ||
| 287 | _, | ||
| 288 | |||
| 289 | pub fn unwrap(opt: Optional) ?Ref { | ||
| 290 | return if (opt != .none) @enumFromInt(@intFromEnum(opt)) else null; | ||
| 291 | } | ||
| 292 | }; | ||
| 285 | }; | 293 | }; |
| 286 | 294 | ||
| 287 | pub const Bin = struct { | 295 | pub const Bin = struct { |
| ... | @@ -359,6 +367,15 @@ pub const Inst = struct { | ... | @@ -359,6 +367,15 @@ pub const Inst = struct { |
| 359 | } | 367 | } |
| 360 | }; | 368 | }; |
| 361 | 369 | ||
| 370 | pub const ArrayInit = struct { | ||
| 371 | /// Optionally provide a result type for more reliable type coercion. | ||
| 372 | /// If specified, this must be an array type. Otherwise, the first | ||
| 373 | /// element's type is used. | ||
| 374 | type_hint: Ref.Optional, | ||
| 375 | /// The items in the array | ||
| 376 | list: RefList, | ||
| 377 | }; | ||
| 378 | |||
| 362 | /// This will contain trailing data depending on the .kind | 379 | /// This will contain trailing data depending on the .kind |
| 363 | pub const Decl = struct { | 380 | pub const Decl = struct { |
| 364 | name: NullTerminatedString, | 381 | name: NullTerminatedString, |
| ... | @@ -562,9 +579,10 @@ const std = @import("std"); | ... | @@ -562,9 +579,10 @@ const std = @import("std"); |
| 562 | const mem = std.mem; | 579 | const mem = std.mem; |
| 563 | const assert = std.debug.assert; | 580 | const assert = std.debug.assert; |
| 564 | 581 | ||
| 565 | const Cache = @import("Cache.zig"); | 582 | const core = @import("mod.zig"); |
| 583 | const Cache = core.Cache; | ||
| 566 | 584 | ||
| 567 | const Ast = @import("Ast.zig"); | 585 | const Ast = core.Ast; |
| 568 | const Unused = Ast.Unused; | 586 | const Unused = Ast.Unused; |
| 569 | const NodeIndex = Ast.NodeIndex; | 587 | const NodeIndex = Ast.NodeIndex; |
| 570 | const TokenIndex = Ast.TokenIndex; | 588 | const TokenIndex = Ast.TokenIndex; |
| ... | @@ -575,14 +593,14 @@ const optionalTokenIndex = OptionalTokenIndex.init; | ... | @@ -575,14 +593,14 @@ const optionalTokenIndex = OptionalTokenIndex.init; |
| 575 | const optionalNodeIndex = OptionalNodeIndex.init; | 593 | const optionalNodeIndex = OptionalNodeIndex.init; |
| 576 | const optionalExtraIndex = OptionalExtraIndex.init; | 594 | const optionalExtraIndex = OptionalExtraIndex.init; |
| 577 | 595 | ||
| 578 | const dod = @import("array.zig"); | 596 | const dod = core.array; |
| 579 | const Float = dod.Float; | 597 | const Float = dod.Float; |
| 580 | 598 | ||
| 581 | const InternPool = @import("InternPool.zig"); | 599 | const InternPool = core.InternPool; |
| 582 | 600 | ||
| 583 | const Air = @import("Air.zig"); | 601 | const Air = core.Air; |
| 584 | 602 | ||
| 585 | const Comp = @import("Comp.zig"); | 603 | const Comp = core.Comp; |
| 586 | 604 | ||
| 587 | const Report = @import("Report.zig"); | 605 | const Report = core.Report; |
| 588 | const LazyNode = Report.LazyNode; | 606 | const LazyNode = Report.LazyNode; |
src/core/Transform.zig+35-20| ... | @@ -235,7 +235,7 @@ const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{ | ... | @@ -235,7 +235,7 @@ const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{ |
| 235 | .{ "bool", .bool_type }, | 235 | .{ "bool", .bool_type }, |
| 236 | .{ "noreturn", .noreturn_type }, | 236 | .{ "noreturn", .noreturn_type }, |
| 237 | .{ "type", .type_type }, | 237 | .{ "type", .type_type }, |
| 238 | .{ "std", .standard_library }, | 238 | // .{ "std", .standard_library }, |
| 239 | }); | 239 | }); |
| 240 | 240 | ||
| 241 | fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.Index { | 241 | fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.Index { |
| ... | @@ -559,14 +559,14 @@ fn transformExpressionToBlock( | ... | @@ -559,14 +559,14 @@ fn transformExpressionToBlock( |
| 559 | 559 | ||
| 560 | // TODO: evaluate if `if` blocks would be fine in sema with instruction refs | 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. | 561 | // with this extra if check here, we filter out non-blocks. |
| 562 | if (result.toValue() != null) { | 562 | if (result.value() != null) { |
| 563 | assert(block == null); | 563 | assert(block == null); |
| 564 | return result; | 564 | return result; |
| 565 | } | 565 | } |
| 566 | } | 566 | } |
| 567 | 567 | ||
| 568 | if (expr) |result| { | 568 | if (expr) |result| { |
| 569 | if (result.toIndex()) |index| { | 569 | if (result.index()) |index| { |
| 570 | if (t.insts.items(.tag)[@intFromEnum(index)] == .block) { | 570 | if (t.insts.items(.tag)[@intFromEnum(index)] == .block) { |
| 571 | return result; | 571 | return result; |
| 572 | } | 572 | } |
| ... | @@ -685,7 +685,7 @@ pub const Builtin = enum { | ... | @@ -685,7 +685,7 @@ pub const Builtin = enum { |
| 685 | } | 685 | } |
| 686 | 686 | ||
| 687 | const base = try t.transformExpression(decl, args[0]) orelse return null; | 687 | const base = try t.transformExpression(decl, args[0]) orelse return null; |
| 688 | const args_list = try t.transformCallArgs(decl, args[1..]) orelse return null; | 688 | const args_list = try t.transformExpressionList(decl, args[1..]) orelse return null; |
| 689 | 689 | ||
| 690 | return try t.addInstRef(.{ | 690 | return try t.addInstRef(.{ |
| 691 | .tag = .js_new, | 691 | .tag = .js_new, |
| ... | @@ -923,6 +923,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -923,6 +923,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 923 | inline .negate, | 923 | inline .negate, |
| 924 | .bool_not, | 924 | .bool_not, |
| 925 | .bit_not, | 925 | .bit_not, |
| 926 | .array_type, | ||
| 926 | => |tag| { | 927 | => |tag| { |
| 927 | const ast_un = t.ast.nodeData(node).un; | 928 | const ast_un = t.ast.nodeData(node).un; |
| 928 | const teg_tag = @field(Teg.Inst.Tag, @tagName(tag)); | 929 | const teg_tag = @field(Teg.Inst.Tag, @tagName(tag)); |
| ... | @@ -1048,7 +1049,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1048,7 +1049,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1048 | const args = t.ast.extraData(call.extra, Ast.Call).args.slice(&t.ast); | 1049 | const args = t.ast.extraData(call.extra, Ast.Call).args.slice(&t.ast); |
| 1049 | 1050 | ||
| 1050 | const base = try t.transformExpression(decl, call.base) orelse return null; | 1051 | const base = try t.transformExpression(decl, call.base) orelse return null; |
| 1051 | const args_list = try t.transformCallArgs(decl, args) orelse return null; | 1052 | const args_list = try t.transformExpressionList(decl, args) orelse return null; |
| 1052 | 1053 | ||
| 1053 | const call_inst = try t.addInstRef(.{ | 1054 | const call_inst = try t.addInstRef(.{ |
| 1054 | .tag = .call, | 1055 | .tag = .call, |
| ... | @@ -1227,11 +1228,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1227,11 +1228,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1227 | ) |field_node, i, j| { | 1228 | ) |field_node, i, j| { |
| 1228 | const field = t.ast.fullStructField(field_node); | 1229 | const field = t.ast.fullStructField(field_node); |
| 1229 | 1230 | ||
| 1230 | if (mem.startsWith(u8, field.id, "@")) { | 1231 | const id = try t.decodeIdentifier(field.id); |
| 1231 | @panic("TODO: string identifiers"); | ||
| 1232 | } | ||
| 1233 | 1232 | ||
| 1234 | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(field.id))); | 1233 | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id))); |
| 1235 | t.extra.setU32(j, @intFromEnum(try t.transformExpressionToBlock(decl, field.type, false))); | 1234 | t.extra.setU32(j, @intFromEnum(try t.transformExpressionToBlock(decl, field.type, false))); |
| 1236 | } | 1235 | } |
| 1237 | 1236 | ||
| ... | @@ -1264,11 +1263,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1264,11 +1263,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1264 | ) |field_node, i, j| { | 1263 | ) |field_node, i, j| { |
| 1265 | const field = t.ast.fullStructField(field_node); | 1264 | const field = t.ast.fullStructField(field_node); |
| 1266 | 1265 | ||
| 1267 | if (mem.startsWith(u8, field.id, "@")) { | 1266 | const id = try t.decodeIdentifier(field.id); |
| 1268 | @panic("TODO: string identifiers"); | ||
| 1269 | } | ||
| 1270 | 1267 | ||
| 1271 | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(field.id))); | 1268 | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id))); |
| 1272 | t.extra.setU32(j, @intFromEnum((try t.transformExpression(decl, field.type)) orelse | 1269 | t.extra.setU32(j, @intFromEnum((try t.transformExpression(decl, field.type)) orelse |
| 1273 | @panic("TODO: print better error"))); | 1270 | @panic("TODO: print better error"))); |
| 1274 | } | 1271 | } |
| ... | @@ -1286,11 +1283,29 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc | ... | @@ -1286,11 +1283,29 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1286 | }); | 1283 | }); |
| 1287 | }, | 1284 | }, |
| 1288 | 1285 | ||
| 1286 | .array_init => { | ||
| 1287 | const array_init = t.ast.nodeData(node).list; | ||
| 1288 | |||
| 1289 | const items = try t.transformExpressionList(decl, array_init.slice(&t.ast)) orelse | ||
| 1290 | return null; | ||
| 1291 | |||
| 1292 | return try t.addInstRef(.{ | ||
| 1293 | .tag = .array_init, | ||
| 1294 | .data = EncodedData.encode(.array_init, .{ | ||
| 1295 | .src_node = decl.nodeOffset(node), | ||
| 1296 | .extra_index = try t.extra.append(t.gpa, Inst.ArrayInit, .{ | ||
| 1297 | .type_hint = .none, | ||
| 1298 | .list = items, | ||
| 1299 | }), | ||
| 1300 | }), | ||
| 1301 | }); | ||
| 1302 | }, | ||
| 1303 | |||
| 1289 | else => |tag| std.debug.panic("TODO: expr {s}", .{@tagName(tag)}), | 1304 | else => |tag| std.debug.panic("TODO: expr {s}", .{@tagName(tag)}), |
| 1290 | } | 1305 | } |
| 1291 | } | 1306 | } |
| 1292 | 1307 | ||
| 1293 | fn transformCallArgs(t: *Transform, decl: BlockContext, args: []const NodeIndex) !?RefList { | 1308 | fn transformExpressionList(t: *Transform, decl: BlockContext, args: []const NodeIndex) !?RefList { |
| 1294 | const extra_top = try t.extra.alloc(t.gpa, args.len); | 1309 | const extra_top = try t.extra.alloc(t.gpa, args.len); |
| 1295 | 1310 | ||
| 1296 | for (args, extra_top..) |arg, i| { | 1311 | for (args, extra_top..) |arg, i| { |
| ... | @@ -1492,7 +1507,7 @@ fn transformBlockStatement( | ... | @@ -1492,7 +1507,7 @@ fn transformBlockStatement( |
| 1492 | .node = node, | 1507 | .node = node, |
| 1493 | .data = switch (var_decl.is_const) { | 1508 | .data = switch (var_decl.is_const) { |
| 1494 | true => .{ .immutable = mem_ref }, | 1509 | true => .{ .immutable = mem_ref }, |
| 1495 | false => .{ .mutable = mem_ref.toIndex().? }, | 1510 | false => .{ .mutable = mem_ref.index().? }, |
| 1496 | }, | 1511 | }, |
| 1497 | }; | 1512 | }; |
| 1498 | 1513 | ||
| ... | @@ -1696,16 +1711,16 @@ fn addDecodedSlicedString(t: *Transform, escaped: []const u8) !SlicedString { | ... | @@ -1696,16 +1711,16 @@ fn addDecodedSlicedString(t: *Transform, escaped: []const u8) !SlicedString { |
| 1696 | const decoded = if (Tokenizer.stringHasEscapes(escaped)) brk: { | 1711 | const decoded = if (Tokenizer.stringHasEscapes(escaped)) brk: { |
| 1697 | const len = Tokenizer.decodeStringCount(escaped); | 1712 | const len = Tokenizer.decodeStringCount(escaped); |
| 1698 | 1713 | ||
| 1699 | // use the scratch arraylist as a temporary storage for | 1714 | // Use the scratch arraylist as a temporary storage for decoding the |
| 1700 | // decoding the string. this is done so that duplicate | 1715 | // string. this is done so that duplicate strings will get de-duplicated |
| 1701 | // strings will get de-duplicated in string_bytes | 1716 | // in string_bytes. It is more likely that there will be remaining bytes |
| 1702 | // | 1717 | // in `.scratch` vs `.id_decode_buf` |
| 1703 | // TODO: see if this is a useless optimization | ||
| 1704 | try t.scratch.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1); | 1718 | try t.scratch.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1); |
| 1705 | const buf = mem.sliceAsBytes(t.scratch.unusedCapacitySlice()); | 1719 | const buf = mem.sliceAsBytes(t.scratch.unusedCapacitySlice()); |
| 1706 | 1720 | ||
| 1707 | break :brk Tokenizer.decodeStringBuf(escaped, buf); | 1721 | break :brk Tokenizer.decodeStringBuf(escaped, buf); |
| 1708 | } else escaped; | 1722 | } else escaped; |
| 1723 | |||
| 1709 | return t.addSlicedString(decoded); | 1724 | return t.addSlicedString(decoded); |
| 1710 | } | 1725 | } |
| 1711 | 1726 |
src/core/array.zig+9| ... | @@ -372,6 +372,15 @@ pub fn ExtraStore(comptime set_kind: StoreKind, comptime set_mutability: Mutabil | ... | @@ -372,6 +372,15 @@ pub fn ExtraStore(comptime set_kind: StoreKind, comptime set_mutability: Mutabil |
| 372 | store.list.items.len += len; | 372 | store.list.items.len += len; |
| 373 | return @intCast(idx); | 373 | return @intCast(idx); |
| 374 | } | 374 | } |
| 375 | |||
| 376 | pub fn appendSlice(store: *Store, gpa: Allocator, data: anytype) !u32 { | ||
| 377 | const idx = store.list.items.len; | ||
| 378 | comptime assert(@sizeOf(@TypeOf(data[0])) == @sizeOf(u32)); | ||
| 379 | comptime assert(@alignOf(@TypeOf(data[0])) == @alignOf(u32)); | ||
| 380 | try store.list.appendSlice(gpa, @ptrCast(data)); | ||
| 381 | store.list.items.len += data.len; | ||
| 382 | return @intCast(idx); | ||
| 383 | } | ||
| 375 | }; | 384 | }; |
| 376 | } | 385 | } |
| 377 | 386 |
src/core/js/Link.zig+36-1| ... | @@ -289,6 +289,19 @@ fn printValue(link: *Link, value: InternPool.Value, level: Level, writer: anytyp | ... | @@ -289,6 +289,19 @@ fn printValue(link: *Link, value: InternPool.Value, level: Level, writer: anytyp |
| 289 | try writer.writeAll(")"); | 289 | try writer.writeAll(")"); |
| 290 | }, | 290 | }, |
| 291 | 291 | ||
| 292 | .array_value => |array_value| { | ||
| 293 | try writer.writeAll("["); | ||
| 294 | |||
| 295 | for (array_value.items, 0..) |inner_value, i| { | ||
| 296 | if (i != 0) | ||
| 297 | try writer.writeAll(","); | ||
| 298 | |||
| 299 | // TODO: handle special identifiers | ||
| 300 | try link.printValue(inner_value, .assignment, writer); | ||
| 301 | } | ||
| 302 | try writer.writeAll("]"); | ||
| 303 | }, | ||
| 304 | |||
| 292 | else => unreachable, // this value is not a plain_value | 305 | else => unreachable, // this value is not a plain_value |
| 293 | } | 306 | } |
| 294 | } | 307 | } |
| ... | @@ -410,6 +423,18 @@ fn printExprLevel( | ... | @@ -410,6 +423,18 @@ fn printExprLevel( |
| 410 | try writer.writeAll(")"); | 423 | try writer.writeAll(")"); |
| 411 | }, | 424 | }, |
| 412 | 425 | ||
| 426 | .index => { | ||
| 427 | const bin: Tree.Bin = tree.nodeData(.index, node); | ||
| 428 | if (Level.access_and_call.needsParens(level)) | ||
| 429 | try writer.writeAll("("); | ||
| 430 | try link.printExprLevel(tree, bin.lhs, level, writer); | ||
| 431 | try writer.writeAll("["); | ||
| 432 | try link.printExprLevel(tree, bin.rhs, level, writer); | ||
| 433 | try writer.writeAll("]"); | ||
| 434 | if (Level.access_and_call.needsParens(level)) | ||
| 435 | try writer.writeAll(")"); | ||
| 436 | }, | ||
| 437 | |||
| 413 | inline .negate, | 438 | inline .negate, |
| 414 | .bool_not, | 439 | .bool_not, |
| 415 | .pre_dec, | 440 | .pre_dec, |
| ... | @@ -505,7 +530,7 @@ fn printExprLevel( | ... | @@ -505,7 +530,7 @@ fn printExprLevel( |
| 505 | 530 | ||
| 506 | try writer.writeAll(":"); | 531 | try writer.writeAll(":"); |
| 507 | 532 | ||
| 508 | try link.printExpr(tree, value, writer); | 533 | try link.printExprLevel(tree, value, .assignment, writer); |
| 509 | } | 534 | } |
| 510 | 535 | ||
| 511 | try writer.writeAll("}"); | 536 | try writer.writeAll("}"); |
| ... | @@ -513,6 +538,16 @@ fn printExprLevel( | ... | @@ -513,6 +538,16 @@ fn printExprLevel( |
| 513 | if (level == .statement) | 538 | if (level == .statement) |
| 514 | try writer.writeAll(")"); | 539 | try writer.writeAll(")"); |
| 515 | }, | 540 | }, |
| 541 | |||
| 542 | .array => { | ||
| 543 | const items: Tree.IndexList = tree.nodeData(.array, node); | ||
| 544 | try writer.writeAll("["); | ||
| 545 | for (items.slice(tree), 0..) |item, i| { | ||
| 546 | if (i != 0) try writer.writeAll(", "); | ||
| 547 | try link.printExprLevel(tree, item, .assignment, writer); | ||
| 548 | } | ||
| 549 | try writer.writeAll("]"); | ||
| 550 | }, | ||
| 516 | } | 551 | } |
| 517 | } | 552 | } |
| 518 | 553 |
src/core/js/NodeGen.zig+5-1| ... | @@ -422,7 +422,7 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { | ... | @@ -422,7 +422,7 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { |
| 422 | try c.markExpression(inst, node); | 422 | try c.markExpression(inst, node); |
| 423 | }, | 423 | }, |
| 424 | 424 | ||
| 425 | // else => |tag| std.debug.panic("TODO JS codeGenInst for .{s}", .{@tagName(tag)}), | 425 | else => |tag| std.debug.panic("TODO JS codeGenInst for .{s}", .{@tagName(tag)}), |
| 426 | } | 426 | } |
| 427 | } | 427 | } |
| 428 | 428 | ||
| ... | @@ -495,6 +495,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index { | ... | @@ -495,6 +495,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index { |
| 495 | .function_type, | 495 | .function_type, |
| 496 | .simple_type, | 496 | .simple_type, |
| 497 | .struct_type, | 497 | .struct_type, |
| 498 | .array_type, | ||
| 498 | => unreachable, // Type values are not allowed here | 499 | => unreachable, // Type values are not allowed here |
| 499 | 500 | ||
| 500 | .int32, | 501 | .int32, |
| ... | @@ -503,6 +504,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index { | ... | @@ -503,6 +504,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index { |
| 503 | .string, | 504 | .string, |
| 504 | .simple_value, | 505 | .simple_value, |
| 505 | .struct_value, | 506 | .struct_value, |
| 507 | .array_value, | ||
| 506 | => return try c.add(.plain_value, .{ .value = v }), | 508 | => return try c.add(.plain_value, .{ .value = v }), |
| 507 | 509 | ||
| 508 | // Hoist functions as top level values | 510 | // Hoist functions as top level values |
| ... | @@ -666,6 +668,8 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool { | ... | @@ -666,6 +668,8 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool { |
| 666 | .bool_not, | 668 | .bool_not, |
| 667 | .store, | 669 | .store, |
| 668 | .struct_init, | 670 | .struct_init, |
| 671 | .array_init, | ||
| 672 | .index, | ||
| 669 | => true, | 673 | => true, |
| 670 | 674 | ||
| 671 | // JavaScript variable declarations require statements. | 675 | // JavaScript variable declarations require statements. |
src/core/js/Tree.zig+4| ... | @@ -45,6 +45,8 @@ pub const Tag = enum { | ... | @@ -45,6 +45,8 @@ pub const Tag = enum { |
| 45 | field, | 45 | field, |
| 46 | ternary, | 46 | ternary, |
| 47 | object_literal, | 47 | object_literal, |
| 48 | array, | ||
| 49 | index, | ||
| 48 | // Prefix ops | 50 | // Prefix ops |
| 49 | negate, | 51 | negate, |
| 50 | bool_not, | 52 | bool_not, |
| ... | @@ -81,6 +83,7 @@ pub const Tag = enum { | ... | @@ -81,6 +83,7 @@ pub const Tag = enum { |
| 81 | .eq, | 83 | .eq, |
| 82 | .lt, | 84 | .lt, |
| 83 | .gt, | 85 | .gt, |
| 86 | .index, | ||
| 84 | => Bin, | 87 | => Bin, |
| 85 | .ternary => Ternary, | 88 | .ternary => Ternary, |
| 86 | .negate, | 89 | .negate, |
| ... | @@ -89,6 +92,7 @@ pub const Tag = enum { | ... | @@ -89,6 +92,7 @@ pub const Tag = enum { |
| 89 | .pre_dec, | 92 | .pre_dec, |
| 90 | => Un, | 93 | => Un, |
| 91 | .object_literal => ObjectLiteral, | 94 | .object_literal => ObjectLiteral, |
| 95 | .array => IndexList, | ||
| 92 | .call, .new => Call, | 96 | .call, .new => Call, |
| 93 | .field => Field, | 97 | .field => Field, |
| 94 | .stmt_if => StmtIf, | 98 | .stmt_if => StmtIf, |
src/core/main.zig+7-7| ... | @@ -28,14 +28,14 @@ pub fn main() !void { | ... | @@ -28,14 +28,14 @@ pub fn main() !void { |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | const use_gpa = builtin.mode == .Debug; | 30 | const use_gpa = builtin.mode == .Debug; |
| 31 | var gpa: if (use_gpa) std.heap.GeneralPurposeAllocator(.{ | 31 | var gpa = if (use_gpa) |
| 32 | .stack_trace_frames = 100, | 32 | std.heap.GeneralPurposeAllocator(.{ |
| 33 | }) else struct {} = .{}; | 33 | .stack_trace_frames = 100, |
| 34 | defer _ = if (use_gpa) gpa.deinit(); | 34 | }){} |
| 35 | const alloc = if (use_gpa) | ||
| 36 | gpa.allocator() | ||
| 37 | else | 35 | else |
| 38 | std.heap.c_allocator; | 36 | std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 37 | defer _ = if (use_gpa) gpa.deinit(); | ||
| 38 | const alloc = gpa.allocator(); | ||
| 39 | 39 | ||
| 40 | var maybe_file_path: ?[]const u8 = null; | 40 | var maybe_file_path: ?[]const u8 = null; |
| 41 | defer if (maybe_file_path) |path| alloc.free(path); | 41 | defer if (maybe_file_path) |path| alloc.free(path); |
src/core/mod.zig+32-4| ... | @@ -29,6 +29,9 @@ pub const Report = @import("Report.zig"); | ... | @@ -29,6 +29,9 @@ pub const Report = @import("Report.zig"); |
| 29 | /// Semantic Analysis visits a `Teg` declaration and produces `Air` | 29 | /// Semantic Analysis visits a `Teg` declaration and produces `Air` |
| 30 | pub const Sema = @import("Sema.zig"); | 30 | pub const Sema = @import("Sema.zig"); |
| 31 | 31 | ||
| 32 | pub const Stack = @import("stack.zig").Stack; | ||
| 33 | pub const U32Stack = Stack(u32); | ||
| 34 | |||
| 32 | /// Untyped Intermediate Representation. Flat instruction list | 35 | /// Untyped Intermediate Representation. Flat instruction list |
| 33 | pub const Teg = @import("Teg.zig"); | 36 | pub const Teg = @import("Teg.zig"); |
| 34 | 37 | ||
| ... | @@ -40,8 +43,6 @@ pub const Transform = @import("Transform.zig"); | ... | @@ -40,8 +43,6 @@ pub const Transform = @import("Transform.zig"); |
| 40 | 43 | ||
| 41 | pub const unicode_props = @import("unicode_props.zig"); | 44 | pub const unicode_props = @import("unicode_props.zig"); |
| 42 | 45 | ||
| 43 | pub const util = @import("util.zig"); | ||
| 44 | |||
| 45 | // sort-lines: end | 46 | // sort-lines: end |
| 46 | 47 | ||
| 47 | // The rest of this file is utility functions, which are not specific to any of | 48 | // The rest of this file is utility functions, which are not specific to any of |
| ... | @@ -64,8 +65,35 @@ pub inline fn assertFailed(cond: bool) bool { | ... | @@ -64,8 +65,35 @@ pub inline fn assertFailed(cond: bool) bool { |
| 64 | } | 65 | } |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | pub const detectTTYFromWriter = util.detectTTYFromWriter; | 68 | /// If `value` can be cast to `i32` |
| 68 | pub const Ordinal = util.Ordinal; | 69 | pub fn inInt32Range(value: anytype) bool { |
| 70 | return switch (@typeInfo(@TypeOf(value))) { | ||
| 71 | .Int, | ||
| 72 | .ComptimeInt, | ||
| 73 | => value >= std.math.minInt(i32) and | ||
| 74 | value <= std.math.maxInt(i32), | ||
| 75 | |||
| 76 | .Float, | ||
| 77 | .ComptimeFloat, | ||
| 78 | => std.math.isFinite(value) and | ||
| 79 | value >= std.math.minInt(i32) and | ||
| 80 | value <= std.math.maxInt(i32) and | ||
| 81 | (@rem(value, 1) == 0), | ||
| 82 | |||
| 83 | else => @compileError(unreachable), | ||
| 84 | }; | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn detectTTYFromWriter(writer: std.io.AnyWriter) std.io.tty.Config { | ||
| 88 | const fileWriteFn = comptime (std.fs.File{ .handle = undefined }) | ||
| 89 | .writer().any().writeFn; | ||
| 90 | |||
| 91 | if (writer.writeFn == fileWriteFn) { | ||
| 92 | const file: *const std.fs.File = @alignCast(@ptrCast(writer.context)); | ||
| 93 | return std.io.tty.detectConfig(file.*); | ||
| 94 | } | ||
| 95 | return .no_color; | ||
| 96 | } | ||
| 69 | 97 | ||
| 70 | const std = @import("std"); | 98 | const std = @import("std"); |
| 71 | const assert = std.debug.assert; | 99 | const assert = std.debug.assert; |
src/core/stack.zig created+156| ... | @@ -0,0 +1,156 @@ | ||
| 1 | //! This is an abstraction on top of ArrayListUnmanaged primarily intended for | ||
| 2 | //! unbounded temporary memory. Allocations must be freed last-in-first-out, but | ||
| 3 | //! the ability for memory reuse is high. | ||
| 4 | //! | ||
| 5 | //! This structure allows for a mark -> append -> gather pattern, which is | ||
| 6 | //! typically used with a block expression and it's contents, but it also allows | ||
| 7 | //! arbitrary temporary allocations that re-use this memory space. | ||
| 8 | pub fn Stack(comptime Base: type) type { | ||
| 9 | return struct { | ||
| 10 | const Self = @This(); | ||
| 11 | |||
| 12 | list: std.ArrayListUnmanaged(Base) = .{}, | ||
| 13 | |||
| 14 | pub const Mark = struct { | ||
| 15 | stack: *Self, | ||
| 16 | index: usize, | ||
| 17 | |||
| 18 | pub fn deinit(m: Mark) void { | ||
| 19 | if (std.debug.runtime_safety) | ||
| 20 | @memset(m.slice(Base), undefined); | ||
| 21 | m.stack.list.shrinkRetainingCapacity(m.index); | ||
| 22 | } | ||
| 23 | |||
| 24 | pub fn len(m: Mark) usize { | ||
| 25 | return m.stack.list.items.len - m.index; | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn slice(m: Mark, comptime T: type) []T { | ||
| 29 | comptime assert(@alignOf(T) == @alignOf(Base)); | ||
| 30 | comptime assert(@sizeOf(T) == @sizeOf(Base)); | ||
| 31 | return @ptrCast(m.stack.list.items[m.index..]); | ||
| 32 | } | ||
| 33 | }; | ||
| 34 | |||
| 35 | /// Mark this point of the stack, where all future appends are owned by | ||
| 36 | /// the mark. You can then use `slice()` to collect the appends. | ||
| 37 | pub fn mark(stack: *Self) Mark { | ||
| 38 | return .{ | ||
| 39 | .stack = stack, | ||
| 40 | .index = stack.list.items.len, | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | |||
| 44 | pub fn add(stack: *Self, allocator: Allocator, comptime T: type, item: T) !void { | ||
| 45 | comptime assert(@alignOf(T) == @alignOf(Base)); | ||
| 46 | comptime assert(@sizeOf(T) == @sizeOf(Base)); | ||
| 47 | try stack.list.append(allocator, @as(*const Base, @ptrCast(&item)).*); | ||
| 48 | } | ||
| 49 | |||
| 50 | pub fn Reserve(comptime T: type) type { | ||
| 51 | assert(@alignOf(T) == @alignOf(Base)); | ||
| 52 | assert(@sizeOf(T) == @sizeOf(Base)); | ||
| 53 | |||
| 54 | return struct { | ||
| 55 | stack: *Self, | ||
| 56 | start: usize, | ||
| 57 | len: usize, | ||
| 58 | |||
| 59 | pub fn deinit(r: @This()) void { | ||
| 60 | const from = r.stack.list.items[r.start + r.len ..]; | ||
| 61 | const dest = r.stack.list.items[r.start..][0..from.len]; | ||
| 62 | std.mem.copyForwards(u32, dest, from); | ||
| 63 | r.stack.list.shrinkRetainingCapacity(r.start + from.len); | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn at(r: @This(), i: usize) *T { | ||
| 67 | assert(i < r.len); | ||
| 68 | return @ptrCast(&r.stack.list.items[r.start + i]); | ||
| 69 | } | ||
| 70 | |||
| 71 | pub fn slice(r: @This()) []T { | ||
| 72 | return @ptrCast(r.stack.list.items[r.start..][0..r.len]); | ||
| 73 | } | ||
| 74 | |||
| 75 | pub fn sliceReinterpret(r: @This(), comptime Reinterpret: type) []Reinterpret { | ||
| 76 | assert(@alignOf(Reinterpret) == @alignOf(Base)); | ||
| 77 | assert(@sizeOf(Reinterpret) == @sizeOf(Base)); | ||
| 78 | return @ptrCast(r.slice()); | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn reserve( | ||
| 84 | stack: *Self, | ||
| 85 | allocator: Allocator, | ||
| 86 | comptime T: type, | ||
| 87 | len: usize, | ||
| 88 | ) !Reserve(T) { | ||
| 89 | try stack.list.ensureUnusedCapacity(allocator, len); | ||
| 90 | const start = stack.list.items.len; | ||
| 91 | stack.list.items.len += len; | ||
| 92 | return .{ .stack = stack, .start = start, .len = len }; | ||
| 93 | } | ||
| 94 | |||
| 95 | pub fn ReserveMulti(comptime types: anytype) type { | ||
| 96 | const Types = @TypeOf(types); | ||
| 97 | const fields = std.meta.fields(Types); | ||
| 98 | const FieldEnum = std.meta.FieldEnum(Types); | ||
| 99 | |||
| 100 | return struct { | ||
| 101 | stack: *Self, | ||
| 102 | starts: [fields.len]usize, | ||
| 103 | lengths: [fields.len]usize, | ||
| 104 | |||
| 105 | fn Type(comptime field: FieldEnum) type { | ||
| 106 | return @field(types, @tagName(field)); | ||
| 107 | } | ||
| 108 | |||
| 109 | pub fn slice(r: @This(), comptime field: FieldEnum) []Type(field) { | ||
| 110 | const f = comptime std.meta.fieldIndex(Types, @tagName(field)).?; | ||
| 111 | return @ptrCast(r.stack.list.items[r.starts[f]..][0..r.lengths[f]]); | ||
| 112 | } | ||
| 113 | |||
| 114 | pub fn at(r: @This(), comptime field: FieldEnum, i: usize) *Type(field) { | ||
| 115 | const f = comptime std.meta.fieldIndex(Types, @tagName(field)).?; | ||
| 116 | assert(i < r.lengths[f]); | ||
| 117 | return @ptrCast(&r.stack.list.items[r.starts[f] + i]); | ||
| 118 | } | ||
| 119 | |||
| 120 | pub fn deinit(r: @This()) void { | ||
| 121 | const start = r.starts[0]; | ||
| 122 | const len = r.starts[fields.len - 1] - r.starts[0] + r.lengths[fields.len - 1]; | ||
| 123 | |||
| 124 | const from = r.stack.list.items[start + len ..]; | ||
| 125 | const dest = r.stack.list.items[start..][0..from.len]; | ||
| 126 | std.mem.copyForwards(u32, dest, from); | ||
| 127 | r.stack.list.shrinkRetainingCapacity(start + from.len); | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Same as `reserve` but able to group multiple allocations of different types | ||
| 133 | /// into a singular push and pop. | ||
| 134 | pub fn reserveMulti( | ||
| 135 | stack: *Self, | ||
| 136 | allocator: Allocator, | ||
| 137 | comptime types: anytype, | ||
| 138 | lengths: [std.meta.fields(@TypeOf(types)).len]usize, | ||
| 139 | ) !ReserveMulti(types) { | ||
| 140 | var all_lengths: usize = 0; | ||
| 141 | var starts: [std.meta.fields(@TypeOf(types)).len]usize = undefined; | ||
| 142 | const start = stack.list.items.len; | ||
| 143 | inline for (lengths, &starts) |len, *slice_start| { | ||
| 144 | slice_start.* = start + all_lengths; | ||
| 145 | all_lengths += len; | ||
| 146 | } | ||
| 147 | try stack.list.ensureUnusedCapacity(allocator, all_lengths); | ||
| 148 | stack.list.items.len += all_lengths; | ||
| 149 | return .{ .stack = stack, .starts = starts, .lengths = lengths }; | ||
| 150 | } | ||
| 151 | }; | ||
| 152 | } | ||
| 153 | |||
| 154 | const std = @import("std"); | ||
| 155 | const assert = std.debug.assert; | ||
| 156 | const Allocator = std.mem.Allocator; | ||
src/core/util.zig deleted-49| ... | @@ -1,49 +0,0 @@ | ||
| 1 | // TODO: move all decls in this file into mod.zig | ||
| 2 | |||
| 3 | pub fn detectTTYFromWriter(writer: anytype) std.io.tty.Config { | ||
| 4 | if (@hasField(@TypeOf(writer), "unbuffered_writer")) { | ||
| 5 | return detectTTYFromWriter(writer.unbuffered_writer); | ||
| 6 | } | ||
| 7 | if (@TypeOf(writer) == std.fs.File.Writer) { | ||
| 8 | return std.io.tty.detectConfig(writer.context); | ||
| 9 | } | ||
| 10 | if (@TypeOf(writer) == std.io.AnyWriter) { | ||
| 11 | const fileWriteFn = comptime (std.fs.File{ .handle = undefined }) | ||
| 12 | .writer().any().writeFn; | ||
| 13 | if (writer.writeFn == fileWriteFn) { | ||
| 14 | const file: *const std.fs.File = @alignCast(@ptrCast(writer.context)); | ||
| 15 | return std.io.tty.detectConfig(file.*); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | return .no_color; | ||
| 19 | } | ||
| 20 | |||
| 21 | pub fn Ordinal(Int: type) type { | ||
| 22 | comptime assert(@typeInfo(Int).Int.signedness == .unsigned); | ||
| 23 | return enum(Int) { | ||
| 24 | start = 0, | ||
| 25 | _, | ||
| 26 | |||
| 27 | pub fn fromZeroBased(int: Int) @This() { | ||
| 28 | assert(int != std.math.maxInt(Int)); | ||
| 29 | return @enumFromInt(int); | ||
| 30 | } | ||
| 31 | pub fn fromOneBased(int: Int) @This() { | ||
| 32 | assert(int > 0); | ||
| 33 | return @enumFromInt(int - 1); | ||
| 34 | } | ||
| 35 | pub fn zeroBased(ord: @This()) Int { | ||
| 36 | return @intFromEnum(ord); | ||
| 37 | } | ||
| 38 | pub fn oneBased(ord: @This()) Int { | ||
| 39 | return @intFromEnum(ord) + 1; | ||
| 40 | } | ||
| 41 | |||
| 42 | pub fn add(ord: @This(), inc: Int) @This() { | ||
| 43 | return fromZeroBased(ord.zeroBased() + inc); | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | |||
| 48 | const std = @import("std"); | ||
| 49 | const assert = std.debug.assert; | ||