| 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 | 1 | ## 0.1.4 |
| 2 | 2 | |
| 3 | 3 | - feat: implement basic struct types (#42) |
| 4 | - feat: implement basic array types (#67) | |
| 4 | 5 | - feat: implement string identifiers (#54) |
| 5 | 6 | - feat: include a `README.html` alongside compiler with documentation and code samples. |
| 6 | 7 | - feat(transform): include a warning for leading zero in numbers |
| ... | ... | @@ -9,6 +10,7 @@ |
| 9 | 10 | - fix(sema): handle recursive function calls |
| 10 | 11 | - fix(sema): quit block analysis when hitting `noreturn` |
| 11 | 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 | 15 | ## 0.1.3 |
| 14 | 16 |
build.zig+6-6| ... | ... | @@ -117,12 +117,12 @@ pub fn build(b: *std.Build) void { |
| 117 | 117 | const win_version: OsVersion = .{ .windows = .win7 }; |
| 118 | 118 | |
| 119 | 119 | inline for (.{ |
| 120 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .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 }), | |
| 122 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos, .os_version_min = mac_version }), | |
| 123 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .macos, .os_version_min = mac_version }), | |
| 124 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .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 }), | |
| 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, .cpu_model = .baseline }), | |
| 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, .cpu_model = .baseline }), | |
| 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, .cpu_model = .baseline }), | |
| 126 | 126 | }) |cross_target| { |
| 127 | 127 | const name = std.fmt.allocPrint( |
| 128 | 128 | b.allocator, |
src/core/Air.Dump.zig+39-6| ... | ... | @@ -92,6 +92,7 @@ pub const State = struct { |
| 92 | 92 | .lt, |
| 93 | 93 | .gt, |
| 94 | 94 | .eq, |
| 95 | .index, | |
| 95 | 96 | => |comptime_tag| { |
| 96 | 97 | const bin: Air.Bin = air.instData(comptime_tag, inst); |
| 97 | 98 | try w.writeAll("("); |
| ... | ... | @@ -185,6 +186,15 @@ pub const State = struct { |
| 185 | 186 | try s.newline(w); |
| 186 | 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 | 347 | try w.writeAll(">"); |
| 338 | 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 | 423 | var state = State{ |
| 389 | 424 | .air = &air, |
| 390 | 425 | .ip = dump.ip, |
| 391 | .tty = detectTTYFromWriter(writer), | |
| 426 | .tty = core.detectTTYFromWriter(writer), | |
| 392 | 427 | .arena = arena.allocator(), |
| 393 | 428 | }; |
| 394 | 429 | try state.dumpInst(writer, .main_block); |
| ... | ... | @@ -399,10 +434,8 @@ pub fn format( |
| 399 | 434 | const std = @import("std"); |
| 400 | 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 | 439 | const Inst = Air.Inst; |
| 404 | ||
| 405 | const InternPool = @import("InternPool.zig"); | |
| 440 | const InternPool = core.InternPool; | |
| 406 | 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 | 25 | store, |
| 26 | 26 | |
| 27 | 27 | struct_init, |
| 28 | array_init, | |
| 28 | 29 | |
| 29 | 30 | field_opaque, |
| 30 | 31 | field_struct, |
| 31 | 32 | |
| 33 | index, | |
| 34 | ||
| 32 | 35 | // bin |
| 33 | 36 | add, |
| 34 | 37 | sub, |
| ... | ... | @@ -55,8 +58,10 @@ pub const Inst = struct { |
| 55 | 58 | .memory => Memory, |
| 56 | 59 | .store => Store, |
| 57 | 60 | .struct_init => StructInit, |
| 61 | .array_init => ArrayInit, | |
| 58 | 62 | .field_opaque => FieldOpaque, |
| 59 | 63 | .field_struct => FieldStruct, |
| 64 | .index, | |
| 60 | 65 | .add, |
| 61 | 66 | .sub, |
| 62 | 67 | .mul, |
| ... | ... | @@ -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 | 285 | pub const FieldStruct = struct { |
| 276 | 286 | base: Inst.Index, |
| 277 | 287 | field_index: u32, |
| ... | ... | @@ -326,6 +336,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In |
| 326 | 336 | .store => air.instData(.memory, air.instData(.store, index).memory).ty, |
| 327 | 337 | .@"if" => air.instData(.@"if", index).data.get(air).union_ty, |
| 328 | 338 | .struct_init => air.instData(.struct_init, index).ty, |
| 339 | .array_init => air.instData(.array_init, index).ty, | |
| 329 | 340 | .arg => air.instData(.arg, index).type, |
| 330 | 341 | |
| 331 | 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 | 378 | .toStructType(ip) orelse unreachable; // not a struct |
| 368 | 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 | 30 | .node_tags = ast.nodes.items(.tag), |
| 31 | 31 | .node_data = ast.nodes.items(.data), |
| 32 | 32 | .node_tokens = ast.nodes.items(.main_token), |
| 33 | .tty = detectTTYFromWriter(writer), | |
| 33 | .tty = core.detectTTYFromWriter(writer), | |
| 34 | 34 | }; |
| 35 | 35 | try dumpNode(writer, &state, 0); |
| 36 | 36 | try writer.writeAll("\n"); |
| ... | ... | @@ -127,6 +127,8 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err |
| 127 | 127 | => try dumpNodeList(writer, state, list, '{', '}'), |
| 128 | 128 | .builtin_call, |
| 129 | 129 | => try dumpNodeList(writer, state, list, '(', ')'), |
| 130 | .array_init, | |
| 131 | => try dumpNodeList(writer, state, list, '[', ']'), | |
| 130 | 132 | else => unreachable, |
| 131 | 133 | } |
| 132 | 134 | }, |
| ... | ... | @@ -145,14 +147,18 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err |
| 145 | 147 | try tty.setColor(writer, .reset); |
| 146 | 148 | var indent = false; |
| 147 | 149 | if (var_decl.type) |type_node| { |
| 150 | try writer.writeAll(" {"); | |
| 148 | 151 | state.indent += 1; |
| 149 | 152 | indent = true; |
| 150 | 153 | try state.newline(writer); |
| 151 | try writer.writeAll(": "); | |
| 154 | try writer.writeAll("type: "); | |
| 152 | 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 | 162 | try dumpNode(writer, state, var_decl.init); |
| 157 | 163 | if (indent) { |
| 158 | 164 | state.indent -= 1; |
| ... | ... | @@ -312,7 +318,8 @@ fn dumpNodeList(writer: anytype, state: *State, list: []const NodeIndex, l: u8, |
| 312 | 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 | 323 | try writer.print("{c}", .{r}); |
| 317 | 324 | } |
| 318 | 325 | |
| ... | ... | @@ -321,10 +328,9 @@ const Dump = @This(); |
| 321 | 328 | const std = @import("std"); |
| 322 | 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 | 333 | const NodeIndex = Ast.NodeIndex; |
| 326 | 334 | const Node = Ast.Node; |
| 327 | 335 | const TokenIndex = Ast.TokenIndex; |
| 328 | 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 | 218 | struct_field, |
| 219 | 219 | /// Struct Init |
| 220 | 220 | struct_init, |
| 221 | /// Array type | |
| 222 | array_type, | |
| 223 | /// Array initialization | |
| 224 | array_init, | |
| 221 | 225 | |
| 222 | 226 | pub fn isAssignBin(op: Tag) bool { |
| 223 | 227 | return switch (op) { |
| ... | ... | @@ -333,6 +337,8 @@ pub const Node = struct { |
| 333 | 337 | .shr_assign = .bin, |
| 334 | 338 | .string = .none, |
| 335 | 339 | .struct_decl = .list, |
| 340 | .array_type = .un, | |
| 341 | .array_init = .list, | |
| 336 | 342 | .struct_field = .un, |
| 337 | 343 | .struct_init = .struct_init, |
| 338 | 344 | .sub = .bin, |
| ... | ... | @@ -784,12 +790,44 @@ const Ast = @This(); |
| 784 | 790 | const std = @import("std"); |
| 785 | 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 | 796 | const Token = Tokenizer.Token; |
| 789 | 797 | const TokenList = Tokenizer.Token.List; |
| 790 | 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"); | |
| 795 | const Ordinal = util.Ordinal; | |
| 816 | pub fn fromOneBased(int: Int) @This() { | |
| 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 | 69 | function_type, |
| 70 | 70 | /// A struct type |
| 71 | 71 | struct_type, |
| 72 | /// An array type | |
| 73 | array_type, | |
| 72 | 74 | /// A type that can be represented with only an enum tag. |
| 73 | 75 | simple_type, |
| 74 | 76 | |
| ... | ... | @@ -92,6 +94,8 @@ pub const Tag = enum(u8) { |
| 92 | 94 | |
| 93 | 95 | struct_value, |
| 94 | 96 | |
| 97 | array_value, | |
| 98 | ||
| 95 | 99 | pub fn Data(comptime tag: Tag) type { |
| 96 | 100 | return switch (tag) { |
| 97 | 101 | .removed, |
| ... | ... | @@ -118,7 +122,9 @@ pub const Tag = enum(u8) { |
| 118 | 122 | .function_type => TypeFunction, |
| 119 | 123 | .string => String.WithLen, |
| 120 | 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 | 146 | /// Trailing: |
| 141 | 147 | /// 0. field_key: NullTerminatedString. Repeated per field_len |
| 142 | 148 | /// 1. field_type: Type. Repeated per field_len |
| 143 | pub const TypeStruct = struct { | |
| 149 | pub const StructType = struct { | |
| 144 | 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 | 166 | /// Trailing: |
| 148 | 167 | /// 0. value: Value. Repeated ty.structInfo().field_len |
| 149 | 168 | pub const StructValue = struct { |
| ... | ... | @@ -155,9 +174,9 @@ pub const Tag = enum(u8) { |
| 155 | 174 | /// This is an index into .items |
| 156 | 175 | pub const Value = enum(u32) { |
| 157 | 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 | 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 | 181 | opaque_type, |
| 163 | 182 | bool_type, |
| ... | ... | @@ -167,6 +186,8 @@ pub const Value = enum(u32) { |
| 167 | 186 | noreturn_type, |
| 168 | 187 | type_type, |
| 169 | 188 | numeric_literal_type, |
| 189 | numeric_literal_array_type, | |
| 190 | noreturn_array_type, | |
| 170 | 191 | |
| 171 | 192 | void, |
| 172 | 193 | bool_true, |
| ... | ... | @@ -175,7 +196,7 @@ pub const Value = enum(u32) { |
| 175 | 196 | float_zero, |
| 176 | 197 | nan, |
| 177 | 198 | empty_string, |
| 178 | standard_library, | |
| 199 | empty_array, | |
| 179 | 200 | |
| 180 | 201 | none = std.math.maxInt(u32), |
| 181 | 202 | _, |
| ... | ... | @@ -183,7 +204,7 @@ pub const Value = enum(u32) { |
| 183 | 204 | /// An array of `Value` existing within the `extra` array. |
| 184 | 205 | /// This type exists to provide a struct with lifetime that is |
| 185 | 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 | 208 | pub const Slice = struct { |
| 188 | 209 | start: u32, |
| 189 | 210 | len: u32, |
| ... | ... | @@ -237,7 +258,7 @@ pub const Value = enum(u32) { |
| 237 | 258 | // TODO: move the formatter out of Air.Dump |
| 238 | 259 | var state: Air.Dump.State = undefined; |
| 239 | 260 | state.ip = ctx.ip; |
| 240 | state.tty = @import("util.zig").detectTTYFromWriter(writer); | |
| 261 | state.tty = core.detectTTYFromWriter(writer); | |
| 241 | 262 | try Air.Dump.State.dumpValue(&state, writer, ctx.val); |
| 242 | 263 | } |
| 243 | 264 | |
| ... | ... | @@ -245,9 +266,11 @@ pub const Value = enum(u32) { |
| 245 | 266 | return switch (value.valueTag(ip)) { |
| 246 | 267 | .removed => unreachable, // UAF |
| 247 | 268 | |
| 248 | .function_type => .type, | |
| 249 | .simple_type => .type, | |
| 250 | .struct_type => .type, | |
| 269 | .function_type, | |
| 270 | .simple_type, | |
| 271 | .struct_type, | |
| 272 | .array_type, | |
| 273 | => .type, | |
| 251 | 274 | |
| 252 | 275 | .simple_value => switch (value.valueData(ip, .simple_value)) { |
| 253 | 276 | .true => .bool, |
| ... | ... | @@ -261,6 +284,7 @@ pub const Value = enum(u32) { |
| 261 | 284 | .string => .string, |
| 262 | 285 | .func => value.toKey(ip).func.ty, |
| 263 | 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 | 297 | .function_type, |
| 274 | 298 | .struct_type, |
| 275 | 299 | .simple_type, |
| 300 | .array_type, | |
| 276 | 301 | => @enumFromInt(@intFromEnum(value)), |
| 277 | 302 | |
| 278 | 303 | // exception: void is a value AND a type |
| ... | ... | @@ -321,6 +346,8 @@ pub const Type = enum(u32) { |
| 321 | 346 | noreturn = @intFromEnum(Value.noreturn_type), |
| 322 | 347 | type = @intFromEnum(Value.type_type), |
| 323 | 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 | 351 | void = @intFromEnum(Value.void), |
| 325 | 352 | _, |
| 326 | 353 | |
| ... | ... | @@ -328,6 +355,7 @@ pub const Type = enum(u32) { |
| 328 | 355 | pub const Tag = enum(u32) { |
| 329 | 356 | function_type = @intFromEnum(InternPool.Tag.function_type), |
| 330 | 357 | struct_type = @intFromEnum(InternPool.Tag.struct_type), |
| 358 | array_type = @intFromEnum(InternPool.Tag.array_type), | |
| 331 | 359 | simple_type = @intFromEnum(InternPool.Tag.simple_type), |
| 332 | 360 | }; |
| 333 | 361 | |
| ... | ... | @@ -362,6 +390,11 @@ pub const Type = enum(u32) { |
| 362 | 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 | 398 | pub fn hasRuntimeRepresentation(ty: Type, ip: *const InternPool) bool { |
| 366 | 399 | return switch (ty) { |
| 367 | 400 | .bool, |
| ... | ... | @@ -377,8 +410,11 @@ pub const Type = enum(u32) { |
| 377 | 410 | => false, |
| 378 | 411 | else => switch (ty.tag(ip)) { |
| 379 | 412 | .simple_type => unreachable, // all simple types are already printed |
| 380 | .function_type => true, | |
| 381 | .struct_type => true, | |
| 413 | .struct_type, | |
| 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 | 464 | .struct_type => { |
| 429 | 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 | 505 | SimpleType.noreturn.key(), |
| 465 | 506 | SimpleType.type.key(), |
| 466 | 507 | SimpleType.numeric_literal.key(), |
| 508 | .{ .array_type = .{ .item_type = Type.numeric_literal } }, | |
| 509 | .{ .array_type = .{ .item_type = Type.noreturn } }, | |
| 467 | 510 | |
| 468 | 511 | SimpleValue.void.key(), |
| 469 | 512 | SimpleValue.true.key(), |
| ... | ... | @@ -471,6 +514,10 @@ pub const static_keys = [_]Key{ |
| 471 | 514 | .{ .int32 = 0 }, |
| 472 | 515 | .{ .float = 0.0 }, |
| 473 | 516 | .{ .float = Float.pure_nan.decode() }, |
| 517 | .{ .array_value = .{ | |
| 518 | .type = Type.noreturn_array, | |
| 519 | .items = &.{}, | |
| 520 | } }, | |
| 474 | 521 | .{ .string = "" }, |
| 475 | 522 | }; |
| 476 | 523 | |
| ... | ... | @@ -782,10 +829,12 @@ pub const Key = union(enum) { |
| 782 | 829 | simple_value: SimpleValue, |
| 783 | 830 | struct_value: StructValue, |
| 784 | 831 | func: Func, |
| 832 | array_value: ArrayValue, | |
| 785 | 833 | // Types |
| 786 | 834 | simple_type: SimpleType, |
| 787 | 835 | function_type: FunctionType, |
| 788 | 836 | struct_type: StructType, |
| 837 | array_type: ArrayType, | |
| 789 | 838 | |
| 790 | 839 | const Adapter = struct { |
| 791 | 840 | intern_pool: *const InternPool, |
| ... | ... | @@ -827,6 +876,7 @@ pub const Key = union(enum) { |
| 827 | 876 | |
| 828 | 877 | pub fn hash(self: StructType, hasher: *Hash) void { |
| 829 | 878 | // TODO: remove this function. structural typing is not used |
| 879 | // this should be hashing it's location / decl, exactly like `Func` | |
| 830 | 880 | std.hash.autoHashStrat(hasher, self, .DeepRecursive); |
| 831 | 881 | } |
| 832 | 882 | }; |
| ... | ... | @@ -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 | 913 | pub const Func = struct { |
| 845 | 914 | ty: Type, |
| 846 | 915 | owner_decl: DeclIndex, |
| ... | ... | @@ -874,22 +943,13 @@ pub const Key = union(enum) { |
| 874 | 943 | .string, |
| 875 | 944 | => |x| Hash.hash(seed, x), |
| 876 | 945 | .float, .comptime_float => |x| Hash.hash(seed, asBytes(&Float.assertPure(x))), |
| 877 | .function_type => |x| { | |
| 878 | var hasher = Hash.init(seed); | |
| 879 | x.hash(&hasher); | |
| 880 | return hasher.final(); | |
| 881 | }, | |
| 882 | .func => |x| { | |
| 883 | var hasher = Hash.init(seed); | |
| 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| { | |
| 946 | inline .function_type, | |
| 947 | .func, | |
| 948 | .struct_type, | |
| 949 | .struct_value, | |
| 950 | .array_value, | |
| 951 | .array_type, | |
| 952 | => |x| { | |
| 893 | 953 | var hasher = Hash.init(seed); |
| 894 | 954 | x.hash(&hasher); |
| 895 | 955 | return hasher.final(); |
| ... | ... | @@ -934,6 +994,14 @@ pub const Key = union(enum) { |
| 934 | 994 | return a_info.ty == b_info.ty and |
| 935 | 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 | 1134 | .function_type = ip.extraTypeFunction(data.decode(.function_type, item.tag)), |
| 1067 | 1135 | }, |
| 1068 | 1136 | .struct_type => { |
| 1069 | const payload: Tag.TypeStruct, var trail = | |
| 1137 | const payload: Tag.StructType, var trail = | |
| 1070 | 1138 | data.decode(.struct_type, item.tag).getTrail(ip); |
| 1071 | 1139 | |
| 1072 | 1140 | return .{ .struct_type = .{ |
| ... | ... | @@ -1085,6 +1153,19 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { |
| 1085 | 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 | 1228 | }, |
| 1148 | 1229 | .struct_type => |s| brk: { |
| 1149 | 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 | 1232 | .fields_len = @intCast(s.field_keys.len), |
| 1152 | 1233 | }, s.field_keys.len * 2); |
| 1153 | 1234 | trail.writeSlice(s.field_keys); |
| ... | ... | @@ -1163,6 +1244,19 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { |
| 1163 | 1244 | trail.writeSlice(s.field_values); |
| 1164 | 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 | 1261 | ip.items.appendAssumeCapacity(item); |
| 1168 | 1262 | const index: Value = @enumFromInt(ip.items.len - 1); |
| ... | ... | @@ -1332,13 +1426,12 @@ const Allocator = std.mem.Allocator; |
| 1332 | 1426 | const Hash = std.hash.Wyhash; |
| 1333 | 1427 | const assert = std.debug.assert; |
| 1334 | 1428 | |
| 1335 | const Comp = @import("Comp.zig"); | |
| 1336 | const Cache = @import("Cache.zig"); | |
| 1337 | ||
| 1338 | const Teg = @import("Teg.zig"); | |
| 1339 | const Air = @import("Air.zig"); | |
| 1340 | ||
| 1341 | const dod = @import("array.zig"); | |
| 1429 | const core = @import("mod.zig"); | |
| 1430 | const Comp = core.Comp; | |
| 1431 | const Cache = core.Cache; | |
| 1432 | const Teg = core.Teg; | |
| 1433 | const Air = core.Air; | |
| 1434 | const dod = core.array; | |
| 1342 | 1435 | const Float = dod.Float; |
| 1343 | 1436 | |
| 1344 | 1437 | const tracer = @import("tracer"); |
src/core/Parse.zig+77-15| ... | ... | @@ -141,6 +141,8 @@ pub const AstError = union(enum) { |
| 141 | 141 | /// Went past depth maximum |
| 142 | 142 | depth_limit: Token, |
| 143 | 143 | |
| 144 | array_type_flipped_hint: Token, | |
| 145 | ||
| 144 | 146 | pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: Report.DebugStoredTrace) !Report { |
| 145 | 147 | return (Report.Err{ .ast = err }).toReport(comp, file, trace); |
| 146 | 148 | } |
| ... | ... | @@ -176,6 +178,13 @@ pub const AstError = union(enum) { |
| 176 | 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 | 188 | .depth_limit => |data| { |
| 180 | 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 | 246 | => try p.scratch.append(p.gpa, try p.parseModuleDecl()), |
| 238 | 247 | |
| 239 | 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 | 251 | .kw_enum, |
| 243 | 252 | .kw_trait, |
| ... | ... | @@ -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 | 396 | const trace = t.trace(@src()); |
| 388 | 397 | defer trace.end(); |
| 389 | 398 | |
| 390 | 399 | const main_token = p.assertToken(.kw_struct); |
| 391 | 400 | |
| 392 | _ = ctx; // TODO: | |
| 393 | 401 | _ = try p.expect(.id); |
| 394 | 402 | |
| 395 | 403 | _ = try p.expect(.start_brace); |
| ... | ... | @@ -586,6 +594,7 @@ fn isExpressionStart(p: *Parse) bool { |
| 586 | 594 | .number_float, |
| 587 | 595 | .number_int, |
| 588 | 596 | .start_brace, |
| 597 | .start_bracket, | |
| 589 | 598 | .start_paren, |
| 590 | 599 | .string, |
| 591 | 600 | .tilde, |
| ... | ... | @@ -840,13 +849,14 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 840 | 849 | |
| 841 | 850 | // Complex expressions |
| 842 | 851 | .builtin_id => try p.parseBuiltinCall(), |
| 843 | .kw_return => try p.parseReturn(), | |
| 852 | .dot => try p.parseAnonInstantiation(), | |
| 844 | 853 | .kw_break => try p.parseBreak(), |
| 854 | .kw_fn => try p.parseAnonFunction(), | |
| 845 | 855 | .kw_if => try p.parseIf(), |
| 846 | .start_paren => try p.parseGroup(), | |
| 856 | .kw_return => try p.parseReturn(), | |
| 847 | 857 | .start_brace => try p.parseBlock(), |
| 848 | .dot => try p.parseAnonInstantiation(), | |
| 849 | .kw_fn => try p.parseAnonFunction(), | |
| 858 | .start_bracket => try p.parseArrayValueOrType(mode), | |
| 859 | .start_paren => try p.parseGroup(), | |
| 850 | 860 | |
| 851 | 861 | // Unary operators |
| 852 | 862 | inline .bang, |
| ... | ... | @@ -900,7 +910,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 900 | 910 | }, |
| 901 | 911 | .start_paren => { |
| 902 | 912 | const main_token = p.nextToken(); |
| 903 | const args = try p.parseCallArgs(); | |
| 913 | const args = try p.parseExpressionList(.end_paren, .argument); | |
| 904 | 914 | |
| 905 | 915 | const extra = try p.addExtra(Ast.Call, .{ |
| 906 | 916 | .args = args, |
| ... | ... | @@ -917,7 +927,12 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex { |
| 917 | 927 | }, |
| 918 | 928 | .start_bracket => { |
| 919 | 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 | 937 | expr = try p.addNode(.{ |
| 923 | 938 | .tag = .index, |
| ... | ... | @@ -1034,7 +1049,7 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex { |
| 1034 | 1049 | |
| 1035 | 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 | 1054 | return try p.addNode(.{ |
| 1040 | 1055 | .tag = .builtin_call, |
| ... | ... | @@ -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 | 1066 | const trace = t.trace(@src()); |
| 1048 | 1067 | defer trace.end(); |
| 1049 | 1068 | |
| ... | ... | @@ -1054,19 +1073,19 @@ fn parseCallArgs(p: *Parse) Error!SubList { |
| 1054 | 1073 | try p.scratch.append(p.gpa, try p.parseExpression(.expression)); |
| 1055 | 1074 | |
| 1056 | 1075 | switch (p.currentTag()) { |
| 1057 | .end_paren => { | |
| 1076 | end_token => { | |
| 1058 | 1077 | break; |
| 1059 | 1078 | }, |
| 1060 | 1079 | .comma => { |
| 1061 | 1080 | _ = p.nextToken(); |
| 1062 | if (p.currentTag() == .end_paren) | |
| 1081 | if (p.currentTag() == end_token) | |
| 1063 | 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 | 1090 | return p.scratchToSubList(scratch_top); |
| 1072 | 1091 | } |
| ... | ... | @@ -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 | 1269 | // helpers |
| 1208 | 1270 | |
| 1209 | 1271 | /// Append a node and return its index. |
src/core/Report.zig+21-3| ... | ... | @@ -490,10 +490,25 @@ pub const Warn = union(enum) { |
| 490 | 490 | |
| 491 | 491 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 492 | 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 | 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 | 514 | /// A variant of std.builtin.StackTrace that stores its data within itself |
| ... | ... | @@ -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 | 1088 | else => {}, |
| 1069 | 1089 | } |
| 1070 | 1090 | |
| ... | ... | @@ -1318,5 +1338,3 @@ const File = Comp.File; |
| 1318 | 1338 | const InternPool = @import("InternPool.zig"); |
| 1319 | 1339 | const Value = InternPool.Value; |
| 1320 | 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 | 36 | |
| 37 | 37 | // temp data: |
| 38 | 38 | inst_map: InstMap = undefined, |
| 39 | /// scratch memory. can be used in any place you might temporarily allocate an arraylist | |
| 40 | /// this is most notably used to store block contents before the block is finalized | |
| 41 | scratch: std.ArrayListUnmanaged(u32) = .{}, | |
| 39 | /// Scratch memory for temporary allocations. | |
| 40 | /// Block contents are appended for marks. | |
| 41 | scratch: core.U32Stack = .{}, | |
| 42 | 42 | |
| 43 | 43 | /// This is only set for function declarations |
| 44 | 44 | params: []Type = undefined, |
| ... | ... | @@ -134,7 +134,7 @@ pub fn deinit(s: *Sema) void { |
| 134 | 134 | |
| 135 | 135 | defer s.* = undefined; |
| 136 | 136 | s.inst_map.deinit(s.arena); |
| 137 | s.scratch.deinit(s.arena); | |
| 137 | s.scratch.list.deinit(s.arena); | |
| 138 | 138 | s.air_extra.deinit(s.gpa); |
| 139 | 139 | s.air_instructions.deinit(s.gpa); |
| 140 | 140 | s.temp_result_map.deinit(s.arena); |
| ... | ... | @@ -205,7 +205,7 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value { |
| 205 | 205 | defer s.gpa.free(s.params); |
| 206 | 206 | |
| 207 | 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 | 209 | const ty = try s.resolveType(src, param); |
| 210 | 210 | if (!ty.hasRuntimeRepresentation(s.ip)) { |
| 211 | 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 | 268 | const t = tracer.trace(@src()); |
| 269 | 269 | defer t.end(); |
| 270 | 270 | |
| 271 | const scratch_top = s.scratch.items.len; | |
| 272 | defer s.scratch.shrinkRetainingCapacity(scratch_top); | |
| 271 | const scratch = s.scratch.mark(); | |
| 272 | defer scratch.deinit(); | |
| 273 | 273 | |
| 274 | 274 | const block = try s.addBlock(); |
| 275 | 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 | 284 | }; |
| 285 | 285 | } |
| 286 | 286 | |
| 287 | if (scratch_top + 1 == s.scratch.items.len) { | |
| 288 | const single_instruction = s.air_instructions.items(.tag)[@intFromEnum(block.block_index) + 1]; | |
| 287 | if (scratch.len() == 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 | 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 | 295 | .decode(.@"break", single_instruction); |
| 292 | 296 | if (data.block_ref == block.block_index) { |
| 293 | 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 | 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 | 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 | 538 | for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| { |
| 535 | 539 | const ref = try s.resolveRef(arg); |
| 536 | 540 | const coerced = try s.coerceRef( |
| 537 | in.op(Report.Operand.argument(@intCast(i))), | |
| 541 | in.op(operandArgument(@intCast(i))), | |
| 538 | 542 | ref, |
| 539 | 543 | param, |
| 540 | 544 | ); |
| ... | ... | @@ -543,7 +547,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 543 | 547 | } else { |
| 544 | 548 | for (call.args.slice(&s.code), 0..) |arg, i| { |
| 545 | 549 | const ref = try s.ensureValueRuntime( |
| 546 | in.op(Report.Operand.argument(@intCast(i))), | |
| 550 | in.op(operandArgument(@intCast(i))), | |
| 547 | 551 | try s.resolveRef(arg), |
| 548 | 552 | ); |
| 549 | 553 | scratch[i] = ref; |
| ... | ... | @@ -571,7 +575,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 571 | 575 | defer s.arena.free(scratch); |
| 572 | 576 | for (call.args.slice(&s.code), 0..) |arg, i| { |
| 573 | 577 | const ref = try s.ensureValueRuntime( |
| 574 | in.op(Report.Operand.argument(@intCast(i))), | |
| 578 | in.op(operandArgument(@intCast(i))), | |
| 575 | 579 | try s.resolveRef(arg), |
| 576 | 580 | ); |
| 577 | 581 | scratch[i] = ref; |
| ... | ... | @@ -635,7 +639,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 635 | 639 | const block = try s.analyzeBlock(in); |
| 636 | 640 | switch (block.decode()) { |
| 637 | 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 | 644 | else => {}, |
| 641 | 645 | } |
| ... | ... | @@ -692,7 +696,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 692 | 696 | .compile_error => { |
| 693 | 697 | const node: Teg.Inst.RefNode = s.code.instData(.compile_error, in); |
| 694 | 698 | const value = try s.resolveCoerceValue( |
| 695 | in.op(Report.Operand.argument(0)), | |
| 699 | in.op(operandArgument(0)), | |
| 696 | 700 | node.operand, |
| 697 | 701 | .string, |
| 698 | 702 | ); |
| ... | ... | @@ -705,42 +709,26 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 705 | 709 | .struct_decl => { |
| 706 | 710 | const node: Teg.Inst.StructDecl = s.code.instPayload(.struct_decl, in); |
| 707 | 711 | |
| 708 | const scratch_top = s.scratch.items.len; | |
| 709 | try s.scratch.ensureUnusedCapacity(s.arena, node.field_len * 2); | |
| 710 | s.scratch.items.len += node.field_len * 2; | |
| 711 | defer s.scratch.shrinkRetainingCapacity(scratch_top); | |
| 712 | ||
| 713 | for ( | |
| 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 | } | |
| 712 | const scratch = try s.scratch.reserveMulti( | |
| 713 | s.arena, | |
| 714 | .{ .keys = NullTerminatedString, .types = Type }, | |
| 715 | .{ node.field_len, node.field_len }, | |
| 716 | ); | |
| 717 | defer scratch.deinit(); | |
| 724 | 718 | |
| 725 | for ( | |
| 726 | node.types(&s.code), | |
| 727 | // We cannot reuse the pointer from scratch between calls to sema | |
| 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 | )); | |
| 719 | // Fill the first half with keys | |
| 720 | for (scratch.slice(.keys), node.keys(&s.code)) |*dest, src| { | |
| 721 | dest.* = try s.ip.getOrPutString(s.gpa, src.slice(s.code.string_bytes), .no_embedded_nulls); | |
| 736 | 722 | } |
| 737 | 723 | |
| 738 | const keys = s.scratch.items[scratch_top..][0..node.field_len]; | |
| 739 | const types = s.scratch.items[scratch_top + node.field_len ..][0..node.field_len]; | |
| 724 | // Fill the second half with coerced values. | |
| 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 | 729 | return try s.makeValueAir(.{ .struct_type = .{ |
| 742 | .field_keys = @ptrCast(keys), | |
| 743 | .field_types = @ptrCast(types), | |
| 730 | .field_keys = scratch.slice(.keys), | |
| 731 | .field_types = scratch.slice(.types), | |
| 744 | 732 | } }); |
| 745 | 733 | }, |
| 746 | 734 | .struct_init => { |
| ... | ... | @@ -760,22 +748,13 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 760 | 748 | var is_constant = true; |
| 761 | 749 | |
| 762 | 750 | // Allocate scratch space to store the values as they are being prepared. |
| 763 | // TODO: make this an abstraction | |
| 764 | const scratch_top = s.scratch.items.len; | |
| 765 | ||
| 766 | try s.scratch.ensureUnusedCapacity(s.arena, struct_type.field_types.len); | |
| 767 | const storage: []Air.Inst.Ref.Optional = | |
| 768 | @ptrCast(s.scratch.unusedCapacitySlice()[0..struct_type.field_types.len]); | |
| 769 | s.scratch.items.len += storage.len; | |
| 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 | } | |
| 751 | const scratch = try s.scratch.reserve( | |
| 752 | s.arena, | |
| 753 | Air.Inst.Ref.Optional, | |
| 754 | struct_type.field_types.len, | |
| 755 | ); | |
| 756 | defer scratch.deinit(); | |
| 757 | @memset(scratch.slice(), .none); | |
| 779 | 758 | |
| 780 | 759 | for (node.keys(&s.code), node.inits(&s.code), 0..) |init_key, init_value, i| { |
| 781 | 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 | 768 | has_errors = true; |
| 790 | 769 | try s.addError(.struct_init_unknown_field, .{ |
| 791 | 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 | 773 | continue; |
| 795 | 774 | }; |
| 796 | if (storage[found_index] != .none) { | |
| 775 | if (scratch.at(found_index).* != .none) { | |
| 797 | 776 | has_errors = true; |
| 798 | 777 | try s.addError(.struct_init_duplicate_field, .{ |
| 799 | 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 | 781 | continue; |
| 803 | 782 | } |
| 804 | // TODO: calling coerceRef / resolveRefRuntime will clobber the `storage` pointer | |
| 805 | 783 | const coerced = s.resolveCoerceRef( |
| 806 | in.op(Report.Operand.argument(@intCast(i))), | |
| 784 | in.op(operandArgument(@intCast(i))), | |
| 807 | 785 | init_value, |
| 808 | 786 | struct_type.field_types[found_index], |
| 809 | 787 | ) catch |e| switch (e) { |
| ... | ... | @@ -811,17 +789,18 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 811 | 789 | // Instead of bailing out with `try`, we will continue the loop. |
| 812 | 790 | has_errors = true; |
| 813 | 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 | 793 | continue; |
| 816 | 794 | }, |
| 817 | 795 | else => return e, |
| 818 | 796 | }; |
| 819 | 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 | 801 | // Check for missing fields |
| 824 | 802 | { |
| 803 | const storage = scratch.slice(); | |
| 825 | 804 | var i: usize = 0; |
| 826 | 805 | while (mem.indexOfScalarPos(Air.Inst.Ref.Optional, storage, i, .none)) |index| { |
| 827 | 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 | 818 | |
| 840 | 819 | if (is_constant) { |
| 841 | 820 | // Convert all items in storage into a Value type |
| 842 | const result_storage: []Value = @ptrCast(storage); | |
| 843 | for (storage, result_storage) |ref, *value| { | |
| 821 | const result_storage = scratch.sliceReinterpret(Value); | |
| 822 | for (scratch.slice(), scratch.sliceReinterpret(Value)) |ref, *value| { | |
| 844 | 823 | value.* = ref.unwrap().?.decode().value; |
| 845 | 824 | } |
| 846 | 825 | return try s.makeValueAir(.{ .struct_value = .{ |
| ... | ... | @@ -848,16 +827,80 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { |
| 848 | 827 | .field_values = result_storage, |
| 849 | 828 | } }); |
| 850 | 829 | } else { |
| 851 | // TODO: make this less error prone | |
| 852 | const result_storage: []u32 = @ptrCast(storage); | |
| 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); | |
| 830 | // Create a runtime struct instantiation | |
| 831 | const value_index = try s.air_extra.appendSlice(s.gpa, scratch.slice()); | |
| 855 | 832 | return try s.addInstRef(.struct_init, .{ |
| 856 | 833 | .ty = ty, |
| 857 | 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 | 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 | 914 | const t = tracer.trace(@src()); |
| 872 | 915 | defer t.end(); |
| 873 | 916 | |
| 874 | const scratch_top = s.scratch.items.len; | |
| 875 | defer s.scratch.shrinkRetainingCapacity(scratch_top); | |
| 917 | const scratch = s.scratch.mark(); | |
| 918 | defer scratch.deinit(); | |
| 876 | 919 | |
| 877 | 920 | const visited = s.resolveRef(in) catch |err| switch (err) { |
| 878 | 921 | error.HitNoReturn => Value.void.toAir(), |
| 879 | 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 | 926 | return visited; |
| 884 | 927 | } |
| 885 | 928 | |
| ... | ... | @@ -894,25 +937,27 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) Error!Air.Inst.Ref { |
| 894 | 937 | .block_ref = block.block_index, |
| 895 | 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 | 943 | fn resolveRefExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) Error!Air.Inst.Ref { |
| 901 | const index = ref.toIndex() orelse { | |
| 902 | return ref.toValue().?.toAir(); | |
| 903 | }; | |
| 944 | return switch (ref.decode()) { | |
| 945 | .value => |value| value.toAir(), | |
| 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| { | |
| 906 | if (mark) { | |
| 907 | switch (air.decode()) { | |
| 908 | .index => |i| s.markInst(i), | |
| 909 | else => {}, | |
| 955 | return air; | |
| 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 | 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 | 1042 | } |
| 998 | 1043 | |
| 999 | 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 { | |
| 1001 | const t = tracer.traceFmt(@src(), "{} to {}", .{ value.dump(s.ip), to.dump(s.ip) }); | |
| 1045 | fn coerceRef(s: *Sema, src: InstOp, ref: Air.Inst.Ref, to: Type) !Air.Inst.Ref { | |
| 1046 | const t = tracer.traceFmt(@src(), "{} to {}", .{ ref.dump(s.ip), to.dump(s.ip) }); | |
| 1002 | 1047 | defer t.end(); |
| 1003 | 1048 | |
| 1004 | const from = s.typeOfRef(value); | |
| 1049 | const from = s.typeOfRef(ref); | |
| 1005 | 1050 | if (from == to) { |
| 1006 | return value; | |
| 1051 | return ref; | |
| 1007 | 1052 | } |
| 1008 | 1053 | if (from == .noreturn) { |
| 1009 | 1054 | return error.HitNoReturn; |
| 1010 | 1055 | } |
| 1011 | 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 | 1060 | .value => |v| return (try s.coerceValue(src, v, to)).toAir(), |
| 1016 | 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 | 1098 | } |
| 1054 | 1099 | |
| 1055 | 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 | 1102 | const from = value.typeOf(s.ip); |
| 1058 | 1103 | if (from == to) { |
| 1059 | 1104 | return value; |
| ... | ... | @@ -1069,11 +1114,7 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value { |
| 1069 | 1114 | .int32 => unreachable, // same type handled |
| 1070 | 1115 | .numeric_literal, .float => switch (value.toKey(s.ip)) { |
| 1071 | 1116 | .comptime_float, .float => |val| { |
| 1072 | if (!math.isFinite(val) or | |
| 1073 | val < math.minInt(i32) or | |
| 1074 | val > math.maxInt(i32) or | |
| 1075 | (@rem(val, 1) != 0)) | |
| 1076 | { | |
| 1117 | if (!core.inInt32Range(val)) { | |
| 1077 | 1118 | return s.fail(.comptime_float_to_int, .{ |
| 1078 | 1119 | .val = val, |
| 1079 | 1120 | .src = s.lazyNode(src), |
| ... | ... | @@ -1115,24 +1156,49 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value { |
| 1115 | 1156 | // non-types do not coerce to types |
| 1116 | 1157 | }, |
| 1117 | 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 | 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 | 1171 | switch (ref.decode()) { |
| 1125 | 1172 | .value => |v| { |
| 1126 | 1173 | const ty = v.typeOf(s.ip); |
| 1127 | 1174 | switch (ty) { |
| 1128 | 1175 | .numeric_literal => { |
| 1129 | try s.report.warn(.numeric_int_var_ambiguity, .{ | |
| 1130 | .src = s.lazyNode(src), | |
| 1131 | }); | |
| 1176 | const float = v.toFloat(s.ip) orelse unreachable; | |
| 1177 | ||
| 1178 | if (!core.inInt32Range(float)) { | |
| 1179 | try s.report.warn(.numeric_int_var_ambiguity, .{ | |
| 1180 | .src = s.lazyNode(src), | |
| 1181 | }); | |
| 1182 | } | |
| 1183 | ||
| 1132 | 1184 | return (try s.makeValue(.{ |
| 1133 | .float = v.toFloat(s.ip) orelse unreachable, | |
| 1185 | .float = float, | |
| 1134 | 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 | 1202 | .type, |
| 1137 | 1203 | .noreturn, |
| 1138 | 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 | 1213 | .string, |
| 1148 | 1214 | .void, |
| 1149 | 1215 | => {}, |
| 1150 | _ => switch (ty.tag(s.ip)) { | |
| 1151 | .simple_type => unreachable, // handled above | |
| 1152 | ||
| 1153 | // Allowed at runtime | |
| 1154 | .function_type => {}, | |
| 1155 | .struct_type => {}, | |
| 1216 | _ => if (!ty.hasRuntimeRepresentation(s.ip)) { | |
| 1217 | return s.fail(.runtime_incompatible_type, .{ | |
| 1218 | .src = s.lazyNode(src), | |
| 1219 | .type = ty, | |
| 1220 | }); | |
| 1156 | 1221 | }, |
| 1157 | 1222 | } |
| 1158 | 1223 | }, |
| ... | ... | @@ -1161,6 +1226,24 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref { |
| 1161 | 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 | 1247 | fn resolveDecl(s: *Sema, src: InstOp, decl_index: Decl.Index) Error!Value { |
| 1165 | 1248 | const decl = s.ip.declPtr(decl_index); |
| 1166 | 1249 | switch (decl.analysis) { |
| ... | ... | @@ -1382,8 +1465,8 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { |
| 1382 | 1465 | const IncompleteBlock = struct { |
| 1383 | 1466 | block_index: Air.Inst.Index, |
| 1384 | 1467 | |
| 1385 | pub fn finish(block: IncompleteBlock, s: *Sema, scratch_top: usize) !Air.Inst.Index { | |
| 1386 | s.setInstData(.block, block.block_index, try s.scratchToIndexList(scratch_top)); | |
| 1468 | pub fn finish(block: IncompleteBlock, s: *Sema, scratch_mark: core.U32Stack.Mark) !Air.Inst.Index { | |
| 1469 | s.setInstData(.block, block.block_index, try s.scratchMarkToIndexList(scratch_mark)); | |
| 1387 | 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 | 1487 | .flags = .{}, |
| 1405 | 1488 | }); |
| 1406 | 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 | 1491 | return index.toRef(); |
| 1409 | 1492 | } |
| 1410 | 1493 | |
| ... | ... | @@ -1423,27 +1506,30 @@ fn makeValue(s: *Sema, key: InternPool.Key) !Value { |
| 1423 | 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 | 1513 | fn makeValueAir(s: *Sema, key: InternPool.Key) !Air.Inst.Ref { |
| 1427 | 1514 | return (try s.makeValue(key)).toAir(); |
| 1428 | 1515 | } |
| 1429 | 1516 | |
| 1430 | 1517 | fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref { |
| 1431 | if (inst.toValue()) |interned| { | |
| 1432 | return interned.toAir(); | |
| 1433 | } | |
| 1434 | const index = inst.toIndex().?; | |
| 1435 | return s.inst_map.get(index) orelse { | |
| 1436 | if (std.debug.runtime_safety) { | |
| 1437 | std.debug.panic("Unmapped operand %{d}", .{@intFromEnum(index)}); | |
| 1438 | } | |
| 1439 | unreachable; | |
| 1518 | return switch (inst.decode()) { | |
| 1519 | .value => |value| value.toAir(), | |
| 1520 | .index => |index| s.inst_map.get(index) orelse { | |
| 1521 | if (std.debug.runtime_safety) { | |
| 1522 | std.debug.panic("Unmapped operand %{d}", .{@intFromEnum(index)}); | |
| 1523 | } | |
| 1524 | unreachable; | |
| 1525 | }, | |
| 1440 | 1526 | }; |
| 1441 | 1527 | } |
| 1442 | 1528 | |
| 1443 | fn scratchToIndexList(s: *Sema, scratch_top: usize) !Air.IndexList { | |
| 1444 | const items = s.scratch.items[scratch_top..]; | |
| 1529 | fn scratchMarkToIndexList(s: *Sema, mark: core.U32Stack.Mark) !Air.IndexList { | |
| 1530 | const items = mark.slice(u32); | |
| 1445 | 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 | 1533 | return .{ |
| 1448 | 1534 | .start = @intCast(extra_top), |
| 1449 | 1535 | .len = @intCast(items.len), |
| ... | ... | @@ -1524,12 +1610,15 @@ const Teg = core.Teg; |
| 1524 | 1610 | const InstOp = Teg.InstOp; |
| 1525 | 1611 | |
| 1526 | 1612 | const Air = core.Air; |
| 1527 | const Report = core.Report; | |
| 1528 | 1613 | const Ast = core.Ast; |
| 1529 | 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 | 1619 | const InternPool = core.InternPool; |
| 1533 | 1620 | const Value = InternPool.Value; |
| 1534 | 1621 | const Type = InternPool.Type; |
| 1535 | 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 | 101 | try tty.setColor(writer, .reset); |
| 102 | 102 | }, |
| 103 | 103 | inline .field, |
| 104 | .index, | |
| 104 | 105 | .add, |
| 105 | 106 | .sub, |
| 106 | 107 | .mul, |
| ... | ... | @@ -172,6 +173,7 @@ const State = struct { |
| 172 | 173 | .fetch, |
| 173 | 174 | .compile_error, |
| 174 | 175 | .type_of, |
| 176 | .array_type, | |
| 175 | 177 | => |comptime_tag| { |
| 176 | 178 | const data = teg.instData(comptime_tag, inst); |
| 177 | 179 | try writer.writeAll("("); |
| ... | ... | @@ -255,28 +257,59 @@ const State = struct { |
| 255 | 257 | try s.newline(writer); |
| 256 | 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 | 290 | fn dumpRef(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void { |
| 262 | if (ref.toIndex()) |index| { | |
| 263 | try s.dumpInst(writer, index); | |
| 264 | } else { | |
| 265 | try s.tty.setColor(writer, .blue); | |
| 266 | try writer.print("%{s}", .{@tagName(ref)}); | |
| 267 | try s.tty.setColor(writer, .reset); | |
| 291 | switch (ref.decode()) { | |
| 292 | .index => |index| try s.dumpInst(writer, index), | |
| 293 | .value => { | |
| 294 | try s.tty.setColor(writer, .blue); | |
| 295 | try writer.print("%{s}", .{@tagName(ref)}); | |
| 296 | try s.tty.setColor(writer, .reset); | |
| 297 | }, | |
| 268 | 298 | } |
| 269 | 299 | } |
| 270 | 300 | |
| 271 | 301 | fn dumpRefMarker(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void { |
| 272 | if (ref.toIndex()) |index| { | |
| 273 | try s.tty.setColor(writer, .blue); | |
| 274 | try writer.print("%{d}", .{@intFromEnum(index)}); | |
| 275 | try s.tty.setColor(writer, .reset); | |
| 276 | } else { | |
| 277 | try s.tty.setColor(writer, .blue); | |
| 278 | try writer.print("%{s}", .{@tagName(ref)}); | |
| 279 | try s.tty.setColor(writer, .reset); | |
| 302 | switch (ref.decode()) { | |
| 303 | .index => |index| { | |
| 304 | try s.tty.setColor(writer, .blue); | |
| 305 | try writer.print("%{d}", .{@intFromEnum(index)}); | |
| 306 | try s.tty.setColor(writer, .reset); | |
| 307 | }, | |
| 308 | .value => { | |
| 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 | 389 | const std = @import("std"); |
| 357 | 390 | const assert = std.debug.assert; |
| 358 | 391 | |
| 359 | const Teg = @import("Teg.zig"); | |
| 360 | ||
| 361 | const detectTTYFromWriter = @import("util.zig").detectTTYFromWriter; | |
| 392 | const core = @import("mod.zig"); | |
| 393 | const Teg = core.Teg; | |
| 394 | const detectTTYFromWriter = core.detectTTYFromWriter; |
src/core/Teg.zig+40-22| ... | ... | @@ -46,6 +46,9 @@ pub const Inst = struct { |
| 46 | 46 | struct_decl, |
| 47 | 47 | struct_init, |
| 48 | 48 | |
| 49 | array_type, | |
| 50 | array_init, | |
| 51 | ||
| 49 | 52 | add, |
| 50 | 53 | sub, |
| 51 | 54 | mul, |
| ... | ... | @@ -70,6 +73,7 @@ pub const Inst = struct { |
| 70 | 73 | bool_not, |
| 71 | 74 | bit_not, |
| 72 | 75 | field, |
| 76 | index, | |
| 73 | 77 | call, |
| 74 | 78 | @"comptime", |
| 75 | 79 | |
| ... | ... | @@ -103,6 +107,7 @@ pub const Inst = struct { |
| 103 | 107 | .js_new => PlNode(JsNew), |
| 104 | 108 | .struct_decl => PlNode(StructDecl), |
| 105 | 109 | .struct_init => PlNode(StructInit), |
| 110 | .array_init => PlNode(ArrayInit), | |
| 106 | 111 | |
| 107 | 112 | // Single instruction operand |
| 108 | 113 | .memory, |
| ... | ... | @@ -118,10 +123,12 @@ pub const Inst = struct { |
| 118 | 123 | .compile_error, |
| 119 | 124 | .@"comptime", |
| 120 | 125 | .type_of, |
| 126 | .array_type, | |
| 121 | 127 | => RefNode, |
| 122 | 128 | |
| 123 | 129 | // Two instruction operands |
| 124 | 130 | .field, |
| 131 | .index, | |
| 125 | 132 | .add, |
| 126 | 133 | .sub, |
| 127 | 134 | .mul, |
| ... | ... | @@ -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 | 161 | const NodeOffset = i32; |
| 155 | 162 | |
| 156 | 163 | pub fn PlNode(comptime Payload: type) type { |
| ... | ... | @@ -184,7 +191,7 @@ pub const Inst = struct { |
| 184 | 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 | 196 | pub fn toRef(i: Index) Inst.Ref { |
| 190 | 197 | return @enumFromInt(static_len + @intFromEnum(i)); |
| ... | ... | @@ -229,6 +236,8 @@ pub const Inst = struct { |
| 229 | 236 | noreturn_type, |
| 230 | 237 | type_type, |
| 231 | 238 | numeric_literal_type, |
| 239 | numeric_literal_array_type, | |
| 240 | noreturn_array_type, | |
| 232 | 241 | |
| 233 | 242 | void, |
| 234 | 243 | bool_true, |
| ... | ... | @@ -237,11 +246,7 @@ pub const Inst = struct { |
| 237 | 246 | float_zero, |
| 238 | 247 | nan, |
| 239 | 248 | empty_string, |
| 240 | standard_library, | |
| 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), | |
| 249 | empty_array, | |
| 245 | 250 | |
| 246 | 251 | _, |
| 247 | 252 | |
| ... | ... | @@ -258,8 +263,7 @@ pub const Inst = struct { |
| 258 | 263 | .{ .value = @enumFromInt(ref_int) }; |
| 259 | 264 | } |
| 260 | 265 | |
| 261 | pub fn toIndex(ref: Ref) ?Index { | |
| 262 | assert(ref != .none); | |
| 266 | pub fn index(ref: Ref) ?Index { | |
| 263 | 267 | const ref_int = @intFromEnum(ref); |
| 264 | 268 | if (ref_int >= Index.static_len) { |
| 265 | 269 | return @enumFromInt(ref_int - Index.static_len); |
| ... | ... | @@ -268,12 +272,7 @@ pub const Inst = struct { |
| 268 | 272 | } |
| 269 | 273 | } |
| 270 | 274 | |
| 271 | pub fn toIndexAllowNone(inst: Ref) ?Index { | |
| 272 | if (inst == .none) return null; | |
| 273 | return toIndex(inst); | |
| 274 | } | |
| 275 | ||
| 276 | pub fn toValue(inst: Ref) ?InternPool.Value { | |
| 275 | pub fn value(inst: Ref) ?InternPool.Value { | |
| 277 | 276 | const ref_int = @intFromEnum(inst); |
| 278 | 277 | |
| 279 | 278 | // TODO: is this > or >= |
| ... | ... | @@ -282,6 +281,15 @@ pub const Inst = struct { |
| 282 | 281 | |
| 283 | 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 | 295 | pub const Bin = 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 | 379 | /// This will contain trailing data depending on the .kind |
| 363 | 380 | pub const Decl = struct { |
| 364 | 381 | name: NullTerminatedString, |
| ... | ... | @@ -562,9 +579,10 @@ const std = @import("std"); |
| 562 | 579 | const mem = std.mem; |
| 563 | 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 | 586 | const Unused = Ast.Unused; |
| 569 | 587 | const NodeIndex = Ast.NodeIndex; |
| 570 | 588 | const TokenIndex = Ast.TokenIndex; |
| ... | ... | @@ -575,14 +593,14 @@ const optionalTokenIndex = OptionalTokenIndex.init; |
| 575 | 593 | const optionalNodeIndex = OptionalNodeIndex.init; |
| 576 | 594 | const optionalExtraIndex = OptionalExtraIndex.init; |
| 577 | 595 | |
| 578 | const dod = @import("array.zig"); | |
| 596 | const dod = core.array; | |
| 579 | 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 | 606 | const LazyNode = Report.LazyNode; |
src/core/Transform.zig+35-20| ... | ... | @@ -235,7 +235,7 @@ const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{ |
| 235 | 235 | .{ "bool", .bool_type }, |
| 236 | 236 | .{ "noreturn", .noreturn_type }, |
| 237 | 237 | .{ "type", .type_type }, |
| 238 | .{ "std", .standard_library }, | |
| 238 | // .{ "std", .standard_library }, | |
| 239 | 239 | }); |
| 240 | 240 | |
| 241 | 241 | fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.Index { |
| ... | ... | @@ -559,14 +559,14 @@ fn transformExpressionToBlock( |
| 559 | 559 | |
| 560 | 560 | // TODO: evaluate if `if` blocks would be fine in sema with instruction refs |
| 561 | 561 | // with this extra if check here, we filter out non-blocks. |
| 562 | if (result.toValue() != null) { | |
| 562 | if (result.value() != null) { | |
| 563 | 563 | assert(block == null); |
| 564 | 564 | return result; |
| 565 | 565 | } |
| 566 | 566 | } |
| 567 | 567 | |
| 568 | 568 | if (expr) |result| { |
| 569 | if (result.toIndex()) |index| { | |
| 569 | if (result.index()) |index| { | |
| 570 | 570 | if (t.insts.items(.tag)[@intFromEnum(index)] == .block) { |
| 571 | 571 | return result; |
| 572 | 572 | } |
| ... | ... | @@ -685,7 +685,7 @@ pub const Builtin = enum { |
| 685 | 685 | } |
| 686 | 686 | |
| 687 | 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 | 690 | return try t.addInstRef(.{ |
| 691 | 691 | .tag = .js_new, |
| ... | ... | @@ -923,6 +923,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 923 | 923 | inline .negate, |
| 924 | 924 | .bool_not, |
| 925 | 925 | .bit_not, |
| 926 | .array_type, | |
| 926 | 927 | => |tag| { |
| 927 | 928 | const ast_un = t.ast.nodeData(node).un; |
| 928 | 929 | const teg_tag = @field(Teg.Inst.Tag, @tagName(tag)); |
| ... | ... | @@ -1048,7 +1049,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1048 | 1049 | const args = t.ast.extraData(call.extra, Ast.Call).args.slice(&t.ast); |
| 1049 | 1050 | |
| 1050 | 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 | 1054 | const call_inst = try t.addInstRef(.{ |
| 1054 | 1055 | .tag = .call, |
| ... | ... | @@ -1227,11 +1228,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 1227 | 1228 | ) |field_node, i, j| { |
| 1228 | 1229 | const field = t.ast.fullStructField(field_node); |
| 1229 | 1230 | |
| 1230 | if (mem.startsWith(u8, field.id, "@")) { | |
| 1231 | @panic("TODO: string identifiers"); | |
| 1232 | } | |
| 1231 | const id = try t.decodeIdentifier(field.id); | |
| 1233 | 1232 | |
| 1234 | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(field.id))); | |
| 1233 | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id))); | |
| 1235 | 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 | 1263 | ) |field_node, i, j| { |
| 1265 | 1264 | const field = t.ast.fullStructField(field_node); |
| 1266 | 1265 | |
| 1267 | if (mem.startsWith(u8, field.id, "@")) { | |
| 1268 | @panic("TODO: string identifiers"); | |
| 1269 | } | |
| 1266 | const id = try t.decodeIdentifier(field.id); | |
| 1270 | 1267 | |
| 1271 | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(field.id))); | |
| 1268 | t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id))); | |
| 1272 | 1269 | t.extra.setU32(j, @intFromEnum((try t.transformExpression(decl, field.type)) orelse |
| 1273 | 1270 | @panic("TODO: print better error"))); |
| 1274 | 1271 | } |
| ... | ... | @@ -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 | 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 | 1309 | const extra_top = try t.extra.alloc(t.gpa, args.len); |
| 1295 | 1310 | |
| 1296 | 1311 | for (args, extra_top..) |arg, i| { |
| ... | ... | @@ -1492,7 +1507,7 @@ fn transformBlockStatement( |
| 1492 | 1507 | .node = node, |
| 1493 | 1508 | .data = switch (var_decl.is_const) { |
| 1494 | 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 | 1711 | const decoded = if (Tokenizer.stringHasEscapes(escaped)) brk: { |
| 1697 | 1712 | const len = Tokenizer.decodeStringCount(escaped); |
| 1698 | 1713 | |
| 1699 | // use the scratch arraylist as a temporary storage for | |
| 1700 | // decoding the string. this is done so that duplicate | |
| 1701 | // strings will get de-duplicated in string_bytes | |
| 1702 | // | |
| 1703 | // TODO: see if this is a useless optimization | |
| 1714 | // Use the scratch arraylist as a temporary storage for decoding the | |
| 1715 | // string. this is done so that duplicate strings will get de-duplicated | |
| 1716 | // in string_bytes. It is more likely that there will be remaining bytes | |
| 1717 | // in `.scratch` vs `.id_decode_buf` | |
| 1704 | 1718 | try t.scratch.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1); |
| 1705 | 1719 | const buf = mem.sliceAsBytes(t.scratch.unusedCapacitySlice()); |
| 1706 | 1720 | |
| 1707 | 1721 | break :brk Tokenizer.decodeStringBuf(escaped, buf); |
| 1708 | 1722 | } else escaped; |
| 1723 | ||
| 1709 | 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 | 372 | store.list.items.len += len; |
| 373 | 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 | 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 | 305 | else => unreachable, // this value is not a plain_value |
| 293 | 306 | } |
| 294 | 307 | } |
| ... | ... | @@ -410,6 +423,18 @@ fn printExprLevel( |
| 410 | 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 | 438 | inline .negate, |
| 414 | 439 | .bool_not, |
| 415 | 440 | .pre_dec, |
| ... | ... | @@ -505,7 +530,7 @@ fn printExprLevel( |
| 505 | 530 | |
| 506 | 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 | 536 | try writer.writeAll("}"); |
| ... | ... | @@ -513,6 +538,16 @@ fn printExprLevel( |
| 513 | 538 | if (level == .statement) |
| 514 | 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 | 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 | 495 | .function_type, |
| 496 | 496 | .simple_type, |
| 497 | 497 | .struct_type, |
| 498 | .array_type, | |
| 498 | 499 | => unreachable, // Type values are not allowed here |
| 499 | 500 | |
| 500 | 501 | .int32, |
| ... | ... | @@ -503,6 +504,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index { |
| 503 | 504 | .string, |
| 504 | 505 | .simple_value, |
| 505 | 506 | .struct_value, |
| 507 | .array_value, | |
| 506 | 508 | => return try c.add(.plain_value, .{ .value = v }), |
| 507 | 509 | |
| 508 | 510 | // Hoist functions as top level values |
| ... | ... | @@ -666,6 +668,8 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool { |
| 666 | 668 | .bool_not, |
| 667 | 669 | .store, |
| 668 | 670 | .struct_init, |
| 671 | .array_init, | |
| 672 | .index, | |
| 669 | 673 | => true, |
| 670 | 674 | |
| 671 | 675 | // JavaScript variable declarations require statements. |
src/core/js/Tree.zig+4| ... | ... | @@ -45,6 +45,8 @@ pub const Tag = enum { |
| 45 | 45 | field, |
| 46 | 46 | ternary, |
| 47 | 47 | object_literal, |
| 48 | array, | |
| 49 | index, | |
| 48 | 50 | // Prefix ops |
| 49 | 51 | negate, |
| 50 | 52 | bool_not, |
| ... | ... | @@ -81,6 +83,7 @@ pub const Tag = enum { |
| 81 | 83 | .eq, |
| 82 | 84 | .lt, |
| 83 | 85 | .gt, |
| 86 | .index, | |
| 84 | 87 | => Bin, |
| 85 | 88 | .ternary => Ternary, |
| 86 | 89 | .negate, |
| ... | ... | @@ -89,6 +92,7 @@ pub const Tag = enum { |
| 89 | 92 | .pre_dec, |
| 90 | 93 | => Un, |
| 91 | 94 | .object_literal => ObjectLiteral, |
| 95 | .array => IndexList, | |
| 92 | 96 | .call, .new => Call, |
| 93 | 97 | .field => Field, |
| 94 | 98 | .stmt_if => StmtIf, |
src/core/main.zig+7-7| ... | ... | @@ -28,14 +28,14 @@ pub fn main() !void { |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | 30 | const use_gpa = builtin.mode == .Debug; |
| 31 | var gpa: if (use_gpa) std.heap.GeneralPurposeAllocator(.{ | |
| 32 | .stack_trace_frames = 100, | |
| 33 | }) else struct {} = .{}; | |
| 34 | defer _ = if (use_gpa) gpa.deinit(); | |
| 35 | const alloc = if (use_gpa) | |
| 36 | gpa.allocator() | |
| 31 | var gpa = if (use_gpa) | |
| 32 | std.heap.GeneralPurposeAllocator(.{ | |
| 33 | .stack_trace_frames = 100, | |
| 34 | }){} | |
| 37 | 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 | 40 | var maybe_file_path: ?[]const u8 = null; |
| 41 | 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 | 29 | /// Semantic Analysis visits a `Teg` declaration and produces `Air` |
| 30 | 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 | 35 | /// Untyped Intermediate Representation. Flat instruction list |
| 33 | 36 | pub const Teg = @import("Teg.zig"); |
| 34 | 37 | |
| ... | ... | @@ -40,8 +43,6 @@ pub const Transform = @import("Transform.zig"); |
| 40 | 43 | |
| 41 | 44 | pub const unicode_props = @import("unicode_props.zig"); |
| 42 | 45 | |
| 43 | pub const util = @import("util.zig"); | |
| 44 | ||
| 45 | 46 | // sort-lines: end |
| 46 | 47 | |
| 47 | 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 | 65 | } |
| 65 | 66 | } |
| 66 | 67 | |
| 67 | pub const detectTTYFromWriter = util.detectTTYFromWriter; | |
| 68 | pub const Ordinal = util.Ordinal; | |
| 68 | /// If `value` can be cast to `i32` | |
| 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 | 98 | const std = @import("std"); |
| 71 | 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; |