From 53ee7a83199844ed2d12713b4817127001550a0d Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Sun, 2 Jun 2024 21:00:16 -0700 Subject: [PATCH] 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. --- .gitignore | 1 + CHANGELOG.md | 7 ++++++- build.zig | 15 +++++++++++++-- demo.todo | 10 ---------- src/core/Comp.zig | 7 +++++-- src/core/Report.zig | 12 ++++++++++++ src/core/Sema.zig | 46 ++++++++++++++++++++++++++------------------- 7 files changed, 64 insertions(+), 34 deletions(-) delete mode 100644 demo.todo diff --git a/.gitignore b/.gitignore index ae163bea1d23a4d4b8708c5171bdf61396efa7e2..2446d06c444a6b07b3495e1013a0c39c12d819ee 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ .zig-cache zig-out +*.spall diff --git a/CHANGELOG.md b/CHANGELOG.md index cfd8175aba330b45c6fc9a28a89615b87102ad39..7fc10bd6e5508a5477d5bbfa780c74c98600168e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ +## 0.1.4 + +- fix(sema): miscompilation using left side of all binary expression for both sides +- fix(sema): handle recursive function calls + ## 0.1.3 -- feat(sema): coersion: numeric\_literal -> float +- feat(sema): coersion: numeric_literal -> float - feat(sema): prevent passing non-runtime compatible types like `type` to opaque functions - feat(sema): properly validate and coerce binary operations - feat(sema): string concat with `+` diff --git a/build.zig b/build.zig index d6729aaeef876a735eb11a9479a9dbe85e04f4ab..a06d4d03ca4118a959f2e2ab4f4b8eafffa66ed0 100644 --- a/build.zig +++ b/build.zig @@ -8,9 +8,18 @@ pub fn build(b: *std.Build) void { const native_target = b.resolveTargetQuery(.{}); const target = b.standardTargetOptions(.{}); - const optimize_fallback = b.standardOptimizeOption(.{}); + + // I disagree with how the standard optimize flag works + const optimize_fallback = switch (b.release_mode) { + .off => b.standardOptimizeOption(.{}), + else => b.option( + std.builtin.OptimizeMode, + "optimize", + "Prioritize performance, safety, or binary size", + ), + }; const optimize: std.builtin.OptimizeMode = switch (b.release_mode) { - .off => optimize_fallback, + .off => optimize_fallback.?, .safe => .ReleaseSafe, .fast => .ReleaseFast, .small => .ReleaseSmall, @@ -28,6 +37,8 @@ pub fn build(b: *std.Build) void { const native_debug = buildWithTarget(b, native_target, .Debug, .none); const main = buildWithTarget(b, target, optimize, tracing); + b.installArtifact(main.compiler); + inline for (.{ .{ main.compiler, "compiler" }, .{ main.lsp, "lsp" }, diff --git a/demo.todo b/demo.todo deleted file mode 100644 index 0a2e8fb7b365173c38ff8aa3b1263685f9b2dc96..0000000000000000000000000000000000000000 --- a/demo.todo +++ /dev/null @@ -1,10 +0,0 @@ -fn something(a: int) -> bool { - if a == 0 { - return false; - } - - a > 100 -} - -let print = @js("console").log; -print(something(0xFF), something(0)); diff --git a/src/core/Comp.zig b/src/core/Comp.zig index 9c9791273104a5940a38ae8a40328ba6785112a9..7a5a6be493cacaeb571011bdf35ccaa30c888e89 100644 --- a/src/core/Comp.zig +++ b/src/core/Comp.zig @@ -214,7 +214,7 @@ pub const File = struct { pub const Decl = struct { name: InternPool.NullTerminatedString, - val: InternPool.Value, + val: ?InternPool.Value, /// The direct parent namespace of the Decl. In the case of the Decl /// corresponding to a file, this is the namespace of the struct, since /// there is no parent. @@ -242,6 +242,9 @@ pub const Decl = struct { /// Semantic analysis for this Decl is running right now. /// This state detects dependency loops. in_progress, + /// Semantic analysis for this Decl is running right now + /// But .value is valid + in_progress_prototype_available, /// The file corresponding to this Decl had a parse error or ZIR error. /// There will be a corresponding ErrorMsg in Zcu.failed_files. file_failure, @@ -348,7 +351,7 @@ pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !N const tracked_inst = try comp.intern_pool.trackInst(comp.gpa, file, decl_index); const decl = try comp.intern_pool.createDecl(comp.gpa, .{ .name = name, - .val = undefined, + .val = null, .src_namespace = ns, .teg_decl_index = tracked_inst.toOptional(), .analysis = .unreferenced, diff --git a/src/core/Report.zig b/src/core/Report.zig index 5cfc4ec59718f2026b2d50a6c3253961e378b679..19f9e105bf58485f40406c4cd14314061a55f8d4 100644 --- a/src/core/Report.zig +++ b/src/core/Report.zig @@ -268,6 +268,18 @@ pub const Err = union(enum) { ); } }, + dependency_loop: struct { + src: LazyNode, + + fn build(data: @This(), ctx: *MsgContext) !void { + const node = try data.src.get(ctx); + try ctx.msgNode( + node, + "type dependency loop (TODO: explain this better, consider using less inferred return types)", + .{}, + ); + } + }, }; pub const Warn = union(enum) { diff --git a/src/core/Sema.zig b/src/core/Sema.zig index dbcef0664f2fbc093f9f44754ecc12b88edcd1e3..6c610b1b64933c06c2dc99cf8aa7aa12e0f3f3e8 100644 --- a/src/core/Sema.zig +++ b/src/core/Sema.zig @@ -160,12 +160,29 @@ pub fn analyzeDeclaration(s: *Sema) Error!Value { fn_decl.return_type, ); + const func_prototype = (try s.ip.get(s.gpa, .{ + .type_function = .{ + .param_types = s.params, + .return_type = return_type, + }, + })).toTypeUnsafe(s.ip); + const value = try s.ip.get(s.gpa, .{ + .func = .{ + .ty = func_prototype, + .owner_decl = s.decl_index, + .teg_body_inst = s.decl.teg_decl_index.unwrap().?, + }, + }); + s.decl.val = value; + + s.decl.analysis = .in_progress_prototype_available; + const root_block = fn_decl.body; switch (root_block.decode()) { - .value => |value| { + .value => |val| { const coerced = try s.coerceValue( s.inst_map.start.op(.operand), - value, + val, return_type, ); const block = try s.addBlock(); @@ -180,20 +197,6 @@ pub fn analyzeDeclaration(s: *Sema) Error!Value { }, } - const func_prototype = (try s.ip.get(s.gpa, .{ - .type_function = .{ - .param_types = s.params, - .return_type = return_type, - }, - })).toTypeUnsafe(s.ip); - const value = try s.ip.get(s.gpa, .{ - .func = .{ - .ty = func_prototype, - .owner_decl = s.decl_index, - .teg_body_inst = s.decl.teg_decl_index.unwrap().?, - }, - }); - s.decl.val = value; return value; } @@ -296,7 +299,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { std.debug.print("did not find {s}\n", .{decl_name.toSlice(s.ip)}); return error.AnalysisFail; }; - const resolved = try s.resolveDecl(decl_index); + const resolved = try s.resolveDecl(in.op(.main_node), decl_index); return resolved.toAir(); }, .param => { @@ -813,7 +816,7 @@ fn ensureValueRuntime(s: *Sema, src: InstOp, ref: Air.Inst.Ref) !Air.Inst.Ref { return ref; } -fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value { +fn resolveDecl(s: *Sema, src: InstOp, decl_index: Decl.Index) Error!Value { const decl = s.ip.declPtr(decl_index); switch (decl.analysis) { .unreferenced => { @@ -846,8 +849,13 @@ fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value { return value; }, + .in_progress => { + return s.fail(.dependency_loop, .{ + .src = s.lazyNode(src), + }); + }, else => { - return decl.val; + return decl.val.?; }, } } -- 2.54.0