authorchloe caruso <git@paperclover.net> 2024-09-16 16:53:43-04:00
committerchloe caruso <git@paperclover.net> 2024-09-16 16:53:43-04:00
log356157c8138cd42491a0d77543abf10b155543ba
tree4d7616561fb287fb6016c75c3bdefa4ad426ba7b
parent2812f22f250fd017b6acdea5aeda821e026169a0

feat: inferred return type


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 time14- feat: allow instantiating `@extern` at compile time
15- feat: implement tuples15- feat: implement tuples
16- feat: implement multiline string literals16- feat: implement multiline string literals
17- feat: implement inferred return type
17- fix: crash when printing unused struct declaration error18- fix: crash when printing unused struct declaration error
18- fix: provide array result type to sema19- fix: provide array result type to sema
19- fix: crash when analyzing an empty array without a result type20- 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; // autofix290 @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 orelse391 return base_type.toUnresolvedFunctionPrototype(ip).?.return_type orelse
392 // unreachable // return type was not resolved392 unreachable; // return type was not resolved
393 @panic("TODO: resolve return type");
394 },393 },
395394
396 // Field accesses on structs are special-cased by storing the field395 // 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 {
119119
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) orelse122 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 };
124132
125 try comp.jobs.writeItem(.{ .analysis = .{ .au = .wrap(.{ .decl = decl_index }) } });133 try comp.jobs.writeItem(.{ .analysis = .{ .au = .wrap(.{ .decl = decl_index }) } });
126134
...@@ -136,7 +144,14 @@ pub fn runAllJobs(comp: *Comp) !void {...@@ -136,7 +144,14 @@ pub fn runAllJobs(comp: *Comp) !void {
136 }144 }
137 }145 }
138146
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}
141156
142const AnalysisJobError = Allocator.Error || error{ AnalysisFail, TooManyValues };157const 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;
168183
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 }
174196
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 };
196218
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 semantic484 /// 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`. Functions485 /// 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 },
464488
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 }
469493
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};
518533
519pub const Func = struct {};
520
521/// A namespace is a container for declarations. Currently the only form of534/// A namespace is a container for declarations. Currently the only form of
522/// namespace that exists is file-scope. In the future, the `module` keyword535/// namespace that exists is file-scope. In the future, the `module` keyword
523/// will create nested namespaces, and it is probable that `struct` and other536/// 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.
75decl_val_deps: std.AutoArrayHashMapUnmanaged(DeclIndex, DepEntry.Index) = .{},75decl_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.
78resolved_func_deps: std.AutoArrayHashMapUnmanaged(Value, DepEntry.Index) = .{},
7679
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 entries81/// 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 };
180185
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 }
460465
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 printed577 .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 for677/// State that is mutable during semantic analysis. This data is not used for
673/// equality or hashing.678/// equality or hashing.
674pub const FuncAnalysis = packed struct(u32) {679pub const FuncAnalysis = packed struct(u32) {
675 state: State,680 state: AnalysisState,
676681 _: 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};
700683
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,
979962
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),
10691058
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 };
10761071
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};
12621257
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
1261pub 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
1263pub const Dependee = union(enum) {1269pub 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};
12671274
1268pub const DepEntry = extern struct {1275pub 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);
16911699
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,
5757
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,
6464
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),
10561062
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}
436450
437pub fn step(s: *Sema) Error!?Value {451pub fn step(s: *Sema) Error!?Air.Inst.Ref {
438 s.instruction_step_lock.lock(); // Semantic analysis must not be recursive452 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();
440454
...@@ -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 already477 // the instruction pointer has been moved already
464 assert(s.frame.currentIndex() != start_index); // control flow loop478 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 block539 assert(s.frame.mode == .run_time); // never emitted in a comptime block
529 assert(s.owner.kind == .func); // only emitted in function body540 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 };
982993
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 }
10001019
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);
14221441
1423 const func: InternPool.Key.FunctionType = ty.toFunctionPrototype(s.ip) orelse1442 const func: InternPool.Key.FunctionType = ty.toUnresolvedFunctionPrototype(s.ip) orelse
1424 return Value.opaque_type.toAir();1443 return Value.opaque_type.toAir();
14251444
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}
19611987
1962fn getDecl(sema: *Sema, decl_index: Decl.Index) !Value {1988fn 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}
19862013
2014/// Interrupts if there is an unresolved inferred return type.
2015fn 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
1987fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {2047fn 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 });
21322194
src/core/array.zig+36-33
...@@ -2,40 +2,40 @@ const extra = @This();...@@ -2,40 +2,40 @@ const extra = @This();
22
3pub const Mutability = enum { @"const", @"var" };3pub const Mutability = enum { @"const", @"var" };
44
5fn setAsU32(items: []u32, comptime T: type, index: usize, payload: T) void {5fn 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];
1313 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 },
2117
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 }
2824
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 }
3430
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}
4141
...@@ -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 _,
118118
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 }
123122
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 after127 /// 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}
197200
198/// The number of 'u32's this type consumes in `ip.extra`.201/// The number of 'u32's this type consumes in `ip.extra`.
199pub fn extraSizeOf(comptime T: type) usize {202pub 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();
3333
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);
3636
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 }
326326
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 printed333 // 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();
311311
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}
1818
19fn f(a: float) -> float { a }19fn f(a: float) { a }
2020
21fn addFloat(a: float, b: float) -> float {21fn addFloat(a: float, b: float) -> float {
22 a + b22 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 ** b42// a ** b
43// }43// }
4444
45fn i(a: int) -> int { a }45fn i(a: int) { a }
4646
47fn addInt(a: int, b: int) -> int {47fn addInt(a: int, b: int) -> int {
48 a + b48 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}
1313
14fn i(a: int) -> int { a }14fn i(a: int) { a }
test/behavior/inferred_return_type created+10
...@@ -0,0 +1,10 @@
1let assertEqual = @extern("assertEqual");
2
3fn getString() {
4 "hello"
5}
6
7fn main {
8 let str = getString();
9 assertEqual(str, "hello");
10}
test/behavior/tuple+1-2
...@@ -1,8 +1,7 @@...@@ -1,8 +1,7 @@
1let assertEqual = @extern("assertEqual");1let assertEqual = @extern("assertEqual");
22
3fn hello() -> tuple (int, float) {3fn hello() -> tuple (int, float) {
4 let x: tuple (int, float) = (5, 2.5);4 (5, 2.5)
5 x
6}5}
76
8fn main {7fn main {