Commit 032c722d20

sin-ack <77421532+sin-ack@users.noreply.github.com>
2022-04-30 04:53:06
Sema: Fix many-pointer array concatenation at comptime (#11512)
* Sema: Correctly determine whether array_cat lhs and rhs are single ptrs Many-pointers are also not single-pointers and wouldn't be considered here. This commit makes the conditions use the appropriately-named isSinglePointer instead. * Sema: Correctly obtain ArrayInfo for many-pointer concatenation Many-pointers at comptime have a known size like slices and can be used in array concatenation. This fixes a stage1 regression. * test: Add comptime manyptr concatenation test Co-authored-by: sin-ack <sin-ack@users.noreply.github.com>
1 parent 609896a
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -9106,8 +9106,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len);
             const final_len = lhs_len + rhs_len;
             const final_len_including_sent = final_len + @boolToInt(res_sent != null);
-            const lhs_single_ptr = lhs_ty.zigTypeTag() == .Pointer and !lhs_ty.isSlice();
-            const rhs_single_ptr = rhs_ty.zigTypeTag() == .Pointer and !rhs_ty.isSlice();
+            const lhs_single_ptr = lhs_ty.isSinglePointer();
+            const rhs_single_ptr = rhs_ty.isSinglePointer();
             const lhs_sub_val = if (lhs_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val;
             const rhs_sub_val = if (rhs_single_ptr) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val;
             var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
@@ -9160,17 +9160,21 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.R
         .Array => t.arrayInfo(),
         .Pointer => blk: {
             const ptrinfo = t.ptrInfo().data;
-            if (ptrinfo.size == .Slice) {
-                const val = try sema.resolveConstValue(block, src, inst);
-                return Type.ArrayInfo{
-                    .elem_type = t.childType(),
-                    .sentinel = t.sentinel(),
-                    .len = val.sliceLen(sema.mod),
-                };
+            switch (ptrinfo.size) {
+                .Slice, .Many => {
+                    const val = try sema.resolveConstValue(block, src, inst);
+                    return Type.ArrayInfo{
+                        .elem_type = t.childType(),
+                        .sentinel = t.sentinel(),
+                        .len = val.sliceLen(sema.mod),
+                    };
+                },
+                .One => {
+                    if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null;
+                    break :blk ptrinfo.pointee_type.arrayInfo();
+                },
+                .C => return null,
             }
-            if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null;
-            if (ptrinfo.size != .One) return null;
-            break :blk ptrinfo.pointee_type.arrayInfo();
         },
         else => null,
     };
test/behavior/basic.zig
@@ -709,6 +709,31 @@ test "string concatenation" {
     try expect(b[len] == 0);
 }
 
+fn manyptrConcat(comptime s: [*:0]const u8) [*:0]const u8 {
+    return "very " ++ s;
+}
+
+test "comptime manyptr concatenation" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
+
+    const s = "epic";
+    const actual = manyptrConcat(s);
+    const expected = "very epic";
+
+    const len = mem.len(actual);
+    const len_with_null = len + 1;
+    {
+        var i: u32 = 0;
+        while (i < len_with_null) : (i += 1) {
+            try expect(actual[i] == expected[i]);
+        }
+    }
+    try expect(actual[len] == 0);
+    try expect(expected[len] == 0);
+}
+
 test "thread local variable" {
     if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO