15 files changed, 237 insertions(+), 131 deletions(-)
CHANGELOG.md+1| ... | ... | @@ -14,6 +14,7 @@ |
| 14 | 14 | - feat: allow instantiating `@extern` at compile time |
| 15 | 15 | - feat: implement tuples |
| 16 | 16 | - feat: implement multiline string literals |
| 17 | - feat: implement inferred return type | |
| 17 | 18 | - fix: crash when printing unused struct declaration error |
| 18 | 19 | - fix: provide array result type to sema |
| 19 | 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 | 268 | args, |
| 269 | 269 | stderr.get(), |
| 270 | 270 | stderr.tty, |
| 271 | true, | |
| 272 | 271 | ) catch |e| switch (e) { |
| 273 | 272 | error.InvalidArguments => { |
| 274 | 273 | stderr.buffered_writer.flush() catch {}; |
| ... | ... | @@ -287,9 +286,8 @@ pub fn parseWithErrorWriter( |
| 287 | 286 | args: []const []const u8, |
| 288 | 287 | err: std.io.AnyWriter, |
| 289 | 288 | tty: std.io.tty.Config, |
| 290 | comptime exit_on_error: bool, | |
| 291 | 289 | ) ParseError!p.Parsed() { |
| 292 | _ = exit_on_error; // autofix | |
| 290 | @setEvalBranchQuota(std.math.maxInt(u32)); | |
| 293 | 291 | var out: p.Parsed() = .{}; |
| 294 | 292 | var had_errors = false; |
| 295 | 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 | 388 | .call => { |
| 389 | 389 | const base_type = air.typeOf(air.instData(.call, index).base, ip); |
| 390 | 390 | if (base_type == .@"opaque") return .@"opaque"; |
| 391 | return base_type.toFunctionPrototype(ip).?.return_type orelse | |
| 392 | // unreachable // return type was not resolved | |
| 393 | @panic("TODO: resolve return type"); | |
| 391 | return base_type.toUnresolvedFunctionPrototype(ip).?.return_type orelse | |
| 392 | unreachable; // return type was not resolved | |
| 394 | 393 | }, |
| 395 | 394 | |
| 396 | 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 | 119 | |
| 120 | 120 | const ns = try comp.indexTegModule(file, .main_module_inst); |
| 121 | 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 | |
| 123 | @panic("missing 'fn main' in root module"); | |
| 122 | const decl_index = comp.intern_pool.namespacePtr(ns).lookupDirect(comp, main_str) orelse { | |
| 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 | 133 | try comp.jobs.writeItem(.{ .analysis = .{ .au = .wrap(.{ .decl = decl_index }) } }); |
| 126 | 134 | |
| ... | ... | @@ -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 | 157 | const AnalysisJobError = Allocator.Error || error{ AnalysisFail, TooManyValues }; |
| ... | ... | @@ -166,10 +181,17 @@ fn runAnalysisJob(comp: *Comp, unit: InternPool.AnalysisUnit, first_blocked_sema |
| 166 | 181 | errdefer _ = sema.deinit(.free_all); |
| 167 | 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 | 191 | if (first_blocked_sema) |blocked_sema| |
| 170 | 192 | try sema.blocked_semas.append(comp.gpa, blocked_sema); |
| 171 | } else { | |
| 172 | assert(first_blocked_sema == null); | |
| 193 | } else if (first_blocked_sema) |blocked_sema| { | |
| 194 | try gop.value_ptr.*.blocked_semas.append(comp.gpa, blocked_sema); | |
| 173 | 195 | } |
| 174 | 196 | |
| 175 | 197 | const sema: *Sema = gop.value_ptr.*; |
| ... | ... | @@ -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 | 228 | comp.ongoing_analysis.removeByPtr(gop.key_ptr); |
| 198 | 229 | try comp.jobs.ensureUnusedCapacity(sema.blocked_semas.items.len); |
| 199 | 230 | for (sema.blocked_semas.items) |other_sema| { |
| ... | ... | @@ -453,29 +484,13 @@ pub const Decl = struct { |
| 453 | 484 | /// the function type is analyzed with this set to `in_progress`; however, the semantic |
| 454 | 485 | /// analysis of the function body is performed with this value set to `success`. Functions |
| 455 | 486 | /// have their own analysis status field. |
| 456 | analysis: enum { | |
| 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 | }, | |
| 487 | analysis: InternPool.AnalysisState, | |
| 464 | 488 | |
| 465 | 489 | pub fn value(decl: *const Decl) InternPool.Value { |
| 466 | 490 | assert(decl.analysis == .complete); |
| 467 | 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 | 494 | pub fn fmtJson(decl: *const Decl, comp: *const Comp) JsonFormatter { |
| 480 | 495 | return .{ .decl = decl, .comp = comp }; |
| 481 | 496 | } |
| ... | ... | @@ -516,8 +531,6 @@ pub const Decl = struct { |
| 516 | 531 | pub const Index = InternPool.DeclIndex; |
| 517 | 532 | }; |
| 518 | 533 | |
| 519 | pub const Func = struct {}; | |
| 520 | ||
| 521 | 534 | /// A namespace is a container for declarations. Currently the only form of |
| 522 | 535 | /// namespace that exists is file-scope. In the future, the `module` keyword |
| 523 | 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 | 73 | /// Dependencies on the value of a Decl. |
| 74 | 74 | /// Value is index into `dep_entries` of the first dependency on this Decl value. |
| 75 | 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 | 80 | /// Given a `Depender`, points to an entry in `dep_entries` whose `depender` |
| 78 | 81 | /// matches. The `next_dependee` field can be used to iterate all such entries |
| ... | ... | @@ -176,6 +179,8 @@ pub const Tag = enum(u8) { |
| 176 | 179 | owner_decl: DeclIndex, |
| 177 | 180 | ty: Type, |
| 178 | 181 | teg_body_inst: TrackedInst.Index, |
| 182 | ||
| 183 | pub const ty_index = 2; | |
| 179 | 184 | }; |
| 180 | 185 | |
| 181 | 186 | /// Trailing: |
| ... | ... | @@ -458,7 +463,7 @@ pub const Type = enum(u32) { |
| 458 | 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 | 467 | if (ty.tag(ip) != .function_type) return null; |
| 463 | 468 | return ip.valueToKey(ty.value()).function_type; |
| 464 | 469 | } |
| ... | ... | @@ -571,7 +576,7 @@ pub const Type = enum(u32) { |
| 571 | 576 | switch (ty.tag(ip)) { |
| 572 | 577 | .simple_type => unreachable, // all simple types are already printed |
| 573 | 578 | .function_type => { |
| 574 | const function_type = ty.toFunctionPrototype(ip).?; | |
| 579 | const function_type = ty.toUnresolvedFunctionPrototype(ip).?; | |
| 575 | 580 | try writer.writeAll("fn("); |
| 576 | 581 | for (function_type.param_types, 0..) |param, i| { |
| 577 | 582 | if (i != 0) try writer.writeAll(", "); |
| ... | ... | @@ -672,30 +677,8 @@ pub const static_keys = [_]Key{ |
| 672 | 677 | /// State that is mutable during semantic analysis. This data is not used for |
| 673 | 678 | /// equality or hashing. |
| 674 | 679 | pub const FuncAnalysis = packed struct(u32) { |
| 675 | state: State, | |
| 676 | ||
| 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 | }; | |
| 680 | state: AnalysisState, | |
| 681 | _: u30 = 0, | |
| 699 | 682 | }; |
| 700 | 683 | |
| 701 | 684 | /// An index into `string_bytes`. |
| ... | ... | @@ -977,6 +960,11 @@ pub const Key = union(enum) { |
| 977 | 960 | /// the resolved return type. |
| 978 | 961 | return_type: ?Type, |
| 979 | 962 | |
| 963 | pub const Resolved = struct { | |
| 964 | param_types: []Type, | |
| 965 | return_type: Type, | |
| 966 | }; | |
| 967 | ||
| 980 | 968 | pub fn hash(self: FunctionType, hasher: *Hash) void { |
| 981 | 969 | const t = tracer.trace(@src()); |
| 982 | 970 | defer t.end(); |
| ... | ... | @@ -1066,12 +1054,19 @@ pub const Key = union(enum) { |
| 1066 | 1054 | ty: Type, |
| 1067 | 1055 | owner_decl: DeclIndex, |
| 1068 | 1056 | teg_body_inst: TrackedInst.Index, |
| 1057 | analysis: ExtraIndex(AnalysisState), | |
| 1069 | 1058 | |
| 1070 | 1059 | pub fn hash(self: Func, hasher: *Hash) void { |
| 1071 | 1060 | std.hash.autoHash(hasher, self.ty); |
| 1072 | 1061 | std.hash.autoHash(hasher, self.owner_decl); |
| 1073 | 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 | 1072 | pub fn hash32(key: Key, ip: *const InternPool) 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 | 1269 | pub const Dependee = union(enum) { |
| 1264 | 1270 | src_hash: TrackedInst.Index, |
| 1265 | 1271 | decl_val: DeclIndex, |
| 1272 | resolved_func: Value, | |
| 1266 | 1273 | }; |
| 1267 | 1274 | |
| 1268 | 1275 | pub const DepEntry = extern struct { |
| ... | ... | @@ -1393,11 +1400,13 @@ pub fn valueToKey(ip: *const InternPool, index: Value) Key { |
| 1393 | 1400 | }, |
| 1394 | 1401 | .string => .{ .string = data.decode(.string, item.tag).get(ip).toSlice(ip) }, |
| 1395 | 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 | 1405 | return .{ .func = .{ |
| 1398 | 1406 | .ty = func.ty, |
| 1399 | 1407 | .owner_decl = func.owner_decl, |
| 1400 | 1408 | .teg_body_inst = func.teg_body_inst, |
| 1409 | .analysis = @enumFromInt(@intFromEnum(base_index)), | |
| 1401 | 1410 | } }; |
| 1402 | 1411 | }, |
| 1403 | 1412 | .function_type => .{ |
| ... | ... | @@ -1497,9 +1506,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) !Value { |
| 1497 | 1506 | ), |
| 1498 | 1507 | .func => |f| brk: { |
| 1499 | 1508 | const extra = try ip.extra.append(gpa, Tag.FuncDecl, .{ |
| 1500 | .analysis = .{ | |
| 1501 | .state = .none, | |
| 1502 | }, | |
| 1509 | .analysis = .{ .state = .unreferenced }, | |
| 1503 | 1510 | .owner_decl = f.owner_decl, |
| 1504 | 1511 | .ty = f.ty, |
| 1505 | 1512 | .teg_body_inst = f.teg_body_inst, |
| ... | ... | @@ -1687,6 +1694,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalysisUnit, de |
| 1687 | 1694 | const gop = try switch (tag) { |
| 1688 | 1695 | .src_hash => ip.src_hash_deps, |
| 1689 | 1696 | .decl_val => ip.decl_val_deps, |
| 1697 | .resolved_func => ip.resolved_func_deps, | |
| 1690 | 1698 | }.getOrPut(gpa, dependee_payload); |
| 1691 | 1699 | |
| 1692 | 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 | 56 | src: LazyNode, |
| 57 | 57 | |
| 58 | 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 | 62 | cannot_be_comptime: struct { |
| 63 | 63 | src: LazyNode, |
| 64 | 64 | |
| 65 | 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 | 75 | unknown_identifier: struct { |
| ... | ... | @@ -154,7 +160,7 @@ pub const Err = union(enum) { |
| 154 | 160 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 155 | 161 | try ctx.msgNode( |
| 156 | 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 | 1061 | components: ArrayListUnmanaged(Component), |
| 1056 | 1062 | |
| 1057 | 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 | 1068 | var arr: std.ArrayListUnmanaged(u8) = .{}; |
| 1059 | 1069 | try std.fmt.format(arr.writer(m.arena), fmt, args); |
| 1060 | 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 | 419 | switch (s.owner.unwrap()) { |
| 420 | 420 | .decl => |decl_index| { |
| 421 | 421 | const decl = s.ip.declPtr(decl_index); |
| 422 | decl.raw_value = result; | |
| 422 | decl.raw_value = result.value() orelse unreachable; | |
| 423 | 423 | decl.analysis = .complete; |
| 424 | 424 | }, |
| 425 | 425 | .func => |value| { |
| 426 | const type_of_result = s.typeOfRef(result); | |
| 426 | 427 | const air = try s.func.?.finalize(s.gpa); |
| 427 | 428 | try s.comp.jobs.writeItem(.{ .codegen_func = .{ |
| 428 | 429 | .value = value, |
| 429 | 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 | 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 | 452 | s.instruction_step_lock.lock(); // Semantic analysis must not be recursive |
| 439 | 453 | defer s.instruction_step_lock.unlock(); |
| 440 | 454 | |
| ... | ... | @@ -463,10 +477,7 @@ pub fn step(s: *Sema) Error!?Value { |
| 463 | 477 | // the instruction pointer has been moved already |
| 464 | 478 | assert(s.frame.currentIndex() != start_index); // control flow loop |
| 465 | 479 | }, |
| 466 | .stop => return switch (s.owner.kind) { | |
| 467 | .decl => s.inst_map.get(s.frame.block_index).decode().value, | |
| 468 | .func => .void, | |
| 469 | }, | |
| 480 | .stop => return s.inst_map.get(s.frame.block_index), | |
| 470 | 481 | }, |
| 471 | 482 | } |
| 472 | 483 | }, |
| ... | ... | @@ -527,7 +538,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 527 | 538 | const param: Teg.DataNode = s.frame.currentData(.param); |
| 528 | 539 | assert(s.frame.mode == .run_time); // never emitted in a comptime block |
| 529 | 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 | 542 | return s.addRef(.arg, .{ |
| 532 | 543 | .type = param_types[param.data], |
| 533 | 544 | .arg_number = param.data, |
| ... | ... | @@ -724,7 +735,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 724 | 735 | const scratch = try s.arena().alloc(Air.Inst.Ref, call.args.len); |
| 725 | 736 | defer s.arena().free(scratch); |
| 726 | 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 | 739 | return s.fail(.call_non_function, .{ |
| 729 | 740 | .call = s.lazyNode(.operand), |
| 730 | 741 | .ty = base_type, |
| ... | ... | @@ -981,21 +992,29 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 981 | 992 | const mark = core.U32Stack.Mark{ .stack = &s.scratch, .index = s.frame.mark_index }; |
| 982 | 993 | |
| 983 | 994 | if (s.frame_stack.items.len == 0) { |
| 984 | s.inst_map.add(s.frame.block_index, operand); | |
| 985 | ||
| 986 | 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 | 1004 | const indexes = mark.slice(u32); |
| 988 | 1005 | try func.extra.list.ensureUnusedCapacity(s.gpa, indexes.len + 1); |
| 989 | 1006 | const start = func.extra.list.items.len; |
| 990 | 1007 | func.extra.list.appendSliceAssumeCapacity(indexes); |
| 991 | 1008 | func.extra.list.appendAssumeCapacity(@intFromEnum(try s.addIndexUntracked(.@"break", .{ |
| 992 | 1009 | .block = .main_block, |
| 993 | .value = operand, | |
| 1010 | .value = coerced, | |
| 994 | 1011 | }))); |
| 995 | 1012 | func.instructions.items(.data)[0] = .encode(.block, .{ |
| 996 | 1013 | .start = @intCast(start), |
| 997 | 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 | 1020 | return .stop; |
| ... | ... | @@ -1420,7 +1439,7 @@ fn stepInner(s: *Sema, comptime tag: Teg.Tag) Error!InstructionKind.fromTag(tag) |
| 1420 | 1439 | const operand = s.getRef(result.call_base, .no_mark); |
| 1421 | 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 | 1443 | return Value.opaque_type.toAir(); |
| 1425 | 1444 | |
| 1426 | 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 | 1856 | .type = ty, |
| 1838 | 1857 | }), |
| 1839 | 1858 | .@"opaque" => @panic("TODO: represent comptime-known opaques better"), |
| 1840 | _ => if (!ty.hasPossibleRepresentation(s.ip)) { | |
| 1841 | return s.fail(.runtime_incompatible_type, .{ | |
| 1842 | .src = src, | |
| 1843 | .type = ty, | |
| 1844 | }); | |
| 1859 | _ => { | |
| 1860 | if (!ty.hasPossibleRepresentation(s.ip)) { | |
| 1861 | return s.fail(.runtime_incompatible_type, .{ | |
| 1862 | .src = src, | |
| 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 | 1985 | } |
| 1960 | 1986 | } |
| 1961 | 1987 | |
| 1962 | fn getDecl(sema: *Sema, decl_index: Decl.Index) !Value { | |
| 1963 | if (sema.owner.kind == .decl) { | |
| 1964 | if (decl_index == sema.owner.unwrap().decl) { | |
| 1965 | return sema.ip.declPtr(decl_index).value(); | |
| 1988 | fn getDecl(s: *Sema, decl_index: Decl.Index) !Value { | |
| 1989 | if (s.owner.kind == .decl) { | |
| 1990 | if (decl_index == s.owner.unwrap().decl) { | |
| 1991 | return s.ip.declPtr(decl_index).value(); | |
| 1966 | 1992 | } |
| 1967 | 1993 | } |
| 1968 | const decl_data = sema.ip.declPtr(decl_index); | |
| 1969 | try sema.declareDependency(.{ .decl_val = decl_index }); | |
| 1994 | const decl_data = s.ip.declPtr(decl_index); | |
| 1995 | try s.declareDependency(.{ .decl_val = decl_index }); | |
| 1970 | 1996 | const au: AnalysisUnit = .wrap(.{ .decl = decl_index }); |
| 1971 | 1997 | switch (decl_data.analysis) { |
| 1972 | 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 | 2000 | return error.Interrupt; |
| 1975 | 2001 | }, |
| 1976 | .waiting, .in_progress => { | |
| 1977 | const other_sema = sema.comp.ongoing_analysis.get(au) orelse unreachable; | |
| 1978 | try other_sema.blocked_semas.append(sema.gpa, sema); | |
| 2002 | .in_progress => { | |
| 2003 | const other_sema = s.comp.ongoing_analysis.get(au) orelse | |
| 2004 | unreachable; // analysis stopped but did not update state | |
| 2005 | try other_sema.blocked_semas.append(s.gpa, s); | |
| 1979 | 2006 | return error.Interrupt; |
| 1980 | 2007 | }, |
| 1981 | 2008 | .complete => { |
| ... | ... | @@ -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 | 2047 | fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { |
| 1988 | 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 | 2182 | .return_type = return_type, |
| 2123 | 2183 | }, |
| 2124 | 2184 | }); |
| 2185 | // TODO: make a dedicated function to insert a new function | |
| 2125 | 2186 | const func_value = try s.makeValue(.{ |
| 2126 | 2187 | .func = .{ |
| 2127 | 2188 | .ty = func_type, |
| 2128 | 2189 | .owner_decl = s.techDebtDeclIndex(), // TODO: ooh, this is a fun one! |
| 2129 | 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 | 2 | |
| 3 | 3 | pub const Mutability = enum { @"const", @"var" }; |
| 4 | 4 | |
| 5 | fn setAsU32(items: []u32, comptime T: type, index: usize, payload: T) void { | |
| 6 | const fields = std.meta.fields(@TypeOf(payload)); | |
| 7 | var i = index; | |
| 8 | inline for (fields) |field| { | |
| 9 | const value = @field(payload, field.name); | |
| 10 | switch (field.type) { | |
| 11 | u32 => items[i] = value, | |
| 12 | i32 => items[i] = @bitCast(value), | |
| 13 | ||
| 14 | [16]u8 => { | |
| 15 | const u32s: [4]u32 = @bitCast(value); | |
| 16 | items[i] = u32s[0]; | |
| 17 | items[i + 1] = u32s[1]; | |
| 18 | items[i + 2] = u32s[2]; | |
| 19 | items[i + 3] = u32s[3]; | |
| 20 | }, | |
| 5 | fn setAsU32(items: []u32, comptime T: type, i: usize, payload: T) void { | |
| 6 | switch (T) { | |
| 7 | u32 => items[i] = payload, | |
| 8 | i32 => items[i] = @bitCast(payload), | |
| 9 | ||
| 10 | [16]u8 => { | |
| 11 | const u32s: [4]u32 = @bitCast(payload); | |
| 12 | items[i] = u32s[0]; | |
| 13 | items[i + 1] = u32s[1]; | |
| 14 | items[i + 2] = u32s[2]; | |
| 15 | items[i + 3] = u32s[3]; | |
| 16 | }, | |
| 21 | 17 | |
| 22 | else => brk: { | |
| 23 | const info = @typeInfo(field.type); | |
| 24 | if (info == .@"enum") { | |
| 25 | items[i] = @intFromEnum(value); | |
| 26 | break :brk; | |
| 27 | } | |
| 18 | else => brk: { | |
| 19 | const info = @typeInfo(T); | |
| 20 | if (info == .@"enum") { | |
| 21 | items[i] = @intFromEnum(payload); | |
| 22 | break :brk; | |
| 23 | } | |
| 28 | 24 | |
| 29 | const struct_data = info.@"struct"; | |
| 30 | if (struct_data.layout == .@"packed") { | |
| 31 | items[i] = @bitCast(value); | |
| 32 | break :brk; | |
| 33 | } | |
| 25 | const struct_data = info.@"struct"; | |
| 26 | if (struct_data.layout == .@"packed") { | |
| 27 | items[i] = @bitCast(payload); | |
| 28 | break :brk; | |
| 29 | } | |
| 34 | 30 | |
| 35 | @call(.always_inline, setAsU32, .{ items, field.type, i, value }); | |
| 36 | }, | |
| 37 | } | |
| 38 | i += extraSizeOf(field.type); | |
| 31 | const fields = std.meta.fields(@TypeOf(payload)); | |
| 32 | comptime var idx: usize = 0; | |
| 33 | inline for (fields) |field| { | |
| 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 | 116 | return enum(u32) { |
| 117 | 117 | _, |
| 118 | 118 | |
| 119 | // Reads the data from `store.extra` and returns it. | |
| 120 | 119 | pub fn get(index: @This(), owner: anytype) T { |
| 121 | 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 | 127 | /// Returns data with a trailing reader to read data stored immediately after |
| 125 | 128 | /// the main data structure. What is stored should be documented on `T` |
| 126 | 129 | pub fn getTrail(index: @This(), owner: anytype) struct { |
| ... | ... | @@ -196,7 +199,7 @@ pub fn EncodedData(comptime Tag: type, backing_type: type) type { |
| 196 | 199 | } |
| 197 | 200 | |
| 198 | 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 | 203 | return switch (T) { |
| 201 | 204 | u32, |
| 202 | 205 | i32, |
src/core/js/Js.zig+1-1| ... | ... | @@ -32,7 +32,7 @@ pub fn handleFunctionAir( |
| 32 | 32 | try ng.build(); |
| 33 | 33 | |
| 34 | 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 | 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 | 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 | 331 | comp.runAllJobs() catch |err| switch (err) { |
| 329 | 332 | error.AnalysisFail => { |
| 330 | 333 | // error is already printed |
src/manual/generate-man.zig+1-1| ... | ... | @@ -310,7 +310,7 @@ fn processMarkdown( |
| 310 | 310 | defer prog.end(); |
| 311 | 311 | |
| 312 | 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 | 314 | build.stdin_behavior = .Pipe; |
| 315 | 315 | build.stdout_behavior = .Pipe; |
| 316 | 316 | build.stderr_behavior = .Pipe; |
test/behavior/arithmetic+2-2| ... | ... | @@ -16,7 +16,7 @@ fn main { |
| 16 | 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 | 21 | fn addFloat(a: float, b: float) -> float { |
| 22 | 22 | a + b |
| ... | ... | @@ -42,7 +42,7 @@ fn divFloat(a: float, b: float) -> float { |
| 42 | 42 | // a ** b |
| 43 | 43 | // } |
| 44 | 44 | |
| 45 | fn i(a: int) -> int { a } | |
| 45 | fn i(a: int) { a } | |
| 46 | 46 | |
| 47 | 47 | fn addInt(a: int, b: int) -> int { |
| 48 | 48 | a + b |
test/behavior/array+1-1| ... | ... | @@ -11,4 +11,4 @@ fn main { |
| 11 | 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 | 1 | let assertEqual = @extern("assertEqual"); |
| 2 | 2 | |
| 3 | 3 | fn hello() -> tuple (int, float) { |
| 4 | let x: tuple (int, float) = (5, 2.5); | |
| 5 | x | |
| 4 | (5, 2.5) | |
| 6 | 5 | } |
| 7 | 6 | |
| 8 | 7 | fn main { |