| author | |
| committer | |
| log | 99826a2ba89ccd80caaa4eeb47c59a71ddfe76b6 |
| tree | 1e769aff2edca88474f60f28846da7a4a62e18f0 |
| parent | 37afab2addab5809e1419a09e3be5ea4f3ee5501 |
Previously if a decl failed its capture scope would be deallocated and
set to undefined which would then lead to invalid dereference in
`zirClosureGet`. To avoid this set the capture scope to a special
failed state and fail the current decl with dependency failure if
the failed state is encountered in `zirClosureGet`.
Closes #12433
Closes #12530
Closes #125934 files changed, 70 insertions(+), 1 deletions(-)
src/Module.zig+10| ... | ... | @@ -345,6 +345,15 @@ pub const CaptureScope = struct { |
| 345 | 345 | /// During sema, this map is backed by the gpa. Once sema completes, |
| 346 | 346 | /// it is reallocated using the value_arena. |
| 347 | 347 | captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{}, |
| 348 | ||
| 349 | pub fn failed(noalias self: *const @This()) bool { | |
| 350 | return self.captures.available == 0 and self.captures.size == std.math.maxInt(u32); | |
| 351 | } | |
| 352 | ||
| 353 | pub fn fail(noalias self: *@This()) void { | |
| 354 | self.captures.available = 0; | |
| 355 | self.captures.size = std.math.maxInt(u32); | |
| 356 | } | |
| 348 | 357 | }; |
| 349 | 358 | |
| 350 | 359 | pub const WipCaptureScope = struct { |
| ... | ... | @@ -383,6 +392,7 @@ pub const WipCaptureScope = struct { |
| 383 | 392 | pub fn deinit(noalias self: *@This()) void { |
| 384 | 393 | if (!self.finalized) { |
| 385 | 394 | self.scope.captures.deinit(self.gpa); |
| 395 | self.scope.fail(); | |
| 386 | 396 | } |
| 387 | 397 | self.* = undefined; |
| 388 | 398 | } |
src/Sema.zig+10-1| ... | ... | @@ -5956,7 +5956,6 @@ fn analyzeCall( |
| 5956 | 5956 | error.NeededSourceLocation => { |
| 5957 | 5957 | _ = sema.inst_map.remove(inst); |
| 5958 | 5958 | const decl = sema.mod.declPtr(block.src_decl); |
| 5959 | child_block.src_decl = block.src_decl; | |
| 5960 | 5959 | try sema.analyzeInlineCallArg( |
| 5961 | 5960 | block, |
| 5962 | 5961 | &child_block, |
| ... | ... | @@ -13740,6 +13739,16 @@ fn zirClosureGet( |
| 13740 | 13739 | const tv = while (true) { |
| 13741 | 13740 | // Note: We don't need to add a dependency here, because |
| 13742 | 13741 | // decls always depend on their lexical parents. |
| 13742 | ||
| 13743 | // Fail this decl if a scope it depended on failed. | |
| 13744 | if (scope.failed()) { | |
| 13745 | if (sema.owner_func) |owner_func| { | |
| 13746 | owner_func.state = .dependency_failure; | |
| 13747 | } else { | |
| 13748 | sema.owner_decl.analysis = .dependency_failure; | |
| 13749 | } | |
| 13750 | return error.AnalysisFail; | |
| 13751 | } | |
| 13743 | 13752 | if (scope.captures.getPtr(inst_data.inst)) |tv| { |
| 13744 | 13753 | break tv; |
| 13745 | 13754 | } |
test/cases/compile_errors/closure_get_depends_on_failed_decl.zig created+26| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | pub inline fn instanceRequestAdapter() void {} | |
| 2 | ||
| 3 | pub inline fn requestAdapter( | |
| 4 | comptime callbackArg: fn () callconv(.Inline) void, | |
| 5 | ) void { | |
| 6 | _ = (struct { | |
| 7 | pub fn callback() callconv(.C) void { | |
| 8 | callbackArg(); | |
| 9 | } | |
| 10 | }).callback; | |
| 11 | instanceRequestAdapter(undefined); // note wrong number of arguments here | |
| 12 | } | |
| 13 | ||
| 14 | inline fn foo() void {} | |
| 15 | ||
| 16 | pub export fn entry() void { | |
| 17 | requestAdapter(foo); | |
| 18 | } | |
| 19 | ||
| 20 | // error | |
| 21 | // backend=stage2 | |
| 22 | // target=native | |
| 23 | // | |
| 24 | // :11:5: error: expected 0 argument(s), found 1 | |
| 25 | // :1:12: note: function declared here | |
| 26 | // :17:19: note: called from here |
test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig created+24| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | fn Observable(comptime T: type) type { | |
| 2 | return struct { | |
| 3 | fn map(Src: T, Dst: anytype, function: fn (T) Dst) Dst { | |
| 4 | _ = Src; | |
| 5 | _ = function; | |
| 6 | return Observable(Dst); | |
| 7 | } | |
| 8 | }; | |
| 9 | } | |
| 10 | ||
| 11 | fn u32Tou64(x: u32) u64 { | |
| 12 | _ = x; | |
| 13 | return 0; | |
| 14 | } | |
| 15 | ||
| 16 | pub export fn entry() void { | |
| 17 | Observable(u32).map(u32, u64, u32Tou64(0)); | |
| 18 | } | |
| 19 | ||
| 20 | // error | |
| 21 | // backend=stage2 | |
| 22 | // target=native | |
| 23 | // | |
| 24 | // :17:25: error: expected type 'u32', found 'type' |