authorchloe caruso <git@paperclover.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
logb1f58dc720475cbc004da8de483b2aeae4fdb1fc
tree8499f019e6984acda8b117f2074b2a0697316154
parent5b5a3038aa08c430f60533153b0823267b3dbdff

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 @@
11## 0.1.4
22
33- feat: implement basic struct types (#42)
4- feat: implement basic array types (#67)
45- feat: implement string identifiers (#54)
56- feat: include a `README.html` alongside compiler with documentation and code samples.
67- feat(transform): include a warning for leading zero in numbers
......@@ -9,6 +10,7 @@
910- fix(sema): handle recursive function calls
1011- fix(sema): quit block analysis when hitting `noreturn`
1112- 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
1315## 0.1.3
1416
build.zig+6-6
......@@ -117,12 +117,12 @@ pub fn build(b: *std.Build) void {
117117 const win_version: OsVersion = .{ .windows = .win7 };
118118
119119 inline for (.{
120 b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version }),
121 b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version }),
122 b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos, .os_version_min = mac_version }),
123 b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .macos, .os_version_min = mac_version }),
124 b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu }),
125 b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu }),
120 b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version, .cpu_model = .baseline }),
121 b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version, .cpu_model = .baseline }),
122 b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos, .os_version_min = mac_version, .cpu_model = .baseline }),
123 b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .macos, .os_version_min = mac_version, .cpu_model = .baseline }),
124 b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu, .cpu_model = .baseline }),
125 b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu, .cpu_model = .baseline }),
126126 }) |cross_target| {
127127 const name = std.fmt.allocPrint(
128128 b.allocator,
src/core/Air.Dump.zig+39-6
......@@ -92,6 +92,7 @@ pub const State = struct {
9292 .lt,
9393 .gt,
9494 .eq,
95 .index,
9596 => |comptime_tag| {
9697 const bin: Air.Bin = air.instData(comptime_tag, inst);
9798 try w.writeAll("(");
......@@ -185,6 +186,15 @@ pub const State = struct {
185186 try s.newline(w);
186187 try w.writeAll("})");
187188 },
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 },
188198 }
189199 }
190200
......@@ -337,6 +347,31 @@ pub const State = struct {
337347 try w.writeAll(">");
338348 try tty.setColor(w, .reset);
339349 },
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 },
340375 }
341376 }
342377
......@@ -388,7 +423,7 @@ pub fn format(
388423 var state = State{
389424 .air = &air,
390425 .ip = dump.ip,
391 .tty = detectTTYFromWriter(writer),
426 .tty = core.detectTTYFromWriter(writer),
392427 .arena = arena.allocator(),
393428 };
394429 try state.dumpInst(writer, .main_block);
......@@ -399,10 +434,8 @@ pub fn format(
399434const std = @import("std");
400435const assert = std.debug.assert;
401436
402const Air = @import("Air.zig");
437const core = @import("mod.zig");
438const Air = core.Air;
403439const Inst = Air.Inst;
404
405const InternPool = @import("InternPool.zig");
440const InternPool = core.InternPool;
406441const Value = InternPool.Value;
407
408const detectTTYFromWriter = @import("util.zig").detectTTYFromWriter;
src/core/Air.zig+19
......@@ -25,10 +25,13 @@ pub const Inst = struct {
2525 store,
2626
2727 struct_init,
28 array_init,
2829
2930 field_opaque,
3031 field_struct,
3132
33 index,
34
3235 // bin
3336 add,
3437 sub,
......@@ -55,8 +58,10 @@ pub const Inst = struct {
5558 .memory => Memory,
5659 .store => Store,
5760 .struct_init => StructInit,
61 .array_init => ArrayInit,
5862 .field_opaque => FieldOpaque,
5963 .field_struct => FieldStruct,
64 .index,
6065 .add,
6166 .sub,
6267 .mul,
......@@ -272,6 +277,11 @@ pub const StructInit = struct {
272277 }
273278};
274279
280pub const ArrayInit = struct {
281 ty: Type,
282 list: RefList,
283};
284
275285pub const FieldStruct = struct {
276286 base: Inst.Index,
277287 field_index: u32,
......@@ -326,6 +336,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In
326336 .store => air.instData(.memory, air.instData(.store, index).memory).ty,
327337 .@"if" => air.instData(.@"if", index).data.get(air).union_ty,
328338 .struct_init => air.instData(.struct_init, index).ty,
339 .array_init => air.instData(.array_init, index).ty,
329340 .arg => air.instData(.arg, index).type,
330341
331342 // 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
367378 .toStructType(ip) orelse unreachable; // not a struct
368379 return base_type.field_types[inst.field_index];
369380 },
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 },
370389 };
371390}
372391
src/core/Ast.Dump.zig+14-8
......@@ -30,7 +30,7 @@ pub fn format(
3030 .node_tags = ast.nodes.items(.tag),
3131 .node_data = ast.nodes.items(.data),
3232 .node_tokens = ast.nodes.items(.main_token),
33 .tty = detectTTYFromWriter(writer),
33 .tty = core.detectTTYFromWriter(writer),
3434 };
3535 try dumpNode(writer, &state, 0);
3636 try writer.writeAll("\n");
......@@ -127,6 +127,8 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err
127127 => try dumpNodeList(writer, state, list, '{', '}'),
128128 .builtin_call,
129129 => try dumpNodeList(writer, state, list, '(', ')'),
130 .array_init,
131 => try dumpNodeList(writer, state, list, '[', ']'),
130132 else => unreachable,
131133 }
132134 },
......@@ -145,14 +147,18 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err
145147 try tty.setColor(writer, .reset);
146148 var indent = false;
147149 if (var_decl.type) |type_node| {
150 try writer.writeAll(" {");
148151 state.indent += 1;
149152 indent = true;
150153 try state.newline(writer);
151 try writer.writeAll(": ");
154 try writer.writeAll("type: ");
152155 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(" = ");
154161 }
155 try writer.writeAll(" = ");
156162 try dumpNode(writer, state, var_decl.init);
157163 if (indent) {
158164 state.indent -= 1;
......@@ -312,7 +318,8 @@ fn dumpNodeList(writer: anytype, state: *State, list: []const NodeIndex, l: u8,
312318 try dumpNode(writer, state, node);
313319 }
314320 }
315 try state.newline(writer);
321 if (list.len > 0)
322 try state.newline(writer);
316323 try writer.print("{c}", .{r});
317324}
318325
......@@ -321,10 +328,9 @@ const Dump = @This();
321328const std = @import("std");
322329const assert = std.debug.assert;
323330
324const Ast = @import("Ast.zig");
331const core = @import("mod.zig");
332const Ast = core.Ast;
325333const NodeIndex = Ast.NodeIndex;
326334const Node = Ast.Node;
327335const TokenIndex = Ast.TokenIndex;
328336const Call = Ast.Call;
329
330const detectTTYFromWriter = @import("util.zig").detectTTYFromWriter;
src/core/Ast.zig+42-4
......@@ -218,6 +218,10 @@ pub const Node = struct {
218218 struct_field,
219219 /// Struct Init
220220 struct_init,
221 /// Array type
222 array_type,
223 /// Array initialization
224 array_init,
221225
222226 pub fn isAssignBin(op: Tag) bool {
223227 return switch (op) {
......@@ -333,6 +337,8 @@ pub const Node = struct {
333337 .shr_assign = .bin,
334338 .string = .none,
335339 .struct_decl = .list,
340 .array_type = .un,
341 .array_init = .list,
336342 .struct_field = .un,
337343 .struct_init = .struct_init,
338344 .sub = .bin,
......@@ -784,12 +790,44 @@ const Ast = @This();
784790const std = @import("std");
785791const assert = std.debug.assert;
786792
787const Tokenizer = @import("Tokenizer.zig");
793const core = @import("mod.zig");
794
795const Tokenizer = core.Tokenizer;
788796const Token = Tokenizer.Token;
789797const TokenList = Tokenizer.Token.List;
790798const 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");
795const Ordinal = util.Ordinal;
816 pub fn fromOneBased(int: Int) @This() {
817 assert(int > 0);
818 return @enumFromInt(int - 1);
819 }
820
821 pub fn zeroBased(ord: @This()) Int {
822 return @intFromEnum(ord);
823 }
824
825 pub fn oneBased(ord: @This()) Int {
826 return @intFromEnum(ord) + 1;
827 }
828
829 pub fn add(ord: @This(), inc: Int) @This() {
830 return fromZeroBased(ord.zeroBased() + inc);
831 }
832 };
833}
src/core/InternPool.zig+130-37
......@@ -69,6 +69,8 @@ pub const Tag = enum(u8) {
6969 function_type,
7070 /// A struct type
7171 struct_type,
72 /// An array type
73 array_type,
7274 /// A type that can be represented with only an enum tag.
7375 simple_type,
7476
......@@ -92,6 +94,8 @@ pub const Tag = enum(u8) {
9294
9395 struct_value,
9496
97 array_value,
98
9599 pub fn Data(comptime tag: Tag) type {
96100 return switch (tag) {
97101 .removed,
......@@ -118,7 +122,9 @@ pub const Tag = enum(u8) {
118122 .function_type => TypeFunction,
119123 .string => String.WithLen,
120124 .struct_value => StructValue,
121 .struct_type => TypeStruct,
125 .struct_type => StructType,
126 .array_type => ArrayType,
127 .array_value => ArrayValue,
122128 };
123129 }
124130
......@@ -140,10 +146,23 @@ pub const Tag = enum(u8) {
140146 /// Trailing:
141147 /// 0. field_key: NullTerminatedString. Repeated per field_len
142148 /// 1. field_type: Type. Repeated per field_len
143 pub const TypeStruct = struct {
149 pub const StructType = struct {
144150 fields_len: u32,
145151 };
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
147166 /// Trailing:
148167 /// 0. value: Value. Repeated ty.structInfo().field_len
149168 pub const StructValue = struct {
......@@ -155,9 +174,9 @@ pub const Tag = enum(u8) {
155174/// This is an index into .items
156175pub const Value = enum(u32) {
157176 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;
159178 pub const first_value: Value = .void;
160 pub const last_value: Value = .empty_string;
179 pub const last_value: Value = .empty_array;
161180
162181 opaque_type,
163182 bool_type,
......@@ -167,6 +186,8 @@ pub const Value = enum(u32) {
167186 noreturn_type,
168187 type_type,
169188 numeric_literal_type,
189 numeric_literal_array_type,
190 noreturn_array_type,
170191
171192 void,
172193 bool_true,
......@@ -175,7 +196,7 @@ pub const Value = enum(u32) {
175196 float_zero,
176197 nan,
177198 empty_string,
178 standard_library,
199 empty_array,
179200
180201 none = std.math.maxInt(u32),
181202 _,
......@@ -183,7 +204,7 @@ pub const Value = enum(u32) {
183204 /// An array of `Value` existing within the `extra` array.
184205 /// This type exists to provide a struct with lifetime that is
185206 /// not invalidated when items are added to the `InternPool`.
186 /// TODO: Replace with ExtraStore.Slice(Value)
207 // TODO: Replace with ExtraStore.Slice(Value)
187208 pub const Slice = struct {
188209 start: u32,
189210 len: u32,
......@@ -237,7 +258,7 @@ pub const Value = enum(u32) {
237258 // TODO: move the formatter out of Air.Dump
238259 var state: Air.Dump.State = undefined;
239260 state.ip = ctx.ip;
240 state.tty = @import("util.zig").detectTTYFromWriter(writer);
261 state.tty = core.detectTTYFromWriter(writer);
241262 try Air.Dump.State.dumpValue(&state, writer, ctx.val);
242263 }
243264
......@@ -245,9 +266,11 @@ pub const Value = enum(u32) {
245266 return switch (value.valueTag(ip)) {
246267 .removed => unreachable, // UAF
247268
248 .function_type => .type,
249 .simple_type => .type,
250 .struct_type => .type,
269 .function_type,
270 .simple_type,
271 .struct_type,
272 .array_type,
273 => .type,
251274
252275 .simple_value => switch (value.valueData(ip, .simple_value)) {
253276 .true => .bool,
......@@ -261,6 +284,7 @@ pub const Value = enum(u32) {
261284 .string => .string,
262285 .func => value.toKey(ip).func.ty,
263286 .struct_value => value.toKey(ip).struct_value.ty,
287 .array_value => value.toKey(ip).array_value.type,
264288 };
265289 }
266290
......@@ -273,6 +297,7 @@ pub const Value = enum(u32) {
273297 .function_type,
274298 .struct_type,
275299 .simple_type,
300 .array_type,
276301 => @enumFromInt(@intFromEnum(value)),
277302
278303 // exception: void is a value AND a type
......@@ -321,6 +346,8 @@ pub const Type = enum(u32) {
321346 noreturn = @intFromEnum(Value.noreturn_type),
322347 type = @intFromEnum(Value.type_type),
323348 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),
324351 void = @intFromEnum(Value.void),
325352 _,
326353
......@@ -328,6 +355,7 @@ pub const Type = enum(u32) {
328355 pub const Tag = enum(u32) {
329356 function_type = @intFromEnum(InternPool.Tag.function_type),
330357 struct_type = @intFromEnum(InternPool.Tag.struct_type),
358 array_type = @intFromEnum(InternPool.Tag.array_type),
331359 simple_type = @intFromEnum(InternPool.Tag.simple_type),
332360 };
333361
......@@ -362,6 +390,11 @@ pub const Type = enum(u32) {
362390 return ip.valueToKey(ty.value()).struct_type;
363391 }
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
365398 pub fn hasRuntimeRepresentation(ty: Type, ip: *const InternPool) bool {
366399 return switch (ty) {
367400 .bool,
......@@ -377,8 +410,11 @@ pub const Type = enum(u32) {
377410 => false,
378411 else => switch (ty.tag(ip)) {
379412 .simple_type => unreachable, // all simple types are already printed
380 .function_type => true,
381 .struct_type => true,
413 .struct_type,
414 => false,
415 .function_type,
416 => true,
417 .array_type => ty.toArrayType(ip).?.item_type.hasRuntimeRepresentation(ip),
382418 },
383419 };
384420 }
......@@ -428,6 +464,11 @@ pub const Type = enum(u32) {
428464 .struct_type => {
429465 try writer.writeAll("[struct]");
430466 },
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 },
431472 }
432473 }
433474};
......@@ -464,6 +505,8 @@ pub const static_keys = [_]Key{
464505 SimpleType.noreturn.key(),
465506 SimpleType.type.key(),
466507 SimpleType.numeric_literal.key(),
508 .{ .array_type = .{ .item_type = Type.numeric_literal } },
509 .{ .array_type = .{ .item_type = Type.noreturn } },
467510
468511 SimpleValue.void.key(),
469512 SimpleValue.true.key(),
......@@ -471,6 +514,10 @@ pub const static_keys = [_]Key{
471514 .{ .int32 = 0 },
472515 .{ .float = 0.0 },
473516 .{ .float = Float.pure_nan.decode() },
517 .{ .array_value = .{
518 .type = Type.noreturn_array,
519 .items = &.{},
520 } },
474521 .{ .string = "" },
475522};
476523
......@@ -782,10 +829,12 @@ pub const Key = union(enum) {
782829 simple_value: SimpleValue,
783830 struct_value: StructValue,
784831 func: Func,
832 array_value: ArrayValue,
785833 // Types
786834 simple_type: SimpleType,
787835 function_type: FunctionType,
788836 struct_type: StructType,
837 array_type: ArrayType,
789838
790839 const Adapter = struct {
791840 intern_pool: *const InternPool,
......@@ -827,6 +876,7 @@ pub const Key = union(enum) {
827876
828877 pub fn hash(self: StructType, hasher: *Hash) void {
829878 // TODO: remove this function. structural typing is not used
879 // this should be hashing it's location / decl, exactly like `Func`
830880 std.hash.autoHashStrat(hasher, self, .DeepRecursive);
831881 }
832882 };
......@@ -841,6 +891,25 @@ pub const Key = union(enum) {
841891 }
842892 };
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
844913 pub const Func = struct {
845914 ty: Type,
846915 owner_decl: DeclIndex,
......@@ -874,22 +943,13 @@ pub const Key = union(enum) {
874943 .string,
875944 => |x| Hash.hash(seed, x),
876945 .float, .comptime_float => |x| Hash.hash(seed, asBytes(&Float.assertPure(x))),
877 .function_type => |x| {
878 var hasher = Hash.init(seed);
879 x.hash(&hasher);
880 return hasher.final();
881 },
882 .func => |x| {
883 var hasher = Hash.init(seed);
884 x.hash(&hasher);
885 return hasher.final();
886 },
887 .struct_type => |x| {
888 var hasher = Hash.init(seed);
889 x.hash(&hasher);
890 return hasher.final();
891 },
892 .struct_value => |x| {
946 inline .function_type,
947 .func,
948 .struct_type,
949 .struct_value,
950 .array_value,
951 .array_type,
952 => |x| {
893953 var hasher = Hash.init(seed);
894954 x.hash(&hasher);
895955 return hasher.final();
......@@ -934,6 +994,14 @@ pub const Key = union(enum) {
934994 return a_info.ty == b_info.ty and
935995 std.mem.eql(Value, a_info.field_values, b_info.field_values);
936996 },
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 },
9371005 };
9381006 }
9391007};
......@@ -1066,7 +1134,7 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key {
10661134 .function_type = ip.extraTypeFunction(data.decode(.function_type, item.tag)),
10671135 },
10681136 .struct_type => {
1069 const payload: Tag.TypeStruct, var trail =
1137 const payload: Tag.StructType, var trail =
10701138 data.decode(.struct_type, item.tag).getTrail(ip);
10711139
10721140 return .{ .struct_type = .{
......@@ -1085,6 +1153,19 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key {
10851153 ).get(ip),
10861154 } };
10871155 },
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 },
10881169 };
10891170}
10901171
......@@ -1147,7 +1228,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
11471228 },
11481229 .struct_type => |s| brk: {
11491230 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, .{
11511232 .fields_len = @intCast(s.field_keys.len),
11521233 }, s.field_keys.len * 2);
11531234 trail.writeSlice(s.field_keys);
......@@ -1163,6 +1244,19 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
11631244 trail.writeSlice(s.field_values);
11641245 break :brk Item.encode(.struct_value, extra);
11651246 },
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 },
11661260 };
11671261 ip.items.appendAssumeCapacity(item);
11681262 const index: Value = @enumFromInt(ip.items.len - 1);
......@@ -1332,13 +1426,12 @@ const Allocator = std.mem.Allocator;
13321426const Hash = std.hash.Wyhash;
13331427const assert = std.debug.assert;
13341428
1335const Comp = @import("Comp.zig");
1336const Cache = @import("Cache.zig");
1337
1338const Teg = @import("Teg.zig");
1339const Air = @import("Air.zig");
1340
1341const dod = @import("array.zig");
1429const core = @import("mod.zig");
1430const Comp = core.Comp;
1431const Cache = core.Cache;
1432const Teg = core.Teg;
1433const Air = core.Air;
1434const dod = core.array;
13421435const Float = dod.Float;
13431436
13441437const tracer = @import("tracer");
src/core/Parse.zig+77-15
......@@ -141,6 +141,8 @@ pub const AstError = union(enum) {
141141 /// Went past depth maximum
142142 depth_limit: Token,
143143
144 array_type_flipped_hint: Token,
145
144146 pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: Report.DebugStoredTrace) !Report {
145147 return (Report.Err{ .ast = err }).toReport(comp, file, trace);
146148 }
......@@ -176,6 +178,13 @@ pub const AstError = union(enum) {
176178 data.got.tag.description(),
177179 });
178180 },
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 },
179188 .depth_limit => |data| {
180189 try ctx.msg(.{ .token_stored = data }, "syntax tree is nested too deeply (limit: {d})", .{depth_limit});
181190 },
......@@ -237,7 +246,7 @@ fn parseModuleMembers(p: *Parse) Error!SubList {
237246 => try p.scratch.append(p.gpa, try p.parseModuleDecl()),
238247
239248 .kw_struct,
240 => try p.scratch.append(p.gpa, try p.parseStructDecl(.declaration)),
249 => try p.scratch.append(p.gpa, try p.parseStructDecl()),
241250
242251 .kw_enum,
243252 .kw_trait,
......@@ -383,13 +392,12 @@ fn parseParamDecl(p: *Parse, types: TypePresence) Error!NodeIndex {
383392 });
384393}
385394
386fn parseStructDecl(p: *Parse, ctx: ExprParseContext) Error!NodeIndex {
395fn parseStructDecl(p: *Parse) Error!NodeIndex {
387396 const trace = t.trace(@src());
388397 defer trace.end();
389398
390399 const main_token = p.assertToken(.kw_struct);
391400
392 _ = ctx; // TODO:
393401 _ = try p.expect(.id);
394402
395403 _ = try p.expect(.start_brace);
......@@ -586,6 +594,7 @@ fn isExpressionStart(p: *Parse) bool {
586594 .number_float,
587595 .number_int,
588596 .start_brace,
597 .start_bracket,
589598 .start_paren,
590599 .string,
591600 .tilde,
......@@ -840,13 +849,14 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
840849
841850 // Complex expressions
842851 .builtin_id => try p.parseBuiltinCall(),
843 .kw_return => try p.parseReturn(),
852 .dot => try p.parseAnonInstantiation(),
844853 .kw_break => try p.parseBreak(),
854 .kw_fn => try p.parseAnonFunction(),
845855 .kw_if => try p.parseIf(),
846 .start_paren => try p.parseGroup(),
856 .kw_return => try p.parseReturn(),
847857 .start_brace => try p.parseBlock(),
848 .dot => try p.parseAnonInstantiation(),
849 .kw_fn => try p.parseAnonFunction(),
858 .start_bracket => try p.parseArrayValueOrType(mode),
859 .start_paren => try p.parseGroup(),
850860
851861 // Unary operators
852862 inline .bang,
......@@ -900,7 +910,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
900910 },
901911 .start_paren => {
902912 const main_token = p.nextToken();
903 const args = try p.parseCallArgs();
913 const args = try p.parseExpressionList(.end_paren, .argument);
904914
905915 const extra = try p.addExtra(Ast.Call, .{
906916 .args = args,
......@@ -917,7 +927,12 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
917927 },
918928 .start_bracket => {
919929 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
922937 expr = try p.addNode(.{
923938 .tag = .index,
......@@ -1034,7 +1049,7 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex {
10341049
10351050 _ = try p.expect(.start_paren);
10361051
1037 const args = try p.parseCallArgs();
1052 const args = try p.parseExpressionList(.end_paren, .argument);
10381053
10391054 return try p.addNode(.{
10401055 .tag = .builtin_call,
......@@ -1043,7 +1058,11 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex {
10431058 });
10441059}
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 {
10471066 const trace = t.trace(@src());
10481067 defer trace.end();
10491068
......@@ -1054,19 +1073,19 @@ fn parseCallArgs(p: *Parse) Error!SubList {
10541073 try p.scratch.append(p.gpa, try p.parseExpression(.expression));
10551074
10561075 switch (p.currentTag()) {
1057 .end_paren => {
1076 end_token => {
10581077 break;
10591078 },
10601079 .comma => {
10611080 _ = p.nextToken();
1062 if (p.currentTag() == .end_paren)
1081 if (p.currentTag() == end_token)
10631082 break;
10641083 },
1065 else => return p.failUnexpectedToken(.argument),
1084 else => return p.failUnexpectedToken(fail_context),
10661085 }
10671086 }
10681087
1069 _ = try p.expect(.end_paren);
1088 _ = try p.expect(end_token);
10701089
10711090 return p.scratchToSubList(scratch_top);
10721091}
......@@ -1204,6 +1223,49 @@ fn parseAnonFunction(p: *Parse) Error!NodeIndex {
12041223 });
12051224}
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
12071269// helpers
12081270
12091271/// Append a node and return its index.
src/core/Report.zig+21-3
......@@ -490,10 +490,25 @@ pub const Warn = union(enum) {
490490
491491 fn build(data: @This(), ctx: *MsgContext) !void {
492492 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\"", .{});
494494 try ctx.msgAlone("add \": float\" or \": int\" after the variable name to set the type", .{});
495495 }
496496 },
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 },
497512};
498513
499514/// A variant of std.builtin.StackTrace that stores its data within itself
......@@ -1065,6 +1080,11 @@ pub const LazyNode = struct {
10651080 },
10661081 }
10671082 },
1083 .array_init => {
1084 assert(node_tag == .array_init);
1085 const items = data.list.slice(ctx.ast);
1086 return items[lazy.operand.getArgId()];
1087 },
10681088 else => {},
10691089 }
10701090
......@@ -1318,5 +1338,3 @@ const File = Comp.File;
13181338const InternPool = @import("InternPool.zig");
13191339const Value = InternPool.Value;
13201340const Type = InternPool.Type;
1321
1322const util = @import("util.zig");
src/core/Sema.zig+222-133
......@@ -36,9 +36,9 @@ namespace_index: Namespace.Index,
3636
3737// temp data:
3838inst_map: InstMap = undefined,
39/// scratch memory. can be used in any place you might temporarily allocate an arraylist
40/// this is most notably used to store block contents before the block is finalized
41scratch: std.ArrayListUnmanaged(u32) = .{},
39/// Scratch memory for temporary allocations.
40/// Block contents are appended for marks.
41scratch: core.U32Stack = .{},
4242
4343/// This is only set for function declarations
4444params: []Type = undefined,
......@@ -134,7 +134,7 @@ pub fn deinit(s: *Sema) void {
134134
135135 defer s.* = undefined;
136136 s.inst_map.deinit(s.arena);
137 s.scratch.deinit(s.arena);
137 s.scratch.list.deinit(s.arena);
138138 s.air_extra.deinit(s.gpa);
139139 s.air_instructions.deinit(s.gpa);
140140 s.temp_result_map.deinit(s.arena);
......@@ -205,7 +205,7 @@ pub fn analyzeFunctionDeclaration(s: *Sema) Error!Value {
205205 defer s.gpa.free(s.params);
206206
207207 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)));
209209 const ty = try s.resolveType(src, param);
210210 if (!ty.hasRuntimeRepresentation(s.ip)) {
211211 return s.fail(.param_comptime_only, .{
......@@ -268,8 +268,8 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
268268 const t = tracer.trace(@src());
269269 defer t.end();
270270
271 const scratch_top = s.scratch.items.len;
272 defer s.scratch.shrinkRetainingCapacity(scratch_top);
271 const scratch = s.scratch.mark();
272 defer scratch.deinit();
273273
274274 const block = try s.addBlock();
275275 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 {
284284 };
285285 }
286286
287 if (scratch_top + 1 == s.scratch.items.len) {
288 const single_instruction = s.air_instructions.items(.tag)[@intFromEnum(block.block_index) + 1];
287 if (scratch.len() == 1) {
288 const air_instructions = s.air_instructions.slice();
289 const single_inst_id = @intFromEnum(block.block_index) + 1;
290 assert(@intFromEnum(scratch.slice(Air.Inst.Index)[0]) == single_inst_id);
291
292 const single_instruction = air_instructions.items(.tag)[single_inst_id];
289293 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]
291295 .decode(.@"break", single_instruction);
292296 if (data.block_ref == block.block_index) {
293297 // 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 {
303307 }
304308 }
305309
306 return (try block.finish(s, scratch_top)).toRef();
310 return (try block.finish(s, scratch)).toRef();
307311}
308312
309313fn 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 {
534538 for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| {
535539 const ref = try s.resolveRef(arg);
536540 const coerced = try s.coerceRef(
537 in.op(Report.Operand.argument(@intCast(i))),
541 in.op(operandArgument(@intCast(i))),
538542 ref,
539543 param,
540544 );
......@@ -543,7 +547,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
543547 } else {
544548 for (call.args.slice(&s.code), 0..) |arg, i| {
545549 const ref = try s.ensureValueRuntime(
546 in.op(Report.Operand.argument(@intCast(i))),
550 in.op(operandArgument(@intCast(i))),
547551 try s.resolveRef(arg),
548552 );
549553 scratch[i] = ref;
......@@ -571,7 +575,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
571575 defer s.arena.free(scratch);
572576 for (call.args.slice(&s.code), 0..) |arg, i| {
573577 const ref = try s.ensureValueRuntime(
574 in.op(Report.Operand.argument(@intCast(i))),
578 in.op(operandArgument(@intCast(i))),
575579 try s.resolveRef(arg),
576580 );
577581 scratch[i] = ref;
......@@ -635,7 +639,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
635639 const block = try s.analyzeBlock(in);
636640 switch (block.decode()) {
637641 .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);
639643 },
640644 else => {},
641645 }
......@@ -692,7 +696,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
692696 .compile_error => {
693697 const node: Teg.Inst.RefNode = s.code.instData(.compile_error, in);
694698 const value = try s.resolveCoerceValue(
695 in.op(Report.Operand.argument(0)),
699 in.op(operandArgument(0)),
696700 node.operand,
697701 .string,
698702 );
......@@ -705,42 +709,26 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
705709 .struct_decl => {
706710 const node: Teg.Inst.StructDecl = s.code.instPayload(.struct_decl, in);
707711
708 const scratch_top = s.scratch.items.len;
709 try s.scratch.ensureUnusedCapacity(s.arena, node.field_len * 2);
710 s.scratch.items.len += node.field_len * 2;
711 defer s.scratch.shrinkRetainingCapacity(scratch_top);
712
713 for (
714 node.keys(&s.code),
715 // Keeping the slice pointer is fine for this loop, since scratch is not resized
716 s.scratch.items[scratch_top..][0..node.field_len],
717 ) |key, *dest| {
718 dest.* = @intFromEnum(try s.ip.getOrPutString(
719 s.gpa,
720 key.slice(s.code.string_bytes),
721 .no_embedded_nulls,
722 ));
723 }
712 const scratch = try s.scratch.reserveMulti(
713 s.arena,
714 .{ .keys = NullTerminatedString, .types = Type },
715 .{ node.field_len, node.field_len },
716 );
717 defer scratch.deinit();
724718
725 for (
726 node.types(&s.code),
727 // We cannot reuse the pointer from scratch between calls to sema
728 scratch_top + node.field_len..,
729 0..,
730 ) |ty, i, arg_i| {
731 s.scratch.items[i] = @intFromEnum(try s.resolveCoerceValue(
732 in.op(Report.Operand.argument(@intCast(arg_i))),
733 ty,
734 .type,
735 ));
719 // Fill the first half with keys
720 for (scratch.slice(.keys), node.keys(&s.code)) |*dest, src| {
721 dest.* = try s.ip.getOrPutString(s.gpa, src.slice(s.code.string_bytes), .no_embedded_nulls);
736722 }
737723
738 const keys = s.scratch.items[scratch_top..][0..node.field_len];
739 const types = s.scratch.items[scratch_top + node.field_len ..][0..node.field_len];
724 // Fill the second half with coerced values.
725 for (node.types(&s.code), 0..) |ty, i| {
726 scratch.at(.types, i).* = try s.resolveType(in.op(operandArgument(@intCast(i))), ty);
727 }
740728
741729 return try s.makeValueAir(.{ .struct_type = .{
742 .field_keys = @ptrCast(keys),
743 .field_types = @ptrCast(types),
730 .field_keys = scratch.slice(.keys),
731 .field_types = scratch.slice(.types),
744732 } });
745733 },
746734 .struct_init => {
......@@ -760,22 +748,13 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
760748 var is_constant = true;
761749
762750 // Allocate scratch space to store the values as they are being prepared.
763 // TODO: make this an abstraction
764 const scratch_top = s.scratch.items.len;
765
766 try s.scratch.ensureUnusedCapacity(s.arena, struct_type.field_types.len);
767 const storage: []Air.Inst.Ref.Optional =
768 @ptrCast(s.scratch.unusedCapacitySlice()[0..struct_type.field_types.len]);
769 s.scratch.items.len += storage.len;
770 @memset(storage, .none);
771
772 const scratch_end = s.scratch.items.len;
773 defer {
774 const from = s.scratch.items[scratch_end..];
775 const dest = s.scratch.items[scratch_top..][0..from.len];
776 std.mem.copyForwards(u32, dest, from);
777 s.scratch.shrinkRetainingCapacity(scratch_top + from.len);
778 }
751 const scratch = try s.scratch.reserve(
752 s.arena,
753 Air.Inst.Ref.Optional,
754 struct_type.field_types.len,
755 );
756 defer scratch.deinit();
757 @memset(scratch.slice(), .none);
779758
780759 for (node.keys(&s.code), node.inits(&s.code), 0..) |init_key, init_value, i| {
781760 // 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 {
789768 has_errors = true;
790769 try s.addError(.struct_init_unknown_field, .{
791770 .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),
793772 });
794773 continue;
795774 };
796 if (storage[found_index] != .none) {
775 if (scratch.at(found_index).* != .none) {
797776 has_errors = true;
798777 try s.addError(.struct_init_duplicate_field, .{
799778 .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),
801780 });
802781 continue;
803782 }
804 // TODO: calling coerceRef / resolveRefRuntime will clobber the `storage` pointer
805783 const coerced = s.resolveCoerceRef(
806 in.op(Report.Operand.argument(@intCast(i))),
784 in.op(operandArgument(@intCast(i))),
807785 init_value,
808786 struct_type.field_types[found_index],
809787 ) catch |e| switch (e) {
......@@ -811,17 +789,18 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
811789 // Instead of bailing out with `try`, we will continue the loop.
812790 has_errors = true;
813791 // 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();
815793 continue;
816794 },
817795 else => return e,
818796 };
819797 if (coerced.kind == .index) is_constant = false;
820 storage[found_index] = coerced.toOptional();
798 scratch.at(found_index).* = coerced.toOptional();
821799 }
822800
823801 // Check for missing fields
824802 {
803 const storage = scratch.slice();
825804 var i: usize = 0;
826805 while (mem.indexOfScalarPos(Air.Inst.Ref.Optional, storage, i, .none)) |index| {
827806 try s.addError(.struct_init_missing_field, .{
......@@ -839,8 +818,8 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
839818
840819 if (is_constant) {
841820 // Convert all items in storage into a Value type
842 const result_storage: []Value = @ptrCast(storage);
843 for (storage, result_storage) |ref, *value| {
821 const result_storage = scratch.sliceReinterpret(Value);
822 for (scratch.slice(), scratch.sliceReinterpret(Value)) |ref, *value| {
844823 value.* = ref.unwrap().?.decode().value;
845824 }
846825 return try s.makeValueAir(.{ .struct_value = .{
......@@ -848,16 +827,80 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
848827 .field_values = result_storage,
849828 } });
850829 } else {
851 // TODO: make this less error prone
852 const result_storage: []u32 = @ptrCast(storage);
853 const value_index = try s.air_extra.alloc(s.gpa, result_storage.len);
854 @memcpy(s.air_extra.list.items[value_index..].ptr, result_storage);
830 // Create a runtime struct instantiation
831 const value_index = try s.air_extra.appendSlice(s.gpa, scratch.slice());
855832 return try s.addInstRef(.struct_init, .{
856833 .ty = ty,
857834 .field_values = value_index,
858835 });
859836 }
860837 },
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
861904 else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}),
862905 }
863906}
......@@ -871,15 +914,15 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) Error!Air.Inst.Ref {
871914 const t = tracer.trace(@src());
872915 defer t.end();
873916
874 const scratch_top = s.scratch.items.len;
875 defer s.scratch.shrinkRetainingCapacity(scratch_top);
917 const scratch = s.scratch.mark();
918 defer scratch.deinit();
876919
877920 const visited = s.resolveRef(in) catch |err| switch (err) {
878921 error.HitNoReturn => Value.void.toAir(),
879922 else => return err,
880923 };
881924
882 if (scratch_top == s.scratch.items.len and visited.kind == .value) {
925 if (scratch.len() == 0 and visited.kind == .value) {
883926 return visited;
884927 }
885928
......@@ -894,25 +937,27 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) Error!Air.Inst.Ref {
894937 .block_ref = block.block_index,
895938 .value = visited,
896939 });
897 return (try block.finish(s, scratch_top)).toRef();
940 return (try block.finish(s, scratch)).toRef();
898941}
899942
900943fn resolveRefExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) Error!Air.Inst.Ref {
901 const index = ref.toIndex() orelse {
902 return ref.toValue().?.toAir();
903 };
944 return switch (ref.decode()) {
945 .value => |value| value.toAir(),
946 .index => |index| {
947 if (s.inst_map.get(index)) |air| {
948 if (mark) {
949 switch (air.decode()) {
950 .index => |i| s.markInst(i),
951 else => {},
952 }
953 }
904954
905 if (s.inst_map.get(index)) |air| {
906 if (mark) {
907 switch (air.decode()) {
908 .index => |i| s.markInst(i),
909 else => {},
955 return air;
910956 }
911 }
912 return air;
913 }
914957
915 return s.analyzeCode(index);
958 return s.analyzeCode(index);
959 },
960 };
916961}
917962
918963fn 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 {
9971042}
9981043
9991044/// 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 {
1001 const t = tracer.traceFmt(@src(), "{} to {}", .{ value.dump(s.ip), to.dump(s.ip) });
1045fn coerceRef(s: *Sema, src: InstOp, ref: Air.Inst.Ref, to: Type) !Air.Inst.Ref {
1046 const t = tracer.traceFmt(@src(), "{} to {}", .{ ref.dump(s.ip), to.dump(s.ip) });
10021047 defer t.end();
10031048
1004 const from = s.typeOfRef(value);
1049 const from = s.typeOfRef(ref);
10051050 if (from == to) {
1006 return value;
1051 return ref;
10071052 }
10081053 if (from == .noreturn) {
10091054 return error.HitNoReturn;
10101055 }
10111056 if (from == .@"opaque") {
1012 return value;
1057 return ref;
10131058 }
1014 const index = switch (value.decode()) {
1059 const index = switch (ref.decode()) {
10151060 .value => |v| return (try s.coerceValue(src, v, to)).toAir(),
10161061 .index => |index| index,
10171062 };
......@@ -1053,7 +1098,7 @@ fn coerceRef(s: *Sema, src: InstOp, value: Air.Inst.Ref, to: Type) !Air.Inst.Ref
10531098}
10541099
10551100/// 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 {
10571102 const from = value.typeOf(s.ip);
10581103 if (from == to) {
10591104 return value;
......@@ -1069,11 +1114,7 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
10691114 .int32 => unreachable, // same type handled
10701115 .numeric_literal, .float => switch (value.toKey(s.ip)) {
10711116 .comptime_float, .float => |val| {
1072 if (!math.isFinite(val) or
1073 val < math.minInt(i32) or
1074 val > math.maxInt(i32) or
1075 (@rem(val, 1) != 0))
1076 {
1117 if (!core.inInt32Range(val)) {
10771118 return s.fail(.comptime_float_to_int, .{
10781119 .val = val,
10791120 .src = s.lazyNode(src),
......@@ -1115,24 +1156,49 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
11151156 // non-types do not coerce to types
11161157 },
11171158 .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 },
11191166 }
11201167 return s.coercionError(src, from, to);
11211168}
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 {
11241171 switch (ref.decode()) {
11251172 .value => |v| {
11261173 const ty = v.typeOf(s.ip);
11271174 switch (ty) {
11281175 .numeric_literal => {
1129 try s.report.warn(.numeric_int_var_ambiguity, .{
1130 .src = s.lazyNode(src),
1131 });
1176 const float = v.toFloat(s.ip) orelse unreachable;
1177
1178 if (!core.inInt32Range(float)) {
1179 try s.report.warn(.numeric_int_var_ambiguity, .{
1180 .src = s.lazyNode(src),
1181 });
1182 }
1183
11321184 return (try s.makeValue(.{
1133 .float = v.toFloat(s.ip) orelse unreachable,
1185 .float = float,
11341186 })).toAir();
11351187 },
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 },
11361202 .type,
11371203 .noreturn,
11381204 => return s.fail(.runtime_incompatible_type, .{
......@@ -1147,12 +1213,11 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref {
11471213 .string,
11481214 .void,
11491215 => {},
1150 _ => switch (ty.tag(s.ip)) {
1151 .simple_type => unreachable, // handled above
1152
1153 // Allowed at runtime
1154 .function_type => {},
1155 .struct_type => {},
1216 _ => if (!ty.hasRuntimeRepresentation(s.ip)) {
1217 return s.fail(.runtime_incompatible_type, .{
1218 .src = s.lazyNode(src),
1219 .type = ty,
1220 });
11561221 },
11571222 }
11581223 },
......@@ -1161,6 +1226,24 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref {
11611226 return ref;
11621227}
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
11641247fn resolveDecl(s: *Sema, src: InstOp, decl_index: Decl.Index) Error!Value {
11651248 const decl = s.ip.declPtr(decl_index);
11661249 switch (decl.analysis) {
......@@ -1382,8 +1465,8 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value {
13821465const IncompleteBlock = struct {
13831466 block_index: Air.Inst.Index,
13841467
1385 pub fn finish(block: IncompleteBlock, s: *Sema, scratch_top: usize) !Air.Inst.Index {
1386 s.setInstData(.block, block.block_index, try s.scratchToIndexList(scratch_top));
1468 pub fn finish(block: IncompleteBlock, s: *Sema, scratch_mark: core.U32Stack.Mark) !Air.Inst.Index {
1469 s.setInstData(.block, block.block_index, try s.scratchMarkToIndexList(scratch_mark));
13871470 return block.block_index;
13881471 }
13891472};
......@@ -1404,7 +1487,7 @@ fn addInstRef(s: *Sema, comptime tag: Air.Inst.Tag, data: tag.Data()) !Air.Inst.
14041487 .flags = .{},
14051488 });
14061489 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);
14081491 return index.toRef();
14091492}
14101493
......@@ -1423,27 +1506,30 @@ fn makeValue(s: *Sema, key: InternPool.Key) !Value {
14231506 return try s.ip.get(s.gpa, key);
14241507}
14251508
1509fn makeType(s: *Sema, key: InternPool.Key) !Type {
1510 return (try s.makeValue(key)).toTypeUnsafe(s.ip);
1511}
1512
14261513fn makeValueAir(s: *Sema, key: InternPool.Key) !Air.Inst.Ref {
14271514 return (try s.makeValue(key)).toAir();
14281515}
14291516
14301517fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref {
1431 if (inst.toValue()) |interned| {
1432 return interned.toAir();
1433 }
1434 const index = inst.toIndex().?;
1435 return s.inst_map.get(index) orelse {
1436 if (std.debug.runtime_safety) {
1437 std.debug.panic("Unmapped operand %{d}", .{@intFromEnum(index)});
1438 }
1439 unreachable;
1518 return switch (inst.decode()) {
1519 .value => |value| value.toAir(),
1520 .index => |index| s.inst_map.get(index) orelse {
1521 if (std.debug.runtime_safety) {
1522 std.debug.panic("Unmapped operand %{d}", .{@intFromEnum(index)});
1523 }
1524 unreachable;
1525 },
14401526 };
14411527}
14421528
1443fn scratchToIndexList(s: *Sema, scratch_top: usize) !Air.IndexList {
1444 const items = s.scratch.items[scratch_top..];
1529fn scratchMarkToIndexList(s: *Sema, mark: core.U32Stack.Mark) !Air.IndexList {
1530 const items = mark.slice(u32);
14451531 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);
14471533 return .{
14481534 .start = @intCast(extra_top),
14491535 .len = @intCast(items.len),
......@@ -1524,12 +1610,15 @@ const Teg = core.Teg;
15241610const InstOp = Teg.InstOp;
15251611
15261612const Air = core.Air;
1527const Report = core.Report;
15281613const Ast = core.Ast;
15291614const store = core.array;
1530const tracer = @import("tracer");
1615
1616const Report = core.Report;
1617const operandArgument = Report.Operand.argument;
15311618
15321619const InternPool = core.InternPool;
15331620const Value = InternPool.Value;
15341621const Type = InternPool.Type;
15351622const NullTerminatedString = InternPool.NullTerminatedString;
1623
1624const tracer = @import("tracer");
src/core/Teg.Dump.zig+50-17
......@@ -101,6 +101,7 @@ const State = struct {
101101 try tty.setColor(writer, .reset);
102102 },
103103 inline .field,
104 .index,
104105 .add,
105106 .sub,
106107 .mul,
......@@ -172,6 +173,7 @@ const State = struct {
172173 .fetch,
173174 .compile_error,
174175 .type_of,
176 .array_type,
175177 => |comptime_tag| {
176178 const data = teg.instData(comptime_tag, inst);
177179 try writer.writeAll("(");
......@@ -255,28 +257,59 @@ const State = struct {
255257 try s.newline(writer);
256258 try writer.writeAll("}");
257259 },
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 },
258287 }
259288 }
260289
261290 fn dumpRef(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void {
262 if (ref.toIndex()) |index| {
263 try s.dumpInst(writer, index);
264 } else {
265 try s.tty.setColor(writer, .blue);
266 try writer.print("%{s}", .{@tagName(ref)});
267 try s.tty.setColor(writer, .reset);
291 switch (ref.decode()) {
292 .index => |index| try s.dumpInst(writer, index),
293 .value => {
294 try s.tty.setColor(writer, .blue);
295 try writer.print("%{s}", .{@tagName(ref)});
296 try s.tty.setColor(writer, .reset);
297 },
268298 }
269299 }
270300
271301 fn dumpRefMarker(s: *State, writer: anytype, ref: Teg.Inst.Ref) !void {
272 if (ref.toIndex()) |index| {
273 try s.tty.setColor(writer, .blue);
274 try writer.print("%{d}", .{@intFromEnum(index)});
275 try s.tty.setColor(writer, .reset);
276 } else {
277 try s.tty.setColor(writer, .blue);
278 try writer.print("%{s}", .{@tagName(ref)});
279 try s.tty.setColor(writer, .reset);
302 switch (ref.decode()) {
303 .index => |index| {
304 try s.tty.setColor(writer, .blue);
305 try writer.print("%{d}", .{@intFromEnum(index)});
306 try s.tty.setColor(writer, .reset);
307 },
308 .value => {
309 try s.tty.setColor(writer, .blue);
310 try writer.print("%{s}", .{@tagName(ref)});
311 try s.tty.setColor(writer, .reset);
312 },
280313 }
281314 }
282315
......@@ -356,6 +389,6 @@ pub fn format(
356389const std = @import("std");
357390const assert = std.debug.assert;
358391
359const Teg = @import("Teg.zig");
360
361const detectTTYFromWriter = @import("util.zig").detectTTYFromWriter;
392const core = @import("mod.zig");
393const Teg = core.Teg;
394const detectTTYFromWriter = core.detectTTYFromWriter;
src/core/Teg.zig+40-22
......@@ -46,6 +46,9 @@ pub const Inst = struct {
4646 struct_decl,
4747 struct_init,
4848
49 array_type,
50 array_init,
51
4952 add,
5053 sub,
5154 mul,
......@@ -70,6 +73,7 @@ pub const Inst = struct {
7073 bool_not,
7174 bit_not,
7275 field,
76 index,
7377 call,
7478 @"comptime",
7579
......@@ -103,6 +107,7 @@ pub const Inst = struct {
103107 .js_new => PlNode(JsNew),
104108 .struct_decl => PlNode(StructDecl),
105109 .struct_init => PlNode(StructInit),
110 .array_init => PlNode(ArrayInit),
106111
107112 // Single instruction operand
108113 .memory,
......@@ -118,10 +123,12 @@ pub const Inst = struct {
118123 .compile_error,
119124 .@"comptime",
120125 .type_of,
126 .array_type,
121127 => RefNode,
122128
123129 // Two instruction operands
124130 .field,
131 .index,
125132 .add,
126133 .sub,
127134 .mul,
......@@ -150,7 +157,7 @@ pub const Inst = struct {
150157 }
151158 };
152159
153 /// TODO: make enum? idk
160 // TODO: make this an enum
154161 const NodeOffset = i32;
155162
156163 pub fn PlNode(comptime Payload: type) type {
......@@ -184,7 +191,7 @@ pub const Inst = struct {
184191 main_module_inst = 0,
185192 _,
186193
187 pub const static_len = @intFromEnum(Ref.empty_string) + 1;
194 pub const static_len = @intFromEnum(Ref.empty_array) + 1;
188195
189196 pub fn toRef(i: Index) Inst.Ref {
190197 return @enumFromInt(static_len + @intFromEnum(i));
......@@ -229,6 +236,8 @@ pub const Inst = struct {
229236 noreturn_type,
230237 type_type,
231238 numeric_literal_type,
239 numeric_literal_array_type,
240 noreturn_array_type,
232241
233242 void,
234243 bool_true,
......@@ -237,11 +246,7 @@ pub const Inst = struct {
237246 float_zero,
238247 nan,
239248 empty_string,
240 standard_library,
241
242 /// This Ref does not correspond to any instruction or constant
243 /// value and may instead be used as a sentinel to indicate null.
244 none = std.math.maxInt(u32),
249 empty_array,
245250
246251 _,
247252
......@@ -258,8 +263,7 @@ pub const Inst = struct {
258263 .{ .value = @enumFromInt(ref_int) };
259264 }
260265
261 pub fn toIndex(ref: Ref) ?Index {
262 assert(ref != .none);
266 pub fn index(ref: Ref) ?Index {
263267 const ref_int = @intFromEnum(ref);
264268 if (ref_int >= Index.static_len) {
265269 return @enumFromInt(ref_int - Index.static_len);
......@@ -268,12 +272,7 @@ pub const Inst = struct {
268272 }
269273 }
270274
271 pub fn toIndexAllowNone(inst: Ref) ?Index {
272 if (inst == .none) return null;
273 return toIndex(inst);
274 }
275
276 pub fn toValue(inst: Ref) ?InternPool.Value {
275 pub fn value(inst: Ref) ?InternPool.Value {
277276 const ref_int = @intFromEnum(inst);
278277
279278 // TODO: is this > or >=
......@@ -282,6 +281,15 @@ pub const Inst = struct {
282281
283282 return @enumFromInt(ref_int);
284283 }
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 };
285293 };
286294
287295 pub const Bin = struct {
......@@ -359,6 +367,15 @@ pub const Inst = struct {
359367 }
360368 };
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
362379 /// This will contain trailing data depending on the .kind
363380 pub const Decl = struct {
364381 name: NullTerminatedString,
......@@ -562,9 +579,10 @@ const std = @import("std");
562579const mem = std.mem;
563580const 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;
568586const Unused = Ast.Unused;
569587const NodeIndex = Ast.NodeIndex;
570588const TokenIndex = Ast.TokenIndex;
......@@ -575,14 +593,14 @@ const optionalTokenIndex = OptionalTokenIndex.init;
575593const optionalNodeIndex = OptionalNodeIndex.init;
576594const optionalExtraIndex = OptionalExtraIndex.init;
577595
578const dod = @import("array.zig");
596const dod = core.array;
579597const 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;
588606const LazyNode = Report.LazyNode;
src/core/Transform.zig+35-20
......@@ -235,7 +235,7 @@ const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{
235235 .{ "bool", .bool_type },
236236 .{ "noreturn", .noreturn_type },
237237 .{ "type", .type_type },
238 .{ "std", .standard_library },
238 // .{ "std", .standard_library },
239239});
240240
241241fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.Index {
......@@ -559,14 +559,14 @@ fn transformExpressionToBlock(
559559
560560 // TODO: evaluate if `if` blocks would be fine in sema with instruction refs
561561 // with this extra if check here, we filter out non-blocks.
562 if (result.toValue() != null) {
562 if (result.value() != null) {
563563 assert(block == null);
564564 return result;
565565 }
566566 }
567567
568568 if (expr) |result| {
569 if (result.toIndex()) |index| {
569 if (result.index()) |index| {
570570 if (t.insts.items(.tag)[@intFromEnum(index)] == .block) {
571571 return result;
572572 }
......@@ -685,7 +685,7 @@ pub const Builtin = enum {
685685 }
686686
687687 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
690690 return try t.addInstRef(.{
691691 .tag = .js_new,
......@@ -923,6 +923,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
923923 inline .negate,
924924 .bool_not,
925925 .bit_not,
926 .array_type,
926927 => |tag| {
927928 const ast_un = t.ast.nodeData(node).un;
928929 const teg_tag = @field(Teg.Inst.Tag, @tagName(tag));
......@@ -1048,7 +1049,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
10481049 const args = t.ast.extraData(call.extra, Ast.Call).args.slice(&t.ast);
10491050
10501051 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
10531054 const call_inst = try t.addInstRef(.{
10541055 .tag = .call,
......@@ -1227,11 +1228,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
12271228 ) |field_node, i, j| {
12281229 const field = t.ast.fullStructField(field_node);
12291230
1230 if (mem.startsWith(u8, field.id, "@")) {
1231 @panic("TODO: string identifiers");
1232 }
1231 const id = try t.decodeIdentifier(field.id);
12331232
1234 t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(field.id)));
1233 t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id)));
12351234 t.extra.setU32(j, @intFromEnum(try t.transformExpressionToBlock(decl, field.type, false)));
12361235 }
12371236
......@@ -1264,11 +1263,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
12641263 ) |field_node, i, j| {
12651264 const field = t.ast.fullStructField(field_node);
12661265
1267 if (mem.startsWith(u8, field.id, "@")) {
1268 @panic("TODO: string identifiers");
1269 }
1266 const id = try t.decodeIdentifier(field.id);
12701267
1271 t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(field.id)));
1268 t.extra.setU32(i, @intFromEnum(try t.addNullTerminatedString(id)));
12721269 t.extra.setU32(j, @intFromEnum((try t.transformExpression(decl, field.type)) orelse
12731270 @panic("TODO: print better error")));
12741271 }
......@@ -1286,11 +1283,29 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
12861283 });
12871284 },
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
12891304 else => |tag| std.debug.panic("TODO: expr {s}", .{@tagName(tag)}),
12901305 }
12911306}
12921307
1293fn transformCallArgs(t: *Transform, decl: BlockContext, args: []const NodeIndex) !?RefList {
1308fn transformExpressionList(t: *Transform, decl: BlockContext, args: []const NodeIndex) !?RefList {
12941309 const extra_top = try t.extra.alloc(t.gpa, args.len);
12951310
12961311 for (args, extra_top..) |arg, i| {
......@@ -1492,7 +1507,7 @@ fn transformBlockStatement(
14921507 .node = node,
14931508 .data = switch (var_decl.is_const) {
14941509 true => .{ .immutable = mem_ref },
1495 false => .{ .mutable = mem_ref.toIndex().? },
1510 false => .{ .mutable = mem_ref.index().? },
14961511 },
14971512 };
14981513
......@@ -1696,16 +1711,16 @@ fn addDecodedSlicedString(t: *Transform, escaped: []const u8) !SlicedString {
16961711 const decoded = if (Tokenizer.stringHasEscapes(escaped)) brk: {
16971712 const len = Tokenizer.decodeStringCount(escaped);
16981713
1699 // use the scratch arraylist as a temporary storage for
1700 // decoding the string. this is done so that duplicate
1701 // strings will get de-duplicated in string_bytes
1702 //
1703 // TODO: see if this is a useless optimization
1714 // Use the scratch arraylist as a temporary storage for decoding the
1715 // string. this is done so that duplicate strings will get de-duplicated
1716 // in string_bytes. It is more likely that there will be remaining bytes
1717 // in `.scratch` vs `.id_decode_buf`
17041718 try t.scratch.ensureUnusedCapacity(t.gpa, @divFloor(len, @sizeOf(u32)) + 1);
17051719 const buf = mem.sliceAsBytes(t.scratch.unusedCapacitySlice());
17061720
17071721 break :brk Tokenizer.decodeStringBuf(escaped, buf);
17081722 } else escaped;
1723
17091724 return t.addSlicedString(decoded);
17101725}
17111726
src/core/array.zig+9
......@@ -372,6 +372,15 @@ pub fn ExtraStore(comptime set_kind: StoreKind, comptime set_mutability: Mutabil
372372 store.list.items.len += len;
373373 return @intCast(idx);
374374 }
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 }
375384 };
376385}
377386
src/core/js/Link.zig+36-1
......@@ -289,6 +289,19 @@ fn printValue(link: *Link, value: InternPool.Value, level: Level, writer: anytyp
289289 try writer.writeAll(")");
290290 },
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
292305 else => unreachable, // this value is not a plain_value
293306 }
294307}
......@@ -410,6 +423,18 @@ fn printExprLevel(
410423 try writer.writeAll(")");
411424 },
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
413438 inline .negate,
414439 .bool_not,
415440 .pre_dec,
......@@ -505,7 +530,7 @@ fn printExprLevel(
505530
506531 try writer.writeAll(":");
507532
508 try link.printExpr(tree, value, writer);
533 try link.printExprLevel(tree, value, .assignment, writer);
509534 }
510535
511536 try writer.writeAll("}");
......@@ -513,6 +538,16 @@ fn printExprLevel(
513538 if (level == .statement)
514539 try writer.writeAll(")");
515540 },
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 },
516551 }
517552}
518553
src/core/js/NodeGen.zig+5-1
......@@ -422,7 +422,7 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {
422422 try c.markExpression(inst, node);
423423 },
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)}),
426426 }
427427}
428428
......@@ -495,6 +495,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {
495495 .function_type,
496496 .simple_type,
497497 .struct_type,
498 .array_type,
498499 => unreachable, // Type values are not allowed here
499500
500501 .int32,
......@@ -503,6 +504,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {
503504 .string,
504505 .simple_value,
505506 .struct_value,
507 .array_value,
506508 => return try c.add(.plain_value, .{ .value = v }),
507509
508510 // Hoist functions as top level values
......@@ -666,6 +668,8 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {
666668 .bool_not,
667669 .store,
668670 .struct_init,
671 .array_init,
672 .index,
669673 => true,
670674
671675 // JavaScript variable declarations require statements.
src/core/js/Tree.zig+4
......@@ -45,6 +45,8 @@ pub const Tag = enum {
4545 field,
4646 ternary,
4747 object_literal,
48 array,
49 index,
4850 // Prefix ops
4951 negate,
5052 bool_not,
......@@ -81,6 +83,7 @@ pub const Tag = enum {
8183 .eq,
8284 .lt,
8385 .gt,
86 .index,
8487 => Bin,
8588 .ternary => Ternary,
8689 .negate,
......@@ -89,6 +92,7 @@ pub const Tag = enum {
8992 .pre_dec,
9093 => Un,
9194 .object_literal => ObjectLiteral,
95 .array => IndexList,
9296 .call, .new => Call,
9397 .field => Field,
9498 .stmt_if => StmtIf,
src/core/main.zig+7-7
......@@ -28,14 +28,14 @@ pub fn main() !void {
2828 }
2929
3030 const use_gpa = builtin.mode == .Debug;
31 var gpa: if (use_gpa) std.heap.GeneralPurposeAllocator(.{
32 .stack_trace_frames = 100,
33 }) else struct {} = .{};
34 defer _ = if (use_gpa) gpa.deinit();
35 const alloc = if (use_gpa)
36 gpa.allocator()
31 var gpa = if (use_gpa)
32 std.heap.GeneralPurposeAllocator(.{
33 .stack_trace_frames = 100,
34 }){}
3735 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
4040 var maybe_file_path: ?[]const u8 = null;
4141 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");
2929/// Semantic Analysis visits a `Teg` declaration and produces `Air`
3030pub const Sema = @import("Sema.zig");
3131
32pub const Stack = @import("stack.zig").Stack;
33pub const U32Stack = Stack(u32);
34
3235/// Untyped Intermediate Representation. Flat instruction list
3336pub const Teg = @import("Teg.zig");
3437
......@@ -40,8 +43,6 @@ pub const Transform = @import("Transform.zig");
4043
4144pub const unicode_props = @import("unicode_props.zig");
4245
43pub const util = @import("util.zig");
44
4546// sort-lines: end
4647
4748// 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 {
6465 }
6566}
6667
67pub const detectTTYFromWriter = util.detectTTYFromWriter;
68pub const Ordinal = util.Ordinal;
68/// If `value` can be cast to `i32`
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
7098const std = @import("std");
7199const 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;