| author | |
| committer | |
| log | 4c3e0c2241261637620ce10461adfdfd5558e422 |
| tree | ade4d65fbedef996513b417eb893eec8b43f805a |
| parent | f59b8e991584b1b97a8c3dad81223d2974f4eab5 |
| signature | Commit is signed but in an unrecognized format. |
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); |
| 48 | 49 | ||
| 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 | } |
| 187 | 206 | ||
| ... | @@ -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 { |
| 25 | 25 | ||
| 26 | struct_init, | 26 | struct_init, |
| 27 | array_init, | 27 | array_init, |
| 28 | tuple_init, | ||
| 28 | 29 | ||
| 29 | field_opaque, | 30 | field_opaque, |
| 30 | field_struct, | 31 | field_struct, |
| 32 | field_tuple, | ||
| 31 | field_array, | 33 | field_array, |
| 32 | 34 | ||
| 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 | }; |
| 288 | 291 | ||
| 292 | pub 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 | |||
| 289 | pub const FieldStruct = struct { | 304 | pub 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, |
| 346 | 362 | ||
| 347 | // A handful of binary operators are lowered to a form where both sides | 363 | // 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 | }, |
| 377 | 395 | ||
| 378 | // Field accesses on structs are special-cased by storing the field | 396 | // 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 type | 409 | .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 | } |
| 396 | 422 |
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 payload | 158 | => 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) { |
| 180 | 180 | ||
| 181 | /// Trailing: | 181 | /// Trailing: |
| 182 | /// 0. param_type: Index to param type. Repeated .params_len | 182 | /// 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 | }; |
| 187 | 187 | ||
| 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 | }; |
| 602 | 618 | ||
| 603 | pub const Item = struct { | 619 | pub const Item = struct { |
| ... | @@ -956,7 +972,10 @@ pub const Key = union(enum) { | ... | @@ -956,7 +972,10 @@ pub const Key = union(enum) { |
| 956 | 972 | ||
| 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, | ||
| 960 | 979 | ||
| 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); |
| 1493 | 1512 | ||
| 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 { |
| 1566 | 1585 | ||
| 1567 | fn extraTypeFunction( | 1586 | fn 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 Analysis | 1 | //! Semantic Analysis converts `Teg` |
| 2 | //! | 2 | //! |
| 3 | //! Designed as a nonrecursive pause/resumable state machine, Sema converts | 3 | //! 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 values | 7 | //! Sema's responsibilities include |
| 7 | //! - Type checking | 8 | //! - Operation validation and reporting of most compile errors. |
| 9 | //! - Evaluation of `comptime` code. | ||
| 10 | //! - Declaring dependencies on other analysis units. | ||
| 8 | 11 | ||
| 9 | comp: *Comp, | 12 | comp: *Comp, |
| 10 | ip: *InternPool, | 13 | ip: *InternPool, |
| ... | @@ -175,6 +178,7 @@ const Frame = struct { | ... | @@ -175,6 +178,7 @@ const Frame = struct { |
| 175 | 178 | ||
| 176 | undefined: void, | 179 | undefined: void, |
| 177 | conditional: Conditional, | 180 | conditional: Conditional, |
| 181 | func: Func, | ||
| 178 | }; | 182 | }; |
| 179 | 183 | ||
| 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 | }; |
| 185 | 189 | ||
| 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 compilation | 492 | // 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" }; |
| 501 | 515 | ||
| 502 | fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag).Result() { | 516 | fn 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 InternPool | 525 | // 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 | // }, |
| 879 | 901 | ||
| 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(); | ||
| 897 | 921 | ||
| 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 | } |
| 909 | 947 | ||
| 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); |
| 914 | 952 | 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; |
| 929 | 967 | }, | |
| 930 | s.inst_map.add(s.frame.currentIndex(), func_value.toAir()); | 968 | } |
| 931 | 969 | }; | |
| 932 | s.frame.advance(); | ||
| 933 | 970 | ||
| 934 | // TODO: move this queue once we perform one of these | 971 | try s.postVisitFunctionPrototype(reserve, return_type); |
| 935 | // - depend on the return type of an inferred-return-type function | 972 | 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) |
| 978 | 1014 | ||
| 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(); | ||
| 987 | 1025 | ||
| 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 | } |
| 1030 | 1141 | ||
| ... | @@ -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; |
| 1143 | 1254 | ||
| 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); |
| 1152 | 1262 | ||
| 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 | } |
| 1195 | 1304 | ||
| 1196 | // Check for missing fields | 1305 | 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; | ||
| 1210 | 1316 | ||
| 1211 | if (has_errors) | 1317 | if (has_errors) |
| 1212 | return Error.AnalysisFail; | 1318 | return Error.AnalysisFail; |
| 1213 | 1319 | ||
| 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)); |
| 1226 | 1335 | ||
| 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 | }, |
| 1241 | 1349 | ||
| 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); |
| 1244 | 1419 | ||
| ... | @@ -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 | } |
| 1731 | 1911 | ||
| 1912 | fn 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 | |||
| 1732 | fn lookupDecl(s: *Sema, name: NullTerminatedString) InternPool.DeclIndex { | 1950 | fn 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 | } |
| 1864 | 2082 | ||
| 2083 | fn 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 | |||
| 2115 | const FunctionReserve = core.U32Stack.ReserveMulti(.{ .params = Type, .captures = Air.Inst.Ref }); | ||
| 2116 | |||
| 2117 | fn 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 | // |
| 1866 | 2146 | ||
| 1867 | fn tempAir(s: *const Sema) Air { | 2147 | fn 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, |
| 153 | 154 | ||
| 154 | // Single instruction operand | 155 | // 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, |
| 169 | 170 | ||
| 170 | // Two instruction operands | 171 | // 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 { |
| 135 | 135 | ||
| 136 | /// Represents a tokenization error. Instead of reporting an error during | 136 | /// Represents a tokenization error. Instead of reporting an error during |
| 137 | /// tokenization, parsing will begin, and an error will be reported when | 137 | /// 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 this | 276 | /// 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 knowable | 279 | /// 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) { |
| 283 | 283 |
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 free | 137 | .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 because | 140 | // 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 attempt | 142 | // 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(","); |
| 311 | 311 | ||
| 312 | // TODO: handle special identifiers | 312 | try link.printValue(inner_value, .assignment, writer); |
| 313 | } | ||
| 314 | try writer.writeAll("]"); | ||
| 315 | }, | ||
| 316 | |||
| 317 | .tuple_value => |tuple_value| { | ||
| 318 | try writer.writeAll("["); | ||
| 319 | |||
| 320 | for (tuple_value.items, 0..) |inner_value, i| { | ||
| 321 | if (i != 0) | ||
| 322 | try writer.writeAll(","); | ||
| 323 | |||
| 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 | }, |
| 285 | 285 | ||
| 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 | }, |
| 460 | 469 | ||
| 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); |
| 463 | 472 | ||
| 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); |
| 466 | 475 | ||
| 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 | } |
| 483 | 514 | ||
| ... | @@ -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 | } |
| 589 | 620 | ||
| 621 | fn 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 | |||
| 590 | fn addTemporary(c: *NodeGen) !Node.Index { | 628 | fn 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, |
| 745 | 785 | ||
| 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 { |
| 15 | 15 | ||
| 16 | pub const Mark = struct { | 16 | pub const Mark = struct { |
| 17 | stack: *Self, | 17 | stack: *Self, |
| 18 | index: usize, | 18 | index: u32, |
| 19 | 19 | ||
| 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 | } |
| 25 | 25 | ||
| 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 | } |
| 29 | 29 | ||
| 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 | } |
| 45 | 45 | ||
| ... | @@ -101,8 +101,8 @@ pub fn Stack(comptime Base: type) type { | ... | @@ -101,8 +101,8 @@ pub fn Stack(comptime Base: type) type { |
| 101 | 101 | ||
| 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, |
| 106 | 106 | ||
| 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 | } |
| 133 | 152 | ||
| ... | @@ -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 @@ |
| 1 | let assertEqual = @extern("assertEqual"); | 1 | let assertEqual = @extern("assertEqual"); |
| 2 | 2 | ||
| 3 | fn hello(x: type) -> y(x, float) { | 3 | fn hello() -> tuple (int, float) { |
| 4 | (5, 2.5) | 4 | let x: tuple (int, float) = (5, 2.5); |
| 5 | x | ||
| 5 | } | 6 | } |
| 6 | 7 | ||
| 7 | fn main { | 8 | fn 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 | } |
| 12 | 13 | ||
| 13 | fn f(a: float) -> float { a } | 14 | fn f(a: float) -> float { a } |