15 files changed, 237 insertions(+), 131 deletions(-)
CHANGELOG.md+1| ... | @@ -14,6 +14,7 @@ | ... | @@ -14,6 +14,7 @@ |
| 14 | - feat: allow instantiating `@extern` at compile time | 14 | - feat: allow instantiating `@extern` at compile time |
| 15 | - feat: implement tuples | 15 | - feat: implement tuples |
| 16 | - feat: implement multiline string literals | 16 | - feat: implement multiline string literals |
| 17 | - feat: implement inferred return type | ||
| 17 | - fix: crash when printing unused struct declaration error | 18 | - fix: crash when printing unused struct declaration error |
| 18 | - fix: provide array result type to sema | 19 | - fix: provide array result type to sema |
| 19 | - fix: crash when analyzing an empty array without a result type | 20 | - fix: crash when analyzing an empty array without a result type |
src/Cli.zig+1-3| ... | @@ -268,7 +268,6 @@ pub fn parse( | ... | @@ -268,7 +268,6 @@ pub fn parse( |
| 268 | args, | 268 | args, |
| 269 | stderr.get(), | 269 | stderr.get(), |
| 270 | stderr.tty, | 270 | stderr.tty, |
| 271 | true, | ||
| 272 | ) catch |e| switch (e) { | 271 | ) catch |e| switch (e) { |
| 273 | error.InvalidArguments => { | 272 | error.InvalidArguments => { |
| 274 | stderr.buffered_writer.flush() catch {}; | 273 | stderr.buffered_writer.flush() catch {}; |
| ... | @@ -287,9 +286,8 @@ pub fn parseWithErrorWriter( | ... | @@ -287,9 +286,8 @@ pub fn parseWithErrorWriter( |
| 287 | args: []const []const u8, | 286 | args: []const []const u8, |
| 288 | err: std.io.AnyWriter, | 287 | err: std.io.AnyWriter, |
| 289 | tty: std.io.tty.Config, | 288 | tty: std.io.tty.Config, |
| 290 | comptime exit_on_error: bool, | ||
| 291 | ) ParseError!p.Parsed() { | 289 | ) ParseError!p.Parsed() { |
| 292 | _ = exit_on_error; // autofix | 290 | @setEvalBranchQuota(std.math.maxInt(u32)); |
| 293 | var out: p.Parsed() = .{}; | 291 | var out: p.Parsed() = .{}; |
| 294 | var had_errors = false; | 292 | var had_errors = false; |
| 295 | var positionals_counted: usize = 0; | 293 | var positionals_counted: usize = 0; |
src/core/Air.zig+2-3| ... | @@ -388,9 +388,8 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In | ... | @@ -388,9 +388,8 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *const InternPool) In |
| 388 | .call => { | 388 | .call => { |
| 389 | const base_type = air.typeOf(air.instData(.call, index).base, ip); | 389 | const base_type = air.typeOf(air.instData(.call, index).base, ip); |
| 390 | if (base_type == .@"opaque") return .@"opaque"; | 390 | if (base_type == .@"opaque") return .@"opaque"; |
| 391 | return base_type.toFunctionPrototype(ip).?.return_type orelse | 391 | return base_type.toUnresolvedFunctionPrototype(ip).?.return_type orelse |
| 392 | // unreachable // return type was not resolved | 392 | unreachable; // return type was not resolved |
| 393 | @panic("TODO: resolve return type"); | ||
| 394 | }, | 393 | }, |
| 395 | 394 | ||
| 396 | // Field accesses on structs are special-cased by storing the field | 395 | // Field accesses on structs are special-cased by storing the field |
src/core/Comp.zig+37-24| ... | @@ -119,8 +119,16 @@ pub fn enqueueEntryModule(comp: *Comp, file: *File) !Decl.Index { | ... | @@ -119,8 +119,16 @@ pub fn enqueueEntryModule(comp: *Comp, file: *File) !Decl.Index { |
| 119 | 119 | ||
| 120 | const ns = try comp.indexTegModule(file, .main_module_inst); | 120 | const ns = try comp.indexTegModule(file, .main_module_inst); |
| 121 | const main_str = try comp.intern_pool.getOrPutString(comp.gpa, "main", .no_embedded_nulls); | 121 | const main_str = try comp.intern_pool.getOrPutString(comp.gpa, "main", .no_embedded_nulls); |
| 122 | const decl_index = comp.intern_pool.namespacePtr(ns).lookupDirect(comp, main_str) orelse | 122 | const decl_index = comp.intern_pool.namespacePtr(ns).lookupDirect(comp, main_str) orelse { |
| 123 | @panic("missing 'fn main' in root module"); | 123 | // TODO: add a helper function into Report to render a single error |
| 124 | var rb = Report.Builder.init(comp.gpa); | ||
| 125 | defer rb.deinit(); | ||
| 126 | try rb.err(.no_main_func, .{}); | ||
| 127 | const r: Report = try rb.finalize(); | ||
| 128 | defer r.deinit(comp.gpa); | ||
| 129 | try r.dump(.{ .comp = .{ .comp = comp, .file = file } }, comp.out); | ||
| 130 | return error.TransformFail; | ||
| 131 | }; | ||
| 124 | 132 | ||
| 125 | try comp.jobs.writeItem(.{ .analysis = .{ .au = .wrap(.{ .decl = decl_index }) } }); | 133 | try comp.jobs.writeItem(.{ .analysis = .{ .au = .wrap(.{ .decl = decl_index }) } }); |
| 126 | 134 | ||
| ... | @@ -136,7 +144,14 @@ pub fn runAllJobs(comp: *Comp) !void { | ... | @@ -136,7 +144,14 @@ pub fn runAllJobs(comp: *Comp) !void { |
| 136 | } | 144 | } |
| 137 | } | 145 | } |
| 138 | 146 | ||
| 139 | assert(comp.ongoing_analysis.size == 0); | 147 | if (core.assertFailed(comp.ongoing_analysis.size == 0)) { |
| 148 | std.debug.print("Unresumed AnalysisUnits:\n", .{}); | ||
| 149 | var it = comp.ongoing_analysis.keyIterator(); | ||
| 150 | while (it.next()) |au| { | ||
| 151 | std.debug.print("- {}\n", .{au.dump(&comp.intern_pool)}); | ||
| 152 | } | ||
| 153 | std.debug.panic("runAllJobs returned with {d} remaining ongoing_analysis", .{comp.ongoing_analysis.size}); | ||
| 154 | } | ||
| 140 | } | 155 | } |
| 141 | 156 | ||
| 142 | const AnalysisJobError = Allocator.Error || error{ AnalysisFail, TooManyValues }; | 157 | const AnalysisJobError = Allocator.Error || error{ AnalysisFail, TooManyValues }; |
| ... | @@ -166,10 +181,17 @@ fn runAnalysisJob(comp: *Comp, unit: InternPool.AnalysisUnit, first_blocked_sema | ... | @@ -166,10 +181,17 @@ fn runAnalysisJob(comp: *Comp, unit: InternPool.AnalysisUnit, first_blocked_sema |
| 166 | errdefer _ = sema.deinit(.free_all); | 181 | errdefer _ = sema.deinit(.free_all); |
| 167 | gop.value_ptr.* = sema; | 182 | gop.value_ptr.* = sema; |
| 168 | 183 | ||
| 184 | switch (unit.unwrap()) { | ||
| 185 | .decl => decl.analysis = .in_progress, | ||
| 186 | .func => |v| { | ||
| 187 | v.toKey(&comp.intern_pool).func.analysis.set(&comp.intern_pool, .in_progress); | ||
| 188 | }, | ||
| 189 | } | ||
| 190 | |||
| 169 | if (first_blocked_sema) |blocked_sema| | 191 | if (first_blocked_sema) |blocked_sema| |
| 170 | try sema.blocked_semas.append(comp.gpa, blocked_sema); | 192 | try sema.blocked_semas.append(comp.gpa, blocked_sema); |
| 171 | } else { | 193 | } else if (first_blocked_sema) |blocked_sema| { |
| 172 | assert(first_blocked_sema == null); | 194 | try gop.value_ptr.*.blocked_semas.append(comp.gpa, blocked_sema); |
| 173 | } | 195 | } |
| 174 | 196 | ||
| 175 | const sema: *Sema = gop.value_ptr.*; | 197 | const sema: *Sema = gop.value_ptr.*; |
| ... | @@ -194,6 +216,15 @@ fn runAnalysisJob(comp: *Comp, unit: InternPool.AnalysisUnit, first_blocked_sema | ... | @@ -194,6 +216,15 @@ fn runAnalysisJob(comp: *Comp, unit: InternPool.AnalysisUnit, first_blocked_sema |
| 194 | }, | 216 | }, |
| 195 | }; | 217 | }; |
| 196 | 218 | ||
| 219 | switch (unit.unwrap()) { | ||
| 220 | .decl => |index| { | ||
| 221 | comp.intern_pool.declPtr(index).analysis = .complete; | ||
| 222 | }, | ||
| 223 | .func => |v| { | ||
| 224 | v.toKey(&comp.intern_pool).func.analysis.set(&comp.intern_pool, .complete); | ||
| 225 | }, | ||
| 226 | } | ||
| 227 | |||
| 197 | comp.ongoing_analysis.removeByPtr(gop.key_ptr); | 228 | comp.ongoing_analysis.removeByPtr(gop.key_ptr); |
| 198 | try comp.jobs.ensureUnusedCapacity(sema.blocked_semas.items.len); | 229 | try comp.jobs.ensureUnusedCapacity(sema.blocked_semas.items.len); |
| 199 | for (sema.blocked_semas.items) |other_sema| { | 230 | for (sema.blocked_semas.items) |other_sema| { |
| ... | @@ -453,29 +484,13 @@ pub const Decl = struct { | ... | @@ -453,29 +484,13 @@ pub const Decl = struct { |
| 453 | /// the function type is analyzed with this set to `in_progress`; however, the semantic | 484 | /// the function type is analyzed with this set to `in_progress`; however, the semantic |
| 454 | /// analysis of the function body is performed with this value set to `success`. Functions | 485 | /// analysis of the function body is performed with this value set to `success`. Functions |
| 455 | /// have their own analysis status field. | 486 | /// have their own analysis status field. |
| 456 | analysis: enum { | 487 | analysis: InternPool.AnalysisState, |
| 457 | /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore | ||
| 458 | /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced. | ||
| 459 | unreferenced, | ||
| 460 | in_progress, | ||
| 461 | waiting, | ||
| 462 | complete, | ||
| 463 | }, | ||
| 464 | 488 | ||
| 465 | pub fn value(decl: *const Decl) InternPool.Value { | 489 | pub fn value(decl: *const Decl) InternPool.Value { |
| 466 | assert(decl.analysis == .complete); | 490 | assert(decl.analysis == .complete); |
| 467 | return decl.raw_value orelse unreachable; | 491 | return decl.raw_value orelse unreachable; |
| 468 | } | 492 | } |
| 469 | 493 | ||
| 470 | pub fn @"type"(decl: *const Decl, ip: *const InternPool) InternPool.Type { | ||
| 471 | return switch (decl.analysis) { | ||
| 472 | .unreferenced => unreachable, | ||
| 473 | .in_progress => unreachable, | ||
| 474 | .in_progress_prototype_available => decl.raw_value.?.toTypeUnsafe(ip), | ||
| 475 | .complete => decl.raw_value.?.typeOf(ip), | ||
| 476 | }; | ||
| 477 | } | ||
| 478 | |||
| 479 | pub fn fmtJson(decl: *const Decl, comp: *const Comp) JsonFormatter { | 494 | pub fn fmtJson(decl: *const Decl, comp: *const Comp) JsonFormatter { |
| 480 | return .{ .decl = decl, .comp = comp }; | 495 | return .{ .decl = decl, .comp = comp }; |
| 481 | } | 496 | } |
| ... | @@ -516,8 +531,6 @@ pub const Decl = struct { | ... | @@ -516,8 +531,6 @@ pub const Decl = struct { |
| 516 | pub const Index = InternPool.DeclIndex; | 531 | pub const Index = InternPool.DeclIndex; |
| 517 | }; | 532 | }; |
| 518 | 533 | ||
| 519 | pub const Func = struct {}; | ||
| 520 | |||
| 521 | /// A namespace is a container for declarations. Currently the only form of | 534 | /// A namespace is a container for declarations. Currently the only form of |
| 522 | /// namespace that exists is file-scope. In the future, the `module` keyword | 535 | /// namespace that exists is file-scope. In the future, the `module` keyword |
| 523 | /// will create nested namespaces, and it is probable that `struct` and other | 536 | /// will create nested namespaces, and it is probable that `struct` and other |
src/core/InternPool.zig+38-30| ... | @@ -73,6 +73,9 @@ src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index) | ... | @@ -73,6 +73,9 @@ src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index) |
| 73 | /// Dependencies on the value of a Decl. | 73 | /// Dependencies on the value of a Decl. |
| 74 | /// Value is index into `dep_entries` of the first dependency on this Decl value. | 74 | /// Value is index into `dep_entries` of the first dependency on this Decl value. |
| 75 | decl_val_deps: std.AutoArrayHashMapUnmanaged(DeclIndex, DepEntry.Index) = .{}, | 75 | decl_val_deps: std.AutoArrayHashMapUnmanaged(DeclIndex, DepEntry.Index) = .{}, |
| 76 | /// Dependencies on the value of a Decl. | ||
| 77 | /// Value is index into `dep_entries` of the first dependency on this Decl value. | ||
| 78 | resolved_func_deps: std.AutoArrayHashMapUnmanaged(Value, DepEntry.Index) = .{}, | ||
| 76 | 79 | ||
| 77 | /// Given a `Depender`, points to an entry in `dep_entries` whose `depender` | 80 | /// Given a `Depender`, points to an entry in `dep_entries` whose `depender` |
| 78 | /// matches. The `next_dependee` field can be used to iterate all such entries | 81 | /// matches. The `next_dependee` field can be used to iterate all such entries |
| ... | @@ -176,6 +179,8 @@ pub const Tag = enum(u8) { | ... | @@ -176,6 +179,8 @@ pub const Tag = enum(u8) { |
| 176 | owner_decl: DeclIndex, | 179 | owner_decl: DeclIndex, |
| 177 | ty: Type, | 180 | ty: Type, |
| 178 | teg_body_inst: TrackedInst.Index, | 181 | teg_body_inst: TrackedInst.Index, |
| 182 | |||
| 183 | pub const ty_index = 2; | ||
| 179 | }; | 184 | }; |
| 180 | 185 | ||
| 181 | /// Trailing: | 186 | /// Trailing: |
| ... | @@ -458,7 +463,7 @@ pub const Type = enum(u32) { | ... | @@ -458,7 +463,7 @@ pub const Type = enum(u32) { |
| 458 | return @enumFromInt(@intFromEnum(ty.value().valueTag(ip))); | 463 | return @enumFromInt(@intFromEnum(ty.value().valueTag(ip))); |
| 459 | } | 464 | } |
| 460 | 465 | ||
| 461 | pub fn toFunctionPrototype(ty: Type, ip: *const InternPool) ?Key.FunctionType { | 466 | pub fn toUnresolvedFunctionPrototype(ty: Type, ip: *const InternPool) ?Key.FunctionType { |
| 462 | if (ty.tag(ip) != .function_type) return null; | 467 | if (ty.tag(ip) != .function_type) return null; |
| 463 | return ip.valueToKey(ty.value()).function_type; | 468 | return ip.valueToKey(ty.value()).function_type; |
| 464 | } | 469 | } |
| ... | @@ -571,7 +576,7 @@ pub const Type = enum(u32) { | ... | @@ -571,7 +576,7 @@ pub const Type = enum(u32) { |
| 571 | switch (ty.tag(ip)) { | 576 | switch (ty.tag(ip)) { |
| 572 | .simple_type => unreachable, // all simple types are already printed | 577 | .simple_type => unreachable, // all simple types are already printed |
| 573 | .function_type => { | 578 | .function_type => { |
| 574 | const function_type = ty.toFunctionPrototype(ip).?; | 579 | const function_type = ty.toUnresolvedFunctionPrototype(ip).?; |
| 575 | try writer.writeAll("fn("); | 580 | try writer.writeAll("fn("); |
| 576 | for (function_type.param_types, 0..) |param, i| { | 581 | for (function_type.param_types, 0..) |param, i| { |
| 577 | if (i != 0) try writer.writeAll(", "); | 582 | if (i != 0) try writer.writeAll(", "); |
| ... | @@ -672,30 +677,8 @@ pub const static_keys = [_]Key{ | ... | @@ -672,30 +677,8 @@ pub const static_keys = [_]Key{ |
| 672 | /// State that is mutable during semantic analysis. This data is not used for | 677 | /// State that is mutable during semantic analysis. This data is not used for |
| 673 | /// equality or hashing. | 678 | /// equality or hashing. |
| 674 | pub const FuncAnalysis = packed struct(u32) { | 679 | pub const FuncAnalysis = packed struct(u32) { |
| 675 | state: State, | 680 | state: AnalysisState, |
| 676 | 681 | _: u30 = 0, | |
| 677 | pub const State = enum(u32) { | ||
| 678 | none, | ||
| 679 | // /// This function has not yet undergone analysis, because we have not | ||
| 680 | // /// seen a potential runtime call. It may be analyzed in future. none, | ||
| 681 | // /// Analysis for this function has been queued, but not yet completed. | ||
| 682 | // queued, | ||
| 683 | // /// This function intentionally only has ZIR generated because it is marked | ||
| 684 | // /// inline, which means no runtime version of the function will be generated. | ||
| 685 | // inline_only, | ||
| 686 | // in_progress, | ||
| 687 | // /// There will be a corresponding ErrorMsg in Module.failed_decls | ||
| 688 | // sema_failure, | ||
| 689 | // /// This function might be OK but it depends on another Decl which did not | ||
| 690 | // /// successfully complete semantic analysis. | ||
| 691 | // dependency_failure, | ||
| 692 | // /// There will be a corresponding ErrorMsg in Module.failed_decls. | ||
| 693 | // /// Indicates that semantic analysis succeeded, but code generation for | ||
| 694 | // /// this function failed. | ||
| 695 | // codegen_failure, | ||
| 696 | // /// Semantic analysis and code generation of this function succeeded. | ||
| 697 | // success, | ||
| 698 | }; | ||
| 699 | }; | 682 | }; |
| 700 | 683 | ||
| 701 | /// An index into `string_bytes`. | 684 | /// An index into `string_bytes`. |
| ... | @@ -977,6 +960,11 @@ pub const Key = union(enum) { | ... | @@ -977,6 +960,11 @@ pub const Key = union(enum) { |
| 977 | /// the resolved return type. | 960 | /// the resolved return type. |
| 978 | return_type: ?Type, | 961 | return_type: ?Type, |
| 979 | 962 | ||
| 963 | pub const Resolved = struct { | ||
| 964 | param_types: []Type, | ||
| 965 | return_type: Type, | ||
| 966 | }; | ||
| 967 | |||
| 980 | pub fn hash(self: FunctionType, hasher: *Hash) void { | 968 | pub fn hash(self: FunctionType, hasher: *Hash) void { |
| 981 | const t = tracer.trace(@src()); | 969 | const t = tracer.trace(@src()); |
| 982 | defer t.end(); | 970 | defer t.end(); |
| ... | @@ -1066,12 +1054,19 @@ pub const Key = union(enum) { | ... | @@ -1066,12 +1054,19 @@ pub const Key = union(enum) { |
| 1066 | ty: Type, | 1054 | ty: Type, |
| 1067 | owner_decl: DeclIndex, | 1055 | owner_decl: DeclIndex, |
| 1068 | teg_body_inst: TrackedInst.Index, | 1056 | teg_body_inst: TrackedInst.Index, |
| 1057 | analysis: ExtraIndex(AnalysisState), | ||
| 1069 | 1058 | ||
| 1070 | pub fn hash(self: Func, hasher: *Hash) void { | 1059 | pub fn hash(self: Func, hasher: *Hash) void { |
| 1071 | std.hash.autoHash(hasher, self.ty); | 1060 | std.hash.autoHash(hasher, self.ty); |
| 1072 | std.hash.autoHash(hasher, self.owner_decl); | 1061 | std.hash.autoHash(hasher, self.owner_decl); |
| 1073 | std.hash.autoHash(hasher, self.teg_body_inst); | 1062 | std.hash.autoHash(hasher, self.teg_body_inst); |
| 1074 | } | 1063 | } |
| 1064 | |||
| 1065 | /// This should only be used to convert an inferred return type into a reified return type. | ||
| 1066 | pub fn updateType(self: Func, ip: *InternPool, new_type: Type) void { | ||
| 1067 | assert(new_type.tag(ip) == .function_type); | ||
| 1068 | ip.extra.set(Type, @intFromEnum(self.analysis) + Tag.FuncDecl.ty_index, new_type); | ||
| 1069 | } | ||
| 1075 | }; | 1070 | }; |
| 1076 | 1071 | ||
| 1077 | pub fn hash32(key: Key, ip: *const InternPool) u32 { | 1072 | pub fn hash32(key: Key, ip: *const InternPool) u32 { |
| ... | @@ -1260,9 +1255,21 @@ pub const AnalysisUnit = packed struct(u32) { | ... | @@ -1260,9 +1255,21 @@ pub const AnalysisUnit = packed struct(u32) { |
| 1260 | } | 1255 | } |
| 1261 | }; | 1256 | }; |
| 1262 | 1257 | ||
| 1258 | /// Every AU has a state. | ||
| 1259 | /// For Decls, state is stored on the decl | ||
| 1260 | /// For Functions, state is stored on the Key.Func | ||
| 1261 | pub const AnalysisState = enum(u2) { | ||
| 1262 | /// This Unit corresponds to an AST Node that has not been referenced yet, and therefore | ||
| 1263 | /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced. | ||
| 1264 | unreferenced, | ||
| 1265 | in_progress, | ||
| 1266 | complete, | ||
| 1267 | }; | ||
| 1268 | |||
| 1263 | pub const Dependee = union(enum) { | 1269 | pub const Dependee = union(enum) { |
| 1264 | src_hash: TrackedInst.Index, | 1270 | src_hash: TrackedInst.Index, |
| 1265 | decl_val: DeclIndex, | 1271 | decl_val: DeclIndex, |
| 1272 | resolved_func: Value, | ||
| 1266 | }; | 1273 | }; |
| 1267 | 1274 | ||
| 1268 | pub const DepEntry = extern struct { | 1275 | pub const DepEntry = extern struct { |
| ... | @@ -1393,11 +1400,13 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { | ... | @@ -1393,11 +1400,13 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { |
| 1393 | }, | 1400 | }, |
| 1394 | .string => .{ .string = data.decode(.string, item.tag).get(ip).toSlice(ip) }, | 1401 | .string => .{ .string = data.decode(.string, item.tag).get(ip).toSlice(ip) }, |
| 1395 | .func => { | 1402 | .func => { |
| 1396 | const func = data.decode(.func, item.tag).get(ip); | 1403 | const base_index = data.decode(.func, item.tag); |
| 1404 | const func = base_index.get(ip); | ||
| 1397 | return .{ .func = .{ | 1405 | return .{ .func = .{ |
| 1398 | .ty = func.ty, | 1406 | .ty = func.ty, |
| 1399 | .owner_decl = func.owner_decl, | 1407 | .owner_decl = func.owner_decl, |
| 1400 | .teg_body_inst = func.teg_body_inst, | 1408 | .teg_body_inst = func.teg_body_inst, |
| 1409 | .analysis = @enumFromInt(@intFromEnum(base_index)), | ||
| 1401 | } }; | 1410 | } }; |
| 1402 | }, | 1411 | }, |
| 1403 | .function_type => .{ | 1412 | .function_type => .{ |
| ... | @@ -1497,9 +1506,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { | ... | @@ -1497,9 +1506,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { |
| 1497 | ), | 1506 | ), |
| 1498 | .func => |f| brk: { | 1507 | .func => |f| brk: { |
| 1499 | const extra = try ip.extra.append(gpa, Tag.FuncDecl, .{ | 1508 | const extra = try ip.extra.append(gpa, Tag.FuncDecl, .{ |
| 1500 | .analysis = .{ | 1509 | .analysis = .{ .state = .unreferenced }, |
| 1501 | .state = .none, | ||
| 1502 | }, | ||
| 1503 | .owner_decl = f.owner_decl, | 1510 | .owner_decl = f.owner_decl, |
| 1504 | .ty = f.ty, | 1511 | .ty = f.ty, |
| 1505 | .teg_body_inst = f.teg_body_inst, | 1512 | .teg_body_inst = f.teg_body_inst, |
| ... | @@ -1687,6 +1694,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalysisUnit, de | ... | @@ -1687,6 +1694,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalysisUnit, de |
| 1687 | const gop = try switch (tag) { | 1694 | const gop = try switch (tag) { |
| 1688 | .src_hash => ip.src_hash_deps, | 1695 | .src_hash => ip.src_hash_deps, |
| 1689 | .decl_val => ip.decl_val_deps, | 1696 | .decl_val => ip.decl_val_deps, |
| 1697 | .resolved_func => ip.resolved_func_deps, | ||
| 1690 | }.getOrPut(gpa, dependee_payload); | 1698 | }.getOrPut(gpa, dependee_payload); |
| 1691 | 1699 | ||
| 1692 | if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) { | 1700 | if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) { |
src/core/Report.zig+13-3| ... | @@ -56,14 +56,20 @@ pub const Err = union(enum) { | ... | @@ -56,14 +56,20 @@ pub const Err = union(enum) { |
| 56 | src: LazyNode, | 56 | src: LazyNode, |
| 57 | 57 | ||
| 58 | fn build(data: @This(), ctx: *MsgContext) !void { | 58 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 59 | try ctx.msgNode(try data.src.get(ctx), "Value must be known at compile-time", .{}); | 59 | try ctx.msgNode(try data.src.get(ctx), "value must be known at compile-time", .{}); |
| 60 | } | 60 | } |
| 61 | }, | 61 | }, |
| 62 | cannot_be_comptime: struct { | 62 | cannot_be_comptime: struct { |
| 63 | src: LazyNode, | 63 | src: LazyNode, |
| 64 | 64 | ||
| 65 | fn build(data: @This(), ctx: *MsgContext) !void { | 65 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 66 | try ctx.msgNode(try data.src.get(ctx), "Operation cannot be performed at compile-time (TODO: make this error more specific)", .{}); | 66 | try ctx.msgNode(try data.src.get(ctx), "operation cannot be performed at compile-time (TODO: make this error more specific)", .{}); |
| 67 | } | ||
| 68 | }, | ||
| 69 | no_main_func: struct { | ||
| 70 | fn build(_: @This(), ctx: *MsgContext) !void { | ||
| 71 | // TODO: variant that includes file, but no location | ||
| 72 | try ctx.msgToken(0, "missing top-level function \"main\"", .{}); | ||
| 67 | } | 73 | } |
| 68 | }, | 74 | }, |
| 69 | unknown_identifier: struct { | 75 | unknown_identifier: struct { |
| ... | @@ -154,7 +160,7 @@ pub const Err = union(enum) { | ... | @@ -154,7 +160,7 @@ pub const Err = union(enum) { |
| 154 | fn build(data: @This(), ctx: *MsgContext) !void { | 160 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 155 | try ctx.msgNode( | 161 | try ctx.msgNode( |
| 156 | data.node, | 162 | data.node, |
| 157 | "Cannot infer struct initialization type", | 163 | "cannot infer struct initialization type", |
| 158 | .{}, | 164 | .{}, |
| 159 | ); | 165 | ); |
| 160 | } | 166 | } |
| ... | @@ -1055,6 +1061,10 @@ pub const MsgContext = struct { | ... | @@ -1055,6 +1061,10 @@ pub const MsgContext = struct { |
| 1055 | components: ArrayListUnmanaged(Component), | 1061 | components: ArrayListUnmanaged(Component), |
| 1056 | 1062 | ||
| 1057 | pub fn msg(m: *MsgContext, highlight: Highlight, comptime fmt: []const u8, args: anytype) !void { | 1063 | pub fn msg(m: *MsgContext, highlight: Highlight, comptime fmt: []const u8, args: anytype) !void { |
| 1064 | comptime if (!std.mem.startsWith(u8, fmt, "TODO")) switch (fmt[0]) { | ||
| 1065 | 'A'...'Z' => @compileError("template invalid: " ++ fmt), // style guide: lowercase | ||
| 1066 | else => {}, | ||
| 1067 | }; | ||
| 1058 | var arr: std.ArrayListUnmanaged(u8) = .{}; | 1068 | var arr: std.ArrayListUnmanaged(u8) = .{}; |
| 1059 | try std.fmt.format(arr.writer(m.arena), fmt, args); | 1069 | try std.fmt.format(arr.writer(m.arena), fmt, args); |
| 1060 | try m.components.append(m.arena, .{ | 1070 | try m.components.append(m.arena, .{ |
src/core/Sema.zig+89-27| ... | @@ -419,22 +419,36 @@ pub fn run(s: *Sema) Error!void { | ... | @@ -419,22 +419,36 @@ pub fn run(s: *Sema) Error!void { |
| 419 | switch (s.owner.unwrap()) { | 419 | switch (s.owner.unwrap()) { |
| 420 | .decl => |decl_index| { | 420 | .decl => |decl_index| { |
| 421 | const decl = s.ip.declPtr(decl_index); | 421 | const decl = s.ip.declPtr(decl_index); |
| 422 | decl.raw_value = result; | 422 | decl.raw_value = result.value() orelse unreachable; |
| 423 | decl.analysis = .complete; | 423 | decl.analysis = .complete; |
| 424 | }, | 424 | }, |
| 425 | .func => |value| { | 425 | .func => |value| { |
| 426 | const type_of_result = s.typeOfRef(result); | ||
| 426 | const air = try s.func.?.finalize(s.gpa); | 427 | const air = try s.func.?.finalize(s.gpa); |
| 427 | try s.comp.jobs.writeItem(.{ .codegen_func = .{ | 428 | try s.comp.jobs.writeItem(.{ .codegen_func = .{ |
| 428 | .value = value, | 429 | .value = value, |
| 429 | .air = air, | 430 | .air = air, |
| 430 | } }); | 431 | } }); |
| 432 | |||
| 433 | // If needed, update the function with its resolved return type | ||
| 434 | const ty = value.typeOf(s.ip); | ||
| 435 | const proto = ty.toUnresolvedFunctionPrototype(s.ip) orelse | ||
| 436 | unreachable; // must be a function type | ||
| 437 | |||
| 438 | if (proto.return_type == null) { | ||
| 439 | const new_type = try s.makeType(.{ .function_type = .{ | ||
| 440 | .param_types = proto.param_types, | ||
| 441 | .return_type = type_of_result, | ||
| 442 | } }); | ||
| 443 | value.toKey(s.ip).func.updateType(s.ip, new_type); | ||
| 444 | } | ||
| 431 | }, | 445 | }, |
| 432 | } | 446 | } |
| 433 | return; | 447 | return; |
| 434 | }; | 448 | }; |
| 435 | } | 449 | } |
| 436 | 450 | ||
| 437 | pub fn step(s: *Sema) Error!?Value { | 451 | pub fn step(s: *Sema) Error!?Air.Inst.Ref { |
| 438 | s.instruction_step_lock.lock(); // Semantic analysis must not be recursive | 452 | s.instruction_step_lock.lock(); // Semantic analysis must not be recursive |
| 439 | defer s.instruction_step_lock.unlock(); | 453 | defer s.instruction_step_lock.unlock(); |
| 440 | 454 | ||
| ... | @@ -463,10 +477,7 @@ pub fn step(s: *Sema) Error!?Value { | ... | @@ -463,10 +477,7 @@ pub fn step(s: *Sema) Error!?Value { |
| 463 | // the instruction pointer has been moved already | 477 | // the instruction pointer has been moved already |
| 464 | assert(s.frame.currentIndex() != start_index); // control flow loop | 478 | assert(s.frame.currentIndex() != start_index); // control flow loop |
| 465 | }, | 479 | }, |
| 466 | .stop => return switch (s.owner.kind) { | 480 | .stop => return s.inst_map.get(s.frame.block_index), |
| 467 | .decl => s.inst_map.get(s.frame.block_index).decode().value, | ||
| 468 | .func => .void, | ||
| 469 | }, | ||
| 470 | }, | 481 | }, |
| 471 | } | 482 | } |
| 472 | }, | 483 | }, |
| ... | @@ -527,7 +538,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) | ... | @@ -527,7 +538,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 527 | const param: Teg.DataNode = s.frame.currentData(.param); | 538 | const param: Teg.DataNode = s.frame.currentData(.param); |
| 528 | assert(s.frame.mode == .run_time); // never emitted in a comptime block | 539 | assert(s.frame.mode == .run_time); // never emitted in a comptime block |
| 529 | assert(s.owner.kind == .func); // only emitted in function body | 540 | assert(s.owner.kind == .func); // only emitted in function body |
| 530 | const param_types = s.owner.unwrap().func.toKey(s.ip).func.ty.toFunctionPrototype(s.ip).?.param_types; | 541 | const param_types = s.owner.unwrap().func.toKey(s.ip).func.ty.toUnresolvedFunctionPrototype(s.ip).?.param_types; |
| 531 | return s.addRef(.arg, .{ | 542 | return s.addRef(.arg, .{ |
| 532 | .type = param_types[param.data], | 543 | .type = param_types[param.data], |
| 533 | .arg_number = param.data, | 544 | .arg_number = param.data, |
| ... | @@ -724,7 +735,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) | ... | @@ -724,7 +735,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 724 | const scratch = try s.arena().alloc(Air.Inst.Ref, call.args.len); | 735 | const scratch = try s.arena().alloc(Air.Inst.Ref, call.args.len); |
| 725 | defer s.arena().free(scratch); | 736 | defer s.arena().free(scratch); |
| 726 | if (base_type != .@"opaque") { | 737 | if (base_type != .@"opaque") { |
| 727 | const proto = base_type.toFunctionPrototype(s.ip) orelse { | 738 | const proto = try s.resolveFunctionPrototype(base, base_type) orelse { |
| 728 | return s.fail(.call_non_function, .{ | 739 | return s.fail(.call_non_function, .{ |
| 729 | .call = s.lazyNode(.operand), | 740 | .call = s.lazyNode(.operand), |
| 730 | .ty = base_type, | 741 | .ty = base_type, |
| ... | @@ -981,21 +992,29 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) | ... | @@ -981,21 +992,29 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 981 | const mark = core.U32Stack.Mark{ .stack = &s.scratch, .index = s.frame.mark_index }; | 992 | const mark = core.U32Stack.Mark{ .stack = &s.scratch, .index = s.frame.mark_index }; |
| 982 | 993 | ||
| 983 | if (s.frame_stack.items.len == 0) { | 994 | if (s.frame_stack.items.len == 0) { |
| 984 | s.inst_map.add(s.frame.block_index, operand); | ||
| 985 | |||
| 986 | if (s.func) |*func| { | 995 | if (s.func) |*func| { |
| 996 | const proto = s.owner.unwrap().func.typeOf(s.ip).toUnresolvedFunctionPrototype(s.ip) orelse | ||
| 997 | unreachable; // must be a function | ||
| 998 | const coerced = if (proto.return_type) |return_coercion| | ||
| 999 | try s.coerceRef(s.lazyNode(.operand), operand, return_coercion) | ||
| 1000 | else | ||
| 1001 | operand; | ||
| 1002 | s.inst_map.add(s.frame.block_index, coerced); | ||
| 1003 | |||
| 987 | const indexes = mark.slice(u32); | 1004 | const indexes = mark.slice(u32); |
| 988 | try func.extra.list.ensureUnusedCapacity(s.gpa, indexes.len + 1); | 1005 | try func.extra.list.ensureUnusedCapacity(s.gpa, indexes.len + 1); |
| 989 | const start = func.extra.list.items.len; | 1006 | const start = func.extra.list.items.len; |
| 990 | func.extra.list.appendSliceAssumeCapacity(indexes); | 1007 | func.extra.list.appendSliceAssumeCapacity(indexes); |
| 991 | func.extra.list.appendAssumeCapacity(@intFromEnum(try s.addIndexUntracked(.@"break", .{ | 1008 | func.extra.list.appendAssumeCapacity(@intFromEnum(try s.addIndexUntracked(.@"break", .{ |
| 992 | .block = .main_block, | 1009 | .block = .main_block, |
| 993 | .value = operand, | 1010 | .value = coerced, |
| 994 | }))); | 1011 | }))); |
| 995 | func.instructions.items(.data)[0] = .encode(.block, .{ | 1012 | func.instructions.items(.data)[0] = .encode(.block, .{ |
| 996 | .start = @intCast(start), | 1013 | .start = @intCast(start), |
| 997 | .len = @intCast(indexes.len + 1), | 1014 | .len = @intCast(indexes.len + 1), |
| 998 | }); | 1015 | }); |
| 1016 | } else { | ||
| 1017 | s.inst_map.add(s.frame.block_index, operand); | ||
| 999 | } | 1018 | } |
| 1000 | 1019 | ||
| 1001 | return .stop; | 1020 | return .stop; |
| ... | @@ -1420,7 +1439,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) | ... | @@ -1420,7 +1439,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 1420 | const operand = s.getRef(result.call_base, .no_mark); | 1439 | const operand = s.getRef(result.call_base, .no_mark); |
| 1421 | const ty = s.typeOfRef(operand); | 1440 | const ty = s.typeOfRef(operand); |
| 1422 | 1441 | ||
| 1423 | const func: InternPool.Key.FunctionType = ty.toFunctionPrototype(s.ip) orelse | 1442 | const func: InternPool.Key.FunctionType = ty.toUnresolvedFunctionPrototype(s.ip) orelse |
| 1424 | return Value.opaque_type.toAir(); | 1443 | return Value.opaque_type.toAir(); |
| 1425 | 1444 | ||
| 1426 | if (result.argument >= func.param_types.len) | 1445 | if (result.argument >= func.param_types.len) |
| ... | @@ -1837,11 +1856,18 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst | ... | @@ -1837,11 +1856,18 @@ fn ensureValueRuntime(s: *Sema, src: LazyNode, ref: Air.Inst.Ref) Error!Air.Inst |
| 1837 | .type = ty, | 1856 | .type = ty, |
| 1838 | }), | 1857 | }), |
| 1839 | .@"opaque" => @panic("TODO: represent comptime-known opaques better"), | 1858 | .@"opaque" => @panic("TODO: represent comptime-known opaques better"), |
| 1840 | _ => if (!ty.hasPossibleRepresentation(s.ip)) { | 1859 | _ => { |
| 1841 | return s.fail(.runtime_incompatible_type, .{ | 1860 | if (!ty.hasPossibleRepresentation(s.ip)) { |
| 1842 | .src = src, | 1861 | return s.fail(.runtime_incompatible_type, .{ |
| 1843 | .type = ty, | 1862 | .src = src, |
| 1844 | }); | 1863 | .type = ty, |
| 1864 | }); | ||
| 1865 | } | ||
| 1866 | |||
| 1867 | // When a function is used as a runtime operand, it needs to | ||
| 1868 | // have a resolved return type, so that calling it later is | ||
| 1869 | // able to know what it returns. | ||
| 1870 | _ = try s.resolveFunctionPrototype(ref, ty); | ||
| 1845 | }, | 1871 | }, |
| 1846 | } | 1872 | } |
| 1847 | }, | 1873 | }, |
| ... | @@ -1959,23 +1985,24 @@ fn lookupDecl(s: *Sema, name: NullTerminatedString) InternPool.DeclIndex { | ... | @@ -1959,23 +1985,24 @@ fn lookupDecl(s: *Sema, name: NullTerminatedString) InternPool.DeclIndex { |
| 1959 | } | 1985 | } |
| 1960 | } | 1986 | } |
| 1961 | 1987 | ||
| 1962 | fn getDecl(sema: *Sema, decl_index: Decl.Index) !Value { | 1988 | fn getDecl(s: *Sema, decl_index: Decl.Index) !Value { |
| 1963 | if (sema.owner.kind == .decl) { | 1989 | if (s.owner.kind == .decl) { |
| 1964 | if (decl_index == sema.owner.unwrap().decl) { | 1990 | if (decl_index == s.owner.unwrap().decl) { |
| 1965 | return sema.ip.declPtr(decl_index).value(); | 1991 | return s.ip.declPtr(decl_index).value(); |
| 1966 | } | 1992 | } |
| 1967 | } | 1993 | } |
| 1968 | const decl_data = sema.ip.declPtr(decl_index); | 1994 | const decl_data = s.ip.declPtr(decl_index); |
| 1969 | try sema.declareDependency(.{ .decl_val = decl_index }); | 1995 | try s.declareDependency(.{ .decl_val = decl_index }); |
| 1970 | const au: AnalysisUnit = .wrap(.{ .decl = decl_index }); | 1996 | const au: AnalysisUnit = .wrap(.{ .decl = decl_index }); |
| 1971 | switch (decl_data.analysis) { | 1997 | switch (decl_data.analysis) { |
| 1972 | .unreferenced => { | 1998 | .unreferenced => { |
| 1973 | try sema.comp.jobs.writeItem(.{ .analysis = .{ .au = au, .first_blocked_sema = sema } }); | 1999 | try s.comp.jobs.writeItem(.{ .analysis = .{ .au = au, .first_blocked_sema = s } }); |
| 1974 | return error.Interrupt; | 2000 | return error.Interrupt; |
| 1975 | }, | 2001 | }, |
| 1976 | .waiting, .in_progress => { | 2002 | .in_progress => { |
| 1977 | const other_sema = sema.comp.ongoing_analysis.get(au) orelse unreachable; | 2003 | const other_sema = s.comp.ongoing_analysis.get(au) orelse |
| 1978 | try other_sema.blocked_semas.append(sema.gpa, sema); | 2004 | unreachable; // analysis stopped but did not update state |
| 2005 | try other_sema.blocked_semas.append(s.gpa, s); | ||
| 1979 | return error.Interrupt; | 2006 | return error.Interrupt; |
| 1980 | }, | 2007 | }, |
| 1981 | .complete => { | 2008 | .complete => { |
| ... | @@ -1984,6 +2011,39 @@ fn getDecl(sema: *Sema, decl_index: Decl.Index) !Value { | ... | @@ -1984,6 +2011,39 @@ fn getDecl(sema: *Sema, decl_index: Decl.Index) !Value { |
| 1984 | } | 2011 | } |
| 1985 | } | 2012 | } |
| 1986 | 2013 | ||
| 2014 | /// Interrupts if there is an unresolved inferred return type. | ||
| 2015 | fn resolveFunctionPrototype(s: *Sema, ref: Air.Inst.Ref, ty: Type) !?InternPool.Key.FunctionType.Resolved { | ||
| 2016 | const proto = ty.toUnresolvedFunctionPrototype(s.ip) orelse | ||
| 2017 | return null; // not a function prototype | ||
| 2018 | |||
| 2019 | if (proto.return_type) |return_type| | ||
| 2020 | return .{ | ||
| 2021 | .param_types = proto.param_types, | ||
| 2022 | .return_type = return_type, | ||
| 2023 | }; | ||
| 2024 | |||
| 2025 | const value = ref.value() orelse | ||
| 2026 | unreachable; // cast functions cannot have inferred return types. | ||
| 2027 | |||
| 2028 | try s.declareDependency(.{ .resolved_func = value }); | ||
| 2029 | |||
| 2030 | const func = value.toKey(s.ip).func; | ||
| 2031 | const au: AnalysisUnit = .wrap(.{ .func = value }); | ||
| 2032 | switch (func.analysis.get(s.ip)) { | ||
| 2033 | .unreferenced => { | ||
| 2034 | try s.comp.jobs.writeItem(.{ .analysis = .{ .au = au, .first_blocked_sema = s } }); | ||
| 2035 | return error.Interrupt; | ||
| 2036 | }, | ||
| 2037 | .in_progress => { | ||
| 2038 | const other_sema = s.comp.ongoing_analysis.get(au) orelse | ||
| 2039 | unreachable; // analysis stopped but did not update state | ||
| 2040 | try other_sema.blocked_semas.append(s.gpa, s); | ||
| 2041 | return error.Interrupt; | ||
| 2042 | }, | ||
| 2043 | .complete => unreachable, // analyzed but did not set return type | ||
| 2044 | } | ||
| 2045 | } | ||
| 2046 | |||
| 1987 | fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { | 2047 | fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { |
| 1988 | try sema.ip.addDependency(sema.gpa, sema.owner, dependee); | 2048 | try sema.ip.addDependency(sema.gpa, sema.owner, dependee); |
| 1989 | } | 2049 | } |
| ... | @@ -2122,11 +2182,13 @@ fn postVisitFunctionPrototype(s: *Sema, reserve: FunctionReserve, return_type: ? | ... | @@ -2122,11 +2182,13 @@ fn postVisitFunctionPrototype(s: *Sema, reserve: FunctionReserve, return_type: ? |
| 2122 | .return_type = return_type, | 2182 | .return_type = return_type, |
| 2123 | }, | 2183 | }, |
| 2124 | }); | 2184 | }); |
| 2185 | // TODO: make a dedicated function to insert a new function | ||
| 2125 | const func_value = try s.makeValue(.{ | 2186 | const func_value = try s.makeValue(.{ |
| 2126 | .func = .{ | 2187 | .func = .{ |
| 2127 | .ty = func_type, | 2188 | .ty = func_type, |
| 2128 | .owner_decl = s.techDebtDeclIndex(), // TODO: ooh, this is a fun one! | 2189 | .owner_decl = s.techDebtDeclIndex(), // TODO: ooh, this is a fun one! |
| 2129 | .teg_body_inst = tracked_inst, | 2190 | .teg_body_inst = tracked_inst, |
| 2191 | .analysis = undefined, // not used when appending | ||
| 2130 | }, | 2192 | }, |
| 2131 | }); | 2193 | }); |
| 2132 | 2194 |
src/core/array.zig+36-33| ... | @@ -2,40 +2,40 @@ const extra = @This(); | ... | @@ -2,40 +2,40 @@ const extra = @This(); |
| 2 | 2 | ||
| 3 | pub const Mutability = enum { @"const", @"var" }; | 3 | pub const Mutability = enum { @"const", @"var" }; |
| 4 | 4 | ||
| 5 | fn setAsU32(items: []u32, comptime T: type, index: usize, payload: T) void { | 5 | fn setAsU32(items: []u32, comptime T: type, i: usize, payload: T) void { |
| 6 | const fields = std.meta.fields(@TypeOf(payload)); | 6 | switch (T) { |
| 7 | var i = index; | 7 | u32 => items[i] = payload, |
| 8 | inline for (fields) |field| { | 8 | i32 => items[i] = @bitCast(payload), |
| 9 | const value = @field(payload, field.name); | 9 | |
| 10 | switch (field.type) { | 10 | [16]u8 => { |
| 11 | u32 => items[i] = value, | 11 | const u32s: [4]u32 = @bitCast(payload); |
| 12 | i32 => items[i] = @bitCast(value), | 12 | items[i] = u32s[0]; |
| 13 | 13 | items[i + 1] = u32s[1]; | |
| 14 | [16]u8 => { | 14 | items[i + 2] = u32s[2]; |
| 15 | const u32s: [4]u32 = @bitCast(value); | 15 | items[i + 3] = u32s[3]; |
| 16 | items[i] = u32s[0]; | 16 | }, |
| 17 | items[i + 1] = u32s[1]; | ||
| 18 | items[i + 2] = u32s[2]; | ||
| 19 | items[i + 3] = u32s[3]; | ||
| 20 | }, | ||
| 21 | 17 | ||
| 22 | else => brk: { | 18 | else => brk: { |
| 23 | const info = @typeInfo(field.type); | 19 | const info = @typeInfo(T); |
| 24 | if (info == .@"enum") { | 20 | if (info == .@"enum") { |
| 25 | items[i] = @intFromEnum(value); | 21 | items[i] = @intFromEnum(payload); |
| 26 | break :brk; | 22 | break :brk; |
| 27 | } | 23 | } |
| 28 | 24 | ||
| 29 | const struct_data = info.@"struct"; | 25 | const struct_data = info.@"struct"; |
| 30 | if (struct_data.layout == .@"packed") { | 26 | if (struct_data.layout == .@"packed") { |
| 31 | items[i] = @bitCast(value); | 27 | items[i] = @bitCast(payload); |
| 32 | break :brk; | 28 | break :brk; |
| 33 | } | 29 | } |
| 34 | 30 | ||
| 35 | @call(.always_inline, setAsU32, .{ items, field.type, i, value }); | 31 | const fields = std.meta.fields(@TypeOf(payload)); |
| 36 | }, | 32 | comptime var idx: usize = 0; |
| 37 | } | 33 | inline for (fields) |field| { |
| 38 | i += extraSizeOf(field.type); | 34 | const value = @field(payload, field.name); |
| 35 | @call(.always_inline, setAsU32, .{ items, field.type, i + idx, value }); | ||
| 36 | idx += comptime extraSizeOf(field.type); | ||
| 37 | } | ||
| 38 | }, | ||
| 39 | } | 39 | } |
| 40 | } | 40 | } |
| 41 | 41 | ||
| ... | @@ -116,11 +116,14 @@ fn ExtraWrap(comptime kind: StoreKind) type { | ... | @@ -116,11 +116,14 @@ fn ExtraWrap(comptime kind: StoreKind) type { |
| 116 | return enum(u32) { | 116 | return enum(u32) { |
| 117 | _, | 117 | _, |
| 118 | 118 | ||
| 119 | // Reads the data from `store.extra` and returns it. | ||
| 120 | pub fn get(index: @This(), owner: anytype) T { | 119 | pub fn get(index: @This(), owner: anytype) T { |
| 121 | return kind.resolveStoreConst(owner).get(T, @intFromEnum(index)); | 120 | return kind.resolveStoreConst(owner).get(T, @intFromEnum(index)); |
| 122 | } | 121 | } |
| 123 | 122 | ||
| 123 | pub fn set(index: @This(), owner: anytype, payload: T) void { | ||
| 124 | return kind.resolveStoreMut(owner).set(T, @intFromEnum(index), payload); | ||
| 125 | } | ||
| 126 | |||
| 124 | /// Returns data with a trailing reader to read data stored immediately after | 127 | /// Returns data with a trailing reader to read data stored immediately after |
| 125 | /// the main data structure. What is stored should be documented on `T` | 128 | /// the main data structure. What is stored should be documented on `T` |
| 126 | pub fn getTrail(index: @This(), owner: anytype) struct { | 129 | pub fn getTrail(index: @This(), owner: anytype) struct { |
| ... | @@ -196,7 +199,7 @@ pub fn EncodedData(comptime Tag: type, backing_type: type) type { | ... | @@ -196,7 +199,7 @@ pub fn EncodedData(comptime Tag: type, backing_type: type) type { |
| 196 | } | 199 | } |
| 197 | 200 | ||
| 198 | /// The number of 'u32's this type consumes in `ip.extra`. | 201 | /// The number of 'u32's this type consumes in `ip.extra`. |
| 199 | pub fn extraSizeOf(comptime T: type) usize { | 202 | pub inline fn extraSizeOf(comptime T: type) usize { |
| 200 | return switch (T) { | 203 | return switch (T) { |
| 201 | u32, | 204 | u32, |
| 202 | i32, | 205 | i32, |
src/core/js/Js.zig+1-1| ... | @@ -32,7 +32,7 @@ pub fn handleFunctionAir( | ... | @@ -32,7 +32,7 @@ pub fn handleFunctionAir( |
| 32 | try ng.build(); | 32 | try ng.build(); |
| 33 | 33 | ||
| 34 | const tree = try ng.finalize(); | 34 | const tree = try ng.finalize(); |
| 35 | try backend.codegen_map.put(comp.gpa, value, tree); | 35 | try backend.codegen_map.putNoClobber(comp.gpa, value, tree); |
| 36 | 36 | ||
| 37 | return 0; | 37 | return 0; |
| 38 | } | 38 | } |
src/main.zig+4-1| ... | @@ -324,7 +324,10 @@ fn cmdBuildOrRun(ctx: Context, comptime mode: enum { build, run }) !void { | ... | @@ -324,7 +324,10 @@ fn cmdBuildOrRun(ctx: Context, comptime mode: enum { build, run }) !void { |
| 324 | try teg.dumpToWriter(stderr, null); | 324 | try teg.dumpToWriter(stderr, null); |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | const entry_point = try comp.enqueueEntryModule(main_file); | 327 | const entry_point = comp.enqueueEntryModule(main_file) catch |err| switch (err) { |
| 328 | error.TransformFail => std.process.exit(1), | ||
| 329 | else => |e| return e, | ||
| 330 | }; | ||
| 328 | comp.runAllJobs() catch |err| switch (err) { | 331 | comp.runAllJobs() catch |err| switch (err) { |
| 329 | error.AnalysisFail => { | 332 | error.AnalysisFail => { |
| 330 | // error is already printed | 333 | // error is already printed |
src/manual/generate-man.zig+1-1| ... | @@ -310,7 +310,7 @@ fn processMarkdown( | ... | @@ -310,7 +310,7 @@ fn processMarkdown( |
| 310 | defer prog.end(); | 310 | defer prog.end(); |
| 311 | 311 | ||
| 312 | var prog_step = prog.start("Compile", 1); | 312 | var prog_step = prog.start("Compile", 1); |
| 313 | var build = std.process.Child.init(&.{ compiler_path, "/dev/stdin" }, gpa); | 313 | var build = std.process.Child.init(&.{ compiler_path, "build", "/dev/stdin" }, gpa); |
| 314 | build.stdin_behavior = .Pipe; | 314 | build.stdin_behavior = .Pipe; |
| 315 | build.stdout_behavior = .Pipe; | 315 | build.stdout_behavior = .Pipe; |
| 316 | build.stderr_behavior = .Pipe; | 316 | build.stderr_behavior = .Pipe; |
test/behavior/arithmetic+2-2| ... | @@ -16,7 +16,7 @@ fn main { | ... | @@ -16,7 +16,7 @@ fn main { |
| 16 | assertEqual(subInt(528, -43), i(571)); | 16 | assertEqual(subInt(528, -43), i(571)); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | fn f(a: float) -> float { a } | 19 | fn f(a: float) { a } |
| 20 | 20 | ||
| 21 | fn addFloat(a: float, b: float) -> float { | 21 | fn addFloat(a: float, b: float) -> float { |
| 22 | a + b | 22 | a + b |
| ... | @@ -42,7 +42,7 @@ fn divFloat(a: float, b: float) -> float { | ... | @@ -42,7 +42,7 @@ fn divFloat(a: float, b: float) -> float { |
| 42 | // a ** b | 42 | // a ** b |
| 43 | // } | 43 | // } |
| 44 | 44 | ||
| 45 | fn i(a: int) -> int { a } | 45 | fn i(a: int) { a } |
| 46 | 46 | ||
| 47 | fn addInt(a: int, b: int) -> int { | 47 | fn addInt(a: int, b: int) -> int { |
| 48 | a + b | 48 | a + b |
test/behavior/array+1-1| ... | @@ -11,4 +11,4 @@ fn main { | ... | @@ -11,4 +11,4 @@ fn main { |
| 11 | assertEqual(array[i(1) + 2] + array[i(2) + 2], i(2)); | 11 | assertEqual(array[i(1) + 2] + array[i(2) + 2], i(2)); |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | fn i(a: int) -> int { a } | 14 | fn i(a: int) { a } |
test/behavior/inferred_return_type created+10| ... | @@ -0,0 +1,10 @@ | ||
| 1 | let assertEqual = @extern("assertEqual"); | ||
| 2 | |||
| 3 | fn getString() { | ||
| 4 | "hello" | ||
| 5 | } | ||
| 6 | |||
| 7 | fn main { | ||
| 8 | let str = getString(); | ||
| 9 | assertEqual(str, "hello"); | ||
| 10 | } | ||
test/behavior/tuple+1-2| ... | @@ -1,8 +1,7 @@ | ... | @@ -1,8 +1,7 @@ |
| 1 | let assertEqual = @extern("assertEqual"); | 1 | let assertEqual = @extern("assertEqual"); |
| 2 | 2 | ||
| 3 | fn hello() -> tuple (int, float) { | 3 | fn hello() -> tuple (int, float) { |
| 4 | let x: tuple (int, float) = (5, 2.5); | 4 | (5, 2.5) |
| 5 | x | ||
| 6 | } | 5 | } |
| 7 | 6 | ||
| 8 | fn main { | 7 | fn main { |