authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-07-23 04:35:26-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-23 04:35:26-07:00
log7514b71286a8659c2917b78acf7f36b5da7e9657
tree8499f019e6984acda8b117f2074b2a0697316154
parent70f503e63e255d19e78dac36a1742e18562291c7
signature Signed by PGP key B5690EEEBB952194

Implement Arrays (#67)

To make it easier to create unbounded temporary allocations, such as in the case of creating a []Value for an array value, a new abstraction for `s.scratch` is added: `core.Stack` and `core.U32Stack`. This provides a safer interface for these operations.

21 files changed, 946 insertions(+), 333 deletions(-)

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