authorchloe caruso <git@paperclover.net> 2024-09-16 03:14:19-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-09-16 03:14:19-04:00
log67e9829ce50923b93235b8afc60467dc22e9d548
treef00d3c26bc8cceec5e3c7cd8c3b571c413946451
parent26118bf1f3068249f8bf76edccaf663c0c5518c8

feat: implement tuple types (#109)

this regresses return types by making analysis of a function's return type when it has not been inferred yet panic. it does not provide any code to prevent the panic

20 files changed, 1245 insertions(+), 472 deletions(-)

CHANGELOG.md+2-1
......@@ -10,7 +10,8 @@
1010- feat: add array index syntax via `x[1]` and `x.1`
1111- feat: add `len` property to arrays
1212- feat: rename `noreturn` to `never`
13- feat: allow instantiating `@extern`
13- feat: allow instantiating `@extern` at compile time
14- feat: implement tuples
1415- fix: crash when printing unused struct declaration error
1516- fix: provide array result type to sema
1617- fix: crash when analyzing an empty array without a result type
src/cli.zig+6-3
......@@ -8,7 +8,7 @@ options: []const Option = &.{},
88positionals: Positionals = .none,
99help: HelpConfig = .{},
1010
11const Option = struct {
11pub const Option = struct {
1212 /// Should be formatted as the flag name: '--dump-ir'
1313 name: []const u8,
1414 /// Displayed in generated help menu
......@@ -32,6 +32,10 @@ const Option = struct {
3232 }
3333 };
3434
35 pub fn optional(comptime cond: bool, opts: []const Option) []const Option {
36 return if (cond) opts else &.{};
37 }
38
3539 const help: Option = .{
3640 .name = "--help",
3741 .short = 'h',
......@@ -631,8 +635,7 @@ pub fn main() !void {
631635 const program_name, const args = try Cli.programArgsAlloc(alloc);
632636 defer Cli.programArgsFree(alloc, args);
633637
634 const parsed = config.parse(args[1..]) catch
635 config.showHelpAndExit(program_name);
638 const parsed = config.parse(program_name, args[1..]);
636639
637640 std.debug.print("--flag: {any}\n", .{parsed.flag});
638641 std.debug.print("--str: {?s}\n", .{parsed.str});
src/cli_compiler.zig created+146
......@@ -0,0 +1,146 @@
1//! For testing the core compiler with minimal feature and
2//! additional debugging parameters.
3
4const config = @import("config");
5
6const arg_config: Cli = .{
7 .options = &.{
8 .{ .short = 'i', .name = "--dump-ir", .desc = "Dump intermediate representations" },
9 .{ .name = "--dump-state", .desc = "Dump compiler state to JSON", .kind = .string },
10 .{ .short = 'r', .name = "--report-trace", .desc = "Print stack traces to errors" },
11 .{ .short = 'L', .name = "--no-leak-checks", .desc = "" },
12 },
13 .positionals = .single,
14 .help = .{
15 .usage = "[options...] [entry file]",
16 },
17};
18
19pub fn main() !u8 {
20 try tracer.init();
21 defer tracer.deinit();
22 try tracer.init_thread(null);
23 defer tracer.deinit_thread();
24
25 // unicode props is asserted to be initialized before core.Tokenizer is used
26 // this will result in a static page allocation, which is never cleaned up.
27 try core.unicode_props.init();
28
29 const use_gpa = builtin.mode == .Debug;
30 var gpa = if (use_gpa)
31 std.heap.GeneralPurposeAllocator(.{
32 .stack_trace_frames = 100,
33 }){}
34 else
35 std.heap.ArenaAllocator.init(std.heap.page_allocator);
36 var no_leak_checks = false;
37 defer _ = if (use_gpa and !no_leak_checks) gpa.deinit();
38 const alloc = gpa.allocator();
39
40 // Current working directory is required for CLI resolution.
41 var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;
42 const cwd = try std.posix.getcwd(&cwd_buf);
43
44 // Command line argument parsing.
45 const program_name = "zig build compiler --";
46 _, const args = try Cli.programArgsAlloc(alloc, program_name);
47 defer Cli.programArgsFree(alloc, args);
48 const opts = arg_config.parse(program_name, args);
49
50 const file_path = try std.fs.path.resolve(alloc, &.{
51 cwd,
52 opts.positionals orelse
53 arg_config.showHelpAndExit(program_name),
54 });
55 defer alloc.free(file_path);
56
57 no_leak_checks = opts.no_leak_checks orelse false;
58
59 // Execute compiler
60 {
61 var js_backend = core.Js{};
62
63 var comp = try core.Comp.init(.{
64 .gpa = tracer.tracingAllocator(&alloc),
65 .options = .{
66 .dump_ir = opts.dump_ir orelse false,
67 },
68 .backend = .{ .js = &js_backend },
69 });
70 defer comp.deinit();
71
72 defer if (opts.dump_state) |path| {
73 dumpState(&comp, path) catch |err| {
74 std.debug.print("Failed to dump compilation state: {}", .{err});
75 };
76 };
77
78 const main_file = try comp.getFile(file_path);
79
80 var fail_report: Report = undefined;
81 const teg = main_file.getTeg(&comp, &fail_report) catch |err| switch (err) {
82 error.TransformFail => {
83 std.debug.print("Transform failed:\n", .{});
84 try fail_report.dump(.initComp(&comp, main_file), comp.out);
85 fail_report.deinit(comp.gpa);
86 return 1;
87 },
88 error.ParseFail => {
89 // Already printed.
90 return 1;
91 },
92 else => return err,
93 };
94
95 if (comp.options.dump_ir) {
96 const stderr = std.io.getStdErr().writer();
97 try teg.dumpToWriter(stderr, null);
98 }
99
100 const entry_point = try comp.enqueueEntryModule(main_file);
101 comp.runAllJobs() catch |err| switch (err) {
102 error.AnalysisFail => {
103 // error is already printed
104 return 1;
105 },
106 else => return err,
107 };
108
109 {
110 // Output to standard out
111 const output = std.io.getStdOut().writer();
112 var bufw = std.io.bufferedWriter(output);
113 const writer = bufw.writer();
114 try js_backend.link(&comp, entry_point, writer.any());
115 try bufw.flush();
116 }
117 }
118
119 std.process.cleanExit();
120 return 0;
121}
122
123const core = @import("core");
124const Comp = core.Comp;
125const Report = core.Report;
126
127const tracer = @import("tracer");
128pub const tracer_impl = @field(tracer, @tagName(@import("config").tracing_mode));
129
130const std = @import("std");
131const builtin = @import("builtin");
132const Cli = @import("Cli.zig");
133
134fn dumpState(comp: *const Comp, dump_path: []const u8) !void {
135 const file = try std.fs.cwd().createFile(dump_path, .{});
136 defer file.close();
137
138 var bufw = std.io.bufferedWriter(file.writer());
139 var jw = std.json.writeStream(bufw.writer(), .{
140 .whitespace = .indent_2,
141 });
142
143 try comp.jsonStringify(&jw);
144 try bufw.writer().writeAll("\n");
145 try bufw.flush();
146}
src/cli_transform.zig+1-2
......@@ -25,8 +25,7 @@ pub fn main() !u8 {
2525 const program_name, const raw_args = try Cli.programArgsAlloc(alloc, "transform");
2626 defer Cli.programArgsFree(alloc, raw_args);
2727
28 const args = arg_config.parse(raw_args) catch
29 arg_config.showHelpAndExit(program_name);
28 const args = arg_config.parse(program_name, raw_args);
3029
3130 const file_path = args.positionals orelse
3231 arg_config.fatalWithHelp(program_name, "missing filepath argument\n", .{});
src/core/Air.Dump.zig+39-1
......@@ -130,6 +130,16 @@ pub const State = struct {
130130 try tty.setColor(w, .reset);
131131 try w.writeAll(")");
132132 },
133 .field_tuple => {
134 const field: Air.FieldStruct = air.instData(.field_tuple, inst);
135 try w.writeAll("(");
136 try s.dumpInstRef(w, field.base);
137 try w.writeAll(", ");
138 try tty.setColor(w, .magenta);
139 try w.print("{d}", .{field.field_index});
140 try tty.setColor(w, .reset);
141 try w.writeAll(")");
142 },
133143 inline .call, .js_new => |t| {
134144 const call: Air.Call = air.instData(t, inst);
135145 const call_data = call.call_data.get(air);
......@@ -182,6 +192,15 @@ pub const State = struct {
182192 try s.dumpRefList(w, array_init.list.get(air).slice(air), '[', ']');
183193 try w.writeAll(")");
184194 },
195 .tuple_init => {
196 // const array_init: Air.ArrayInit = air.instData(.array_init, inst);
197 // const array_type = array_init.ty.toArrayType(s.ip).?;
198
199 // try w.writeAll("(");
200 // try s.dumpValue(w, array_type.item_type.value());
201 // try s.dumpRefList(w, array_init.list.get(air).slice(air), '[', ']');
202 // try w.writeAll(")");
203 },
185204 }
186205 }
187206
......@@ -233,7 +252,10 @@ pub const State = struct {
233252 try tty.setColor(w, .blue);
234253 try w.writeAll(") -> ");
235254 try tty.setColor(w, .reset);
236 try s.dumpValue(w, ty.return_type.value());
255 if (ty.return_type) |return_type|
256 try s.dumpValue(w, return_type.value())
257 else
258 try w.writeAll("[inferred]");
237259 },
238260 inline .float, .int32, .comptime_float => |data, tag| {
239261 try tty.setColor(w, .bright_black);
......@@ -379,6 +401,22 @@ pub const State = struct {
379401 }
380402 try w.writeAll("'");
381403 },
404 .tuple_type => |tuple_type| {
405 try w.writeAll("(");
406 for (tuple_type.items, 0..) |item, i| {
407 if (i != 0) try w.writeAll(", ");
408 try s.dumpValue(w, item.value());
409 }
410 try w.writeAll(")");
411 },
412 .tuple_value => |tuple_value| {
413 try w.writeAll("(");
414 for (tuple_value.items, 0..) |item, i| {
415 if (i != 0) try w.writeAll(", ");
416 try s.dumpValue(w, item);
417 }
418 try w.writeAll(")");
419 },
382420 }
383421 }
384422
src/core/Air.zig+28-2
......@@ -25,9 +25,11 @@ pub const Inst = struct {
2525
2626 struct_init,
2727 array_init,
28 tuple_init,
2829
2930 field_opaque,
3031 field_struct,
32 field_tuple,
3133 field_array,
3234
3335 array_len,
......@@ -58,7 +60,8 @@ pub const Inst = struct {
5860 .store => Store,
5961 .struct_init => StructInit,
6062 .array_init => ArrayInit,
61 .field_struct => FieldStruct,
63 .tuple_init => TupleInit,
64 .field_struct, .field_tuple => FieldStruct,
6265 .field_opaque,
6366 .field_array,
6467 .add,
......@@ -286,6 +289,18 @@ pub const ArrayInit = struct {
286289 list: ExtraIndex(RefList),
287290};
288291
292pub const TupleInit = struct {
293 ty: Type,
294 field_values: u32,
295
296 pub fn valuesPtr(
297 init: *const TupleInit,
298 air: *const Air,
299 ) [*]const Air.Inst.Ref {
300 return @ptrCast(air.extra.list.items[init.field_values..].ptr);
301 }
302};
303
289304pub const FieldStruct = struct {
290305 base: Inst.Index,
291306 field_index: u32,
......@@ -342,6 +357,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In
342357 .@"if" => air.instData(.@"if", index).data.get(air).union_ty,
343358 .struct_init => air.instData(.struct_init, index).ty,
344359 .array_init => air.instData(.array_init, index).ty,
360 .tuple_init => air.instData(.tuple_init, index).ty,
345361 .arg => air.instData(.arg, index).type,
346362
347363 // A handful of binary operators are lowered to a form where both sides
......@@ -372,7 +388,9 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In
372388 .call => {
373389 const base_type = air.typeOf(air.instData(.call, index).base, ip);
374390 if (base_type == .@"opaque") return .@"opaque";
375 return base_type.toFunctionPrototype(ip).?.return_type;
391 return base_type.toFunctionPrototype(ip).?.return_type orelse
392 // unreachable // return type was not resolved
393 @panic("TODO: resolve return type");
376394 },
377395
378396 // Field accesses on structs are special-cased by storing the field
......@@ -391,6 +409,14 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In
391409 .toArrayType(ip) orelse unreachable; // field_array demands array type
392410 return base_type.item_type;
393411 },
412
413 // Tuple accesses use the tuple they are accessing into.
414 .field_tuple => {
415 const inst = air.instData(.field_tuple, index);
416 const base_type = air.typeOfIndex(inst.base, ip)
417 .toTupleType(ip) orelse unreachable; // field_tuple demands tuple type
418 return base_type.items[inst.field_index];
419 },
394420 };
395421}
396422
src/core/Ast.Dump.zig+2
......@@ -126,6 +126,8 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err
126126 .struct_decl,
127127 => try dumpNodeList(writer, state, list, '{', '}'),
128128 .builtin_call,
129 .tuple_init,
130 .tuple_type,
129131 => try dumpNodeList(writer, state, list, '(', ')'),
130132 .array_init,
131133 => try dumpNodeList(writer, state, list, '[', ']'),
src/core/InternPool.zig+146-12
......@@ -101,6 +101,8 @@ pub const Tag = enum(u8) {
101101 struct_type,
102102 /// An array type
103103 array_type,
104 /// A tuple type
105 tuple_type,
104106 /// A type that can be represented with only an enum tag.
105107 simple_type,
106108
......@@ -129,6 +131,8 @@ pub const Tag = enum(u8) {
129131 external_value,
130132 /// A value masked as "opaque" type
131133 opaque_value,
134 /// A tuple value
135 tuple_value,
132136
133137 pub fn Data(comptime tag: Tag) type {
134138 return switch (tag) {
......@@ -154,13 +158,15 @@ pub const Tag = enum(u8) {
154158 => unreachable, // does not have a payload
155159 .float, .comptime_float => Float,
156160 .func => FuncDecl,
157 .function_type => TypeFunction,
161 .function_type => FunctionType,
158162 .string => String.WithLen,
159163 .struct_value => StructValue,
160164 .struct_type => StructType,
161165 .array_type => ArrayType,
162166 .array_value => ArrayValue,
163167 .external_value => ExternalValue,
168 .tuple_type => TupleType,
169 .tuple_value => TupleValue,
164170 };
165171 }
166172
......@@ -174,9 +180,9 @@ pub const Tag = enum(u8) {
174180
175181 /// Trailing:
176182 /// 0. param_type: Index to param type. Repeated .params_len
177 pub const TypeFunction = struct {
183 pub const FunctionType = struct {
178184 params_len: u32,
179 return_type: Type,
185 return_type: Type.Optional,
180186 };
181187
182188 /// Trailing:
......@@ -199,6 +205,20 @@ pub const Tag = enum(u8) {
199205 items_len: u32,
200206 };
201207
208 /// Trailing:
209 /// 0. item: Type. Repeated per .items_len
210 pub const TupleType = struct {
211 items_len: u32,
212 };
213
214 /// Trailing:
215 /// 0. value: Value. Repeated per .items_len
216 pub const TupleValue = struct {
217 /// This is the tuple type, not the item type
218 type: Type,
219 items_len: u32,
220 };
221
202222 /// Trailing:
203223 /// 0. value: Value. Repeated .ty.structInfo().field_len
204224 pub const StructValue = struct {
......@@ -216,9 +236,9 @@ pub const Tag = enum(u8) {
216236/// This is an index into .items
217237pub const Value = enum(u32) {
218238 pub const first_type: Value = .opaque_type;
219 pub const last_type: Value = .numeric_literal_array_type;
239 pub const last_type: Value = .empty_tuple_type;
220240 pub const first_value: Value = .void;
221 pub const last_value: Value = .empty_array;
241 pub const last_value: Value = .empty_tuple;
222242
223243 opaque_type,
224244 bool_type,
......@@ -230,6 +250,7 @@ pub const Value = enum(u32) {
230250 numeric_literal_type,
231251 numeric_literal_array_type,
232252 never_array_type,
253 empty_tuple_type,
233254
234255 void,
235256 bool_true,
......@@ -239,6 +260,7 @@ pub const Value = enum(u32) {
239260 nan,
240261 empty_string,
241262 empty_array,
263 empty_tuple,
242264
243265 none = std.math.maxInt(u32),
244266 _,
......@@ -316,6 +338,7 @@ pub const Value = enum(u32) {
316338 .simple_type,
317339 .struct_type,
318340 .array_type,
341 .tuple_type,
319342 => .type,
320343
321344 .simple_value => switch (value.valueData(ip, .simple_value)) {
......@@ -332,6 +355,7 @@ pub const Value = enum(u32) {
332355 .struct_value => value.toKey(ip).struct_value.ty,
333356 .array_value => value.toKey(ip).array_value.type,
334357 .external_value, .opaque_value => .@"opaque",
358 .tuple_value => value.toKey(ip).tuple_value.type,
335359 };
336360 }
337361
......@@ -345,6 +369,7 @@ pub const Value = enum(u32) {
345369 .struct_type,
346370 .simple_type,
347371 .array_type,
372 .tuple_type,
348373 => @enumFromInt(@intFromEnum(value)),
349374
350375 // exception: void is a value AND a type
......@@ -395,6 +420,7 @@ pub const Type = enum(u32) {
395420 numeric_literal = @intFromEnum(Value.numeric_literal_type),
396421 numeric_literal_array = @intFromEnum(Value.numeric_literal_array_type),
397422 never_array = @intFromEnum(Value.never_array_type),
423 empty_tuple = @intFromEnum(Value.empty_tuple_type),
398424 void = @intFromEnum(Value.void),
399425 _,
400426
......@@ -404,6 +430,7 @@ pub const Type = enum(u32) {
404430 struct_type = @intFromEnum(InternPool.Tag.struct_type),
405431 array_type = @intFromEnum(InternPool.Tag.array_type),
406432 simple_type = @intFromEnum(InternPool.Tag.simple_type),
433 tuple_type = @intFromEnum(InternPool.Tag.tuple_type),
407434 };
408435
409436 /// An array of `Index` existing within the `extra` array.
......@@ -446,6 +473,11 @@ pub const Type = enum(u32) {
446473 return ip.valueToKey(ty.value()).array_type;
447474 }
448475
476 pub fn toTupleType(ty: Type, ip: *const InternPool) ?Key.TupleType {
477 if (ty.tag(ip) != .tuple_type) return null;
478 return ip.valueToKey(ty.value()).tuple_type;
479 }
480
449481 /// If it is possible to represent this type at runtime. Some types do not
450482 /// have a defined representation, such as structs (to allow field renaming, unwrapping).
451483 pub fn hasPossibleRepresentation(ty: Type, ip: *const InternPool) bool {
......@@ -466,6 +498,15 @@ pub const Type = enum(u32) {
466498 .struct_type,
467499 .function_type,
468500 => true,
501 .tuple_type => {
502 const tuple_type = ty.toTupleType(ip).?;
503 for (tuple_type.items) |item| {
504 if (!item.hasPossibleRepresentation(ip)) {
505 return false;
506 }
507 }
508 return true;
509 },
469510 .array_type => ty.toArrayType(ip).?.item_type.hasPossibleRepresentation(ip),
470511 },
471512 };
......@@ -490,6 +531,7 @@ pub const Type = enum(u32) {
490531 .simple_type => unreachable, // all simple types are already printed
491532 .struct_type,
492533 .function_type,
534 .tuple_type,
493535 => false,
494536 .array_type => ty.toArrayType(ip).?.item_type.hasDefinedRepresentation(ip),
495537 },
......@@ -536,7 +578,10 @@ pub const Type = enum(u32) {
536578 try writer.print("{}", .{param.displayName(ip)});
537579 }
538580 try writer.writeAll(") -> ");
539 try writer.print("{}", .{function_type.return_type.displayName(ip)});
581 if (function_type.return_type) |return_type|
582 try writer.print("{}", .{return_type.displayName(ip)})
583 else
584 try writer.writeAll("[inferred]");
540585 },
541586 .struct_type => {
542587 try writer.writeAll("[struct]");
......@@ -546,8 +591,29 @@ pub const Type = enum(u32) {
546591 const array_type = ty.toArrayType(ip).?;
547592 try writer.print("{}", .{array_type.item_type.displayName(ip)});
548593 },
594 .tuple_type => {
595 try writer.writeAll("(");
596 for (ty.toTupleType(ip).?.items, 0..) |item, i| {
597 if (i != 0) try writer.writeAll(", ");
598 try writer.print("{}", .{item.displayName(ip)});
599 }
600 try writer.writeAll(")");
601 },
549602 }
550603 }
604
605 pub const Optional = enum(u32) {
606 none = std.math.maxInt(u32),
607 _,
608
609 pub fn init(opt: ?Type) Optional {
610 return if (opt) |o| @enumFromInt(@intFromEnum(o)) else .none;
611 }
612
613 pub fn unwrap(opt: Optional) ?Type {
614 return if (opt != .none) @enumFromInt(@intFromEnum(opt)) else null;
615 }
616 };
551617};
552618
553619pub const Item = struct {
......@@ -584,6 +650,7 @@ pub const static_keys = [_]Key{
584650 SimpleType.numeric_literal.key(),
585651 .{ .array_type = .{ .item_type = Type.numeric_literal } },
586652 .{ .array_type = .{ .item_type = Type.never } },
653 .{ .tuple_type = .{ .items = &.{} } },
587654
588655 SimpleValue.void.key(),
589656 SimpleValue.true.key(),
......@@ -596,6 +663,10 @@ pub const static_keys = [_]Key{
596663 .items = &.{},
597664 } },
598665 .{ .string = "" },
666 .{ .tuple_value = .{
667 .type = .empty_tuple,
668 .items = &.{},
669 } },
599670};
600671
601672/// State that is mutable during semantic analysis. This data is not used for
......@@ -876,11 +947,13 @@ pub const Key = union(enum) {
876947 func: Func,
877948 array_value: ArrayValue,
878949 opaque_value: Value,
950 tuple_value: TupleValue,
879951 // Types
880952 simple_type: SimpleType,
881953 function_type: FunctionType,
882954 struct_type: StructType,
883955 array_type: ArrayType,
956 tuple_type: TupleType,
884957 external_value: ExternalValue,
885958
886959 const Adapter = struct {
......@@ -899,7 +972,10 @@ pub const Key = union(enum) {
899972
900973 pub const FunctionType = struct {
901974 param_types: []Type,
902 return_type: Type,
975 /// If a return type is missing, it is inferred and not resolved yet.
976 /// When analysis figures out a return type, a new `FunctionType` with
977 /// the resolved return type.
978 return_type: ?Type,
903979
904980 pub fn hash(self: FunctionType, hasher: *Hash) void {
905981 const t = tracer.trace(@src());
......@@ -946,6 +1022,14 @@ pub const Key = union(enum) {
9461022 }
9471023 };
9481024
1025 pub const TupleType = struct {
1026 items: []const Type,
1027
1028 pub fn hash(self: TupleType, hasher: *Hash) void {
1029 std.hash.autoHashStrat(hasher, self.items, .Deep);
1030 }
1031 };
1032
9491033 pub const ExternalValue = struct {
9501034 /// A list of strings to generate the property access
9511035 /// so `console.log` would be .{ "console", "log" }
......@@ -967,6 +1051,17 @@ pub const Key = union(enum) {
9671051 }
9681052 };
9691053
1054 pub const TupleValue = struct {
1055 /// This is the tuple type, not the item type
1056 type: Type,
1057 items: []const Value,
1058
1059 pub fn hash(self: TupleValue, hasher: *Hash) void {
1060 std.hash.autoHash(hasher, self.type);
1061 std.hash.autoHashStrat(hasher, self.items, .Deep);
1062 }
1063 };
1064
9701065 pub const Func = struct {
9711066 ty: Type,
9721067 owner_decl: DeclIndex,
......@@ -1008,6 +1103,8 @@ pub const Key = union(enum) {
10081103 .array_value,
10091104 .array_type,
10101105 .external_value,
1106 .tuple_type,
1107 .tuple_value,
10111108 => |x| {
10121109 var hasher = Hash.init(seed);
10131110 x.hash(&hasher);
......@@ -1047,7 +1144,7 @@ pub const Key = union(enum) {
10471144 return false;
10481145 },
10491146 .struct_type => {
1050 return false; // structs are nomial
1147 return false; // structs are nominal
10511148 },
10521149 .struct_value => |a_info| {
10531150 const b_info = b.struct_value;
......@@ -1066,6 +1163,15 @@ pub const Key = union(enum) {
10661163 const b_info = b.external_value;
10671164 return std.mem.eql(NullTerminatedString, a_info.key, b_info.key);
10681165 },
1166 .tuple_type => |a_info| {
1167 const b_info = b.tuple_type;
1168 return a_info.items.len == b_info.items.len and std.mem.eql(Type, a_info.items, b_info.items);
1169 },
1170 .tuple_value => |a_info| {
1171 const b_info = b.tuple_value;
1172 return a_info.type == b_info.type and
1173 std.mem.eql(Value, a_info.items, b_info.items);
1174 },
10691175 };
10701176 }
10711177};
......@@ -1336,6 +1442,19 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key {
13361442 .key = trail.readNullTerminatedStringSlice(payload.key_count).get(ip),
13371443 } };
13381444 },
1445 .tuple_type => {
1446 const payload: Tag.TupleType, var trail = data.decode(.tuple_type, item.tag).getTrail(ip);
1447 return .{ .tuple_type = .{
1448 .items = trail.readTypeSlice(payload.items_len).get(ip),
1449 } };
1450 },
1451 .tuple_value => {
1452 const payload: Tag.TupleValue, var trail = data.decode(.tuple_value, item.tag).getTrail(ip);
1453 return .{ .tuple_value = .{
1454 .type = payload.type,
1455 .items = trail.readValueSlice(payload.items_len).get(ip),
1456 } };
1457 },
13391458 };
13401459}
13411460
......@@ -1391,9 +1510,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
13911510 .function_type => |f| brk: {
13921511 for (f.param_types) |ty| ty.assertValid(ip);
13931512
1394 const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.TypeFunction, .{
1513 const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.FunctionType, .{
13951514 .params_len = @intCast(f.param_types.len),
1396 .return_type = f.return_type,
1515 .return_type = .init(f.return_type),
13971516 }, f.param_types.len);
13981517 defer trail.end();
13991518 trail.writeSlice(f.param_types);
......@@ -1439,6 +1558,21 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
14391558 trail.writeSlice(s.key);
14401559 break :brk Item.encode(.external_value, extra);
14411560 },
1561 .tuple_type => |s| brk: {
1562 const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.TupleType, .{
1563 .items_len = @intCast(s.items.len),
1564 }, s.items.len);
1565 trail.writeSlice(s.items);
1566 break :brk Item.encode(.tuple_type, extra);
1567 },
1568 .tuple_value => |s| brk: {
1569 const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.TupleValue, .{
1570 .type = s.type,
1571 .items_len = @intCast(s.items.len),
1572 }, s.items.len);
1573 trail.writeSlice(s.items);
1574 break :brk Item.encode(.tuple_value, extra);
1575 },
14421576 };
14431577 ip.items.appendAssumeCapacity(item);
14441578 const index: Value = @enumFromInt(ip.items.len - 1);
......@@ -1451,12 +1585,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
14511585
14521586fn extraTypeFunction(
14531587 ip: *const InternPool,
1454 extra_index: ExtraIndex(Tag.TypeFunction),
1588 extra_index: ExtraIndex(Tag.FunctionType),
14551589) Key.FunctionType {
14561590 const function_type, var trail = extra_index.getTrail(ip);
14571591 const param_types = trail.readTypeSlice(function_type.params_len);
14581592 const result: Key.FunctionType = .{
1459 .return_type = function_type.return_type,
1593 .return_type = function_type.return_type.unwrap(),
14601594 .param_types = param_types.get(ip),
14611595 };
14621596 return result;
src/core/Parse.zig+296-258
......@@ -104,15 +104,216 @@ pub const ExpectedContext = enum {
104104 }
105105};
106106
107const Operator = enum(u8) {
108 add = @intFromEnum(Token.Tag.plus),
109 sub = @intFromEnum(Token.Tag.minus),
110 mul = @intFromEnum(Token.Tag.star),
111 div = @intFromEnum(Token.Tag.slash),
112 rem = @intFromEnum(Token.Tag.percent),
113 pow = @intFromEnum(Token.Tag.star_star),
114 @"and" = @intFromEnum(Token.Tag.and_and),
115 @"or" = @intFromEnum(Token.Tag.pipe_pipe),
116 bit_and = @intFromEnum(Token.Tag.@"and"),
117 bit_or = @intFromEnum(Token.Tag.pipe),
118 bit_xor = @intFromEnum(Token.Tag.caret),
119 shl = @intFromEnum(Token.Tag.lt_lt),
120 shr = @intFromEnum(Token.Tag.gt_gt),
121 eq = @intFromEnum(Token.Tag.equal_equal),
122 ne = @intFromEnum(Token.Tag.bang_equal),
123 lt = @intFromEnum(Token.Tag.lt),
124 le = @intFromEnum(Token.Tag.lt_equal),
125 gt = @intFromEnum(Token.Tag.gt),
126 ge = @intFromEnum(Token.Tag.gt_equal),
127 // Following operators are assignment operators
128 assign = @intFromEnum(Token.Tag.equal),
129 add_assign = @intFromEnum(Token.Tag.plus_equal),
130 sub_assign = @intFromEnum(Token.Tag.minus_equal),
131 mul_assign = @intFromEnum(Token.Tag.star_equal),
132 div_assign = @intFromEnum(Token.Tag.slash_equal),
133 rem_assign = @intFromEnum(Token.Tag.percent_equal),
134 pow_assign = @intFromEnum(Token.Tag.star_star_equal),
135 and_assign = @intFromEnum(Token.Tag.and_and_equal),
136 or_assign = @intFromEnum(Token.Tag.pipe_pipe_equal),
137 bit_and_assign = @intFromEnum(Token.Tag.and_equal),
138 bit_or_assign = @intFromEnum(Token.Tag.pipe_equal),
139 bit_xor_assign = @intFromEnum(Token.Tag.caret_equal),
140 shl_assign = @intFromEnum(Token.Tag.lt_lt_equal),
141 shr_assign = @intFromEnum(Token.Tag.gt_gt_equal),
142
143 const Direction = enum { ltr, rtl };
144
145 /// Only the binary operator levels are actually used,
146 /// but all are listed for reference.
147 const Level = enum {
148 /// e. e[] e()
149 accessor,
150 /// new
151 new,
152 /// ++e, --e, !e, ~e, +e, -e, typeof, void, delete
153 prefix,
154 /// **
155 exponentiation,
156 /// *, /, %
157 multiplicative,
158 /// +, -
159 additive,
160 /// <<, >>, >>>
161 bitwise_shift,
162 /// <, <=, >, >=
163 relational,
164 /// ==, !=
165 equality,
166 /// &
167 bit_and,
168 /// ^
169 bit_xor,
170 /// |
171 bit_or,
172 /// &&
173 logical_and,
174 /// ||, ??
175 logical_or,
176 /// =, +=, -=, *=, /=, %=, / <<=, >>=, >>>=, &=, ^=, |=
177 assign,
178 };
179
180 pub fn direction(op: Operator) Direction {
181 return switch (op) {
182 .pow => .rtl,
183
184 .assign,
185 .add_assign,
186 .sub_assign,
187 .mul_assign,
188 .div_assign,
189 .rem_assign,
190 .pow_assign,
191 .and_assign,
192 .or_assign,
193 .bit_and_assign,
194 .bit_or_assign,
195 .bit_xor_assign,
196 .shl_assign,
197 .shr_assign,
198 => .rtl,
199
200 else => .ltr,
201 };
202 }
203
204 pub fn level(op: Operator) Level {
205 return switch (op) {
206 .pow => .exponentiation,
207 .mul, .div, .rem => .multiplicative,
208 .add, .sub => .additive,
209 .shl, .shr => .bitwise_shift,
210 .lt, .le, .gt, .ge => .relational,
211 .eq, .ne => .equality,
212 .@"and" => .logical_and,
213 .@"or" => .logical_or,
214 .bit_and => .bit_and,
215 .bit_xor => .bit_xor,
216 .bit_or => .bit_or,
217 .assign,
218 .add_assign,
219 .sub_assign,
220 .mul_assign,
221 .div_assign,
222 .rem_assign,
223 .pow_assign,
224 .and_assign,
225 .or_assign,
226 .bit_and_assign,
227 .bit_or_assign,
228 .bit_xor_assign,
229 .shl_assign,
230 .shr_assign,
231 => .assign,
232 };
233 }
234
235 const lut = brk: {
236 const token_tags = std.enums.values(Token.Tag);
237 const op_tags = std.enums.values(Operator);
238
239 var is_operator_lut: [token_tags.len]bool = .{false} ** token_tags.len;
240 for (op_tags) |op| {
241 is_operator_lut[@intFromEnum(op)] = true;
242 }
243
244 var ast_tag_lut: [token_tags.len]Node.Tag = .{.root} ** token_tags.len;
245 for (op_tags) |op| {
246 ast_tag_lut[@intFromEnum(op)] = @field(Node.Tag, @tagName(op));
247 }
248
249 break :brk struct {
250 const is_operator = is_operator_lut;
251 const ast_tag = ast_tag_lut;
252 };
253 };
254
255 pub fn fromToken(tag: Token.Tag) ?Operator {
256 return if (lut.is_operator[@intFromEnum(tag)])
257 @enumFromInt(@intFromEnum(tag))
258 else
259 null;
260 }
261
262 pub fn astTag(op: Operator) Node.Tag {
263 const tag = lut.ast_tag[@intFromEnum(op)];
264 assert(tag != .root); // invalid operator or lut construction
265 return tag;
266 }
267
268 pub fn isAssign(op: Operator) bool {
269 return switch (op) {
270 .assign,
271 .add_assign,
272 .sub_assign,
273 .mul_assign,
274 .div_assign,
275 .rem_assign,
276 .pow_assign,
277 .and_assign,
278 .or_assign,
279 .bit_and_assign,
280 .bit_or_assign,
281 .bit_xor_assign,
282 .shl_assign,
283 .shr_assign,
284 => true,
285 else => false,
286 };
287 }
288};
289
290const ParseExprMode = enum {
291 /// Standard parsing
292 expression,
293 /// Statements allow assignment binary operators in addition to other expressions.
294 statement,
295 /// This bans struct initializations so that parsing stops at a '{'
296 /// Example: "if hello {" must not be parsed as a struct initialization.
297 preceding_block,
298};
299
300/// Some constructs are valid expressions as well as valid top-level
301/// declarations. Top level declarations must have associated names, but the
302/// anonymous versions of these must not have names.
303const ExprParseContext = enum {
304 declaration,
305 expression,
306};
307
308const TypePresence = enum {
309 types_optional,
310 types_required,
311};
312
107313/// When error.SyntaxError is returned, `p.err` is set to this as well as `p.pos`
108314/// When printing these, token list and Ast is not actually available, so full tokens
109315/// are stored in the errors.
110316pub const AstError = union(enum) {
111 // /// Failure during tokenization
112 // tokenization: struct {
113 // err: Tokenizer.TokenizeError,
114 // byte: ByteOffset,
115 // },
116317 /// Unexpected data, expected a token.
117318 unexpected_token: struct {
118319 expected: Token.Tag,
......@@ -134,23 +335,6 @@ pub const AstError = union(enum) {
134335
135336 pub fn build(err: AstError, ctx: *Report.MsgContext) !void {
136337 switch (err) {
137 // .tokenization => |data| {
138 // // TODO: consider making tokenizer emit AstError more directly,
139 // // so that it can just annotate start and ends
140 // try ctx.msg(switch (data.err) {
141 // error.UnterminatedString => .{ .codepoint_eol = data.byte },
142 // else => .{ .codepoint = data.byte },
143 // }, "{s}", .{switch (data.err) {
144 // error.OutOfMemory => unreachable,
145 // error.InvalidSyntax => "unexpected token",
146 // error.TrailingNumericSeparator => "trailing numeric separator",
147 // error.UnterminatedString => "unterminated string literal",
148 // error.InvalidEscapeSequence => "invalid escape sequence",
149 // error.InvalidEscapeCodepoint => "invalid escape codepoint",
150 // error.InvalidUtf8 => "file is not valid utf8",
151 // error.GitConflict => "git merge conflict encountered",
152 // }});
153 // },
154338 .unexpected_token => |data| {
155339 try ctx.msg(.{ .token_stored = data.got }, "expected {s}, but found {s}", .{
156340 data.expected.description(),
......@@ -230,7 +414,8 @@ fn parseModuleMembers(p: *Parse) Error!SubList {
230414 .kw_pub,
231415 => try p.scratch.append(p.gpa, try p.parseModuleDecl()),
232416
233 .kw_struct => try p.scratch.append(p.gpa, try p.parseStructDecl()),
417 .kw_struct => try p.scratch.append(p.gpa, try p.parseStructDecl(.declaration)),
418 .kw_tuple => try p.scratch.append(p.gpa, try p.parseStructDecl(.declaration)),
234419
235420 .id => if (p.isContextualKeyword("test")) {
236421 try p.scratch.append(p.gpa, try p.parseTestDecl());
......@@ -292,14 +477,6 @@ fn parseModuleDecl(p: *Parse) Error!NodeIndex {
292477 };
293478}
294479
295/// Some constructs are valid expressions as well as valid top-level
296/// declarations. Top level declarations must have associated names, but the
297/// anonymous versions of these must not have names.
298const ExprParseContext = enum {
299 declaration,
300 expression,
301};
302
303480fn parseFnDecl(p: *Parse) Error!NodeIndex {
304481 const trace = t.trace(@src());
305482 defer trace.end();
......@@ -339,8 +516,6 @@ fn parseFnDecl(p: *Parse) Error!NodeIndex {
339516 });
340517}
341518
342const TypePresence = enum { types_optional, types_required };
343
344519fn parseParamList(p: *Parse, types: TypePresence) Error!SubList {
345520 const trace = t.trace(@src());
346521 defer trace.end();
......@@ -390,13 +565,16 @@ fn parseParamDecl(p: *Parse, types: TypePresence) Error!NodeIndex {
390565 });
391566}
392567
393fn parseStructDecl(p: *Parse) Error!NodeIndex {
568fn parseStructDecl(p: *Parse, ctx: ExprParseContext) Error!NodeIndex {
394569 const trace = t.trace(@src());
395570 defer trace.end();
396571
397572 const main_token = p.assertToken(.kw_struct);
398573
399 _ = try p.expect(.id);
574 switch (ctx) {
575 .declaration => _ = try p.expect(.id),
576 else => {},
577 }
400578
401579 _ = try p.expect(.start_brace);
402580
......@@ -409,6 +587,28 @@ fn parseStructDecl(p: *Parse) Error!NodeIndex {
409587 });
410588}
411589
590fn parseTupleDecl(p: *Parse, ctx: ExprParseContext) Error!NodeIndex {
591 const trace = t.trace(@src());
592 defer trace.end();
593
594 const main_token = p.assertToken(.kw_tuple);
595
596 switch (ctx) {
597 .declaration => _ = try p.expect(.id),
598 else => {},
599 }
600
601 _ = try p.expect(.start_paren);
602
603 const fields = try p.parseExpressionList(.end_paren, .expression);
604
605 return try p.addNode(.{
606 .tag = .tuple_type,
607 .main_token = main_token,
608 .data = .{ .list = fields },
609 });
610}
611
412612fn parseTestDecl(p: *Parse) Error!NodeIndex {
413613 const trace = t.trace(@src());
414614 defer trace.end();
......@@ -447,9 +647,7 @@ fn parseStructFieldList(p: *Parse) Error!SubList {
447647 try p.scratch.append(p.gpa, try p.parseStructField());
448648
449649 switch (p.currentTag()) {
450 .comma => {
451 _ = p.nextToken();
452 },
650 .comma => _ = p.nextToken(),
453651 .end_brace => break,
454652 else => return p.failUnexpectedToken(.struct_field),
455653 }
......@@ -578,13 +776,13 @@ fn parseVarDecl(p: *Parse) Error!NodeIndex {
578776 _ = try p.expect(.id);
579777
580778 const data_type: OptionalNodeIndex = if (p.eatToken(.colon)) |_|
581 optionalNodeIndex(try p.tryParseExpression(.expression))
779 optionalNodeIndex(try p.parseExpressionOrFail(.expression))
582780 else
583781 .none;
584782
585783 _ = try p.expect(.equal);
586784
587 const data_init = try p.tryParseExpression(.expression);
785 const data_init = try p.parseExpressionOrFail(.expression);
588786
589787 _ = try p.expect(.semi);
590788
......@@ -619,204 +817,13 @@ fn isExpressionStart(p: *Parse) bool {
619817 .string,
620818 .tilde,
621819 .kw_fn,
820 .kw_struct,
821 .kw_tuple,
622822 => true,
623823 else => false,
624824 };
625825}
626826
627const Operator = enum(u8) {
628 add = @intFromEnum(Token.Tag.plus),
629 sub = @intFromEnum(Token.Tag.minus),
630 mul = @intFromEnum(Token.Tag.star),
631 div = @intFromEnum(Token.Tag.slash),
632 rem = @intFromEnum(Token.Tag.percent),
633 pow = @intFromEnum(Token.Tag.star_star),
634 @"and" = @intFromEnum(Token.Tag.and_and),
635 @"or" = @intFromEnum(Token.Tag.pipe_pipe),
636 bit_and = @intFromEnum(Token.Tag.@"and"),
637 bit_or = @intFromEnum(Token.Tag.pipe),
638 bit_xor = @intFromEnum(Token.Tag.caret),
639 shl = @intFromEnum(Token.Tag.lt_lt),
640 shr = @intFromEnum(Token.Tag.gt_gt),
641 eq = @intFromEnum(Token.Tag.equal_equal),
642 ne = @intFromEnum(Token.Tag.bang_equal),
643 lt = @intFromEnum(Token.Tag.lt),
644 le = @intFromEnum(Token.Tag.lt_equal),
645 gt = @intFromEnum(Token.Tag.gt),
646 ge = @intFromEnum(Token.Tag.gt_equal),
647 // Following operators are assignment operators
648 assign = @intFromEnum(Token.Tag.equal),
649 add_assign = @intFromEnum(Token.Tag.plus_equal),
650 sub_assign = @intFromEnum(Token.Tag.minus_equal),
651 mul_assign = @intFromEnum(Token.Tag.star_equal),
652 div_assign = @intFromEnum(Token.Tag.slash_equal),
653 rem_assign = @intFromEnum(Token.Tag.percent_equal),
654 pow_assign = @intFromEnum(Token.Tag.star_star_equal),
655 and_assign = @intFromEnum(Token.Tag.and_and_equal),
656 or_assign = @intFromEnum(Token.Tag.pipe_pipe_equal),
657 bit_and_assign = @intFromEnum(Token.Tag.and_equal),
658 bit_or_assign = @intFromEnum(Token.Tag.pipe_equal),
659 bit_xor_assign = @intFromEnum(Token.Tag.caret_equal),
660 shl_assign = @intFromEnum(Token.Tag.lt_lt_equal),
661 shr_assign = @intFromEnum(Token.Tag.gt_gt_equal),
662
663 const Direction = enum { ltr, rtl };
664
665 /// Only the binary operator levels are actually used,
666 /// but all are listed for reference.
667 const Level = enum {
668 /// e. e[] e()
669 accessor,
670 /// new
671 new,
672 /// ++e, --e, !e, ~e, +e, -e, typeof, void, delete
673 prefix,
674 /// **
675 exponentiation,
676 /// *, /, %
677 multiplicative,
678 /// +, -
679 additive,
680 /// <<, >>, >>>
681 bitwise_shift,
682 /// <, <=, >, >=
683 relational,
684 /// ==, !=
685 equality,
686 /// &
687 bit_and,
688 /// ^
689 bit_xor,
690 /// |
691 bit_or,
692 /// &&
693 logical_and,
694 /// ||, ??
695 logical_or,
696 /// =, +=, -=, *=, /=, %=, / <<=, >>=, >>>=, &=, ^=, |=
697 assign,
698 };
699
700 pub fn direction(op: Operator) Direction {
701 return switch (op) {
702 .pow => .rtl,
703
704 .assign,
705 .add_assign,
706 .sub_assign,
707 .mul_assign,
708 .div_assign,
709 .rem_assign,
710 .pow_assign,
711 .and_assign,
712 .or_assign,
713 .bit_and_assign,
714 .bit_or_assign,
715 .bit_xor_assign,
716 .shl_assign,
717 .shr_assign,
718 => .rtl,
719
720 else => .ltr,
721 };
722 }
723
724 pub fn level(op: Operator) Level {
725 return switch (op) {
726 .pow => .exponentiation,
727 .mul, .div, .rem => .multiplicative,
728 .add, .sub => .additive,
729 .shl, .shr => .bitwise_shift,
730 .lt, .le, .gt, .ge => .relational,
731 .eq, .ne => .equality,
732 .@"and" => .logical_and,
733 .@"or" => .logical_or,
734 .bit_and => .bit_and,
735 .bit_xor => .bit_xor,
736 .bit_or => .bit_or,
737 .assign,
738 .add_assign,
739 .sub_assign,
740 .mul_assign,
741 .div_assign,
742 .rem_assign,
743 .pow_assign,
744 .and_assign,
745 .or_assign,
746 .bit_and_assign,
747 .bit_or_assign,
748 .bit_xor_assign,
749 .shl_assign,
750 .shr_assign,
751 => .assign,
752 };
753 }
754
755 const lut = brk: {
756 const token_tags = std.enums.values(Token.Tag);
757 const op_tags = std.enums.values(Operator);
758
759 var is_operator_lut: [token_tags.len]bool = .{false} ** token_tags.len;
760 for (op_tags) |op| {
761 is_operator_lut[@intFromEnum(op)] = true;
762 }
763
764 var ast_tag_lut: [token_tags.len]Node.Tag = .{.root} ** token_tags.len;
765 for (op_tags) |op| {
766 ast_tag_lut[@intFromEnum(op)] = @field(Node.Tag, @tagName(op));
767 }
768
769 break :brk struct {
770 const is_operator = is_operator_lut;
771 const ast_tag = ast_tag_lut;
772 };
773 };
774
775 pub fn fromToken(tag: Token.Tag) ?Operator {
776 return if (lut.is_operator[@intFromEnum(tag)])
777 @enumFromInt(@intFromEnum(tag))
778 else
779 null;
780 }
781
782 pub fn astTag(op: Operator) Node.Tag {
783 const tag = lut.ast_tag[@intFromEnum(op)];
784 assert(tag != .root); // invalid operator or lut construction
785 return tag;
786 }
787
788 pub fn isAssign(op: Operator) bool {
789 return switch (op) {
790 .assign,
791 .add_assign,
792 .sub_assign,
793 .mul_assign,
794 .div_assign,
795 .rem_assign,
796 .pow_assign,
797 .and_assign,
798 .or_assign,
799 .bit_and_assign,
800 .bit_or_assign,
801 .bit_xor_assign,
802 .shl_assign,
803 .shr_assign,
804 => true,
805 else => false,
806 };
807 }
808};
809
810const ParseExprMode = enum {
811 /// Standard parsing
812 expression,
813 /// Statements allow assignment binary operators in addition to other expressions.
814 statement,
815 /// This bans struct initializations so that parsing stops at a '{'
816 /// Example: "if hello {" must not be parsed as a struct initialization.
817 preceding_block,
818};
819
820827fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
821828 const trace = t.trace(@src());
822829 defer trace.end();
......@@ -831,8 +838,13 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
831838
832839 // These unary operators are greedy, so handle them first.
833840 switch (p.currentTag()) {
834 .kw_return => return p.parseReturn(),
835 .kw_break => return p.parseBreak(),
841 .kw_return => return p.parseReturn(mode),
842 .kw_break => return p.parseBreak(mode),
843
844 .kw_assert => return p.parseGreedyUnary(.assert, mode),
845 .kw_comptime => return p.parseGreedyUnary(.@"comptime", mode),
846 .kw_pure => return p.parseGreedyUnary(.pure, mode),
847
836848 else => {},
837849 }
838850
......@@ -846,7 +858,7 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
846858 return expr;
847859}
848860
849fn tryParseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
861fn parseExpressionOrFail(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
850862 return if (p.isExpressionStart())
851863 p.parseExpression(mode)
852864 else
......@@ -858,7 +870,6 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
858870 defer trace.end();
859871
860872 var expr = switch (p.currentTag()) {
861 // Literals
862873 .id => if (p.nextTag() == .colon)
863874 try p.parseLabelExpression()
864875 else
......@@ -867,24 +878,27 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
867878 .number_float => try p.blankNode(.number_float),
868879 .string => try p.blankNode(.string),
869880
870 // Complex expressions
871881 .builtin_id => try p.parseBuiltinCall(),
872 .dot => try p.parseAnonInstantiation(),
873 .kw_break => try p.parseBreak(),
882 .dot => try p.parseAnonStructInit(),
874883 .kw_fn => try p.parseAnonFunction(),
875884 .kw_if => try p.parseIf(),
876 .kw_return => try p.parseReturn(),
885 .kw_tuple => try p.parseTupleType(),
886
877887 .start_brace => try p.parseBlock(),
878888 .start_bracket => try p.parseArrayValueOrType(mode),
879 .start_paren => try p.parseTupleType(),
889 .start_paren => try p.parseTupleInit(),
890
891 .kw_struct => try p.parseStructDecl(.expression),
892
893 .kw_return => return p.parseReturn(mode),
894 .kw_break => return p.parseBreak(mode),
895 .kw_assert => return p.parseGreedyUnary(.assert, mode),
896 .kw_comptime => return p.parseGreedyUnary(.@"comptime", mode),
897 .kw_pure => return p.parseGreedyUnary(.pure, mode),
880898
881 // Unary operators
882899 inline .bang,
883900 .tilde,
884901 .minus,
885 .kw_comptime,
886 .kw_pure,
887 .kw_assert,
888902 => |tag| unary: {
889903 const main_token = p.nextToken();
890904 const expr = try p.parseExpressionPart(mode);
......@@ -955,7 +969,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
955969 return p.fail(.{ .array_type_flipped_hint = p.fullToken(p.pos) });
956970 }
957971
958 const index = try p.tryParseExpression(.expression);
972 const index = try p.parseExpressionOrFail(.expression);
959973
960974 _ = try p.expect(.end_bracket);
961975
......@@ -1020,14 +1034,14 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator, mode: ParseExprMod
10201034 });
10211035}
10221036
1023fn parseReturn(p: *Parse) Error!NodeIndex {
1037fn parseReturn(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
10241038 const trace = t.trace(@src());
10251039 defer trace.end();
10261040
10271041 const main_token = p.assertToken(.kw_return);
10281042
10291043 const expr = if (p.isExpressionStart())
1030 try p.parseExpression(.expression)
1044 try p.parseExpression(mode)
10311045 else
10321046 null;
10331047
......@@ -1040,7 +1054,7 @@ fn parseReturn(p: *Parse) Error!NodeIndex {
10401054 });
10411055}
10421056
1043fn parseBreak(p: *Parse) Error!NodeIndex {
1057fn parseBreak(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
10441058 const trace = t.trace(@src());
10451059 defer trace.end();
10461060
......@@ -1052,7 +1066,7 @@ fn parseBreak(p: *Parse) Error!NodeIndex {
10521066 null;
10531067
10541068 const expr = if (p.isExpressionStart())
1055 try p.parseExpression(.expression)
1069 try p.parseExpression(mode)
10561070 else
10571071 null;
10581072
......@@ -1066,6 +1080,23 @@ fn parseBreak(p: *Parse) Error!NodeIndex {
10661080 });
10671081}
10681082
1083fn parseGreedyUnary(p: *Parse, tag: Node.Tag, mode: ParseExprMode) Error!NodeIndex {
1084 const trace = t.trace(@src());
1085 defer trace.end();
1086
1087 const main_token = p.nextToken();
1088
1089 const expr = try p.parseExpressionOrFail(mode);
1090
1091 return try p.addNode(.{
1092 .tag = tag,
1093 .main_token = main_token,
1094 .data = .{ .un = .{
1095 .expr = expr,
1096 } },
1097 });
1098}
1099
10691100fn parseBuiltinCall(p: *Parse) Error!NodeIndex {
10701101 const trace = t.trace(@src());
10711102 defer trace.end();
......@@ -1083,11 +1114,7 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex {
10831114 });
10841115}
10851116
1086fn parseExpressionList(
1087 p: *Parse,
1088 comptime end_token: Token.Tag,
1089 comptime fail_context: ExpectedContext,
1090) Error!SubList {
1117fn parseExpressionList(p: *Parse, comptime end_token: Token.Tag, fail_context: ExpectedContext) Error!SubList {
10911118 const trace = t.trace(@src());
10921119 defer trace.end();
10931120
......@@ -1161,7 +1188,7 @@ fn parseElseValue(p: *Parse) Error!NodeIndex {
11611188 };
11621189}
11631190
1164fn parseAnonInstantiation(p: *Parse) Error!NodeIndex {
1191fn parseAnonStructInit(p: *Parse) Error!NodeIndex {
11651192 const trace = t.trace(@src());
11661193 defer trace.end();
11671194
......@@ -1272,9 +1299,20 @@ fn parseArrayValueOrType(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
12721299 });
12731300}
12741301
1275fn parseTupleType(p: *Parse) Error!NodeIndex {
1302fn parseTupleInit(p: *Parse) Error!NodeIndex {
12761303 const main_token = p.assertToken(.start_paren);
12771304 const list = try p.parseExpressionList(.end_paren, .expression);
1305 return p.addNode(.{
1306 .tag = .tuple_init,
1307 .main_token = main_token,
1308 .data = .{ .list = list },
1309 });
1310}
1311
1312fn parseTupleType(p: *Parse) Error!NodeIndex {
1313 const main_token = p.assertToken(.kw_tuple);
1314 _ = try p.expect(.start_paren);
1315 const list = try p.parseExpressionList(.end_paren, .expression);
12781316 return p.addNode(.{
12791317 .tag = .tuple_type,
12801318 .main_token = main_token,
src/core/Sema.zig+355-74
......@@ -1,10 +1,13 @@
1//! Semantic Analysis
1//! Semantic Analysis converts `Teg`
22//!
3//! Designed as a nonrecursive pause/resumable state machine, Sema converts
4//! declarations to Intern Pool `Value`s and function bodies to `Air`.
3//! Designed as a nonrecursive state machine which can be paused when an
4//! unfinished dependency is encountered, allowing the thread to work
5//! on other analysis jobs.
56//!
6//! - Compile time resolution of values
7//! - Type checking
7//! Sema's responsibilities include
8//! - Operation validation and reporting of most compile errors.
9//! - Evaluation of `comptime` code.
10//! - Declaring dependencies on other analysis units.
811
912comp: *Comp,
1013ip: *InternPool,
......@@ -175,6 +178,7 @@ const Frame = struct {
175178
176179 undefined: void,
177180 conditional: Conditional,
181 func: Func,
178182 };
179183
180184 const Conditional = enum {
......@@ -183,7 +187,16 @@ const Frame = struct {
183187 runtime_no,
184188 };
185189
190 const Func = union(enum) {
191 return_type,
192 argument: u16,
193 };
194
186195 fn initFromBlock(s: *Sema, block_index: Teg.Inst.Index, mode: AnalysisMode, data: Data) Frame {
196 switch (s.teg.instTag(block_index)) {
197 else => unreachable, // not a block
198 .block, .@"comptime" => {},
199 }
187200 const list = s.teg.instEncodedData(block_index)
188201 .decodeUnsafe(Teg.PlNode(Teg.IndexList), s.teg.instTag(block_index))
189202 .extra_index.get(&s.teg)
......@@ -476,7 +489,8 @@ const InstructionKind = enum {
476489 .@"comptime",
477490 .@"if",
478491 => .control_flow,
479 // these pop frames, which may end the compilation
492 // break is used to pop a frame, which has the
493 // possibility to end analysis
480494 .@"break",
481495 => .pop_control_flow,
482496 // these are statements which do not evaluate to anything (void)
......@@ -501,6 +515,12 @@ const PopControlFlowState = enum { stop, @"continue" };
501515
502516fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag).Result() {
503517 switch (tag) {
518 .block => unreachable, // frame must already be pushed
519 .declaration,
520 .variable_declaration,
521 .test_declaration,
522 => unreachable, // declarations are not analzed by Sema
523
504524 // TODO: pre-evaluate `.param` nodes, since they are always at the start of a function.
505525 // this way we can avoid repeating the non-trivial call into InternPool
506526 .param => {
......@@ -666,6 +686,8 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
666686 return try s.fieldAccessArray(lhs, lhs_type, field.rhs);
667687 } else if (lhs_type.toStructType(s.ip)) |struct_type| {
668688 return try s.fieldAccessStruct(lhs, lhs_type, struct_type, field.rhs);
689 } else if (lhs_type.toTupleType(s.ip)) |tuple_type| {
690 return try s.fieldAccessTuple(lhs, lhs_type, tuple_type, field.rhs);
669691 } else if (lhs.value()) |value| {
670692 if (value.valueTag(s.ip) == .external_value) {
671693 const rhs = try s.getCoercedValue(s.lazyNode(.rhs), field.rhs, .string);
......@@ -877,8 +899,6 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
877899 // return Value.void.toAir();
878900 // },
879901
880 // control flow
881 .block => unreachable, // frame should already be pushed
882902 .@"comptime" => {
883903 const index = s.frame.currentIndex();
884904 try s.pushFrame();
......@@ -892,50 +912,66 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
892912 .{ .params = Type, .captures = Air.Inst.Ref },
893913 .{ data.params.len, data.captures.len },
894914 );
895 defer reserve.deinit();
896 if (data.captures.len > 0) @panic("captures");
915 if (data.captures.len > 0) @panic("TODO: captures");
916
917 var handled_reserve: std.debug.SafetyLock = .{};
918 handled_reserve.lock();
919 defer handled_reserve.assertUnlocked();
920 errdefer handled_reserve.unlock();
897921
898922 for (data.params.slice(&s.teg), 0..) |param, i| {
899923 const src = s.lazyNode(.argument(@intCast(i)));
900 const ty = try s.getType(src, param.unwrap() orelse @panic("TODO: inferred param sets"));
901 if (!ty.hasPossibleRepresentation(s.ip)) {
902 return s.fail(.param_comptime_only, .{
903 .src = src,
904 .type = ty,
905 });
924
925 const known = param.unwrap() orelse @panic("TODO: inferred param sets");
926 switch (known.decode()) {
927 .value => |val| {
928 const ty = (try s.coerceValue(src, val, .type)).toTypeUnsafe(s.ip);
929 if (!ty.hasPossibleRepresentation(s.ip)) {
930 return s.fail(.param_comptime_only, .{
931 .src = src,
932 .type = ty,
933 });
934 }
935 reserve.items(.params)[i] = ty;
936 },
937 .index => |block| {
938 const mode = s.frame.mode;
939 try s.pushFrame();
940 s.frame = Frame.initFromBlock(s, block, mode, .{ .func = .{ .argument = @intCast(i) } });
941 s.frame.mark_index = reserve.index();
942 handled_reserve.unlock();
943 return;
944 },
906945 }
907 reserve.items(.params)[i] = ty;
908946 }
909947
910 const return_type = try s.getType(
911 s.lazyNode(.return_type),
912 data.return_type.unwrap() orelse .void,
913 );
914
915 const tracked_inst = try s.ip.trackInst(s.gpa, s.file, s.frame.currentIndex());
916 const func_type = try s.makeType(.{
917 .function_type = .{
918 .param_types = reserve.items(.params),
919 .return_type = return_type,
920 },
921 });
922 const func_value = try s.makeValue(.{
923 .func = .{
924 .ty = func_type,
925 .owner_decl = s.techDebtDeclIndex(), // TODO: ooh, this is a fun one!
926 .teg_body_inst = tracked_inst,
927 },
928 });
929
930 s.inst_map.add(s.frame.currentIndex(), func_value.toAir());
931
932 s.frame.advance();
948 const return_type = ret: {
949 switch ((data.return_type.unwrap() orelse break :ret null).decode()) {
950 .value => |val| {
951 const ty = (try s.coerceValue(s.lazyNode(.return_type), val, .type)).toTypeUnsafe(s.ip);
952 if (!ty.hasPossibleRepresentation(s.ip)) {
953 return s.fail(.runtime_incompatible_type, .{
954 .src = s.lazyNode(.return_type),
955 .type = ty,
956 });
957 }
958 break :ret ty;
959 },
960 .index => |block| {
961 const mode = s.frame.mode;
962 try s.pushFrame();
963 s.frame = Frame.initFromBlock(s, block, mode, .{ .func = .return_type });
964 s.frame.mark_index = reserve.index();
965 handled_reserve.unlock();
966 return;
967 },
968 }
969 };
933970
934 // TODO: move this queue once we perform one of these
935 // - depend on the return type of an inferred-return-type function
936 // - use the value in any runtime context ()
937 try s.comp.jobs.writeItem(.{ .analysis = .{ .au = .wrap(.{ .func = func_value }) } });
971 try s.postVisitFunctionPrototype(reserve, return_type);
972 handled_reserve.unlock();
938973 },
974
939975 .@"break" => {
940976 const brk: Teg.Break = s.frame.currentPayload(.@"break");
941977 const operand = s.getRef(brk.operand, .marked);
......@@ -978,12 +1014,14 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
9781014
9791015 switch (data.conditional) {
9801016 .comptime_condition => {
1017 assert(operand.kind == .value); // must be comptime
9811018 s.inst_map.add(s.frame.currentIndex(), operand);
9821019 s.frame.advance();
9831020 return .@"continue";
9841021 },
9851022 .runtime_yes => {
9861023 try s.emitRuntimeBlock(if_data.yes.decode().index, operand, mark);
1024 mark.deinit();
9871025
9881026 switch (if_data.no.decode()) {
9891027 .value => {
......@@ -1025,6 +1063,79 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
10251063 }));
10261064 s.frame.advance();
10271065 },
1066 .func => {
1067 const func_data: Teg.Func = s.frame.currentPayload(.func);
1068
1069 const reserve: FunctionReserve = .fromIndex(
1070 &s.scratch,
1071 mark.index,
1072 .{ func_data.params.len, func_data.captures.len },
1073 );
1074
1075 const return_type = switch (data.func) {
1076 .argument => |arg_num| ret: {
1077 for (
1078 func_data.params.slice(&s.teg)[arg_num + 1 ..],
1079 reserve.items(.params)[arg_num + 1 ..],
1080 arg_num + 1..,
1081 ) |param, *dst, i| {
1082 const known = param.unwrap() orelse @panic("TODO: inferred param sets");
1083 switch (known.decode()) {
1084 .value => |val| {
1085 const ty = (try s.coerceValue(s.lazyNode(.argument(@intCast(i))), val, .type)).toTypeUnsafe(s.ip);
1086 if (!ty.hasPossibleRepresentation(s.ip)) {
1087 return s.fail(.param_comptime_only, .{
1088 .src = s.lazyNode(.argument(@intCast(i))),
1089 .type = ty,
1090 });
1091 }
1092 dst.* = ty;
1093 },
1094 .index => |block| {
1095 const mode = s.frame.mode;
1096 try s.pushFrame();
1097 s.frame = Frame.initFromBlock(s, block, mode, .{ .func = .{ .argument = @intCast(i) } });
1098 return .@"continue";
1099 },
1100 }
1101 }
1102
1103 switch ((func_data.return_type.unwrap() orelse break :ret null).decode()) {
1104 .value => |val| {
1105 const ty = (try s.coerceValue(s.lazyNode(.return_type), val, .type)).toTypeUnsafe(s.ip);
1106 if (!ty.hasPossibleRepresentation(s.ip)) {
1107 return s.fail(.runtime_incompatible_type, .{
1108 .src = s.lazyNode(.return_type),
1109 .type = ty,
1110 });
1111 }
1112 break :ret ty;
1113 },
1114 .index => |block| {
1115 const mode = s.frame.mode;
1116 try s.pushFrame();
1117 s.frame = Frame.initFromBlock(s, block, mode, .{ .func = .return_type });
1118 s.frame.mark_index = reserve.index();
1119 return .@"continue";
1120 },
1121 }
1122 },
1123 .return_type => ret: {
1124 const val = operand.value() orelse unreachable; // must be comptime
1125 const ty = (try s.coerceValue(s.lazyNode(.return_type), val, .type)).toTypeUnsafe(s.ip);
1126 if (!ty.hasPossibleRepresentation(s.ip)) {
1127 return s.fail(.runtime_incompatible_type, .{
1128 .src = s.lazyNode(.return_type),
1129 .type = ty,
1130 });
1131 }
1132 break :ret ty;
1133 },
1134 };
1135
1136 try s.postVisitFunctionPrototype(reserve, return_type);
1137 return .@"continue";
1138 },
10281139 else => |t| std.debug.panic("TODO: post-visit .{s}", .{@tagName(t)}),
10291140 }
10301141
......@@ -1141,7 +1252,6 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
11411252 var has_errors = false;
11421253 var is_constant = true;
11431254
1144 // Allocate scratch space to store the values as they are being prepared.
11451255 const scratch = try s.scratch.reserve(
11461256 s.arena(),
11471257 Air.Inst.Ref.Optional,
......@@ -1150,11 +1260,10 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
11501260 defer scratch.deinit();
11511261 @memset(scratch.slice(), .none);
11521262
1263 // The ordering of `storage` is defined by the struct's type, which
1264 // may be different than the instantiation. The fields are visited in
1265 // order of the instantiation.
11531266 for (node.keys(&s.teg), node.inits(&s.teg), 0..) |init_key, init_value, i| {
1154 // The order of `storage` is as ordered by the type, but this
1155 // can be in a different ordering than the initialization. We
1156 // guarantee the ordering the initializers are visited is in the
1157 // order they are declared.
11581267 const found_index = index: for (struct_type.field_keys, 0..) |decl_key, j| {
11591268 if (decl_key.eqlSlice(init_key.slice(s.teg.string_bytes), s.ip))
11601269 break :index j;
......@@ -1181,7 +1290,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
11811290 .marked,
11821291 ) catch |e| switch (e) {
11831292 error.AnalysisFail => {
1184 // Instead of bailing out with `try`, we will continue the loop.
1293 // Instead of bailing out with `try`, all fields will be analyzed
11851294 has_errors = true;
11861295 // Mark something non-null so that duplicate field errors are emitted.
11871296 scratch.at(found_index).* = comptime Value.void.toAir().toOptional();
......@@ -1193,29 +1302,29 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
11931302 scratch.at(found_index).* = coerced.toOptional();
11941303 }
11951304
1196 // Check for missing fields
1197 {
1198 const storage = scratch.slice();
1199 var i: usize = 0;
1200 while (mem.indexOfScalarPos(Air.Inst.Ref.Optional, storage, i, .none)) |index| {
1201 try s.addError(.struct_init_missing_field, .{
1202 .ty = ty,
1203 .field = @intCast(index),
1204 .src = s.lazyNode(.main_node),
1205 });
1206 i = index + 1;
1207 }
1208 if (i != 0) has_errors = true;
1305 const storage = scratch.slice();
1306 var i: usize = 0;
1307 while (mem.indexOfScalarPos(Air.Inst.Ref.Optional, storage, i, .none)) |index| {
1308 try s.addError(.struct_init_missing_field, .{
1309 .ty = ty,
1310 .field = @intCast(index),
1311 .src = s.lazyNode(.main_node),
1312 });
1313 i = index + 1;
12091314 }
1315 if (i != 0) has_errors = true;
12101316
12111317 if (has_errors)
12121318 return Error.AnalysisFail;
12131319
1320 // Now that there are definitely no missing fields, it is safe to
1321 // interpret `scratch` as `Air.Inst.Ref`
1322
12141323 if (is_constant) {
1215 // Convert all items in storage into a Value type
12161324 const result_storage = scratch.sliceReinterpret(Value);
1217 for (scratch.slice(), scratch.sliceReinterpret(Value)) |ref, *value| {
1218 value.* = ref.unwrap().?.decode().value;
1325 for (scratch.slice(), result_storage) |ref, *value| {
1326 // `ref` and `value` alias; this coercion is performed in-place
1327 value.* = (ref.unwrap() orelse unreachable).decode().value;
12191328 }
12201329 return try s.makeValueAir(.{ .struct_value = .{
12211330 .ty = ty,
......@@ -1224,7 +1333,6 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
12241333 } else {
12251334 try s.ensureAtRuntime(s.lazyNode(.main_node));
12261335
1227 // Create a runtime struct instantiation
12281336 const value_index = try s.func.?.extra.appendSlice(s.gpa, scratch.slice());
12291337 return try s.addRef(.struct_init, .{
12301338 .ty = ty,
......@@ -1239,6 +1347,73 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
12391347 return s.makeValueAir(.{ .array_type = .{ .item_type = operand } });
12401348 },
12411349
1350 .tuple_type => {
1351 const data: Teg.TupleType = s.frame.currentPayload(.tuple_type);
1352
1353 const scratch = try s.scratch.reserve(s.arena(), Type, data.items.len);
1354 defer scratch.deinit();
1355
1356 for (data.items.slice(&s.teg), scratch.slice(), 0..) |src, *dst, i| {
1357 dst.* = try s.getType(s.lazyNode(.argument(@intCast(i))), src);
1358 }
1359
1360 return s.makeValueAir(.{ .tuple_type = .{ .items = scratch.slice() } });
1361 },
1362
1363 .tuple_init => {
1364 const data: Teg.TupleInit = s.frame.currentPayload(.tuple_init);
1365
1366 _ = data.type_hint; // TODO: use type hint
1367
1368 const scratch = try s.scratch.reserve(s.arena(), Air.Inst.Ref, data.items.len);
1369 defer scratch.deinit();
1370
1371 var is_const = true;
1372 for (data.items.slice(&s.teg), scratch.slice()) |src, *dst| {
1373 const result = s.getRef(src, .marked);
1374 dst.* = result;
1375 if (result.kind == .index) is_const = false;
1376 }
1377
1378 if (!is_const) {
1379 try s.ensureAtRuntime(s.lazyNode(.main_node));
1380
1381 for (scratch.slice(), 0..) |*ref, i| {
1382 ref.* = try s.ensureValueRuntime(s.lazyNode(.argument(@intCast(i))), ref.*);
1383 }
1384 }
1385
1386 const ty = ty: {
1387 const type_scratch = try s.scratch.reserve(s.arena(), Type, data.items.len);
1388 defer type_scratch.deinit();
1389
1390 for (scratch.slice(), type_scratch.slice()) |val, *ty| {
1391 ty.* = s.typeOfRef(val);
1392 }
1393
1394 break :ty try s.makeType(.{ .tuple_type = .{ .items = type_scratch.slice() } });
1395 };
1396
1397 if (is_const) {
1398 const result_storage = scratch.sliceReinterpret(Value);
1399 for (scratch.slice(), result_storage) |ref, *value| {
1400 // `ref` and `value` alias; this coercion is performed in-place
1401 value.* = ref.decode().value;
1402 }
1403
1404 return try s.makeValueAir(.{ .tuple_value = .{
1405 .items = result_storage,
1406 .type = ty,
1407 } });
1408 } else {
1409 const value_index = try s.func.?.extra.appendSlice(s.gpa, scratch.slice());
1410 return try s.addRef(.struct_init, .{
1411 .field_values = value_index,
1412 .ty = ty,
1413 });
1414 }
1415 },
1416
12421417 .result_type_argument => {
12431418 const result: Teg.ResultTypeArgument = s.frame.currentData(.result_type_argument);
12441419
......@@ -1605,6 +1780,11 @@ fn coerceValue(s: *Sema, src: LazyNode, value: Value, to: Type) Error!Value {
16051780 return s.coerceArrayValue(src, value, to_array_type.item_type);
16061781 }
16071782 }
1783 if (to.toTupleType(s.ip)) |to_tuple_type| {
1784 if (value.valueTag(s.ip) == .tuple_value) {
1785 return s.coerceTupleValue(src, value, from, to, to_tuple_type);
1786 }
1787 }
16081788 },
16091789 }
16101790 return s.coercionError(src, from, to);
......@@ -1615,6 +1795,14 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst
16151795 .value => |v| {
16161796 const ty = v.typeOf(s.ip);
16171797 switch (ty) {
1798 // Allowed at runtime
1799 .bool,
1800 .float,
1801 .int32,
1802 .string,
1803 .void,
1804 .empty_tuple,
1805 => {},
16181806 .numeric_literal => {
16191807 const float = v.toFloat(s.ip) orelse unreachable;
16201808
......@@ -1649,13 +1837,6 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst
16491837 .type = ty,
16501838 }),
16511839 .@"opaque" => @panic("TODO: represent comptime-known opaques better"),
1652 // Allowed at runtime
1653 .bool,
1654 .float,
1655 .int32,
1656 .string,
1657 .void,
1658 => {},
16591840 _ => if (!ty.hasPossibleRepresentation(s.ip)) {
16601841 return s.fail(.runtime_incompatible_type, .{
16611842 .src = src,
......@@ -1728,6 +1909,44 @@ fn coerceArrayValue(s: *Sema, src: LazyNode, value: Value, item_type: Type) !Val
17281909 } });
17291910}
17301911
1912fn coerceTupleValue(
1913 s: *Sema,
1914 src: LazyNode,
1915 value: Value,
1916 value_type: Type,
1917 target_type: Type,
1918 target_key: InternPool.Key.TupleType,
1919) !Value {
1920 const tuple_value = value.toKey(s.ip).tuple_value;
1921 if (tuple_value.items.len != target_key.items.len)
1922 return s.coercionError(src, value_type, target_type);
1923
1924 const len = tuple_value.items.len;
1925
1926 const scratch = try s.scratch.reserveMulti(
1927 s.arena(),
1928 .{ .values = Value, .types = Type },
1929 .{ @intCast(len), @intCast(len) },
1930 );
1931 defer scratch.deinit();
1932
1933 // This function will append new items to the intern pool, invalidating
1934 // existing ones. Copy all data into a stable slice first.
1935 @memcpy(scratch.items(.values), tuple_value.items);
1936 @memcpy(scratch.items(.types), target_key.items);
1937
1938 for (0..len) |i| {
1939 // `s.scratch` can be invalidated by having a tuple of tuples and coercing it.
1940 const coerced = try s.coerceValue(src, scratch.at(.values, i).*, scratch.at(.types, i).*);
1941 scratch.at(.values, i).* = coerced;
1942 }
1943
1944 return try s.makeValue(.{ .tuple_value = .{
1945 .type = target_type,
1946 .items = scratch.items(.values),
1947 } });
1948}
1949
17311950fn lookupDecl(s: *Sema, name: NullTerminatedString) InternPool.DeclIndex {
17321951 var nsi = s.ns_index;
17331952 while (true) {
......@@ -1861,6 +2080,68 @@ fn fieldAccessArray(s: *Sema, lhs: Air.Inst.Ref, lhs_type: Type, rhs_teg: Teg.In
18612080 }
18622081}
18632082
2083fn fieldAccessTuple(
2084 s: *Sema,
2085 lhs: Air.Inst.Ref,
2086 lhs_type: Type,
2087 tuple_type: InternPool.Key.TupleType,
2088 field: Teg.Inst.Ref,
2089) !Air.Inst.Ref {
2090 const index_value = try s.getCoercedValue(s.lazyNode(.rhs), field, .int32);
2091 const index_signed = index_value.toInt32(s.ip) orelse unreachable;
2092 if (index_signed < 0 or index_signed >= tuple_type.items.len) {
2093 return s.fail(.field_lookup_failed, .{
2094 .ty = lhs_type,
2095 .src = s.lazyNode(.lhs),
2096 .field = index_value,
2097 });
2098 }
2099 const index: u32 = @intCast(index_signed);
2100
2101 switch (lhs.decode()) {
2102 .value => |value| {
2103 return value.toKey(s.ip).tuple_value.items[index].toAir();
2104 },
2105 .index => |lhs_index| {
2106 try s.ensureAtRuntime(s.lazyNode(.lhs));
2107 return s.addRef(.field_tuple, .{
2108 .base = lhs_index,
2109 .field_index = index,
2110 });
2111 },
2112 }
2113}
2114
2115const FunctionReserve = core.U32Stack.ReserveMulti(.{ .params = Type, .captures = Air.Inst.Ref });
2116
2117fn postVisitFunctionPrototype(s: *Sema, reserve: FunctionReserve, return_type: ?Type) !void {
2118 const tracked_inst = try s.ip.trackInst(s.gpa, s.file, s.frame.currentIndex());
2119 const func_type = try s.makeType(.{
2120 .function_type = .{
2121 .param_types = reserve.items(.params),
2122 .return_type = return_type,
2123 },
2124 });
2125 const func_value = try s.makeValue(.{
2126 .func = .{
2127 .ty = func_type,
2128 .owner_decl = s.techDebtDeclIndex(), // TODO: ooh, this is a fun one!
2129 .teg_body_inst = tracked_inst,
2130 },
2131 });
2132
2133 s.inst_map.add(s.frame.currentIndex(), func_value.toAir());
2134
2135 s.frame.advance();
2136
2137 reserve.deinit();
2138
2139 // TODO: move this queue once we perform one of these
2140 // - depend on the return type of an inferred-return-type function
2141 // - use the value in any runtime context ()
2142 try s.comp.jobs.writeItem(.{ .analysis = .{ .au = .wrap(.{ .func = func_value }) } });
2143}
2144
18642145//
18652146
18662147fn tempAir(s: *const Sema) Air {
src/core/Teg.Dump.zig+18
......@@ -319,6 +319,24 @@ const State = struct {
319319 if (data.type_hint != .none)
320320 try writer.writeAll(")");
321321 },
322 .tuple_type => {
323 const data: Teg.TupleType = teg.instPayload(.tuple_type, inst);
324
325 try writer.writeAll("(");
326 try s.dumpRefMarkerList(writer, data.items.slice(teg), '[', ']');
327 try writer.writeAll(")");
328 },
329 .tuple_init => {
330 const data: Teg.TupleInit = teg.instPayload(.tuple_init, inst);
331
332 try writer.writeAll("(");
333 if (data.type_hint.unwrap()) |type_hint| {
334 try s.dumpRef(writer, type_hint);
335 try writer.writeAll(", ");
336 }
337 try s.dumpRefMarkerList(writer, data.items.slice(teg), '[', ']');
338 try writer.writeAll(")");
339 },
322340 .result_type_argument => {
323341 const data: Teg.ResultTypeArgument = teg.instData(.result_type_argument, inst);
324342
src/core/Teg.zig+40-4
......@@ -75,6 +75,9 @@ pub const Tag = enum(u8) {
7575 struct_init,
7676 array_init,
7777
78 tuple_init,
79 tuple_type,
80
7881 add,
7982 sub,
8083 mul,
......@@ -123,6 +126,7 @@ pub const Tag = enum(u8) {
123126 .variable_declaration,
124127 .test_declaration,
125128 => PlNode(Decl),
129
126130 .coercion => PlNode(Coercion),
127131 .@"break" => PlNode(Break),
128132 .number => PlNode(Float),
......@@ -136,6 +140,8 @@ pub const Tag = enum(u8) {
136140 .struct_decl => PlNode(StructDecl),
137141 .struct_init => PlNode(StructInit),
138142 .array_init => PlNode(ArrayInit),
143 .tuple_type => PlNode(TupleType),
144 .tuple_init => PlNode(TupleInit),
139145 .func => PlNode(Func),
140146
141147 .load_decl => StrNode,
......@@ -146,7 +152,7 @@ pub const Tag = enum(u8) {
146152 .result_type_tuple_field => ResultTypeTupleField,
147153 .result_type_array_value => ResultTypeArrayValue,
148154
149 // Single instruction operand
155 // One operand instructions
150156 .memory,
151157 .discard,
152158 .ensure_void,
......@@ -162,7 +168,7 @@ pub const Tag = enum(u8) {
162168 .assert,
163169 => RefNode,
164170
165 // Two instruction operands
171 // Two operand instructions
166172 .field,
167173 .index,
168174 .add,
......@@ -230,7 +236,7 @@ pub const Inst = struct {
230236 main_module_inst = 0,
231237 _,
232238
233 pub const static_len = @intFromEnum(Ref.empty_array) + 1;
239 pub const static_len = @intFromEnum(Ref.empty_tuple) + 1;
234240
235241 pub fn toRef(i: Index) Inst.Ref {
236242 return @enumFromInt(static_len + @intFromEnum(i));
......@@ -269,6 +275,7 @@ pub const Inst = struct {
269275 numeric_literal_type,
270276 numeric_literal_array_type,
271277 never_array_type,
278 empty_tuple_type,
272279
273280 void,
274281 bool_true,
......@@ -278,7 +285,7 @@ pub const Inst = struct {
278285 nan,
279286 empty_string,
280287 empty_array,
281
288 empty_tuple,
282289 _,
283290
284291 pub const Decoded = union(enum) {
......@@ -423,6 +430,20 @@ pub const ArrayInit = struct {
423430 list: RefList,
424431};
425432
433pub const TupleType = struct {
434 /// The items in the tuple
435 items: RefList,
436};
437
438pub const TupleInit = struct {
439 /// Optionally provide a result type for more reliable type coercion.
440 /// If specified, this must be an array type. Otherwise, the first
441 /// element's type is used.
442 type_hint: Inst.Ref.Optional,
443 /// The items in the tuple
444 items: RefList,
445};
446
426447pub const Decl = struct {
427448 name: NullTerminatedString,
428449 hash: Cache.BinDigest,
......@@ -697,6 +718,8 @@ pub fn writeToHasher(
697718 .struct_decl,
698719 .struct_init,
699720 .array_init,
721 .tuple_init,
722 .tuple_type,
700723 .func,
701724 .store_decl,
702725 .store_field,
......@@ -902,6 +925,19 @@ fn writePlNodeInnerToHasher(
902925 hasher.add(isolatedIndexValue(decl_index, data.body));
903926 hasher.add(data.instruction_count);
904927 },
928 TupleType => {
929 hasher.add(data.items.len);
930 for (data.items.slice(teg)) |item| {
931 hasher.add(isolatedRefValue(decl_index, item));
932 }
933 },
934 TupleInit => {
935 if (data.type_hint.unwrap()) |type_hint|
936 hasher.add(isolatedRefValue(decl_index, type_hint))
937 else
938 hasher.add(@as(i32, std.math.maxInt(i32)));
939 writeRefListToHasher(hasher, decl_index, data.items.slice(teg));
940 },
905941 else => @compileError("hash not implemented for " ++ @typeName(T)),
906942 }
907943}
src/core/Tokenizer.zig+4-3
......@@ -75,6 +75,7 @@ pub const Token = struct {
7575 kw_return,
7676 kw_struct,
7777 kw_trait,
78 kw_tuple,
7879 kw_var,
7980 kw_while,
8081 // sort-lines: end
......@@ -134,7 +135,7 @@ pub const Token = struct {
134135
135136 /// Represents a tokenization error. Instead of reporting an error during
136137 /// tokenization, parsing will begin, and an error will be reported when
137 /// this token is hit. That way, the error can tell you what it expected.
138 /// this token is hit. That way, the error .
138139 invalid,
139140 invalid_number,
140141 invalid_string,
......@@ -275,8 +276,8 @@ pub const Token = struct {
275276 /// Instead of storing the length of a token, we can recompute this
276277 /// information on demand, as it is not often needed.
277278 ///
278 /// If you are computing the length of something that is knowable
279 /// without the source code, use token.tag.staticLen() instead.
279 /// When computing the length of something that can be known without
280 /// the source code, use token.tag.staticLen() instead.
280281 pub fn len(token: Token, utf8_source: [:0]const u8) u32 {
281282 return token.tag.staticLen() orelse switch (token.tag) {
282283
src/core/Transform.zig+36-4
......@@ -137,10 +137,9 @@ const ResultType = union(enum) {
137137 .used => unreachable, // Use after free
138138 .ref => |r| r,
139139 .type_of => |r| try t.addRef(.type_of, .{
140 // Result types don't have associated nodes, which is ok because
141 // type_of cannot fail in the context we are using it. Instead,
142 // we will use a predictable poison value, so that an attempt
143 // to print this will crash.
140 // Result types do not have associated nodes which is okay as `type_of` cannot
141 // fail in the context it is used in. A predictable poison value is used instead
142 // so that an attempt to print the following will cause a crash.
144143 .src_node = std.math.maxInt(i32),
145144 .operand = r,
146145 }),
......@@ -281,6 +280,7 @@ const Symbol = struct {
281280 value: union {
282281 mutable: Inst.Index,
283282 immutable: Inst.Ref,
283 param_index: u32,
284284 none: void,
285285 },
286286 result_type_ref: Inst.Ref,
......@@ -903,6 +903,7 @@ fn transformExpression(
903903 switch (node_tag) {
904904 .root,
905905 .param_decl,
906 .struct_field,
906907 => unreachable, // not an expression
907908
908909 .id => {
......@@ -1520,6 +1521,37 @@ fn transformExpression(
15201521 });
15211522 },
15221523
1524 .tuple_init => {
1525 const tuple_init = t.ast.nodeData(node).list;
1526
1527 const items = try t.transformExpressionList(ctx, .none, tuple_init.slice(&t.ast)) orelse return null;
1528
1529 if (items.len == 0) {
1530 return .empty_tuple;
1531 }
1532
1533 return try t.addRef(.tuple_init, .{
1534 .src_node = ctx.nodeOffset(node),
1535 .extra_index = try t.extra.append(t.gpa, Teg.TupleInit, .{
1536 .type_hint = .none,
1537 .items = items,
1538 }),
1539 });
1540 },
1541
1542 .tuple_type => {
1543 const tuple_type = t.ast.nodeData(node).list;
1544
1545 const items = try t.transformExpressionList(ctx, .none, tuple_type.slice(&t.ast)) orelse return null;
1546
1547 return try t.addRef(.tuple_type, .{
1548 .src_node = ctx.nodeOffset(node),
1549 .extra_index = try t.extra.append(t.gpa, Teg.TupleType, .{
1550 .items = items,
1551 }),
1552 });
1553 },
1554
15231555 inline .fn_decl, .fn_expr => |tag| {
15241556 const data = switch (tag) {
15251557 .fn_decl => t.ast.fullFnDecl(node),
src/core/js/Js.zig+1-1
......@@ -43,7 +43,7 @@ pub fn link(
4343 entry_point: Comp.Decl.Index,
4444 writer: std.io.AnyWriter,
4545) !void {
46 var state = Link{
46 var state: Link = .{
4747 .comp = comp,
4848 .backend = backend,
4949 };
src/core/js/Link.zig+12-1
......@@ -309,7 +309,18 @@ fn printValue(link: *Link, value: InternPool.Value, level: Level, writer: anytyp
309309 if (i != 0)
310310 try writer.writeAll(",");
311311
312 // TODO: handle special identifiers
312 try link.printValue(inner_value, .assignment, writer);
313 }
314 try writer.writeAll("]");
315 },
316
317 .tuple_value => |tuple_value| {
318 try writer.writeAll("[");
319
320 for (tuple_value.items, 0..) |inner_value, i| {
321 if (i != 0)
322 try writer.writeAll(",");
323
313324 try link.printValue(inner_value, .assignment, writer);
314325 }
315326 try writer.writeAll("]");
src/core/js/NodeGen.zig+44-2
......@@ -283,6 +283,15 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {
283283 try c.markExpression(inst, node);
284284 },
285285
286 .field_tuple => {
287 const data: Air.FieldStruct = c.air.instData(.field_tuple, inst);
288 const node = try c.add(.index, .{
289 .lhs = try c.fetchInstExpression(data.base),
290 .rhs = try c.makeValueInst(.{ .int32 = @intCast(data.field_index) }),
291 });
292 try c.markExpression(inst, node);
293 },
294
286295 .array_len => {
287296 const data: Air.Un = c.air.instData(.array_len, inst);
288297 const node = try c.add(.field, .{
......@@ -459,9 +468,9 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {
459468 },
460469
461470 .array_init => {
462 const struct_init: Air.ArrayInit = c.air.instData(.array_init, inst);
471 const array_init: Air.ArrayInit = c.air.instData(.array_init, inst);
463472
464 const items = struct_init.list.get(&c.air).slice(&c.air);
473 const items = array_init.list.get(&c.air).slice(&c.air);
465474 const store_offset = try c.extra.alloc(c.gpa, items.len);
466475
467476 for (
......@@ -478,6 +487,28 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {
478487 });
479488 try c.markExpression(inst, node);
480489 },
490
491 .tuple_init => {
492 const tuple_init: Air.TupleInit = c.air.instData(.tuple_init, inst);
493
494 const field_count = tuple_init.ty.toTupleType(c.ip).?.items.len;
495 const items = tuple_init.field_values;
496 const store_offset = try c.extra.alloc(c.gpa, field_count);
497
498 for (
499 items..items + field_count,
500 store_offset..,
501 ) |in_i, expr_i| {
502 const in = try c.fetchExpression(@bitCast(c.air.extra.list.items[in_i]));
503 c.extra.list.items[expr_i] = @intFromEnum(in);
504 }
505
506 const node = try c.add(.array, .{
507 .start = store_offset,
508 .len = @intCast(field_count),
509 });
510 try c.markExpression(inst, node);
511 },
481512 }
482513}
483514
......@@ -557,6 +588,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {
557588 .simple_type,
558589 .struct_type,
559590 .array_type,
591 .tuple_type,
560592 => unreachable, // Type values are not allowed here
561593
562594 .int32,
......@@ -566,6 +598,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {
566598 .simple_value,
567599 .struct_value,
568600 .array_value,
601 .tuple_value,
569602 => return try c.add(.plain_value, .{ .value = v }),
570603
571604 .external_value => {
......@@ -585,6 +618,13 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {
585618 }
586619}
587620
621fn makeValueInst(c: *NodeGen, k: InternPool.Key) !Node.Index {
622 return try c.valueToExpression(c.ip.get(c.gpa, k) catch |err| switch (err) {
623 error.TooManyValues => @panic("TODO: handle TooManyValues"),
624 else => |e| return e,
625 });
626}
627
588628fn addTemporary(c: *NodeGen) !Node.Index {
589629 const t = tracer.trace(@src());
590630 defer t.end();
......@@ -725,6 +765,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {
725765 .arg,
726766 .field_opaque,
727767 .field_struct,
768 .field_tuple,
728769 .field_array,
729770 .add,
730771 .sub,
......@@ -739,6 +780,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {
739780 .struct_init,
740781 .array_init,
741782 .array_len,
783 .tuple_init,
742784 => true,
743785
744786 // JavaScript variable declarations require statements.
src/core/stack.zig+28-9
......@@ -15,7 +15,7 @@ pub fn Stack(comptime Base: type) type {
1515
1616 pub const Mark = struct {
1717 stack: *Self,
18 index: usize,
18 index: u32,
1919
2020 pub fn deinit(m: Mark) void {
2121 if (core.runtime_safety)
......@@ -23,8 +23,8 @@ pub fn Stack(comptime Base: type) type {
2323 m.stack.list.shrinkRetainingCapacity(m.index);
2424 }
2525
26 pub fn len(m: Mark) usize {
27 return m.stack.list.items.len - m.index;
26 pub fn len(m: Mark) u32 {
27 return @intCast(m.stack.list.items.len - m.index);
2828 }
2929
3030 pub fn slice(m: Mark, comptime T: type) []T {
......@@ -39,7 +39,7 @@ pub fn Stack(comptime Base: type) type {
3939 pub fn mark(stack: *Self) Mark {
4040 return .{
4141 .stack = stack,
42 .index = stack.list.items.len,
42 .index = @intCast(stack.list.items.len),
4343 };
4444 }
4545
......@@ -101,8 +101,8 @@ pub fn Stack(comptime Base: type) type {
101101
102102 return struct {
103103 stack: *Self,
104 starts: [fields.len]usize,
105 lengths: [fields.len]usize,
104 starts: [fields.len]u32,
105 lengths: [fields.len]u32,
106106
107107 fn Type(comptime field: FieldEnum) type {
108108 return @field(types, @tagName(field));
......@@ -128,6 +128,25 @@ pub fn Stack(comptime Base: type) type {
128128 std.mem.copyForwards(u32, dest, from);
129129 r.stack.list.shrinkRetainingCapacity(start + from.len);
130130 }
131
132 pub fn index(r: @This()) u32 {
133 return r.starts[0];
134 }
135
136 pub fn fromIndex(
137 stack: *Self,
138 start: u32,
139 lengths: [std.meta.fields(@TypeOf(types)).len]u32,
140 ) @This() {
141 var all_lengths: u32 = 0;
142 var starts: [std.meta.fields(@TypeOf(types)).len]u32 = undefined;
143 inline for (lengths, &starts) |len, *slice_start| {
144 slice_start.* = start + all_lengths;
145 all_lengths += len;
146 }
147 stack.list.items.len += all_lengths;
148 return .{ .stack = stack, .starts = starts, .lengths = lengths };
149 }
131150 };
132151 }
133152
......@@ -137,13 +156,13 @@ pub fn Stack(comptime Base: type) type {
137156 stack: *Self,
138157 allocator: Allocator,
139158 comptime types: anytype,
140 lengths: [std.meta.fields(@TypeOf(types)).len]usize,
159 lengths: [std.meta.fields(@TypeOf(types)).len]u32,
141160 ) !ReserveMulti(types) {
142161 var all_lengths: usize = 0;
143 var starts: [std.meta.fields(@TypeOf(types)).len]usize = undefined;
162 var starts: [std.meta.fields(@TypeOf(types)).len]u32 = undefined;
144163 const start = stack.list.items.len;
145164 inline for (lengths, &starts) |len, *slice_start| {
146 slice_start.* = start + all_lengths;
165 slice_start.* = @intCast(start + all_lengths);
147166 all_lengths += len;
148167 }
149168 try stack.list.ensureUnusedCapacity(allocator, all_lengths);
src/main.zig+26-95
......@@ -4,9 +4,18 @@
44//! for example, test the `core` module's compilation features without
55//! needing to compile the formatter.
66
7const have_debug_features = builtin.mode == .Debug;
87const config = @import("config");
98
9const arg_config: Cli = .{
10 .options = .{
11 .{ .short = 'i', .name = "--dump-ir", .desc = "Dump intermediate representations" },
12 .{ .name = "--dump-state", .desc = "Dump compiler state to JSON", .kind = .string },
13 } ++ Cli.Option.optional(config.have_embedded_quickjs, &.{
14 .{ .name = "--run", .desc = "Execute with QuickJS" },
15 }),
16 .positionals = .single,
17};
18
1019pub fn main() !u8 {
1120 try tracer.init();
1221 defer tracer.deinit();
......@@ -24,8 +33,7 @@ pub fn main() !u8 {
2433 }){}
2534 else
2635 std.heap.ArenaAllocator.init(std.heap.page_allocator);
27 var no_leak_checks = false;
28 defer _ = if (use_gpa and !no_leak_checks) gpa.deinit();
36 defer _ = if (use_gpa) gpa.deinit();
2937 const alloc = gpa.allocator();
3038
3139 // Current working directory is required for CLI resolution.
......@@ -33,95 +41,17 @@ pub fn main() !u8 {
3341 const cwd = try std.posix.getcwd(&cwd_buf);
3442
3543 // Command line argument parsing.
36 var maybe_file_path: ?[]const u8 = null;
37 defer if (maybe_file_path) |path| alloc.free(path);
38 var exe_name: ?[]const u8 = null;
39 defer if (exe_name) |path| alloc.free(path);
40 var dump_ir = false;
41 var dump_state: ?[]const u8 = null;
42 var run = false;
43 var has_errors = false;
44 {
45 var args = try std.process.argsWithAllocator(alloc);
46 defer args.deinit();
47
48 exe_name = try alloc.dupe(u8, std.fs.path.basename(args.next() orelse "cli"));
49
50 while (args.next()) |arg| {
51 if (std.mem.startsWith(u8, arg, "-")) {
52 if (have_debug_features) {
53 if (std.mem.eql(u8, arg, "--report-trace")) {
54 core.Report.debug_show_report_trace = true;
55 continue;
56 }
57 if (std.mem.eql(u8, arg, "--no-leak-checks")) {
58 no_leak_checks = true;
59 continue;
60 }
61 }
62
63 if (std.mem.eql(u8, arg, "--dump-ir")) {
64 dump_ir = true;
65 continue;
66 }
67
68 if (std.mem.startsWith(u8, arg, "--dump-state=")) {
69 dump_state = arg["--dump-state=".len..];
70 continue;
71 }
72
73 if (config.have_embedded_quickjs and std.mem.eql(u8, arg, "--run")) {
74 run = true;
75 continue;
76 }
77
78 std.debug.print("error: unknown flag '{s}'\n", .{arg});
79 has_errors = true;
80 break;
81 }
82
83 if (maybe_file_path == null) {
84 maybe_file_path = try std.fs.path.resolve(alloc, &.{ cwd, arg });
85 } else {
86 std.debug.print("error: multiple files specified\n", .{});
87 maybe_file_path = null;
88 break;
89 }
90 }
91 }
92
93 if (maybe_file_path == null) has_errors = true;
94 if (has_errors) {
95 std.debug.print("Usage: {s} [flags] <input file>\n", .{exe_name.?});
96 {
97 std.debug.print(
98 // | 60 chars wrap point|
99 \\
100 \\Flags:
101 \\ --dump-ir Dump intermediate representations
102 \\ --dump-state=<path> Dump compiler state to JSON
103 \\
104 , .{});
105 }
106 if (config.have_embedded_quickjs) {
107 std.debug.print(
108 \\ --run Run program using QuickJS
109 \\
110 , .{});
111 }
112 if (have_debug_features) {
113 std.debug.print(
114 \\
115 \\Debug Mode Flags:
116 \\ --report-trace Print stack traces per error
117 \\ --no-leak-checks
118 \\
119 , .{});
120 }
121 std.debug.print("\nLanguage SDK v" ++ config.version_string ++ "\n", .{});
122 return 1;
123 }
124 const file_path = maybe_file_path.?;
44 const program_name = "zig build compiler --";
45 _, const args = try Cli.programArgsAlloc(alloc, program_name);
46 defer Cli.programArgsFree(alloc, args);
47 const opts = arg_config.parse(program_name, args);
48
49 const file_path = try std.fs.path.resolve(alloc, &.{
50 cwd,
51 opts.positionals orelse
52 arg_config.showHelpAndExit(program_name),
53 });
54 defer alloc.free(file_path);
12555
12656 // Execute compiler
12757 {
......@@ -130,13 +60,13 @@ pub fn main() !u8 {
13060 var comp = try core.Comp.init(.{
13161 .gpa = tracer.tracingAllocator(&alloc),
13262 .options = .{
133 .dump_ir = dump_ir,
63 .dump_ir = opts.dump_ir orelse false,
13464 },
13565 .backend = .{ .js = &js_backend },
13666 });
13767 defer comp.deinit();
13868
139 defer if (dump_state) |path| {
69 defer if (opts.dump_state) |path| {
14070 dumpState(&comp, path) catch |err| {
14171 std.debug.print("Failed to dump compilation state: {}", .{err});
14272 };
......@@ -173,7 +103,7 @@ pub fn main() !u8 {
173103 else => return err,
174104 };
175105
176 if (config.have_embedded_quickjs and run) {
106 if (config.have_embedded_quickjs and (opts.run orelse false)) {
177107 // Execute with QuickJS
178108 var out: std.ArrayListUnmanaged(u8) = .{};
179109 defer out.deinit(alloc);
......@@ -244,6 +174,7 @@ pub const tracer_impl = @field(tracer, @tagName(@import("config").tracing_mode))
244174
245175const std = @import("std");
246176const builtin = @import("builtin");
177const Cli = @import("Cli.zig");
247178
248179fn dumpState(comp: *const Comp, dump_path: []const u8) !void {
249180 const file = try std.fs.cwd().createFile(dump_path, .{});
test/behavior/tuple created+15
......@@ -0,0 +1,15 @@
1let assertEqual = @extern("assertEqual");
2
3fn hello() -> tuple (int, float) {
4 let x: tuple (int, float) = (5, 2.5);
5 x
6}
7
8fn main {
9 let x = hello();
10 assertEqual(x.0, i(5));
11 assertEqual(x.1, f(2.5));
12}
13
14fn f(a: float) -> float { a }
15fn i(a: int) -> int { a }