| author | |
| committer | |
| log | 10d7c1ff53ea46517327987a11764dd46ec8cb7f |
| tree | 6e6404866d079425b9c300892d6367140a97f2a8 |
| parent | b081bdf665c0a29b07b59f4c64a9cb85bcfef98a |
| signature | Commit is signed but in an unrecognized format. |
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 @@ |
| 8 | 8 | ||
| 9 | .zig-cache | 9 | .zig-cache |
| 10 | zig-out | 10 | zig-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.3 | 6 | ## 0.1.3 |
| 2 | 7 | ||
| 3 | - feat(sema): coersion: numeric\_literal -> float | 8 | - feat(sema): coersion: numeric_literal -> float |
| 4 | - feat(sema): prevent passing non-runtime compatible types like `type` to opaque functions | 9 | - feat(sema): prevent passing non-runtime compatible types like `type` to opaque functions |
| 5 | - feat(sema): properly validate and coerce binary operations | 10 | - 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 { |
| 8 | 8 | ||
| 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); |
| 30 | 39 | ||
| 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 @@ | ||
| 1 | fn something(a: int) -> bool { | ||
| 2 | if a == 0 { | ||
| 3 | return false; | ||
| 4 | } | ||
| 5 | |||
| 6 | a > 100 | ||
| 7 | } | ||
| 8 | |||
| 9 | let print = @js("console").log; | ||
| 10 | print(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 { |
| 214 | 214 | ||
| 215 | pub const Decl = struct { | 215 | pub 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 Decl | 218 | /// 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, since | 219 | /// 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 | }; |
| 272 | 284 | ||
| 273 | pub const Warn = union(enum) { | 285 | pub 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 | ); |
| 162 | 162 | ||
| 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 | } |
| 182 | 199 | ||
| 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 | } |
| 199 | 202 | ||
| ... | @@ -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 | } |
| 815 | 818 | ||
| 816 | fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value { | 819 | fn 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 { |
| 846 | 849 | ||
| 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 | } |