Commit 1e67f50211

garrison hinson-hasty <71951273+garrisonhh@users.noreply.github.com>
2024-03-05 19:55:21
Sema: fix compiler crash `@ptrCast`ing optional slice
1 parent 7d41a5c
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -22779,7 +22779,11 @@ fn ptrCastFull(
     }
 
     const ptr = if (src_info.flags.size == .Slice and dest_info.flags.size != .Slice) ptr: {
-        break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty);
+        if (operand_ty.zigTypeTag(mod) == .Optional) {
+            break :ptr try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty);
+        } else {
+            break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty);
+        }
     } else operand;
 
     const dest_ptr_ty = if (dest_info.flags.size == .Slice and src_info.flags.size != .Slice) blk: {
@@ -32564,6 +32568,32 @@ fn analyzeSlicePtr(
     return block.addTyOp(.slice_ptr, result_ty, slice);
 }
 
+fn analyzeOptionalSlicePtr(
+    sema: *Sema,
+    block: *Block,
+    opt_slice_src: LazySrcLoc,
+    opt_slice: Air.Inst.Ref,
+    opt_slice_ty: Type,
+) CompileError!Air.Inst.Ref {
+    const mod = sema.mod;
+    const result_ty = opt_slice_ty.optionalChild(mod).slicePtrFieldType(mod);
+
+    if (try sema.resolveValue(opt_slice)) |opt_val| {
+        if (opt_val.isUndef(mod)) return mod.undefRef(result_ty);
+        const slice_ptr: InternPool.Index = if (opt_val.optionalValue(mod)) |val|
+            val.slicePtr(mod).toIntern()
+        else
+            .null_value;
+
+        return Air.internedToRef(slice_ptr);
+    }
+
+    try sema.requireRuntimeBlock(block, opt_slice_src, null);
+
+    const slice = try block.addTyOp(.optional_payload, opt_slice_ty, opt_slice);
+    return block.addTyOp(.slice_ptr, result_ty, slice);
+}
+
 fn analyzeSliceLen(
     sema: *Sema,
     block: *Block,
test/behavior/cast.zig
@@ -1550,6 +1550,32 @@ test "optional pointer coerced to optional allowzero pointer" {
     try expect(@intFromPtr(q.?) == 4);
 }
 
+test "optional slice coerced to allowzero many pointer" {
+    const a: ?[]const u32 = null;
+    const b: [*]allowzero const u8 = @ptrCast(a);
+    const c = @intFromPtr(b);
+    try std.testing.expect(c == 0);
+}
+
+test "optional slice passed as parameter coerced to allowzero many pointer" {
+    const ns = struct {
+        const Color = struct {
+            r: u8,
+            g: u8,
+            b: u8,
+            a: u8,
+        };
+
+        fn foo(pixels: ?[]const Color) !void {
+            const data: [*]allowzero const u8 = @ptrCast(pixels);
+            const int = @intFromPtr(data);
+            try std.testing.expect(int == 0);
+        }
+    };
+
+    try ns.foo(null);
+}
+
 test "single item pointer to pointer to array to slice" {
     var x: i32 = 1234;
     try expect(@as([]const i32, @as(*[1]i32, &x))[0] == 1234);