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 @@...@@ -10,7 +10,8 @@
10- feat: add array index syntax via `x[1]` and `x.1`10- feat: add array index syntax via `x[1]` and `x.1`
11- feat: add `len` property to arrays11- feat: add `len` property to arrays
12- feat: rename `noreturn` to `never`12- feat: rename `noreturn` to `never`
13- feat: allow instantiating `@extern`13- feat: allow instantiating `@extern` at compile time
14- feat: implement tuples
14- fix: crash when printing unused struct declaration error15- fix: crash when printing unused struct declaration error
15- fix: provide array result type to sema16- fix: provide array result type to sema
16- fix: crash when analyzing an empty array without a result type17- fix: crash when analyzing an empty array without a result type
src/cli.zig+6-3
...@@ -8,7 +8,7 @@ options: []const Option = &.{},...@@ -8,7 +8,7 @@ options: []const Option = &.{},
8positionals: Positionals = .none,8positionals: Positionals = .none,
9help: HelpConfig = .{},9help: HelpConfig = .{},
1010
11const Option = struct {11pub const Option = struct {
12 /// Should be formatted as the flag name: '--dump-ir'12 /// Should be formatted as the flag name: '--dump-ir'
13 name: []const u8,13 name: []const u8,
14 /// Displayed in generated help menu14 /// Displayed in generated help menu
...@@ -32,6 +32,10 @@ const Option = struct {...@@ -32,6 +32,10 @@ const Option = struct {
32 }32 }
33 };33 };
3434
35 pub fn optional(comptime cond: bool, opts: []const Option) []const Option {
36 return if (cond) opts else &.{};
37 }
38
35 const help: Option = .{39 const help: Option = .{
36 .name = "--help",40 .name = "--help",
37 .short = 'h',41 .short = 'h',
...@@ -631,8 +635,7 @@ pub fn main() !void {...@@ -631,8 +635,7 @@ pub fn main() !void {
631 const program_name, const args = try Cli.programArgsAlloc(alloc);635 const program_name, const args = try Cli.programArgsAlloc(alloc);
632 defer Cli.programArgsFree(alloc, args);636 defer Cli.programArgsFree(alloc, args);
633637
634 const parsed = config.parse(args[1..]) catch638 const parsed = config.parse(program_name, args[1..]);
635 config.showHelpAndExit(program_name);
636639
637 std.debug.print("--flag: {any}\n", .{parsed.flag});640 std.debug.print("--flag: {any}\n", .{parsed.flag});
638 std.debug.print("--str: {?s}\n", .{parsed.str});641 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 {...@@ -25,8 +25,7 @@ pub fn main() !u8 {
25 const program_name, const raw_args = try Cli.programArgsAlloc(alloc, "transform");25 const program_name, const raw_args = try Cli.programArgsAlloc(alloc, "transform");
26 defer Cli.programArgsFree(alloc, raw_args);26 defer Cli.programArgsFree(alloc, raw_args);
2727
28 const args = arg_config.parse(raw_args) catch28 const args = arg_config.parse(program_name, raw_args);
29 arg_config.showHelpAndExit(program_name);
3029
31 const file_path = args.positionals orelse30 const file_path = args.positionals orelse
32 arg_config.fatalWithHelp(program_name, "missing filepath argument\n", .{});31 arg_config.fatalWithHelp(program_name, "missing filepath argument\n", .{});
src/core/Air.Dump.zig+39-1
...@@ -130,6 +130,16 @@ pub const State = struct {...@@ -130,6 +130,16 @@ pub const State = struct {
130 try tty.setColor(w, .reset);130 try tty.setColor(w, .reset);
131 try w.writeAll(")");131 try w.writeAll(")");
132 },132 },
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 },
133 inline .call, .js_new => |t| {143 inline .call, .js_new => |t| {
134 const call: Air.Call = air.instData(t, inst);144 const call: Air.Call = air.instData(t, inst);
135 const call_data = call.call_data.get(air);145 const call_data = call.call_data.get(air);
...@@ -182,6 +192,15 @@ pub const State = struct {...@@ -182,6 +192,15 @@ pub const State = struct {
182 try s.dumpRefList(w, array_init.list.get(air).slice(air), '[', ']');192 try s.dumpRefList(w, array_init.list.get(air).slice(air), '[', ']');
183 try w.writeAll(")");193 try w.writeAll(")");
184 },194 },
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 },
185 }204 }
186 }205 }
187206
...@@ -233,7 +252,10 @@ pub const State = struct {...@@ -233,7 +252,10 @@ pub const State = struct {
233 try tty.setColor(w, .blue);252 try tty.setColor(w, .blue);
234 try w.writeAll(") -> ");253 try w.writeAll(") -> ");
235 try tty.setColor(w, .reset);254 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]");
237 },259 },
238 inline .float, .int32, .comptime_float => |data, tag| {260 inline .float, .int32, .comptime_float => |data, tag| {
239 try tty.setColor(w, .bright_black);261 try tty.setColor(w, .bright_black);
...@@ -379,6 +401,22 @@ pub const State = struct {...@@ -379,6 +401,22 @@ pub const State = struct {
379 }401 }
380 try w.writeAll("'");402 try w.writeAll("'");
381 },403 },
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 },
382 }420 }
383 }421 }
384422
src/core/Air.zig+28-2
...@@ -25,9 +25,11 @@ pub const Inst = struct {...@@ -25,9 +25,11 @@ pub const Inst = struct {
2525
26 struct_init,26 struct_init,
27 array_init,27 array_init,
28 tuple_init,
2829
29 field_opaque,30 field_opaque,
30 field_struct,31 field_struct,
32 field_tuple,
31 field_array,33 field_array,
3234
33 array_len,35 array_len,
...@@ -58,7 +60,8 @@ pub const Inst = struct {...@@ -58,7 +60,8 @@ pub const Inst = struct {
58 .store => Store,60 .store => Store,
59 .struct_init => StructInit,61 .struct_init => StructInit,
60 .array_init => ArrayInit,62 .array_init => ArrayInit,
61 .field_struct => FieldStruct,63 .tuple_init => TupleInit,
64 .field_struct, .field_tuple => FieldStruct,
62 .field_opaque,65 .field_opaque,
63 .field_array,66 .field_array,
64 .add,67 .add,
...@@ -286,6 +289,18 @@ pub const ArrayInit = struct {...@@ -286,6 +289,18 @@ pub const ArrayInit = struct {
286 list: ExtraIndex(RefList),289 list: ExtraIndex(RefList),
287};290};
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
289pub const FieldStruct = struct {304pub const FieldStruct = struct {
290 base: Inst.Index,305 base: Inst.Index,
291 field_index: u32,306 field_index: u32,
...@@ -342,6 +357,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In...@@ -342,6 +357,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In
342 .@"if" => air.instData(.@"if", index).data.get(air).union_ty,357 .@"if" => air.instData(.@"if", index).data.get(air).union_ty,
343 .struct_init => air.instData(.struct_init, index).ty,358 .struct_init => air.instData(.struct_init, index).ty,
344 .array_init => air.instData(.array_init, index).ty,359 .array_init => air.instData(.array_init, index).ty,
360 .tuple_init => air.instData(.tuple_init, index).ty,
345 .arg => air.instData(.arg, index).type,361 .arg => air.instData(.arg, index).type,
346362
347 // A handful of binary operators are lowered to a form where both sides363 // 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...@@ -372,7 +388,9 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In
372 .call => {388 .call => {
373 const base_type = air.typeOf(air.instData(.call, index).base, ip);389 const base_type = air.typeOf(air.instData(.call, index).base, ip);
374 if (base_type == .@"opaque") return .@"opaque";390 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");
376 },394 },
377395
378 // Field accesses on structs are special-cased by storing the field396 // 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...@@ -391,6 +409,14 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In
391 .toArrayType(ip) orelse unreachable; // field_array demands array type409 .toArrayType(ip) orelse unreachable; // field_array demands array type
392 return base_type.item_type;410 return base_type.item_type;
393 },411 },
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 },
394 };420 };
395}421}
396422
src/core/Ast.Dump.zig+2
...@@ -126,6 +126,8 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err...@@ -126,6 +126,8 @@ fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Err
126 .struct_decl,126 .struct_decl,
127 => try dumpNodeList(writer, state, list, '{', '}'),127 => try dumpNodeList(writer, state, list, '{', '}'),
128 .builtin_call,128 .builtin_call,
129 .tuple_init,
130 .tuple_type,
129 => try dumpNodeList(writer, state, list, '(', ')'),131 => try dumpNodeList(writer, state, list, '(', ')'),
130 .array_init,132 .array_init,
131 => try dumpNodeList(writer, state, list, '[', ']'),133 => try dumpNodeList(writer, state, list, '[', ']'),
src/core/InternPool.zig+146-12
...@@ -101,6 +101,8 @@ pub const Tag = enum(u8) {...@@ -101,6 +101,8 @@ pub const Tag = enum(u8) {
101 struct_type,101 struct_type,
102 /// An array type102 /// An array type
103 array_type,103 array_type,
104 /// A tuple type
105 tuple_type,
104 /// A type that can be represented with only an enum tag.106 /// A type that can be represented with only an enum tag.
105 simple_type,107 simple_type,
106108
...@@ -129,6 +131,8 @@ pub const Tag = enum(u8) {...@@ -129,6 +131,8 @@ pub const Tag = enum(u8) {
129 external_value,131 external_value,
130 /// A value masked as "opaque" type132 /// A value masked as "opaque" type
131 opaque_value,133 opaque_value,
134 /// A tuple value
135 tuple_value,
132136
133 pub fn Data(comptime tag: Tag) type {137 pub fn Data(comptime tag: Tag) type {
134 return switch (tag) {138 return switch (tag) {
...@@ -154,13 +158,15 @@ pub const Tag = enum(u8) {...@@ -154,13 +158,15 @@ pub const Tag = enum(u8) {
154 => unreachable, // does not have a payload158 => unreachable, // does not have a payload
155 .float, .comptime_float => Float,159 .float, .comptime_float => Float,
156 .func => FuncDecl,160 .func => FuncDecl,
157 .function_type => TypeFunction,161 .function_type => FunctionType,
158 .string => String.WithLen,162 .string => String.WithLen,
159 .struct_value => StructValue,163 .struct_value => StructValue,
160 .struct_type => StructType,164 .struct_type => StructType,
161 .array_type => ArrayType,165 .array_type => ArrayType,
162 .array_value => ArrayValue,166 .array_value => ArrayValue,
163 .external_value => ExternalValue,167 .external_value => ExternalValue,
168 .tuple_type => TupleType,
169 .tuple_value => TupleValue,
164 };170 };
165 }171 }
166172
...@@ -174,9 +180,9 @@ pub const Tag = enum(u8) {...@@ -174,9 +180,9 @@ pub const Tag = enum(u8) {
174180
175 /// Trailing:181 /// Trailing:
176 /// 0. param_type: Index to param type. Repeated .params_len182 /// 0. param_type: Index to param type. Repeated .params_len
177 pub const TypeFunction = struct {183 pub const FunctionType = struct {
178 params_len: u32,184 params_len: u32,
179 return_type: Type,185 return_type: Type.Optional,
180 };186 };
181187
182 /// Trailing:188 /// Trailing:
...@@ -199,6 +205,20 @@ pub const Tag = enum(u8) {...@@ -199,6 +205,20 @@ pub const Tag = enum(u8) {
199 items_len: u32,205 items_len: u32,
200 };206 };
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
202 /// Trailing:222 /// Trailing:
203 /// 0. value: Value. Repeated .ty.structInfo().field_len223 /// 0. value: Value. Repeated .ty.structInfo().field_len
204 pub const StructValue = struct {224 pub const StructValue = struct {
...@@ -216,9 +236,9 @@ pub const Tag = enum(u8) {...@@ -216,9 +236,9 @@ pub const Tag = enum(u8) {
216/// This is an index into .items236/// This is an index into .items
217pub const Value = enum(u32) {237pub const Value = enum(u32) {
218 pub const first_type: Value = .opaque_type;238 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;
220 pub const first_value: Value = .void;240 pub const first_value: Value = .void;
221 pub const last_value: Value = .empty_array;241 pub const last_value: Value = .empty_tuple;
222242
223 opaque_type,243 opaque_type,
224 bool_type,244 bool_type,
...@@ -230,6 +250,7 @@ pub const Value = enum(u32) {...@@ -230,6 +250,7 @@ pub const Value = enum(u32) {
230 numeric_literal_type,250 numeric_literal_type,
231 numeric_literal_array_type,251 numeric_literal_array_type,
232 never_array_type,252 never_array_type,
253 empty_tuple_type,
233254
234 void,255 void,
235 bool_true,256 bool_true,
...@@ -239,6 +260,7 @@ pub const Value = enum(u32) {...@@ -239,6 +260,7 @@ pub const Value = enum(u32) {
239 nan,260 nan,
240 empty_string,261 empty_string,
241 empty_array,262 empty_array,
263 empty_tuple,
242264
243 none = std.math.maxInt(u32),265 none = std.math.maxInt(u32),
244 _,266 _,
...@@ -316,6 +338,7 @@ pub const Value = enum(u32) {...@@ -316,6 +338,7 @@ pub const Value = enum(u32) {
316 .simple_type,338 .simple_type,
317 .struct_type,339 .struct_type,
318 .array_type,340 .array_type,
341 .tuple_type,
319 => .type,342 => .type,
320343
321 .simple_value => switch (value.valueData(ip, .simple_value)) {344 .simple_value => switch (value.valueData(ip, .simple_value)) {
...@@ -332,6 +355,7 @@ pub const Value = enum(u32) {...@@ -332,6 +355,7 @@ pub const Value = enum(u32) {
332 .struct_value => value.toKey(ip).struct_value.ty,355 .struct_value => value.toKey(ip).struct_value.ty,
333 .array_value => value.toKey(ip).array_value.type,356 .array_value => value.toKey(ip).array_value.type,
334 .external_value, .opaque_value => .@"opaque",357 .external_value, .opaque_value => .@"opaque",
358 .tuple_value => value.toKey(ip).tuple_value.type,
335 };359 };
336 }360 }
337361
...@@ -345,6 +369,7 @@ pub const Value = enum(u32) {...@@ -345,6 +369,7 @@ pub const Value = enum(u32) {
345 .struct_type,369 .struct_type,
346 .simple_type,370 .simple_type,
347 .array_type,371 .array_type,
372 .tuple_type,
348 => @enumFromInt(@intFromEnum(value)),373 => @enumFromInt(@intFromEnum(value)),
349374
350 // exception: void is a value AND a type375 // exception: void is a value AND a type
...@@ -395,6 +420,7 @@ pub const Type = enum(u32) {...@@ -395,6 +420,7 @@ pub const Type = enum(u32) {
395 numeric_literal = @intFromEnum(Value.numeric_literal_type),420 numeric_literal = @intFromEnum(Value.numeric_literal_type),
396 numeric_literal_array = @intFromEnum(Value.numeric_literal_array_type),421 numeric_literal_array = @intFromEnum(Value.numeric_literal_array_type),
397 never_array = @intFromEnum(Value.never_array_type),422 never_array = @intFromEnum(Value.never_array_type),
423 empty_tuple = @intFromEnum(Value.empty_tuple_type),
398 void = @intFromEnum(Value.void),424 void = @intFromEnum(Value.void),
399 _,425 _,
400426
...@@ -404,6 +430,7 @@ pub const Type = enum(u32) {...@@ -404,6 +430,7 @@ pub const Type = enum(u32) {
404 struct_type = @intFromEnum(InternPool.Tag.struct_type),430 struct_type = @intFromEnum(InternPool.Tag.struct_type),
405 array_type = @intFromEnum(InternPool.Tag.array_type),431 array_type = @intFromEnum(InternPool.Tag.array_type),
406 simple_type = @intFromEnum(InternPool.Tag.simple_type),432 simple_type = @intFromEnum(InternPool.Tag.simple_type),
433 tuple_type = @intFromEnum(InternPool.Tag.tuple_type),
407 };434 };
408435
409 /// An array of `Index` existing within the `extra` array.436 /// An array of `Index` existing within the `extra` array.
...@@ -446,6 +473,11 @@ pub const Type = enum(u32) {...@@ -446,6 +473,11 @@ pub const Type = enum(u32) {
446 return ip.valueToKey(ty.value()).array_type;473 return ip.valueToKey(ty.value()).array_type;
447 }474 }
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
449 /// If it is possible to represent this type at runtime. Some types do not481 /// If it is possible to represent this type at runtime. Some types do not
450 /// have a defined representation, such as structs (to allow field renaming, unwrapping).482 /// have a defined representation, such as structs (to allow field renaming, unwrapping).
451 pub fn hasPossibleRepresentation(ty: Type, ip: *const InternPool) bool {483 pub fn hasPossibleRepresentation(ty: Type, ip: *const InternPool) bool {
...@@ -466,6 +498,15 @@ pub const Type = enum(u32) {...@@ -466,6 +498,15 @@ pub const Type = enum(u32) {
466 .struct_type,498 .struct_type,
467 .function_type,499 .function_type,
468 => true,500 => 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 },
469 .array_type => ty.toArrayType(ip).?.item_type.hasPossibleRepresentation(ip),510 .array_type => ty.toArrayType(ip).?.item_type.hasPossibleRepresentation(ip),
470 },511 },
471 };512 };
...@@ -490,6 +531,7 @@ pub const Type = enum(u32) {...@@ -490,6 +531,7 @@ pub const Type = enum(u32) {
490 .simple_type => unreachable, // all simple types are already printed531 .simple_type => unreachable, // all simple types are already printed
491 .struct_type,532 .struct_type,
492 .function_type,533 .function_type,
534 .tuple_type,
493 => false,535 => false,
494 .array_type => ty.toArrayType(ip).?.item_type.hasDefinedRepresentation(ip),536 .array_type => ty.toArrayType(ip).?.item_type.hasDefinedRepresentation(ip),
495 },537 },
...@@ -536,7 +578,10 @@ pub const Type = enum(u32) {...@@ -536,7 +578,10 @@ pub const Type = enum(u32) {
536 try writer.print("{}", .{param.displayName(ip)});578 try writer.print("{}", .{param.displayName(ip)});
537 }579 }
538 try writer.writeAll(") -> ");580 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]");
540 },585 },
541 .struct_type => {586 .struct_type => {
542 try writer.writeAll("[struct]");587 try writer.writeAll("[struct]");
...@@ -546,8 +591,29 @@ pub const Type = enum(u32) {...@@ -546,8 +591,29 @@ pub const Type = enum(u32) {
546 const array_type = ty.toArrayType(ip).?;591 const array_type = ty.toArrayType(ip).?;
547 try writer.print("{}", .{array_type.item_type.displayName(ip)});592 try writer.print("{}", .{array_type.item_type.displayName(ip)});
548 },593 },
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 },
549 }602 }
550 }603 }
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 };
551};617};
552618
553pub const Item = struct {619pub const Item = struct {
...@@ -584,6 +650,7 @@ pub const static_keys = [_]Key{...@@ -584,6 +650,7 @@ pub const static_keys = [_]Key{
584 SimpleType.numeric_literal.key(),650 SimpleType.numeric_literal.key(),
585 .{ .array_type = .{ .item_type = Type.numeric_literal } },651 .{ .array_type = .{ .item_type = Type.numeric_literal } },
586 .{ .array_type = .{ .item_type = Type.never } },652 .{ .array_type = .{ .item_type = Type.never } },
653 .{ .tuple_type = .{ .items = &.{} } },
587654
588 SimpleValue.void.key(),655 SimpleValue.void.key(),
589 SimpleValue.true.key(),656 SimpleValue.true.key(),
...@@ -596,6 +663,10 @@ pub const static_keys = [_]Key{...@@ -596,6 +663,10 @@ pub const static_keys = [_]Key{
596 .items = &.{},663 .items = &.{},
597 } },664 } },
598 .{ .string = "" },665 .{ .string = "" },
666 .{ .tuple_value = .{
667 .type = .empty_tuple,
668 .items = &.{},
669 } },
599};670};
600671
601/// State that is mutable during semantic analysis. This data is not used for672/// State that is mutable during semantic analysis. This data is not used for
...@@ -876,11 +947,13 @@ pub const Key = union(enum) {...@@ -876,11 +947,13 @@ pub const Key = union(enum) {
876 func: Func,947 func: Func,
877 array_value: ArrayValue,948 array_value: ArrayValue,
878 opaque_value: Value,949 opaque_value: Value,
950 tuple_value: TupleValue,
879 // Types951 // Types
880 simple_type: SimpleType,952 simple_type: SimpleType,
881 function_type: FunctionType,953 function_type: FunctionType,
882 struct_type: StructType,954 struct_type: StructType,
883 array_type: ArrayType,955 array_type: ArrayType,
956 tuple_type: TupleType,
884 external_value: ExternalValue,957 external_value: ExternalValue,
885958
886 const Adapter = struct {959 const Adapter = struct {
...@@ -899,7 +972,10 @@ pub const Key = union(enum) {...@@ -899,7 +972,10 @@ pub const Key = union(enum) {
899972
900 pub const FunctionType = struct {973 pub const FunctionType = struct {
901 param_types: []Type,974 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
904 pub fn hash(self: FunctionType, hasher: *Hash) void {980 pub fn hash(self: FunctionType, hasher: *Hash) void {
905 const t = tracer.trace(@src());981 const t = tracer.trace(@src());
...@@ -946,6 +1022,14 @@ pub const Key = union(enum) {...@@ -946,6 +1022,14 @@ pub const Key = union(enum) {
946 }1022 }
947 };1023 };
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
949 pub const ExternalValue = struct {1033 pub const ExternalValue = struct {
950 /// A list of strings to generate the property access1034 /// A list of strings to generate the property access
951 /// so `console.log` would be .{ "console", "log" }1035 /// so `console.log` would be .{ "console", "log" }
...@@ -967,6 +1051,17 @@ pub const Key = union(enum) {...@@ -967,6 +1051,17 @@ pub const Key = union(enum) {
967 }1051 }
968 };1052 };
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
970 pub const Func = struct {1065 pub const Func = struct {
971 ty: Type,1066 ty: Type,
972 owner_decl: DeclIndex,1067 owner_decl: DeclIndex,
...@@ -1008,6 +1103,8 @@ pub const Key = union(enum) {...@@ -1008,6 +1103,8 @@ pub const Key = union(enum) {
1008 .array_value,1103 .array_value,
1009 .array_type,1104 .array_type,
1010 .external_value,1105 .external_value,
1106 .tuple_type,
1107 .tuple_value,
1011 => |x| {1108 => |x| {
1012 var hasher = Hash.init(seed);1109 var hasher = Hash.init(seed);
1013 x.hash(&hasher);1110 x.hash(&hasher);
...@@ -1047,7 +1144,7 @@ pub const Key = union(enum) {...@@ -1047,7 +1144,7 @@ pub const Key = union(enum) {
1047 return false;1144 return false;
1048 },1145 },
1049 .struct_type => {1146 .struct_type => {
1050 return false; // structs are nomial1147 return false; // structs are nominal
1051 },1148 },
1052 .struct_value => |a_info| {1149 .struct_value => |a_info| {
1053 const b_info = b.struct_value;1150 const b_info = b.struct_value;
...@@ -1066,6 +1163,15 @@ pub const Key = union(enum) {...@@ -1066,6 +1163,15 @@ pub const Key = union(enum) {
1066 const b_info = b.external_value;1163 const b_info = b.external_value;
1067 return std.mem.eql(NullTerminatedString, a_info.key, b_info.key);1164 return std.mem.eql(NullTerminatedString, a_info.key, b_info.key);
1068 },1165 },
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 },
1069 };1175 };
1070 }1176 }
1071};1177};
...@@ -1336,6 +1442,19 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key {...@@ -1336,6 +1442,19 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key {
1336 .key = trail.readNullTerminatedStringSlice(payload.key_count).get(ip),1442 .key = trail.readNullTerminatedStringSlice(payload.key_count).get(ip),
1337 } };1443 } };
1338 },1444 },
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 },
1339 };1458 };
1340}1459}
13411460
...@@ -1391,9 +1510,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {...@@ -1391,9 +1510,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
1391 .function_type => |f| brk: {1510 .function_type => |f| brk: {
1392 for (f.param_types) |ty| ty.assertValid(ip);1511 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, .{
1395 .params_len = @intCast(f.param_types.len),1514 .params_len = @intCast(f.param_types.len),
1396 .return_type = f.return_type,1515 .return_type = .init(f.return_type),
1397 }, f.param_types.len);1516 }, f.param_types.len);
1398 defer trail.end();1517 defer trail.end();
1399 trail.writeSlice(f.param_types);1518 trail.writeSlice(f.param_types);
...@@ -1439,6 +1558,21 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {...@@ -1439,6 +1558,21 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
1439 trail.writeSlice(s.key);1558 trail.writeSlice(s.key);
1440 break :brk Item.encode(.external_value, extra);1559 break :brk Item.encode(.external_value, extra);
1441 },1560 },
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 },
1442 };1576 };
1443 ip.items.appendAssumeCapacity(item);1577 ip.items.appendAssumeCapacity(item);
1444 const index: Value = @enumFromInt(ip.items.len - 1);1578 const index: Value = @enumFromInt(ip.items.len - 1);
...@@ -1451,12 +1585,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {...@@ -1451,12 +1585,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
14511585
1452fn extraTypeFunction(1586fn extraTypeFunction(
1453 ip: *const InternPool,1587 ip: *const InternPool,
1454 extra_index: ExtraIndex(Tag.TypeFunction),1588 extra_index: ExtraIndex(Tag.FunctionType),
1455) Key.FunctionType {1589) Key.FunctionType {
1456 const function_type, var trail = extra_index.getTrail(ip);1590 const function_type, var trail = extra_index.getTrail(ip);
1457 const param_types = trail.readTypeSlice(function_type.params_len);1591 const param_types = trail.readTypeSlice(function_type.params_len);
1458 const result: Key.FunctionType = .{1592 const result: Key.FunctionType = .{
1459 .return_type = function_type.return_type,1593 .return_type = function_type.return_type.unwrap(),
1460 .param_types = param_types.get(ip),1594 .param_types = param_types.get(ip),
1461 };1595 };
1462 return result;1596 return result;
src/core/Parse.zig+296-258
...@@ -104,15 +104,216 @@ pub const ExpectedContext = enum {...@@ -104,15 +104,216 @@ pub const ExpectedContext = enum {
104 }104 }
105};105};
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
107/// When error.SyntaxError is returned, `p.err` is set to this as well as `p.pos`313/// When error.SyntaxError is returned, `p.err` is set to this as well as `p.pos`
108/// When printing these, token list and Ast is not actually available, so full tokens314/// When printing these, token list and Ast is not actually available, so full tokens
109/// are stored in the errors.315/// are stored in the errors.
110pub const AstError = union(enum) {316pub const AstError = union(enum) {
111 // /// Failure during tokenization
112 // tokenization: struct {
113 // err: Tokenizer.TokenizeError,
114 // byte: ByteOffset,
115 // },
116 /// Unexpected data, expected a token.317 /// Unexpected data, expected a token.
117 unexpected_token: struct {318 unexpected_token: struct {
118 expected: Token.Tag,319 expected: Token.Tag,
...@@ -134,23 +335,6 @@ pub const AstError = union(enum) {...@@ -134,23 +335,6 @@ pub const AstError = union(enum) {
134335
135 pub fn build(err: AstError, ctx: *Report.MsgContext) !void {336 pub fn build(err: AstError, ctx: *Report.MsgContext) !void {
136 switch (err) {337 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 // },
154 .unexpected_token => |data| {338 .unexpected_token => |data| {
155 try ctx.msg(.{ .token_stored = data.got }, "expected {s}, but found {s}", .{339 try ctx.msg(.{ .token_stored = data.got }, "expected {s}, but found {s}", .{
156 data.expected.description(),340 data.expected.description(),
...@@ -230,7 +414,8 @@ fn parseModuleMembers(p: *Parse) Error!SubList {...@@ -230,7 +414,8 @@ fn parseModuleMembers(p: *Parse) Error!SubList {
230 .kw_pub,414 .kw_pub,
231 => try p.scratch.append(p.gpa, try p.parseModuleDecl()),415 => 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
235 .id => if (p.isContextualKeyword("test")) {420 .id => if (p.isContextualKeyword("test")) {
236 try p.scratch.append(p.gpa, try p.parseTestDecl());421 try p.scratch.append(p.gpa, try p.parseTestDecl());
...@@ -292,14 +477,6 @@ fn parseModuleDecl(p: *Parse) Error!NodeIndex {...@@ -292,14 +477,6 @@ fn parseModuleDecl(p: *Parse) Error!NodeIndex {
292 };477 };
293}478}
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
303fn parseFnDecl(p: *Parse) Error!NodeIndex {480fn parseFnDecl(p: *Parse) Error!NodeIndex {
304 const trace = t.trace(@src());481 const trace = t.trace(@src());
305 defer trace.end();482 defer trace.end();
...@@ -339,8 +516,6 @@ fn parseFnDecl(p: *Parse) Error!NodeIndex {...@@ -339,8 +516,6 @@ fn parseFnDecl(p: *Parse) Error!NodeIndex {
339 });516 });
340}517}
341518
342const TypePresence = enum { types_optional, types_required };
343
344fn parseParamList(p: *Parse, types: TypePresence) Error!SubList {519fn parseParamList(p: *Parse, types: TypePresence) Error!SubList {
345 const trace = t.trace(@src());520 const trace = t.trace(@src());
346 defer trace.end();521 defer trace.end();
...@@ -390,13 +565,16 @@ fn parseParamDecl(p: *Parse, types: TypePresence) Error!NodeIndex {...@@ -390,13 +565,16 @@ fn parseParamDecl(p: *Parse, types: TypePresence) Error!NodeIndex {
390 });565 });
391}566}
392567
393fn parseStructDecl(p: *Parse) Error!NodeIndex {568fn parseStructDecl(p: *Parse, ctx: ExprParseContext) Error!NodeIndex {
394 const trace = t.trace(@src());569 const trace = t.trace(@src());
395 defer trace.end();570 defer trace.end();
396571
397 const main_token = p.assertToken(.kw_struct);572 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
401 _ = try p.expect(.start_brace);579 _ = try p.expect(.start_brace);
402580
...@@ -409,6 +587,28 @@ fn parseStructDecl(p: *Parse) Error!NodeIndex {...@@ -409,6 +587,28 @@ fn parseStructDecl(p: *Parse) Error!NodeIndex {
409 });587 });
410}588}
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
412fn parseTestDecl(p: *Parse) Error!NodeIndex {612fn parseTestDecl(p: *Parse) Error!NodeIndex {
413 const trace = t.trace(@src());613 const trace = t.trace(@src());
414 defer trace.end();614 defer trace.end();
...@@ -447,9 +647,7 @@ fn parseStructFieldList(p: *Parse) Error!SubList {...@@ -447,9 +647,7 @@ fn parseStructFieldList(p: *Parse) Error!SubList {
447 try p.scratch.append(p.gpa, try p.parseStructField());647 try p.scratch.append(p.gpa, try p.parseStructField());
448648
449 switch (p.currentTag()) {649 switch (p.currentTag()) {
450 .comma => {650 .comma => _ = p.nextToken(),
451 _ = p.nextToken();
452 },
453 .end_brace => break,651 .end_brace => break,
454 else => return p.failUnexpectedToken(.struct_field),652 else => return p.failUnexpectedToken(.struct_field),
455 }653 }
...@@ -578,13 +776,13 @@ fn parseVarDecl(p: *Parse) Error!NodeIndex {...@@ -578,13 +776,13 @@ fn parseVarDecl(p: *Parse) Error!NodeIndex {
578 _ = try p.expect(.id);776 _ = try p.expect(.id);
579777
580 const data_type: OptionalNodeIndex = if (p.eatToken(.colon)) |_|778 const data_type: OptionalNodeIndex = if (p.eatToken(.colon)) |_|
581 optionalNodeIndex(try p.tryParseExpression(.expression))779 optionalNodeIndex(try p.parseExpressionOrFail(.expression))
582 else780 else
583 .none;781 .none;
584782
585 _ = try p.expect(.equal);783 _ = try p.expect(.equal);
586784
587 const data_init = try p.tryParseExpression(.expression);785 const data_init = try p.parseExpressionOrFail(.expression);
588786
589 _ = try p.expect(.semi);787 _ = try p.expect(.semi);
590788
...@@ -619,204 +817,13 @@ fn isExpressionStart(p: *Parse) bool {...@@ -619,204 +817,13 @@ fn isExpressionStart(p: *Parse) bool {
619 .string,817 .string,
620 .tilde,818 .tilde,
621 .kw_fn,819 .kw_fn,
820 .kw_struct,
821 .kw_tuple,
622 => true,822 => true,
623 else => false,823 else => false,
624 };824 };
625}825}
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
820fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {827fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
821 const trace = t.trace(@src());828 const trace = t.trace(@src());
822 defer trace.end();829 defer trace.end();
...@@ -831,8 +838,13 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {...@@ -831,8 +838,13 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
831838
832 // These unary operators are greedy, so handle them first.839 // These unary operators are greedy, so handle them first.
833 switch (p.currentTag()) {840 switch (p.currentTag()) {
834 .kw_return => return p.parseReturn(),841 .kw_return => return p.parseReturn(mode),
835 .kw_break => return p.parseBreak(),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
836 else => {},848 else => {},
837 }849 }
838850
...@@ -846,7 +858,7 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {...@@ -846,7 +858,7 @@ fn parseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
846 return expr;858 return expr;
847}859}
848860
849fn tryParseExpression(p: *Parse, mode: ParseExprMode) Error!NodeIndex {861fn parseExpressionOrFail(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
850 return if (p.isExpressionStart())862 return if (p.isExpressionStart())
851 p.parseExpression(mode)863 p.parseExpression(mode)
852 else864 else
...@@ -858,7 +870,6 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {...@@ -858,7 +870,6 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
858 defer trace.end();870 defer trace.end();
859871
860 var expr = switch (p.currentTag()) {872 var expr = switch (p.currentTag()) {
861 // Literals
862 .id => if (p.nextTag() == .colon)873 .id => if (p.nextTag() == .colon)
863 try p.parseLabelExpression()874 try p.parseLabelExpression()
864 else875 else
...@@ -867,24 +878,27 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {...@@ -867,24 +878,27 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
867 .number_float => try p.blankNode(.number_float),878 .number_float => try p.blankNode(.number_float),
868 .string => try p.blankNode(.string),879 .string => try p.blankNode(.string),
869880
870 // Complex expressions
871 .builtin_id => try p.parseBuiltinCall(),881 .builtin_id => try p.parseBuiltinCall(),
872 .dot => try p.parseAnonInstantiation(),882 .dot => try p.parseAnonStructInit(),
873 .kw_break => try p.parseBreak(),
874 .kw_fn => try p.parseAnonFunction(),883 .kw_fn => try p.parseAnonFunction(),
875 .kw_if => try p.parseIf(),884 .kw_if => try p.parseIf(),
876 .kw_return => try p.parseReturn(),885 .kw_tuple => try p.parseTupleType(),
886
877 .start_brace => try p.parseBlock(),887 .start_brace => try p.parseBlock(),
878 .start_bracket => try p.parseArrayValueOrType(mode),888 .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
882 inline .bang,899 inline .bang,
883 .tilde,900 .tilde,
884 .minus,901 .minus,
885 .kw_comptime,
886 .kw_pure,
887 .kw_assert,
888 => |tag| unary: {902 => |tag| unary: {
889 const main_token = p.nextToken();903 const main_token = p.nextToken();
890 const expr = try p.parseExpressionPart(mode);904 const expr = try p.parseExpressionPart(mode);
...@@ -955,7 +969,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {...@@ -955,7 +969,7 @@ fn parseExpressionPart(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
955 return p.fail(.{ .array_type_flipped_hint = p.fullToken(p.pos) });969 return p.fail(.{ .array_type_flipped_hint = p.fullToken(p.pos) });
956 }970 }
957971
958 const index = try p.tryParseExpression(.expression);972 const index = try p.parseExpressionOrFail(.expression);
959973
960 _ = try p.expect(.end_bracket);974 _ = try p.expect(.end_bracket);
961975
...@@ -1020,14 +1034,14 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator, mode: ParseExprMod...@@ -1020,14 +1034,14 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator, mode: ParseExprMod
1020 });1034 });
1021}1035}
10221036
1023fn parseReturn(p: *Parse) Error!NodeIndex {1037fn parseReturn(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
1024 const trace = t.trace(@src());1038 const trace = t.trace(@src());
1025 defer trace.end();1039 defer trace.end();
10261040
1027 const main_token = p.assertToken(.kw_return);1041 const main_token = p.assertToken(.kw_return);
10281042
1029 const expr = if (p.isExpressionStart())1043 const expr = if (p.isExpressionStart())
1030 try p.parseExpression(.expression)1044 try p.parseExpression(mode)
1031 else1045 else
1032 null;1046 null;
10331047
...@@ -1040,7 +1054,7 @@ fn parseReturn(p: *Parse) Error!NodeIndex {...@@ -1040,7 +1054,7 @@ fn parseReturn(p: *Parse) Error!NodeIndex {
1040 });1054 });
1041}1055}
10421056
1043fn parseBreak(p: *Parse) Error!NodeIndex {1057fn parseBreak(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
1044 const trace = t.trace(@src());1058 const trace = t.trace(@src());
1045 defer trace.end();1059 defer trace.end();
10461060
...@@ -1052,7 +1066,7 @@ fn parseBreak(p: *Parse) Error!NodeIndex {...@@ -1052,7 +1066,7 @@ fn parseBreak(p: *Parse) Error!NodeIndex {
1052 null;1066 null;
10531067
1054 const expr = if (p.isExpressionStart())1068 const expr = if (p.isExpressionStart())
1055 try p.parseExpression(.expression)1069 try p.parseExpression(mode)
1056 else1070 else
1057 null;1071 null;
10581072
...@@ -1066,6 +1080,23 @@ fn parseBreak(p: *Parse) Error!NodeIndex {...@@ -1066,6 +1080,23 @@ fn parseBreak(p: *Parse) Error!NodeIndex {
1066 });1080 });
1067}1081}
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
1069fn parseBuiltinCall(p: *Parse) Error!NodeIndex {1100fn parseBuiltinCall(p: *Parse) Error!NodeIndex {
1070 const trace = t.trace(@src());1101 const trace = t.trace(@src());
1071 defer trace.end();1102 defer trace.end();
...@@ -1083,11 +1114,7 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex {...@@ -1083,11 +1114,7 @@ fn parseBuiltinCall(p: *Parse) Error!NodeIndex {
1083 });1114 });
1084}1115}
10851116
1086fn parseExpressionList(1117fn parseExpressionList(p: *Parse, comptime end_token: Token.Tag, fail_context: ExpectedContext) Error!SubList {
1087 p: *Parse,
1088 comptime end_token: Token.Tag,
1089 comptime fail_context: ExpectedContext,
1090) Error!SubList {
1091 const trace = t.trace(@src());1118 const trace = t.trace(@src());
1092 defer trace.end();1119 defer trace.end();
10931120
...@@ -1161,7 +1188,7 @@ fn parseElseValue(p: *Parse) Error!NodeIndex {...@@ -1161,7 +1188,7 @@ fn parseElseValue(p: *Parse) Error!NodeIndex {
1161 };1188 };
1162}1189}
11631190
1164fn parseAnonInstantiation(p: *Parse) Error!NodeIndex {1191fn parseAnonStructInit(p: *Parse) Error!NodeIndex {
1165 const trace = t.trace(@src());1192 const trace = t.trace(@src());
1166 defer trace.end();1193 defer trace.end();
11671194
...@@ -1272,9 +1299,20 @@ fn parseArrayValueOrType(p: *Parse, mode: ParseExprMode) Error!NodeIndex {...@@ -1272,9 +1299,20 @@ fn parseArrayValueOrType(p: *Parse, mode: ParseExprMode) Error!NodeIndex {
1272 });1299 });
1273}1300}
12741301
1275fn parseTupleType(p: *Parse) Error!NodeIndex {1302fn parseTupleInit(p: *Parse) Error!NodeIndex {
1276 const main_token = p.assertToken(.start_paren);1303 const main_token = p.assertToken(.start_paren);
1277 const list = try p.parseExpressionList(.end_paren, .expression);1304 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);
1278 return p.addNode(.{1316 return p.addNode(.{
1279 .tag = .tuple_type,1317 .tag = .tuple_type,
1280 .main_token = main_token,1318 .main_token = main_token,
src/core/Sema.zig+355-74
...@@ -1,10 +1,13 @@...@@ -1,10 +1,13 @@
1//! Semantic Analysis1//! Semantic Analysis converts `Teg`
2//!2//!
3//! Designed as a nonrecursive pause/resumable state machine, Sema converts3//! Designed as a nonrecursive state machine which can be paused when an
4//! declarations to Intern Pool `Value`s and function bodies to `Air`.4//! unfinished dependency is encountered, allowing the thread to work
5//! on other analysis jobs.
5//!6//!
6//! - Compile time resolution of values7//! Sema's responsibilities include
7//! - Type checking8//! - Operation validation and reporting of most compile errors.
9//! - Evaluation of `comptime` code.
10//! - Declaring dependencies on other analysis units.
811
9comp: *Comp,12comp: *Comp,
10ip: *InternPool,13ip: *InternPool,
...@@ -175,6 +178,7 @@ const Frame = struct {...@@ -175,6 +178,7 @@ const Frame = struct {
175178
176 undefined: void,179 undefined: void,
177 conditional: Conditional,180 conditional: Conditional,
181 func: Func,
178 };182 };
179183
180 const Conditional = enum {184 const Conditional = enum {
...@@ -183,7 +187,16 @@ const Frame = struct {...@@ -183,7 +187,16 @@ const Frame = struct {
183 runtime_no,187 runtime_no,
184 };188 };
185189
190 const Func = union(enum) {
191 return_type,
192 argument: u16,
193 };
194
186 fn initFromBlock(s: *Sema, block_index: Teg.Inst.Index, mode: AnalysisMode, data: Data) Frame {195 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 }
187 const list = s.teg.instEncodedData(block_index)200 const list = s.teg.instEncodedData(block_index)
188 .decodeUnsafe(Teg.PlNode(Teg.IndexList), s.teg.instTag(block_index))201 .decodeUnsafe(Teg.PlNode(Teg.IndexList), s.teg.instTag(block_index))
189 .extra_index.get(&s.teg)202 .extra_index.get(&s.teg)
...@@ -476,7 +489,8 @@ const InstructionKind = enum {...@@ -476,7 +489,8 @@ const InstructionKind = enum {
476 .@"comptime",489 .@"comptime",
477 .@"if",490 .@"if",
478 => .control_flow,491 => .control_flow,
479 // these pop frames, which may end the compilation492 // break is used to pop a frame, which has the
493 // possibility to end analysis
480 .@"break",494 .@"break",
481 => .pop_control_flow,495 => .pop_control_flow,
482 // these are statements which do not evaluate to anything (void)496 // these are statements which do not evaluate to anything (void)
...@@ -501,6 +515,12 @@ const PopControlFlowState = enum { stop, @"continue" };...@@ -501,6 +515,12 @@ const PopControlFlowState = enum { stop, @"continue" };
501515
502fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag).Result() {516fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag).Result() {
503 switch (tag) {517 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
504 // TODO: pre-evaluate `.param` nodes, since they are always at the start of a function.524 // TODO: pre-evaluate `.param` nodes, since they are always at the start of a function.
505 // this way we can avoid repeating the non-trivial call into InternPool525 // this way we can avoid repeating the non-trivial call into InternPool
506 .param => {526 .param => {
...@@ -666,6 +686,8 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -666,6 +686,8 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
666 return try s.fieldAccessArray(lhs, lhs_type, field.rhs);686 return try s.fieldAccessArray(lhs, lhs_type, field.rhs);
667 } else if (lhs_type.toStructType(s.ip)) |struct_type| {687 } else if (lhs_type.toStructType(s.ip)) |struct_type| {
668 return try s.fieldAccessStruct(lhs, lhs_type, struct_type, field.rhs);688 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);
669 } else if (lhs.value()) |value| {691 } else if (lhs.value()) |value| {
670 if (value.valueTag(s.ip) == .external_value) {692 if (value.valueTag(s.ip) == .external_value) {
671 const rhs = try s.getCoercedValue(s.lazyNode(.rhs), field.rhs, .string);693 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)...@@ -877,8 +899,6 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
877 // return Value.void.toAir();899 // return Value.void.toAir();
878 // },900 // },
879901
880 // control flow
881 .block => unreachable, // frame should already be pushed
882 .@"comptime" => {902 .@"comptime" => {
883 const index = s.frame.currentIndex();903 const index = s.frame.currentIndex();
884 try s.pushFrame();904 try s.pushFrame();
...@@ -892,50 +912,66 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -892,50 +912,66 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
892 .{ .params = Type, .captures = Air.Inst.Ref },912 .{ .params = Type, .captures = Air.Inst.Ref },
893 .{ data.params.len, data.captures.len },913 .{ data.params.len, data.captures.len },
894 );914 );
895 defer reserve.deinit();915 if (data.captures.len > 0) @panic("TODO: captures");
896 if (data.captures.len > 0) @panic("captures");916
917 var handled_reserve: std.debug.SafetyLock = .{};
918 handled_reserve.lock();
919 defer handled_reserve.assertUnlocked();
920 errdefer handled_reserve.unlock();
897921
898 for (data.params.slice(&s.teg), 0..) |param, i| {922 for (data.params.slice(&s.teg), 0..) |param, i| {
899 const src = s.lazyNode(.argument(@intCast(i)));923 const src = s.lazyNode(.argument(@intCast(i)));
900 const ty = try s.getType(src, param.unwrap() orelse @panic("TODO: inferred param sets"));924
901 if (!ty.hasPossibleRepresentation(s.ip)) {925 const known = param.unwrap() orelse @panic("TODO: inferred param sets");
902 return s.fail(.param_comptime_only, .{926 switch (known.decode()) {
903 .src = src,927 .value => |val| {
904 .type = ty,928 const ty = (try s.coerceValue(src, val, .type)).toTypeUnsafe(s.ip);
905 });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 },
906 }945 }
907 reserve.items(.params)[i] = ty;
908 }946 }
909947
910 const return_type = try s.getType(948 const return_type = ret: {
911 s.lazyNode(.return_type),949 switch ((data.return_type.unwrap() orelse break :ret null).decode()) {
912 data.return_type.unwrap() orelse .void,950 .value => |val| {
913 );951 const ty = (try s.coerceValue(s.lazyNode(.return_type), val, .type)).toTypeUnsafe(s.ip);
914952 if (!ty.hasPossibleRepresentation(s.ip)) {
915 const tracked_inst = try s.ip.trackInst(s.gpa, s.file, s.frame.currentIndex());953 return s.fail(.runtime_incompatible_type, .{
916 const func_type = try s.makeType(.{954 .src = s.lazyNode(.return_type),
917 .function_type = .{955 .type = ty,
918 .param_types = reserve.items(.params),956 });
919 .return_type = return_type,957 }
920 },958 break :ret ty;
921 });959 },
922 const func_value = try s.makeValue(.{960 .index => |block| {
923 .func = .{961 const mode = s.frame.mode;
924 .ty = func_type,962 try s.pushFrame();
925 .owner_decl = s.techDebtDeclIndex(), // TODO: ooh, this is a fun one!963 s.frame = Frame.initFromBlock(s, block, mode, .{ .func = .return_type });
926 .teg_body_inst = tracked_inst,964 s.frame.mark_index = reserve.index();
927 },965 handled_reserve.unlock();
928 });966 return;
929967 },
930 s.inst_map.add(s.frame.currentIndex(), func_value.toAir());968 }
931969 };
932 s.frame.advance();
933970
934 // TODO: move this queue once we perform one of these971 try s.postVisitFunctionPrototype(reserve, return_type);
935 // - depend on the return type of an inferred-return-type function972 handled_reserve.unlock();
936 // - use the value in any runtime context ()
937 try s.comp.jobs.writeItem(.{ .analysis = .{ .au = .wrap(.{ .func = func_value }) } });
938 },973 },
974
939 .@"break" => {975 .@"break" => {
940 const brk: Teg.Break = s.frame.currentPayload(.@"break");976 const brk: Teg.Break = s.frame.currentPayload(.@"break");
941 const operand = s.getRef(brk.operand, .marked);977 const operand = s.getRef(brk.operand, .marked);
...@@ -978,12 +1014,14 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -978,12 +1014,14 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
9781014
979 switch (data.conditional) {1015 switch (data.conditional) {
980 .comptime_condition => {1016 .comptime_condition => {
1017 assert(operand.kind == .value); // must be comptime
981 s.inst_map.add(s.frame.currentIndex(), operand);1018 s.inst_map.add(s.frame.currentIndex(), operand);
982 s.frame.advance();1019 s.frame.advance();
983 return .@"continue";1020 return .@"continue";
984 },1021 },
985 .runtime_yes => {1022 .runtime_yes => {
986 try s.emitRuntimeBlock(if_data.yes.decode().index, operand, mark);1023 try s.emitRuntimeBlock(if_data.yes.decode().index, operand, mark);
1024 mark.deinit();
9871025
988 switch (if_data.no.decode()) {1026 switch (if_data.no.decode()) {
989 .value => {1027 .value => {
...@@ -1025,6 +1063,79 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -1025,6 +1063,79 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
1025 }));1063 }));
1026 s.frame.advance();1064 s.frame.advance();
1027 },1065 },
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 },
1028 else => |t| std.debug.panic("TODO: post-visit .{s}", .{@tagName(t)}),1139 else => |t| std.debug.panic("TODO: post-visit .{s}", .{@tagName(t)}),
1029 }1140 }
10301141
...@@ -1141,7 +1252,6 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -1141,7 +1252,6 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
1141 var has_errors = false;1252 var has_errors = false;
1142 var is_constant = true;1253 var is_constant = true;
11431254
1144 // Allocate scratch space to store the values as they are being prepared.
1145 const scratch = try s.scratch.reserve(1255 const scratch = try s.scratch.reserve(
1146 s.arena(),1256 s.arena(),
1147 Air.Inst.Ref.Optional,1257 Air.Inst.Ref.Optional,
...@@ -1150,11 +1260,10 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -1150,11 +1260,10 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
1150 defer scratch.deinit();1260 defer scratch.deinit();
1151 @memset(scratch.slice(), .none);1261 @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.
1153 for (node.keys(&s.teg), node.inits(&s.teg), 0..) |init_key, init_value, i| {1266 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.
1158 const found_index = index: for (struct_type.field_keys, 0..) |decl_key, j| {1267 const found_index = index: for (struct_type.field_keys, 0..) |decl_key, j| {
1159 if (decl_key.eqlSlice(init_key.slice(s.teg.string_bytes), s.ip))1268 if (decl_key.eqlSlice(init_key.slice(s.teg.string_bytes), s.ip))
1160 break :index j;1269 break :index j;
...@@ -1181,7 +1290,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -1181,7 +1290,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
1181 .marked,1290 .marked,
1182 ) catch |e| switch (e) {1291 ) catch |e| switch (e) {
1183 error.AnalysisFail => {1292 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
1185 has_errors = true;1294 has_errors = true;
1186 // Mark something non-null so that duplicate field errors are emitted.1295 // Mark something non-null so that duplicate field errors are emitted.
1187 scratch.at(found_index).* = comptime Value.void.toAir().toOptional();1296 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)...@@ -1193,29 +1302,29 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
1193 scratch.at(found_index).* = coerced.toOptional();1302 scratch.at(found_index).* = coerced.toOptional();
1194 }1303 }
11951304
1196 // Check for missing fields1305 const storage = scratch.slice();
1197 {1306 var i: usize = 0;
1198 const storage = scratch.slice();1307 while (mem.indexOfScalarPos(Air.Inst.Ref.Optional, storage, i, .none)) |index| {
1199 var i: usize = 0;1308 try s.addError(.struct_init_missing_field, .{
1200 while (mem.indexOfScalarPos(Air.Inst.Ref.Optional, storage, i, .none)) |index| {1309 .ty = ty,
1201 try s.addError(.struct_init_missing_field, .{1310 .field = @intCast(index),
1202 .ty = ty,1311 .src = s.lazyNode(.main_node),
1203 .field = @intCast(index),1312 });
1204 .src = s.lazyNode(.main_node),1313 i = index + 1;
1205 });
1206 i = index + 1;
1207 }
1208 if (i != 0) has_errors = true;
1209 }1314 }
1315 if (i != 0) has_errors = true;
12101316
1211 if (has_errors)1317 if (has_errors)
1212 return Error.AnalysisFail;1318 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
1214 if (is_constant) {1323 if (is_constant) {
1215 // Convert all items in storage into a Value type
1216 const result_storage = scratch.sliceReinterpret(Value);1324 const result_storage = scratch.sliceReinterpret(Value);
1217 for (scratch.slice(), scratch.sliceReinterpret(Value)) |ref, *value| {1325 for (scratch.slice(), result_storage) |ref, *value| {
1218 value.* = ref.unwrap().?.decode().value;1326 // `ref` and `value` alias; this coercion is performed in-place
1327 value.* = (ref.unwrap() orelse unreachable).decode().value;
1219 }1328 }
1220 return try s.makeValueAir(.{ .struct_value = .{1329 return try s.makeValueAir(.{ .struct_value = .{
1221 .ty = ty,1330 .ty = ty,
...@@ -1224,7 +1333,6 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -1224,7 +1333,6 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
1224 } else {1333 } else {
1225 try s.ensureAtRuntime(s.lazyNode(.main_node));1334 try s.ensureAtRuntime(s.lazyNode(.main_node));
12261335
1227 // Create a runtime struct instantiation
1228 const value_index = try s.func.?.extra.appendSlice(s.gpa, scratch.slice());1336 const value_index = try s.func.?.extra.appendSlice(s.gpa, scratch.slice());
1229 return try s.addRef(.struct_init, .{1337 return try s.addRef(.struct_init, .{
1230 .ty = ty,1338 .ty = ty,
...@@ -1239,6 +1347,73 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)...@@ -1239,6 +1347,73 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag)
1239 return s.makeValueAir(.{ .array_type = .{ .item_type = operand } });1347 return s.makeValueAir(.{ .array_type = .{ .item_type = operand } });
1240 },1348 },
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
1242 .result_type_argument => {1417 .result_type_argument => {
1243 const result: Teg.ResultTypeArgument = s.frame.currentData(.result_type_argument);1418 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 {...@@ -1605,6 +1780,11 @@ fn coerceValue(s: *Sema, src: LazyNode, value: Value, to: Type) Error!Value {
1605 return s.coerceArrayValue(src, value, to_array_type.item_type);1780 return s.coerceArrayValue(src, value, to_array_type.item_type);
1606 }1781 }
1607 }1782 }
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 }
1608 },1788 },
1609 }1789 }
1610 return s.coercionError(src, from, to);1790 return s.coercionError(src, from, to);
...@@ -1615,6 +1795,14 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst...@@ -1615,6 +1795,14 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst
1615 .value => |v| {1795 .value => |v| {
1616 const ty = v.typeOf(s.ip);1796 const ty = v.typeOf(s.ip);
1617 switch (ty) {1797 switch (ty) {
1798 // Allowed at runtime
1799 .bool,
1800 .float,
1801 .int32,
1802 .string,
1803 .void,
1804 .empty_tuple,
1805 => {},
1618 .numeric_literal => {1806 .numeric_literal => {
1619 const float = v.toFloat(s.ip) orelse unreachable;1807 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...@@ -1649,13 +1837,6 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst
1649 .type = ty,1837 .type = ty,
1650 }),1838 }),
1651 .@"opaque" => @panic("TODO: represent comptime-known opaques better"),1839 .@"opaque" => @panic("TODO: represent comptime-known opaques better"),
1652 // Allowed at runtime
1653 .bool,
1654 .float,
1655 .int32,
1656 .string,
1657 .void,
1658 => {},
1659 _ => if (!ty.hasPossibleRepresentation(s.ip)) {1840 _ => if (!ty.hasPossibleRepresentation(s.ip)) {
1660 return s.fail(.runtime_incompatible_type, .{1841 return s.fail(.runtime_incompatible_type, .{
1661 .src = src,1842 .src = src,
...@@ -1728,6 +1909,44 @@ fn coerceArrayValue(s: *Sema, src: LazyNode, value: Value, item_type: Type) !Val...@@ -1728,6 +1909,44 @@ fn coerceArrayValue(s: *Sema, src: LazyNode, value: Value, item_type: Type) !Val
1728 } });1909 } });
1729}1910}
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
1731fn lookupDecl(s: *Sema, name: NullTerminatedString) InternPool.DeclIndex {1950fn lookupDecl(s: *Sema, name: NullTerminatedString) InternPool.DeclIndex {
1732 var nsi = s.ns_index;1951 var nsi = s.ns_index;
1733 while (true) {1952 while (true) {
...@@ -1861,6 +2080,68 @@ fn fieldAccessArray(s: *Sema, lhs: Air.Inst.Ref, lhs_type: Type, rhs_teg: Teg.In...@@ -1861,6 +2080,68 @@ fn fieldAccessArray(s: *Sema, lhs: Air.Inst.Ref, lhs_type: Type, rhs_teg: Teg.In
1861 }2080 }
1862}2081}
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
1864//2145//
18652146
1866fn tempAir(s: *const Sema) Air {2147fn tempAir(s: *const Sema) Air {
src/core/Teg.Dump.zig+18
...@@ -319,6 +319,24 @@ const State = struct {...@@ -319,6 +319,24 @@ const State = struct {
319 if (data.type_hint != .none)319 if (data.type_hint != .none)
320 try writer.writeAll(")");320 try writer.writeAll(")");
321 },321 },
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 },
322 .result_type_argument => {340 .result_type_argument => {
323 const data: Teg.ResultTypeArgument = teg.instData(.result_type_argument, inst);341 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) {...@@ -75,6 +75,9 @@ pub const Tag = enum(u8) {
75 struct_init,75 struct_init,
76 array_init,76 array_init,
7777
78 tuple_init,
79 tuple_type,
80
78 add,81 add,
79 sub,82 sub,
80 mul,83 mul,
...@@ -123,6 +126,7 @@ pub const Tag = enum(u8) {...@@ -123,6 +126,7 @@ pub const Tag = enum(u8) {
123 .variable_declaration,126 .variable_declaration,
124 .test_declaration,127 .test_declaration,
125 => PlNode(Decl),128 => PlNode(Decl),
129
126 .coercion => PlNode(Coercion),130 .coercion => PlNode(Coercion),
127 .@"break" => PlNode(Break),131 .@"break" => PlNode(Break),
128 .number => PlNode(Float),132 .number => PlNode(Float),
...@@ -136,6 +140,8 @@ pub const Tag = enum(u8) {...@@ -136,6 +140,8 @@ pub const Tag = enum(u8) {
136 .struct_decl => PlNode(StructDecl),140 .struct_decl => PlNode(StructDecl),
137 .struct_init => PlNode(StructInit),141 .struct_init => PlNode(StructInit),
138 .array_init => PlNode(ArrayInit),142 .array_init => PlNode(ArrayInit),
143 .tuple_type => PlNode(TupleType),
144 .tuple_init => PlNode(TupleInit),
139 .func => PlNode(Func),145 .func => PlNode(Func),
140146
141 .load_decl => StrNode,147 .load_decl => StrNode,
...@@ -146,7 +152,7 @@ pub const Tag = enum(u8) {...@@ -146,7 +152,7 @@ pub const Tag = enum(u8) {
146 .result_type_tuple_field => ResultTypeTupleField,152 .result_type_tuple_field => ResultTypeTupleField,
147 .result_type_array_value => ResultTypeArrayValue,153 .result_type_array_value => ResultTypeArrayValue,
148154
149 // Single instruction operand155 // One operand instructions
150 .memory,156 .memory,
151 .discard,157 .discard,
152 .ensure_void,158 .ensure_void,
...@@ -162,7 +168,7 @@ pub const Tag = enum(u8) {...@@ -162,7 +168,7 @@ pub const Tag = enum(u8) {
162 .assert,168 .assert,
163 => RefNode,169 => RefNode,
164170
165 // Two instruction operands171 // Two operand instructions
166 .field,172 .field,
167 .index,173 .index,
168 .add,174 .add,
...@@ -230,7 +236,7 @@ pub const Inst = struct {...@@ -230,7 +236,7 @@ pub const Inst = struct {
230 main_module_inst = 0,236 main_module_inst = 0,
231 _,237 _,
232238
233 pub const static_len = @intFromEnum(Ref.empty_array) + 1;239 pub const static_len = @intFromEnum(Ref.empty_tuple) + 1;
234240
235 pub fn toRef(i: Index) Inst.Ref {241 pub fn toRef(i: Index) Inst.Ref {
236 return @enumFromInt(static_len + @intFromEnum(i));242 return @enumFromInt(static_len + @intFromEnum(i));
...@@ -269,6 +275,7 @@ pub const Inst = struct {...@@ -269,6 +275,7 @@ pub const Inst = struct {
269 numeric_literal_type,275 numeric_literal_type,
270 numeric_literal_array_type,276 numeric_literal_array_type,
271 never_array_type,277 never_array_type,
278 empty_tuple_type,
272279
273 void,280 void,
274 bool_true,281 bool_true,
...@@ -278,7 +285,7 @@ pub const Inst = struct {...@@ -278,7 +285,7 @@ pub const Inst = struct {
278 nan,285 nan,
279 empty_string,286 empty_string,
280 empty_array,287 empty_array,
281288 empty_tuple,
282 _,289 _,
283290
284 pub const Decoded = union(enum) {291 pub const Decoded = union(enum) {
...@@ -423,6 +430,20 @@ pub const ArrayInit = struct {...@@ -423,6 +430,20 @@ pub const ArrayInit = struct {
423 list: RefList,430 list: RefList,
424};431};
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
426pub const Decl = struct {447pub const Decl = struct {
427 name: NullTerminatedString,448 name: NullTerminatedString,
428 hash: Cache.BinDigest,449 hash: Cache.BinDigest,
...@@ -697,6 +718,8 @@ pub fn writeToHasher(...@@ -697,6 +718,8 @@ pub fn writeToHasher(
697 .struct_decl,718 .struct_decl,
698 .struct_init,719 .struct_init,
699 .array_init,720 .array_init,
721 .tuple_init,
722 .tuple_type,
700 .func,723 .func,
701 .store_decl,724 .store_decl,
702 .store_field,725 .store_field,
...@@ -902,6 +925,19 @@ fn writePlNodeInnerToHasher(...@@ -902,6 +925,19 @@ fn writePlNodeInnerToHasher(
902 hasher.add(isolatedIndexValue(decl_index, data.body));925 hasher.add(isolatedIndexValue(decl_index, data.body));
903 hasher.add(data.instruction_count);926 hasher.add(data.instruction_count);
904 },927 },
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 },
905 else => @compileError("hash not implemented for " ++ @typeName(T)),941 else => @compileError("hash not implemented for " ++ @typeName(T)),
906 }942 }
907}943}
src/core/Tokenizer.zig+4-3
...@@ -75,6 +75,7 @@ pub const Token = struct {...@@ -75,6 +75,7 @@ pub const Token = struct {
75 kw_return,75 kw_return,
76 kw_struct,76 kw_struct,
77 kw_trait,77 kw_trait,
78 kw_tuple,
78 kw_var,79 kw_var,
79 kw_while,80 kw_while,
80 // sort-lines: end81 // sort-lines: end
...@@ -134,7 +135,7 @@ pub const Token = struct {...@@ -134,7 +135,7 @@ pub const Token = struct {
134135
135 /// Represents a tokenization error. Instead of reporting an error during136 /// Represents a tokenization error. Instead of reporting an error during
136 /// tokenization, parsing will begin, and an error will be reported when137 /// 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 .
138 invalid,139 invalid,
139 invalid_number,140 invalid_number,
140 invalid_string,141 invalid_string,
...@@ -275,8 +276,8 @@ pub const Token = struct {...@@ -275,8 +276,8 @@ pub const Token = struct {
275 /// Instead of storing the length of a token, we can recompute this276 /// Instead of storing the length of a token, we can recompute this
276 /// information on demand, as it is not often needed.277 /// information on demand, as it is not often needed.
277 ///278 ///
278 /// If you are computing the length of something that is knowable279 /// When computing the length of something that can be known without
279 /// without the source code, use token.tag.staticLen() instead.280 /// the source code, use token.tag.staticLen() instead.
280 pub fn len(token: Token, utf8_source: [:0]const u8) u32 {281 pub fn len(token: Token, utf8_source: [:0]const u8) u32 {
281 return token.tag.staticLen() orelse switch (token.tag) {282 return token.tag.staticLen() orelse switch (token.tag) {
282283
src/core/Transform.zig+36-4
...@@ -137,10 +137,9 @@ const ResultType = union(enum) {...@@ -137,10 +137,9 @@ const ResultType = union(enum) {
137 .used => unreachable, // Use after free137 .used => unreachable, // Use after free
138 .ref => |r| r,138 .ref => |r| r,
139 .type_of => |r| try t.addRef(.type_of, .{139 .type_of => |r| try t.addRef(.type_of, .{
140 // Result types don't have associated nodes, which is ok because140 // Result types do not have associated nodes which is okay as `type_of` cannot
141 // type_of cannot fail in the context we are using it. Instead,141 // fail in the context it is used in. A predictable poison value is used instead
142 // we will use a predictable poison value, so that an attempt142 // so that an attempt to print the following will cause a crash.
143 // to print this will crash.
144 .src_node = std.math.maxInt(i32),143 .src_node = std.math.maxInt(i32),
145 .operand = r,144 .operand = r,
146 }),145 }),
...@@ -281,6 +280,7 @@ const Symbol = struct {...@@ -281,6 +280,7 @@ const Symbol = struct {
281 value: union {280 value: union {
282 mutable: Inst.Index,281 mutable: Inst.Index,
283 immutable: Inst.Ref,282 immutable: Inst.Ref,
283 param_index: u32,
284 none: void,284 none: void,
285 },285 },
286 result_type_ref: Inst.Ref,286 result_type_ref: Inst.Ref,
...@@ -903,6 +903,7 @@ fn transformExpression(...@@ -903,6 +903,7 @@ fn transformExpression(
903 switch (node_tag) {903 switch (node_tag) {
904 .root,904 .root,
905 .param_decl,905 .param_decl,
906 .struct_field,
906 => unreachable, // not an expression907 => unreachable, // not an expression
907908
908 .id => {909 .id => {
...@@ -1520,6 +1521,37 @@ fn transformExpression(...@@ -1520,6 +1521,37 @@ fn transformExpression(
1520 });1521 });
1521 },1522 },
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
1523 inline .fn_decl, .fn_expr => |tag| {1555 inline .fn_decl, .fn_expr => |tag| {
1524 const data = switch (tag) {1556 const data = switch (tag) {
1525 .fn_decl => t.ast.fullFnDecl(node),1557 .fn_decl => t.ast.fullFnDecl(node),
src/core/js/Js.zig+1-1
...@@ -43,7 +43,7 @@ pub fn link(...@@ -43,7 +43,7 @@ pub fn link(
43 entry_point: Comp.Decl.Index,43 entry_point: Comp.Decl.Index,
44 writer: std.io.AnyWriter,44 writer: std.io.AnyWriter,
45) !void {45) !void {
46 var state = Link{46 var state: Link = .{
47 .comp = comp,47 .comp = comp,
48 .backend = backend,48 .backend = backend,
49 };49 };
src/core/js/Link.zig+12-1
...@@ -309,7 +309,18 @@ fn printValue(link: *Link, value: InternPool.Value, level: Level, writer: anytyp...@@ -309,7 +309,18 @@ fn printValue(link: *Link, value: InternPool.Value, level: Level, writer: anytyp
309 if (i != 0)309 if (i != 0)
310 try writer.writeAll(",");310 try writer.writeAll(",");
311311
312 // TODO: handle special identifiers312 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
313 try link.printValue(inner_value, .assignment, writer);324 try link.printValue(inner_value, .assignment, writer);
314 }325 }
315 try writer.writeAll("]");326 try writer.writeAll("]");
src/core/js/NodeGen.zig+44-2
...@@ -283,6 +283,15 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {...@@ -283,6 +283,15 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {
283 try c.markExpression(inst, node);283 try c.markExpression(inst, node);
284 },284 },
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
286 .array_len => {295 .array_len => {
287 const data: Air.Un = c.air.instData(.array_len, inst);296 const data: Air.Un = c.air.instData(.array_len, inst);
288 const node = try c.add(.field, .{297 const node = try c.add(.field, .{
...@@ -459,9 +468,9 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {...@@ -459,9 +468,9 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {
459 },468 },
460469
461 .array_init => {470 .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);
465 const store_offset = try c.extra.alloc(c.gpa, items.len);474 const store_offset = try c.extra.alloc(c.gpa, items.len);
466475
467 for (476 for (
...@@ -478,6 +487,28 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {...@@ -478,6 +487,28 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void {
478 });487 });
479 try c.markExpression(inst, node);488 try c.markExpression(inst, node);
480 },489 },
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 },
481 }512 }
482}513}
483514
...@@ -557,6 +588,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {...@@ -557,6 +588,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {
557 .simple_type,588 .simple_type,
558 .struct_type,589 .struct_type,
559 .array_type,590 .array_type,
591 .tuple_type,
560 => unreachable, // Type values are not allowed here592 => unreachable, // Type values are not allowed here
561593
562 .int32,594 .int32,
...@@ -566,6 +598,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {...@@ -566,6 +598,7 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {
566 .simple_value,598 .simple_value,
567 .struct_value,599 .struct_value,
568 .array_value,600 .array_value,
601 .tuple_value,
569 => return try c.add(.plain_value, .{ .value = v }),602 => return try c.add(.plain_value, .{ .value = v }),
570603
571 .external_value => {604 .external_value => {
...@@ -585,6 +618,13 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {...@@ -585,6 +618,13 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {
585 }618 }
586}619}
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
588fn addTemporary(c: *NodeGen) !Node.Index {628fn addTemporary(c: *NodeGen) !Node.Index {
589 const t = tracer.trace(@src());629 const t = tracer.trace(@src());
590 defer t.end();630 defer t.end();
...@@ -725,6 +765,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {...@@ -725,6 +765,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {
725 .arg,765 .arg,
726 .field_opaque,766 .field_opaque,
727 .field_struct,767 .field_struct,
768 .field_tuple,
728 .field_array,769 .field_array,
729 .add,770 .add,
730 .sub,771 .sub,
...@@ -739,6 +780,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {...@@ -739,6 +780,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {
739 .struct_init,780 .struct_init,
740 .array_init,781 .array_init,
741 .array_len,782 .array_len,
783 .tuple_init,
742 => true,784 => true,
743785
744 // JavaScript variable declarations require statements.786 // JavaScript variable declarations require statements.
src/core/stack.zig+28-9
...@@ -15,7 +15,7 @@ pub fn Stack(comptime Base: type) type {...@@ -15,7 +15,7 @@ pub fn Stack(comptime Base: type) type {
1515
16 pub const Mark = struct {16 pub const Mark = struct {
17 stack: *Self,17 stack: *Self,
18 index: usize,18 index: u32,
1919
20 pub fn deinit(m: Mark) void {20 pub fn deinit(m: Mark) void {
21 if (core.runtime_safety)21 if (core.runtime_safety)
...@@ -23,8 +23,8 @@ pub fn Stack(comptime Base: type) type {...@@ -23,8 +23,8 @@ pub fn Stack(comptime Base: type) type {
23 m.stack.list.shrinkRetainingCapacity(m.index);23 m.stack.list.shrinkRetainingCapacity(m.index);
24 }24 }
2525
26 pub fn len(m: Mark) usize {26 pub fn len(m: Mark) u32 {
27 return m.stack.list.items.len - m.index;27 return @intCast(m.stack.list.items.len - m.index);
28 }28 }
2929
30 pub fn slice(m: Mark, comptime T: type) []T {30 pub fn slice(m: Mark, comptime T: type) []T {
...@@ -39,7 +39,7 @@ pub fn Stack(comptime Base: type) type {...@@ -39,7 +39,7 @@ pub fn Stack(comptime Base: type) type {
39 pub fn mark(stack: *Self) Mark {39 pub fn mark(stack: *Self) Mark {
40 return .{40 return .{
41 .stack = stack,41 .stack = stack,
42 .index = stack.list.items.len,42 .index = @intCast(stack.list.items.len),
43 };43 };
44 }44 }
4545
...@@ -101,8 +101,8 @@ pub fn Stack(comptime Base: type) type {...@@ -101,8 +101,8 @@ pub fn Stack(comptime Base: type) type {
101101
102 return struct {102 return struct {
103 stack: *Self,103 stack: *Self,
104 starts: [fields.len]usize,104 starts: [fields.len]u32,
105 lengths: [fields.len]usize,105 lengths: [fields.len]u32,
106106
107 fn Type(comptime field: FieldEnum) type {107 fn Type(comptime field: FieldEnum) type {
108 return @field(types, @tagName(field));108 return @field(types, @tagName(field));
...@@ -128,6 +128,25 @@ pub fn Stack(comptime Base: type) type {...@@ -128,6 +128,25 @@ pub fn Stack(comptime Base: type) type {
128 std.mem.copyForwards(u32, dest, from);128 std.mem.copyForwards(u32, dest, from);
129 r.stack.list.shrinkRetainingCapacity(start + from.len);129 r.stack.list.shrinkRetainingCapacity(start + from.len);
130 }130 }
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 }
131 };150 };
132 }151 }
133152
...@@ -137,13 +156,13 @@ pub fn Stack(comptime Base: type) type {...@@ -137,13 +156,13 @@ pub fn Stack(comptime Base: type) type {
137 stack: *Self,156 stack: *Self,
138 allocator: Allocator,157 allocator: Allocator,
139 comptime types: anytype,158 comptime types: anytype,
140 lengths: [std.meta.fields(@TypeOf(types)).len]usize,159 lengths: [std.meta.fields(@TypeOf(types)).len]u32,
141 ) !ReserveMulti(types) {160 ) !ReserveMulti(types) {
142 var all_lengths: usize = 0;161 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;
144 const start = stack.list.items.len;163 const start = stack.list.items.len;
145 inline for (lengths, &starts) |len, *slice_start| {164 inline for (lengths, &starts) |len, *slice_start| {
146 slice_start.* = start + all_lengths;165 slice_start.* = @intCast(start + all_lengths);
147 all_lengths += len;166 all_lengths += len;
148 }167 }
149 try stack.list.ensureUnusedCapacity(allocator, all_lengths);168 try stack.list.ensureUnusedCapacity(allocator, all_lengths);
src/main.zig+26-95
...@@ -4,9 +4,18 @@...@@ -4,9 +4,18 @@
4//! for example, test the `core` module's compilation features without4//! for example, test the `core` module's compilation features without
5//! needing to compile the formatter.5//! needing to compile the formatter.
66
7const have_debug_features = builtin.mode == .Debug;
8const config = @import("config");7const 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
10pub fn main() !u8 {19pub fn main() !u8 {
11 try tracer.init();20 try tracer.init();
12 defer tracer.deinit();21 defer tracer.deinit();
...@@ -24,8 +33,7 @@ pub fn main() !u8 {...@@ -24,8 +33,7 @@ pub fn main() !u8 {
24 }){}33 }){}
25 else34 else
26 std.heap.ArenaAllocator.init(std.heap.page_allocator);35 std.heap.ArenaAllocator.init(std.heap.page_allocator);
27 var no_leak_checks = false;36 defer _ = if (use_gpa) gpa.deinit();
28 defer _ = if (use_gpa and !no_leak_checks) gpa.deinit();
29 const alloc = gpa.allocator();37 const alloc = gpa.allocator();
3038
31 // Current working directory is required for CLI resolution.39 // Current working directory is required for CLI resolution.
...@@ -33,95 +41,17 @@ pub fn main() !u8 {...@@ -33,95 +41,17 @@ pub fn main() !u8 {
33 const cwd = try std.posix.getcwd(&cwd_buf);41 const cwd = try std.posix.getcwd(&cwd_buf);
3442
35 // Command line argument parsing.43 // Command line argument parsing.
36 var maybe_file_path: ?[]const u8 = null;44 const program_name = "zig build compiler --";
37 defer if (maybe_file_path) |path| alloc.free(path);45 _, const args = try Cli.programArgsAlloc(alloc, program_name);
38 var exe_name: ?[]const u8 = null;46 defer Cli.programArgsFree(alloc, args);
39 defer if (exe_name) |path| alloc.free(path);47 const opts = arg_config.parse(program_name, args);
40 var dump_ir = false;48
41 var dump_state: ?[]const u8 = null;49 const file_path = try std.fs.path.resolve(alloc, &.{
42 var run = false;50 cwd,
43 var has_errors = false;51 opts.positionals orelse
44 {52 arg_config.showHelpAndExit(program_name),
45 var args = try std.process.argsWithAllocator(alloc);53 });
46 defer args.deinit();54 defer alloc.free(file_path);
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.?;
12555
126 // Execute compiler56 // Execute compiler
127 {57 {
...@@ -130,13 +60,13 @@ pub fn main() !u8 {...@@ -130,13 +60,13 @@ pub fn main() !u8 {
130 var comp = try core.Comp.init(.{60 var comp = try core.Comp.init(.{
131 .gpa = tracer.tracingAllocator(&alloc),61 .gpa = tracer.tracingAllocator(&alloc),
132 .options = .{62 .options = .{
133 .dump_ir = dump_ir,63 .dump_ir = opts.dump_ir orelse false,
134 },64 },
135 .backend = .{ .js = &js_backend },65 .backend = .{ .js = &js_backend },
136 });66 });
137 defer comp.deinit();67 defer comp.deinit();
13868
139 defer if (dump_state) |path| {69 defer if (opts.dump_state) |path| {
140 dumpState(&comp, path) catch |err| {70 dumpState(&comp, path) catch |err| {
141 std.debug.print("Failed to dump compilation state: {}", .{err});71 std.debug.print("Failed to dump compilation state: {}", .{err});
142 };72 };
...@@ -173,7 +103,7 @@ pub fn main() !u8 {...@@ -173,7 +103,7 @@ pub fn main() !u8 {
173 else => return err,103 else => return err,
174 };104 };
175105
176 if (config.have_embedded_quickjs and run) {106 if (config.have_embedded_quickjs and (opts.run orelse false)) {
177 // Execute with QuickJS107 // Execute with QuickJS
178 var out: std.ArrayListUnmanaged(u8) = .{};108 var out: std.ArrayListUnmanaged(u8) = .{};
179 defer out.deinit(alloc);109 defer out.deinit(alloc);
...@@ -244,6 +174,7 @@ pub const tracer_impl = @field(tracer, @tagName(@import("config").tracing_mode))...@@ -244,6 +174,7 @@ pub const tracer_impl = @field(tracer, @tagName(@import("config").tracing_mode))
244174
245const std = @import("std");175const std = @import("std");
246const builtin = @import("builtin");176const builtin = @import("builtin");
177const Cli = @import("Cli.zig");
247178
248fn dumpState(comp: *const Comp, dump_path: []const u8) !void {179fn dumpState(comp: *const Comp, dump_path: []const u8) !void {
249 const file = try std.fs.cwd().createFile(dump_path, .{});180 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 }