authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-06-02 21:00:16-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-06-02 21:00:16-07:00
log10d7c1ff53ea46517327987a11764dd46ec8cb7f
tree6e6404866d079425b9c300892d6367140a97f2a8
parentb081bdf665c0a29b07b59f4c64a9cb85bcfef98a
signature Commit is signed but in an unrecognized format.

fix(sema): handle recursion better

after analyzing return type, we put the decl into a new state, `in_progress_prototype_available`, which means the .value of the decl is now set, and that recursive types can use this function. when inferred return types are added, this logic will have to change, but it is likely this logic is changed regardless to support some form of "decl values with incomplete types", as just the fact of referencing a declaration should not be an error if it is recursive, but only when you call it.

7 files changed, 64 insertions(+), 34 deletions(-)

.gitignore+1
...@@ -8,3 +8,4 @@...@@ -8,3 +8,4 @@
88
9.zig-cache9.zig-cache
10zig-out10zig-out
11*.spall
CHANGELOG.md+6-1
...@@ -1,6 +1,11 @@...@@ -1,6 +1,11 @@
1## 0.1.4
2
3- fix(sema): miscompilation using left side of all binary expression for both sides
4- fix(sema): handle recursive function calls
5
1## 0.1.36## 0.1.3
27
3- feat(sema): coersion: numeric\_literal -> float8- feat(sema): coersion: numeric_literal -> float
4- feat(sema): prevent passing non-runtime compatible types like `type` to opaque functions9- feat(sema): prevent passing non-runtime compatible types like `type` to opaque functions
5- feat(sema): properly validate and coerce binary operations10- feat(sema): properly validate and coerce binary operations
6- feat(sema): string concat with `+`11- feat(sema): string concat with `+`
build.zig+13-2
...@@ -8,9 +8,18 @@ pub fn build(b: *std.Build) void {...@@ -8,9 +8,18 @@ pub fn build(b: *std.Build) void {
88
9 const native_target = b.resolveTargetQuery(.{});9 const native_target = b.resolveTargetQuery(.{});
10 const target = b.standardTargetOptions(.{});10 const target = b.standardTargetOptions(.{});
11 const optimize_fallback = b.standardOptimizeOption(.{});11
12 // I disagree with how the standard optimize flag works
13 const optimize_fallback = switch (b.release_mode) {
14 .off => b.standardOptimizeOption(.{}),
15 else => b.option(
16 std.builtin.OptimizeMode,
17 "optimize",
18 "Prioritize performance, safety, or binary size",
19 ),
20 };
12 const optimize: std.builtin.OptimizeMode = switch (b.release_mode) {21 const optimize: std.builtin.OptimizeMode = switch (b.release_mode) {
13 .off => optimize_fallback,22 .off => optimize_fallback.?,
14 .safe => .ReleaseSafe,23 .safe => .ReleaseSafe,
15 .fast => .ReleaseFast,24 .fast => .ReleaseFast,
16 .small => .ReleaseSmall,25 .small => .ReleaseSmall,
...@@ -28,6 +37,8 @@ pub fn build(b: *std.Build) void {...@@ -28,6 +37,8 @@ pub fn build(b: *std.Build) void {
28 const native_debug = buildWithTarget(b, native_target, .Debug, .none);37 const native_debug = buildWithTarget(b, native_target, .Debug, .none);
29 const main = buildWithTarget(b, target, optimize, tracing);38 const main = buildWithTarget(b, target, optimize, tracing);
3039
40 b.installArtifact(main.compiler);
41
31 inline for (.{42 inline for (.{
32 .{ main.compiler, "compiler" },43 .{ main.compiler, "compiler" },
33 .{ main.lsp, "lsp" },44 .{ main.lsp, "lsp" },
demo.todo deleted-10
...@@ -1,10 +0,0 @@
1fn something(a: int) -> bool {
2 if a == 0 {
3 return false;
4 }
5
6 a > 100
7}
8
9let print = @js("console").log;
10print(something(0xFF), something(0));
src/core/Comp.zig+5-2
...@@ -214,7 +214,7 @@ pub const File = struct {...@@ -214,7 +214,7 @@ pub const File = struct {
214214
215pub const Decl = struct {215pub const Decl = struct {
216 name: InternPool.NullTerminatedString,216 name: InternPool.NullTerminatedString,
217 val: InternPool.Value,217 val: ?InternPool.Value,
218 /// The direct parent namespace of the Decl. In the case of the Decl218 /// The direct parent namespace of the Decl. In the case of the Decl
219 /// corresponding to a file, this is the namespace of the struct, since219 /// corresponding to a file, this is the namespace of the struct, since
220 /// there is no parent.220 /// there is no parent.
...@@ -242,6 +242,9 @@ pub const Decl = struct {...@@ -242,6 +242,9 @@ pub const Decl = struct {
242 /// Semantic analysis for this Decl is running right now.242 /// Semantic analysis for this Decl is running right now.
243 /// This state detects dependency loops.243 /// This state detects dependency loops.
244 in_progress,244 in_progress,
245 /// Semantic analysis for this Decl is running right now
246 /// But .value is valid
247 in_progress_prototype_available,
245 /// The file corresponding to this Decl had a parse error or ZIR error.248 /// The file corresponding to this Decl had a parse error or ZIR error.
246 /// There will be a corresponding ErrorMsg in Zcu.failed_files.249 /// There will be a corresponding ErrorMsg in Zcu.failed_files.
247 file_failure,250 file_failure,
...@@ -348,7 +351,7 @@ pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !N...@@ -348,7 +351,7 @@ pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !N
348 const tracked_inst = try comp.intern_pool.trackInst(comp.gpa, file, decl_index);351 const tracked_inst = try comp.intern_pool.trackInst(comp.gpa, file, decl_index);
349 const decl = try comp.intern_pool.createDecl(comp.gpa, .{352 const decl = try comp.intern_pool.createDecl(comp.gpa, .{
350 .name = name,353 .name = name,
351 .val = undefined,354 .val = null,
352 .src_namespace = ns,355 .src_namespace = ns,
353 .teg_decl_index = tracked_inst.toOptional(),356 .teg_decl_index = tracked_inst.toOptional(),
354 .analysis = .unreferenced,357 .analysis = .unreferenced,
src/core/Report.zig+12
...@@ -268,6 +268,18 @@ pub const Err = union(enum) {...@@ -268,6 +268,18 @@ pub const Err = union(enum) {
268 );268 );
269 }269 }
270 },270 },
271 dependency_loop: struct {
272 src: LazyNode,
273
274 fn build(data: @This(), ctx: *MsgContext) !void {
275 const node = try data.src.get(ctx);
276 try ctx.msgNode(
277 node,
278 "type dependency loop (TODO: explain this better, consider using less inferred return types)",
279 .{},
280 );
281 }
282 },
271};283};
272284
273pub const Warn = union(enum) {285pub const Warn = union(enum) {
src/core/Sema.zig+27-19
...@@ -160,12 +160,29 @@ pub fn analyzeDeclaration(s: *Sema) Error!Value {...@@ -160,12 +160,29 @@ pub fn analyzeDeclaration(s: *Sema) Error!Value {
160 fn_decl.return_type,160 fn_decl.return_type,
161 );161 );
162162
163 const func_prototype = (try s.ip.get(s.gpa, .{
164 .type_function = .{
165 .param_types = s.params,
166 .return_type = return_type,
167 },
168 })).toTypeUnsafe(s.ip);
169 const value = try s.ip.get(s.gpa, .{
170 .func = .{
171 .ty = func_prototype,
172 .owner_decl = s.decl_index,
173 .teg_body_inst = s.decl.teg_decl_index.unwrap().?,
174 },
175 });
176 s.decl.val = value;
177
178 s.decl.analysis = .in_progress_prototype_available;
179
163 const root_block = fn_decl.body;180 const root_block = fn_decl.body;
164 switch (root_block.decode()) {181 switch (root_block.decode()) {
165 .value => |value| {182 .value => |val| {
166 const coerced = try s.coerceValue(183 const coerced = try s.coerceValue(
167 s.inst_map.start.op(.operand),184 s.inst_map.start.op(.operand),
168 value,185 val,
169 return_type,186 return_type,
170 );187 );
171 const block = try s.addBlock();188 const block = try s.addBlock();
...@@ -180,20 +197,6 @@ pub fn analyzeDeclaration(s: *Sema) Error!Value {...@@ -180,20 +197,6 @@ pub fn analyzeDeclaration(s: *Sema) Error!Value {
180 },197 },
181 }198 }
182199
183 const func_prototype = (try s.ip.get(s.gpa, .{
184 .type_function = .{
185 .param_types = s.params,
186 .return_type = return_type,
187 },
188 })).toTypeUnsafe(s.ip);
189 const value = try s.ip.get(s.gpa, .{
190 .func = .{
191 .ty = func_prototype,
192 .owner_decl = s.decl_index,
193 .teg_body_inst = s.decl.teg_decl_index.unwrap().?,
194 },
195 });
196 s.decl.val = value;
197 return value;200 return value;
198}201}
199202
...@@ -296,7 +299,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {...@@ -296,7 +299,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
296 std.debug.print("did not find {s}\n", .{decl_name.toSlice(s.ip)});299 std.debug.print("did not find {s}\n", .{decl_name.toSlice(s.ip)});
297 return error.AnalysisFail;300 return error.AnalysisFail;
298 };301 };
299 const resolved = try s.resolveDecl(decl_index);302 const resolved = try s.resolveDecl(in.op(.main_node), decl_index);
300 return resolved.toAir();303 return resolved.toAir();
301 },304 },
302 .param => {305 .param => {
...@@ -813,7 +816,7 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref {...@@ -813,7 +816,7 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref {
813 return ref;816 return ref;
814}817}
815818
816fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value {819fn resolveDecl(s: *Sema, src: InstOp, decl_index: Decl.Index) Error!Value {
817 const decl = s.ip.declPtr(decl_index);820 const decl = s.ip.declPtr(decl_index);
818 switch (decl.analysis) {821 switch (decl.analysis) {
819 .unreferenced => {822 .unreferenced => {
...@@ -846,8 +849,13 @@ fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value {...@@ -846,8 +849,13 @@ fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value {
846849
847 return value;850 return value;
848 },851 },
852 .in_progress => {
853 return s.fail(.dependency_loop, .{
854 .src = s.lazyNode(src),
855 });
856 },
849 else => {857 else => {
850 return decl.val;858 return decl.val.?;
851 },859 },
852 }860 }
853}861}