authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-25 22:23:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-25 22:33:48-07:00
log83f69af971723d3a0774deb9dfda4b3fcbf0006f
treeba516b2bdd17b1492c6764dd1cc6bbd198cea853
parentb82081e7092a6198e6f8c65524a5829e2e08527b

stage2: implement runtime array multiplication

Additionally: * Sema: fix array cat/mul not setting the sentinel value - This required an LLVM backend enhancement to the handling of the AIR instruction aggregate_init that likely needs to be propagated to the other backends. * Sema: report integer overflow of array concatenation in a proper compile error instead of crashing. * Sema: fix not using proper pointer address space for array cat/mul

4 files changed, 236 insertions(+), 93 deletions(-)

src/Air.zig+2
......@@ -609,6 +609,8 @@ pub const Inst = struct {
609609 /// Some of the elements may be comptime-known.
610610 /// Uses the `ty_pl` field, payload is index of an array of elements, each of which
611611 /// is a `Ref`. Length of the array is given by the vector type.
612 /// If the type is an array with a sentinel, the AIR elements do not include it
613 /// explicitly.
612614 aggregate_init,
613615
614616 /// Constructs a union from a field index and a runtime-known init value.
src/Sema.zig+126-72
......@@ -9264,10 +9264,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
92649264 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
92659265 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
92669266
9267 const lhs_info = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse
9268 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)});
9269 const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse
9270 return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(sema.mod)});
9267 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs);
9268 const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs);
92719269
92729270 const resolved_elem_ty = t: {
92739271 var trash_block = block.makeSubBlock();
......@@ -9319,9 +9317,21 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
93199317
93209318 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
93219319 const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len);
9322 const result_len = lhs_len + rhs_len;
9320 const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) {
9321 error.Overflow => return sema.fail(
9322 block,
9323 src,
9324 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
9325 .{ lhs_len, rhs_len },
9326 ),
9327 };
9328
93239329 const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, sema.mod);
9324 const is_ref = lhs_ty.zigTypeTag() == .Pointer or rhs_ty.zigTypeTag() == .Pointer;
9330 const ptr_addrspace = p: {
9331 if (lhs_ty.zigTypeTag() == .Pointer) break :p lhs_ty.ptrAddressSpace();
9332 if (rhs_ty.zigTypeTag() == .Pointer) break :p rhs_ty.ptrAddressSpace();
9333 break :p null;
9334 };
93259335
93269336 const runtime_src = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| rs: {
93279337 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
......@@ -9348,22 +9358,21 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
93489358 element_vals[result_len] = sent_val;
93499359 }
93509360 const val = try Value.Tag.aggregate.create(sema.arena, element_vals);
9351 return sema.addConstantMaybeRef(block, src, result_ty, val, is_ref);
9361 return sema.addConstantMaybeRef(block, src, result_ty, val, ptr_addrspace != null);
93529362 } else break :rs rhs_src;
93539363 } else lhs_src;
93549364
93559365 try sema.requireRuntimeBlock(block, runtime_src);
93569366
9357 if (is_ref) {
9358 const target = sema.mod.getTarget();
9367 if (ptr_addrspace) |ptr_as| {
93599368 const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
93609369 .pointee_type = result_ty,
9361 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
9370 .@"addrspace" = ptr_as,
93629371 });
93639372 const alloc = try block.addTy(.alloc, alloc_ty);
93649373 const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
93659374 .pointee_type = resolved_elem_ty,
9366 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
9375 .@"addrspace" = ptr_as,
93679376 });
93689377
93699378 var elem_i: usize = 0;
......@@ -9380,6 +9389,12 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
93809389 const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src);
93819390 try sema.storePtr2(block, src, elem_ptr, src, init, rhs_src, .store);
93829391 }
9392 if (res_sent_val) |sent_val| {
9393 const elem_index = try sema.addIntUnsigned(Type.usize, result_len);
9394 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
9395 const init = try sema.addConstant(lhs_info.elem_type, sent_val);
9396 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
9397 }
93839398
93849399 return alloc;
93859400 }
......@@ -9402,30 +9417,35 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
94029417 return block.addAggregateInit(result_ty, element_refs);
94039418}
94049419
9405fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo {
9406 const t = sema.typeOf(inst);
9407 return switch (t.zigTypeTag()) {
9408 .Array => t.arrayInfo(),
9409 .Pointer => blk: {
9410 const ptrinfo = t.ptrInfo().data;
9411 switch (ptrinfo.size) {
9420fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) !Type.ArrayInfo {
9421 const operand_ty = sema.typeOf(operand);
9422 switch (operand_ty.zigTypeTag()) {
9423 .Array => return operand_ty.arrayInfo(),
9424 .Pointer => {
9425 const ptr_info = operand_ty.ptrInfo().data;
9426 switch (ptr_info.size) {
9427 // TODO: in the Many case here this should only work if the type
9428 // has a sentinel, and this code should compute the length based
9429 // on the sentinel value.
94129430 .Slice, .Many => {
9413 const val = try sema.resolveConstValue(block, src, inst);
9431 const val = try sema.resolveConstValue(block, src, operand);
94149432 return Type.ArrayInfo{
9415 .elem_type = t.childType(),
9416 .sentinel = t.sentinel(),
9433 .elem_type = ptr_info.pointee_type,
9434 .sentinel = ptr_info.sentinel,
94179435 .len = val.sliceLen(sema.mod),
94189436 };
94199437 },
94209438 .One => {
9421 if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null;
9422 break :blk ptrinfo.pointee_type.arrayInfo();
9439 if (ptr_info.pointee_type.zigTypeTag() == .Array) {
9440 return ptr_info.pointee_type.arrayInfo();
9441 }
94239442 },
9424 .C => return null,
9443 .C => {},
94259444 }
94269445 },
9427 else => null,
9428 };
9446 else => {},
9447 }
9448 return sema.fail(block, src, "expected indexable; found '{}'", .{operand_ty.fmt(sema.mod)});
94299449}
94309450
94319451fn analyzeTupleMul(
......@@ -9514,65 +9534,99 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
95149534 return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor);
95159535 }
95169536
9517 const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse
9518 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)});
9537 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs);
95199538
9520 const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch
9539 const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch
95219540 return sema.fail(block, rhs_src, "operation results in overflow", .{});
9541 const result_len = try sema.usizeCast(block, src, result_len_u64);
95229542
9523 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
9524 const final_len = try sema.usizeCast(block, src, final_len_u64);
9525 const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null);
9526 const lhs_len = try sema.usizeCast(block, lhs_src, mulinfo.len);
9543 const result_ty = try Type.array(sema.arena, result_len, lhs_info.sentinel, lhs_info.elem_type, sema.mod);
95279544
9528 const is_single_ptr = lhs_ty.zigTypeTag() == .Pointer and !lhs_ty.isSlice();
9529 const lhs_sub_val = if (is_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val;
9545 const ptr_addrspace = if (lhs_ty.zigTypeTag() == .Pointer) lhs_ty.ptrAddressSpace() else null;
9546 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
95309547
9531 var anon_decl = try block.startAnonDecl(src);
9532 defer anon_decl.deinit();
9548 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
9549 const final_len_including_sent = result_len + @boolToInt(lhs_info.sentinel != null);
95339550
9534 const final_ty = if (mulinfo.sentinel) |sent|
9535 try Type.Tag.array_sentinel.create(anon_decl.arena(), .{
9536 .len = final_len,
9537 .elem_type = try mulinfo.elem_type.copy(anon_decl.arena()),
9538 .sentinel = try sent.copy(anon_decl.arena()),
9539 })
9551 const lhs_sub_val = if (lhs_ty.isSinglePointer())
9552 (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
95409553 else
9541 try Type.Tag.array.create(anon_decl.arena(), .{
9542 .len = final_len,
9543 .elem_type = try mulinfo.elem_type.copy(anon_decl.arena()),
9544 });
9545 const buf = try anon_decl.arena().alloc(Value, final_len_including_sent);
9546
9547 // Optimization for the common pattern of a single element repeated N times, such
9548 // as zero-filling a byte array.
9549 const val = if (lhs_len == 1) blk: {
9550 const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, 0);
9551 const copied_val = try elem_val.copy(anon_decl.arena());
9552 break :blk try Value.Tag.repeated.create(anon_decl.arena(), copied_val);
9553 } else blk: {
9554 // the actual loop
9555 var i: usize = 0;
9556 while (i < factor) : (i += 1) {
9557 var j: usize = 0;
9558 while (j < lhs_len) : (j += 1) {
9559 const val = try lhs_sub_val.elemValue(sema.mod, sema.arena, j);
9560 buf[lhs_len * i + j] = try val.copy(anon_decl.arena());
9554 lhs_val;
9555
9556 const val = v: {
9557 // Optimization for the common pattern of a single element repeated N times, such
9558 // as zero-filling a byte array.
9559 if (lhs_len == 1) {
9560 const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, 0);
9561 break :v try Value.Tag.repeated.create(sema.arena, elem_val);
9562 }
9563
9564 const element_vals = try sema.arena.alloc(Value, final_len_including_sent);
9565 var elem_i: usize = 0;
9566 while (elem_i < result_len) {
9567 var lhs_i: usize = 0;
9568 while (lhs_i < lhs_len) : (lhs_i += 1) {
9569 const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_i);
9570 element_vals[elem_i] = elem_val;
9571 elem_i += 1;
95619572 }
95629573 }
9563 if (mulinfo.sentinel) |sent| {
9564 buf[final_len] = try sent.copy(anon_decl.arena());
9574 if (lhs_info.sentinel) |sent_val| {
9575 element_vals[result_len] = sent_val;
95659576 }
9566 break :blk try Value.Tag.aggregate.create(anon_decl.arena(), buf);
9577 break :v try Value.Tag.aggregate.create(sema.arena, element_vals);
95679578 };
9568 const decl = try anon_decl.finish(final_ty, val, 0);
9569 if (lhs_ty.zigTypeTag() == .Pointer) {
9570 return sema.analyzeDeclRef(decl);
9571 } else {
9572 return sema.analyzeDeclVal(block, .unneeded, decl);
9579 return sema.addConstantMaybeRef(block, src, result_ty, val, ptr_addrspace != null);
9580 }
9581
9582 try sema.requireRuntimeBlock(block, lhs_src);
9583
9584 if (ptr_addrspace) |ptr_as| {
9585 const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
9586 .pointee_type = result_ty,
9587 .@"addrspace" = ptr_as,
9588 });
9589 const alloc = try block.addTy(.alloc, alloc_ty);
9590 const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
9591 .pointee_type = lhs_info.elem_type,
9592 .@"addrspace" = ptr_as,
9593 });
9594
9595 var elem_i: usize = 0;
9596 while (elem_i < result_len) {
9597 var lhs_i: usize = 0;
9598 while (lhs_i < lhs_len) : (lhs_i += 1) {
9599 const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
9600 elem_i += 1;
9601 const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i);
9602 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
9603 const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src);
9604 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
9605 }
9606 }
9607 if (lhs_info.sentinel) |sent_val| {
9608 const elem_index = try sema.addIntUnsigned(Type.usize, result_len);
9609 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
9610 const init = try sema.addConstant(lhs_info.elem_type, sent_val);
9611 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
9612 }
9613
9614 return alloc;
9615 }
9616
9617 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
9618 var elem_i: usize = 0;
9619 while (elem_i < result_len) {
9620 var lhs_i: usize = 0;
9621 while (lhs_i < lhs_len) : (lhs_i += 1) {
9622 const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i);
9623 const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src);
9624 element_refs[elem_i] = init;
9625 elem_i += 1;
95739626 }
95749627 }
9575 return sema.fail(block, lhs_src, "TODO runtime array_mul", .{});
9628
9629 return block.addAggregateInit(result_ty, element_refs);
95769630}
95779631
95789632fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/codegen/llvm.zig+20-7
......@@ -7728,7 +7728,14 @@ pub const FuncGen = struct {
77287728 const alloca_inst = self.buildAlloca(llvm_result_ty);
77297729 alloca_inst.setAlignment(result_ty.abiAlignment(target));
77307730
7731 const elem_ty = result_ty.childType();
7731 const array_info = result_ty.arrayInfo();
7732 var elem_ptr_payload: Type.Payload.Pointer = .{
7733 .data = .{
7734 .pointee_type = array_info.elem_type,
7735 .@"addrspace" = .generic,
7736 },
7737 };
7738 const elem_ptr_ty = Type.initPayload(&elem_ptr_payload.base);
77327739
77337740 for (elements) |elem, i| {
77347741 const indices: [2]*const llvm.Value = .{
......@@ -7737,13 +7744,19 @@ pub const FuncGen = struct {
77377744 };
77387745 const elem_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");
77397746 const llvm_elem = try self.resolveInst(elem);
7740 var elem_ptr_payload: Type.Payload.Pointer = .{
7741 .data = .{
7742 .pointee_type = elem_ty,
7743 .@"addrspace" = .generic,
7744 },
7747 self.store(elem_ptr, elem_ptr_ty, llvm_elem, .NotAtomic);
7748 }
7749 if (array_info.sentinel) |sent_val| {
7750 const indices: [2]*const llvm.Value = .{
7751 llvm_usize.constNull(),
7752 llvm_usize.constInt(@intCast(c_uint, array_info.len), .False),
77457753 };
7746 const elem_ptr_ty = Type.initPayload(&elem_ptr_payload.base);
7754 const elem_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");
7755 const llvm_elem = try self.dg.lowerValue(.{
7756 .ty = array_info.elem_type,
7757 .val = sent_val,
7758 });
7759
77477760 self.store(elem_ptr, elem_ptr_ty, llvm_elem, .NotAtomic);
77487761 }
77497762
test/behavior/eval.zig+88-14
......@@ -742,6 +742,23 @@ test "array concatenation of function calls" {
742742 try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 4 }));
743743}
744744
745test "array multiplication of function calls" {
746 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
747 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
748 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
749
750 var a = oneItem(3) ** scalar(2);
751 try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 3 }));
752}
753
754fn oneItem(x: i32) [1]i32 {
755 return [_]i32{x};
756}
757
758fn scalar(x: u32) u32 {
759 return x;
760}
761
745762test "array concatenation peer resolves element types - value" {
746763 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
747764 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
......@@ -751,7 +768,7 @@ test "array concatenation peer resolves element types - value" {
751768 var a = [2]u3{ 1, 7 };
752769 var b = [3]u8{ 200, 225, 255 };
753770 var c = a ++ b;
754 try expect(@TypeOf(c) == [5]u8);
771 comptime assert(@TypeOf(c) == [5]u8);
755772 try expect(c[0] == 1);
756773 try expect(c[1] == 7);
757774 try expect(c[2] == 200);
......@@ -768,7 +785,7 @@ test "array concatenation peer resolves element types - pointer" {
768785 var a = [2]u3{ 1, 7 };
769786 var b = [3]u8{ 200, 225, 255 };
770787 var c = &a ++ &b;
771 try expect(@TypeOf(c) == *[5]u8);
788 comptime assert(@TypeOf(c) == *[5]u8);
772789 try expect(c[0] == 1);
773790 try expect(c[1] == 7);
774791 try expect(c[2] == 200);
......@@ -776,23 +793,80 @@ test "array concatenation peer resolves element types - pointer" {
776793 try expect(c[4] == 255);
777794}
778795
779test "array multiplication forces comptime" {
780 if (builtin.zig_backend != .stage1) {
781 // note: our plan is to change the language to support runtime array
782 // multiplication instead of making this test pass.
783 return error.SkipZigTest; // TODO
784 }
796test "array concatenation sets the sentinel - value" {
797 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
798 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
799 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
800 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
801 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
802 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
785803
786 var a = oneItem(3) ** scalar(2);
787 try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 3 }));
804 var a = [2]u3{ 1, 7 };
805 var b = [3:69]u8{ 200, 225, 255 };
806 var c = a ++ b;
807 comptime assert(@TypeOf(c) == [5:69]u8);
808 try expect(c[0] == 1);
809 try expect(c[1] == 7);
810 try expect(c[2] == 200);
811 try expect(c[3] == 225);
812 try expect(c[4] == 255);
813 var ptr: [*]const u8 = &c;
814 try expect(ptr[5] == 69);
788815}
789816
790fn oneItem(x: i32) [1]i32 {
791 return [_]i32{x};
817test "array concatenation sets the sentinel - pointer" {
818 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
819 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
820 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
821 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
822
823 var a = [2]u3{ 1, 7 };
824 var b = [3:69]u8{ 200, 225, 255 };
825 var c = &a ++ &b;
826 comptime assert(@TypeOf(c) == *[5:69]u8);
827 try expect(c[0] == 1);
828 try expect(c[1] == 7);
829 try expect(c[2] == 200);
830 try expect(c[3] == 225);
831 try expect(c[4] == 255);
832 var ptr: [*]const u8 = c;
833 try expect(ptr[5] == 69);
792834}
793835
794fn scalar(x: u32) u32 {
795 return x;
836test "array multiplication sets the sentinel - value" {
837 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
838 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
839 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
840 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
841 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
842 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
843
844 var a = [2:7]u3{ 1, 6 };
845 var b = a ** 2;
846 comptime assert(@TypeOf(b) == [4:7]u3);
847 try expect(b[0] == 1);
848 try expect(b[1] == 6);
849 try expect(b[2] == 1);
850 try expect(b[3] == 6);
851 var ptr: [*]const u3 = &b;
852 try expect(ptr[4] == 7);
853}
854
855test "array multiplication sets the sentinel - pointer" {
856 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
857 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
858 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
859 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
860
861 var a = [2:7]u3{ 1, 6 };
862 var b = &a ** 2;
863 comptime assert(@TypeOf(b) == *[4:7]u3);
864 try expect(b[0] == 1);
865 try expect(b[1] == 6);
866 try expect(b[2] == 1);
867 try expect(b[3] == 6);
868 var ptr: [*]const u3 = b;
869 try expect(ptr[4] == 7);
796870}
797871
798872test "comptime assign int to optional int" {