authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-09-16 03:06:29-04:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-09-16 03:06:29-04:00
log4c3e0c2241261637620ce10461adfdfd5558e422
treeade4d65fbedef996513b417eb893eec8b43f805a
parentf59b8e991584b1b97a8c3dad81223d2974f4eab5
signature Commit is signed but in an unrecognized format.

finish implementing tuples


12 files changed, 524 insertions(+), 104 deletions(-)

src/cli_compiler.zig+1
...@@ -44,6 +44,7 @@ pub fn main() !u8 {...@@ -44,6 +44,7 @@ pub fn main() !u8 {
44 // Command line argument parsing.44 // Command line argument parsing.
45 const program_name = "zig build compiler --";45 const program_name = "zig build compiler --";
46 _, const args = try Cli.programArgsAlloc(alloc, program_name);46 _, const args = try Cli.programArgsAlloc(alloc, program_name);
47 defer Cli.programArgsFree(alloc, args);
47 const opts = arg_config.parse(program_name, args);48 const opts = arg_config.parse(program_name, args);
4849
49 const file_path = try std.fs.path.resolve(alloc, &.{50 const file_path = try std.fs.path.resolve(alloc, &.{
src/core/Air.Dump.zig+23-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);
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/InternPool.zig+28-9
...@@ -158,7 +158,7 @@ pub const Tag = enum(u8) {...@@ -158,7 +158,7 @@ pub const Tag = enum(u8) {
158 => unreachable, // does not have a payload158 => unreachable, // does not have a payload
159 .float, .comptime_float => Float,159 .float, .comptime_float => Float,
160 .func => FuncDecl,160 .func => FuncDecl,
161 .function_type => TypeFunction,161 .function_type => FunctionType,
162 .string => String.WithLen,162 .string => String.WithLen,
163 .struct_value => StructValue,163 .struct_value => StructValue,
164 .struct_type => StructType,164 .struct_type => StructType,
...@@ -180,9 +180,9 @@ pub const Tag = enum(u8) {...@@ -180,9 +180,9 @@ pub const Tag = enum(u8) {
180180
181 /// Trailing:181 /// Trailing:
182 /// 0. param_type: Index to param type. Repeated .params_len182 /// 0. param_type: Index to param type. Repeated .params_len
183 pub const TypeFunction = struct {183 pub const FunctionType = struct {
184 params_len: u32,184 params_len: u32,
185 return_type: Type,185 return_type: Type.Optional,
186 };186 };
187187
188 /// Trailing:188 /// Trailing:
...@@ -578,7 +578,10 @@ pub const Type = enum(u32) {...@@ -578,7 +578,10 @@ pub const Type = enum(u32) {
578 try writer.print("{}", .{param.displayName(ip)});578 try writer.print("{}", .{param.displayName(ip)});
579 }579 }
580 try writer.writeAll(") -> ");580 try writer.writeAll(") -> ");
581 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]");
582 },585 },
583 .struct_type => {586 .struct_type => {
584 try writer.writeAll("[struct]");587 try writer.writeAll("[struct]");
...@@ -598,6 +601,19 @@ pub const Type = enum(u32) {...@@ -598,6 +601,19 @@ pub const Type = enum(u32) {
598 },601 },
599 }602 }
600 }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 };
601};617};
602618
603pub const Item = struct {619pub const Item = struct {
...@@ -956,7 +972,10 @@ pub const Key = union(enum) {...@@ -956,7 +972,10 @@ pub const Key = union(enum) {
956972
957 pub const FunctionType = struct {973 pub const FunctionType = struct {
958 param_types: []Type,974 param_types: []Type,
959 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,
960979
961 pub fn hash(self: FunctionType, hasher: *Hash) void {980 pub fn hash(self: FunctionType, hasher: *Hash) void {
962 const t = tracer.trace(@src());981 const t = tracer.trace(@src());
...@@ -1491,9 +1510,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {...@@ -1491,9 +1510,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
1491 .function_type => |f| brk: {1510 .function_type => |f| brk: {
1492 for (f.param_types) |ty| ty.assertValid(ip);1511 for (f.param_types) |ty| ty.assertValid(ip);
14931512
1494 const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.TypeFunction, .{1513 const extra, var trail = try ip.extra.appendTrailing(gpa, Tag.FunctionType, .{
1495 .params_len = @intCast(f.param_types.len),1514 .params_len = @intCast(f.param_types.len),
1496 .return_type = f.return_type,1515 .return_type = .init(f.return_type),
1497 }, f.param_types.len);1516 }, f.param_types.len);
1498 defer trail.end();1517 defer trail.end();
1499 trail.writeSlice(f.param_types);1518 trail.writeSlice(f.param_types);
...@@ -1566,12 +1585,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {...@@ -1566,12 +1585,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value {
15661585
1567fn extraTypeFunction(1586fn extraTypeFunction(
1568 ip: *const InternPool,1587 ip: *const InternPool,
1569 extra_index: ExtraIndex(Tag.TypeFunction),1588 extra_index: ExtraIndex(Tag.FunctionType),
1570) Key.FunctionType {1589) Key.FunctionType {
1571 const function_type, var trail = extra_index.getTrail(ip);1590 const function_type, var trail = extra_index.getTrail(ip);
1572 const param_types = trail.readTypeSlice(function_type.params_len);1591 const param_types = trail.readTypeSlice(function_type.params_len);
1573 const result: Key.FunctionType = .{1592 const result: Key.FunctionType = .{
1574 .return_type = function_type.return_type,1593 .return_type = function_type.return_type.unwrap(),
1575 .param_types = param_types.get(ip),1594 .param_types = param_types.get(ip),
1576 };1595 };
1577 return result;1596 return result;
src/core/Sema.zig+347-67
...@@ -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);
...@@ -1729,6 +1909,44 @@ fn coerceArrayValue(s: *Sema, src: LazyNode, value: Value, item_type: Type) !Val...@@ -1729,6 +1909,44 @@ fn coerceArrayValue(s: *Sema, src: LazyNode, value: Value, item_type: Type) !Val
1729 } });1909 } });
1730}1910}
17311911
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
1732fn lookupDecl(s: *Sema, name: NullTerminatedString) InternPool.DeclIndex {1950fn lookupDecl(s: *Sema, name: NullTerminatedString) InternPool.DeclIndex {
1733 var nsi = s.ns_index;1951 var nsi = s.ns_index;
1734 while (true) {1952 while (true) {
...@@ -1862,6 +2080,68 @@ fn fieldAccessArray(s: *Sema, lhs: Air.Inst.Ref, lhs_type: Type, rhs_teg: Teg.In...@@ -1862,6 +2080,68 @@ fn fieldAccessArray(s: *Sema, lhs: Air.Inst.Ref, lhs_type: Type, rhs_teg: Teg.In
1862 }2080 }
1863}2081}
18642082
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
1865//2145//
18662146
1867fn tempAir(s: *const Sema) Air {2147fn tempAir(s: *const Sema) Air {
src/core/Teg.zig+3-2
...@@ -126,6 +126,7 @@ pub const Tag = enum(u8) {...@@ -126,6 +126,7 @@ pub const Tag = enum(u8) {
126 .variable_declaration,126 .variable_declaration,
127 .test_declaration,127 .test_declaration,
128 => PlNode(Decl),128 => PlNode(Decl),
129
129 .coercion => PlNode(Coercion),130 .coercion => PlNode(Coercion),
130 .@"break" => PlNode(Break),131 .@"break" => PlNode(Break),
131 .number => PlNode(Float),132 .number => PlNode(Float),
...@@ -151,7 +152,7 @@ pub const Tag = enum(u8) {...@@ -151,7 +152,7 @@ pub const Tag = enum(u8) {
151 .result_type_tuple_field => ResultTypeTupleField,152 .result_type_tuple_field => ResultTypeTupleField,
152 .result_type_array_value => ResultTypeArrayValue,153 .result_type_array_value => ResultTypeArrayValue,
153154
154 // Single instruction operand155 // One operand instructions
155 .memory,156 .memory,
156 .discard,157 .discard,
157 .ensure_void,158 .ensure_void,
...@@ -167,7 +168,7 @@ pub const Tag = enum(u8) {...@@ -167,7 +168,7 @@ pub const Tag = enum(u8) {
167 .assert,168 .assert,
168 => RefNode,169 => RefNode,
169170
170 // Two instruction operands171 // Two operand instructions
171 .field,172 .field,
172 .index,173 .index,
173 .add,174 .add,
src/core/Tokenizer.zig+3-3
...@@ -135,7 +135,7 @@ pub const Token = struct {...@@ -135,7 +135,7 @@ pub const Token = struct {
135135
136 /// Represents a tokenization error. Instead of reporting an error during136 /// Represents a tokenization error. Instead of reporting an error during
137 /// tokenization, parsing will begin, and an error will be reported when137 /// tokenization, parsing will begin, and an error will be reported when
138 /// this token is hit. That way, the error can tell you what it expected.138 /// this token is hit. That way, the error .
139 invalid,139 invalid,
140 invalid_number,140 invalid_number,
141 invalid_string,141 invalid_string,
...@@ -276,8 +276,8 @@ pub const Token = struct {...@@ -276,8 +276,8 @@ pub const Token = struct {
276 /// Instead of storing the length of a token, we can recompute this276 /// Instead of storing the length of a token, we can recompute this
277 /// information on demand, as it is not often needed.277 /// information on demand, as it is not often needed.
278 ///278 ///
279 /// If you are computing the length of something that is knowable279 /// When computing the length of something that can be known without
280 /// without the source code, use token.tag.staticLen() instead.280 /// the source code, use token.tag.staticLen() instead.
281 pub fn len(token: Token, utf8_source: [:0]const u8) u32 {281 pub fn len(token: Token, utf8_source: [:0]const u8) u32 {
282 return token.tag.staticLen() orelse switch (token.tag) {282 return token.tag.staticLen() orelse switch (token.tag) {
283283
src/core/Transform.zig+4-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,
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+42-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
...@@ -587,6 +618,13 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {...@@ -587,6 +618,13 @@ fn valueToExpression(c: *NodeGen, v: Value) !Node.Index {
587 }618 }
588}619}
589620
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
590fn addTemporary(c: *NodeGen) !Node.Index {628fn addTemporary(c: *NodeGen) !Node.Index {
591 const t = tracer.trace(@src());629 const t = tracer.trace(@src());
592 defer t.end();630 defer t.end();
...@@ -727,6 +765,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {...@@ -727,6 +765,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {
727 .arg,765 .arg,
728 .field_opaque,766 .field_opaque,
729 .field_struct,767 .field_struct,
768 .field_tuple,
730 .field_array,769 .field_array,
731 .add,770 .add,
732 .sub,771 .sub,
...@@ -741,6 +780,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {...@@ -741,6 +780,7 @@ fn canLowerToExpression(c: *const NodeGen, inst: Air.Inst.Ref) bool {
741 .struct_init,780 .struct_init,
742 .array_init,781 .array_init,
743 .array_len,782 .array_len,
783 .tuple_init,
744 => true,784 => true,
745785
746 // 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);
test/behavior/tuple+5-4
...@@ -1,13 +1,14 @@...@@ -1,13 +1,14 @@
1let assertEqual = @extern("assertEqual");1let assertEqual = @extern("assertEqual");
22
3fn hello(x: type) -> y(x, float) {3fn hello() -> tuple (int, float) {
4 (5, 2.5)4 let x: tuple (int, float) = (5, 2.5);
5 x
5}6}
67
7fn main {8fn main {
8 let x = hello();9 let x = hello();
9 assertEqual(x.0, 5);10 assertEqual(x.0, i(5));
10 assertEqual(x.1, 2.5);11 assertEqual(x.1, f(2.5));
11}12}
1213
13fn f(a: float) -> float { a }14fn f(a: float) -> float { a }