authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-09 18:47:42-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-09 18:49:37-07:00
logf736cde397a6abb1399827ed5988c43001706580
treec1ce080f82846a4322eac7be653a78044338e740
parent017d3864de1f337d01726e87534a4e2093c7265f

Sema: implement pointer to tuple to pointer to array coercion

This involved an LLVM backend fix for the aggregate_init instruction.

8 files changed, 80 insertions(+), 41 deletions(-)

src/Sema.zig+44-18
...@@ -16005,9 +16005,17 @@ fn coerce(...@@ -16005,9 +16005,17 @@ fn coerce(
16005 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);16005 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
16006 }16006 }
1600716007
16008 // coercion to C pointer16008 // cast from *T and [*]T to *anyopaque
16009 if (dest_info.size == .C) {16009 // but don't do it if the source type is a double pointer
16010 switch (inst_ty.zigTypeTag()) {16010 if (dest_info.pointee_type.tag() == .anyopaque and inst_ty.zigTypeTag() == .Pointer and
16011 inst_ty.childType().zigTypeTag() != .Pointer)
16012 {
16013 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
16014 }
16015
16016 switch (dest_info.size) {
16017 // coercion to C pointer
16018 .C => switch (inst_ty.zigTypeTag()) {
16011 .Null => {16019 .Null => {
16012 return sema.addConstant(dest_ty, Value.@"null");16020 return sema.addConstant(dest_ty, Value.@"null");
16013 },16021 },
...@@ -16041,22 +16049,10 @@ fn coerce(...@@ -16041,22 +16049,10 @@ fn coerce(
16041 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);16049 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
16042 },16050 },
16043 else => {},16051 else => {},
16044 }16052 },
16045 }
16046
16047 // cast from *T and [*]T to *anyopaque
16048 // but don't do it if the source type is a double pointer
16049 if (dest_info.pointee_type.tag() == .anyopaque and inst_ty.zigTypeTag() == .Pointer and
16050 inst_ty.childType().zigTypeTag() != .Pointer)
16051 {
16052 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
16053 }
16054
16055 switch (dest_info.size) {
16056 .C, .Many => {},
16057 .One => switch (dest_info.pointee_type.zigTypeTag()) {16053 .One => switch (dest_info.pointee_type.zigTypeTag()) {
16058 .Union => {16054 .Union => {
16059 // cast from pointer to anonymous struct to pointer to union16055 // pointer to anonymous struct to pointer to union
16060 if (inst_ty.isSinglePointer() and16056 if (inst_ty.isSinglePointer() and
16061 inst_ty.childType().isAnonStruct() and16057 inst_ty.childType().isAnonStruct() and
16062 !dest_info.mutable)16058 !dest_info.mutable)
...@@ -16065,7 +16061,7 @@ fn coerce(...@@ -16065,7 +16061,7 @@ fn coerce(
16065 }16061 }
16066 },16062 },
16067 .Struct => {16063 .Struct => {
16068 // cast from pointer to anonymous struct to pointer to struct16064 // pointer to anonymous struct to pointer to struct
16069 if (inst_ty.isSinglePointer() and16065 if (inst_ty.isSinglePointer() and
16070 inst_ty.childType().isAnonStruct() and16066 inst_ty.childType().isAnonStruct() and
16071 !dest_info.mutable)16067 !dest_info.mutable)
...@@ -16073,6 +16069,15 @@ fn coerce(...@@ -16073,6 +16069,15 @@ fn coerce(
16073 return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src);16069 return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
16074 }16070 }
16075 },16071 },
16072 .Array => {
16073 // pointer to tuple to pointer to array
16074 if (inst_ty.isSinglePointer() and
16075 inst_ty.childType().isTuple() and
16076 !dest_info.mutable)
16077 {
16078 return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
16079 }
16080 },
16076 else => {},16081 else => {},
16077 },16082 },
16078 .Slice => {16083 .Slice => {
...@@ -16084,6 +16089,7 @@ fn coerce(...@@ -16084,6 +16089,7 @@ fn coerce(
16084 return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);16089 return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
16085 }16090 }
16086 },16091 },
16092 .Many => {},
16087 }16093 }
1608816094
16089 // This will give an extra hint on top of what the bottom of this func would provide.16095 // This will give an extra hint on top of what the bottom of this func would provide.
...@@ -17554,6 +17560,26 @@ fn coerceTupleToSlicePtrs(...@@ -17554,6 +17560,26 @@ fn coerceTupleToSlicePtrs(
17554 return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);17560 return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
17555}17561}
1755617562
17563/// If the lengths match, coerces element-wise.
17564fn coerceTupleToArrayPtrs(
17565 sema: *Sema,
17566 block: *Block,
17567 ptr_array_ty: Type,
17568 array_ty_src: LazySrcLoc,
17569 ptr_tuple: Air.Inst.Ref,
17570 tuple_src: LazySrcLoc,
17571) !Air.Inst.Ref {
17572 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
17573 const ptr_info = ptr_array_ty.ptrInfo().data;
17574 const array_ty = ptr_info.pointee_type;
17575 const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src);
17576 if (ptr_info.@"align" != 0) {
17577 return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{});
17578 }
17579 const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst);
17580 return ptr_array;
17581}
17582
17557/// Handles both tuples and anon struct literals. Coerces field-wise. Reports17583/// Handles both tuples and anon struct literals. Coerces field-wise. Reports
17558/// errors for both extra fields and missing fields.17584/// errors for both extra fields and missing fields.
17559fn coerceTupleToStruct(17585fn coerceTupleToStruct(
src/codegen/llvm.zig+17-4
...@@ -5838,8 +5838,15 @@ pub const FuncGen = struct {...@@ -5838,8 +5838,15 @@ pub const FuncGen = struct {
5838 const llvm_i = llvmFieldIndex(result_ty, i, target, &ptr_ty_buf).?;5838 const llvm_i = llvmFieldIndex(result_ty, i, target, &ptr_ty_buf).?;
5839 indices[1] = llvm_u32.constInt(llvm_i, .False);5839 indices[1] = llvm_u32.constInt(llvm_i, .False);
5840 const field_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");5840 const field_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");
5841 const store_inst = self.builder.buildStore(llvm_elem, field_ptr);5841 var field_ptr_payload: Type.Payload.Pointer = .{
5842 store_inst.setAlignment(result_ty.structFieldAlign(i, target));5842 .data = .{
5843 .pointee_type = self.air.typeOf(elem),
5844 .@"align" = result_ty.structFieldAlign(i, target),
5845 .@"addrspace" = .generic,
5846 },
5847 };
5848 const field_ptr_ty = Type.initPayload(&field_ptr_payload.base);
5849 self.store(field_ptr, field_ptr_ty, llvm_elem, .NotAtomic);
5843 }5850 }
58445851
5845 return alloca_inst;5852 return alloca_inst;
...@@ -5871,8 +5878,14 @@ pub const FuncGen = struct {...@@ -5871,8 +5878,14 @@ pub const FuncGen = struct {
5871 };5878 };
5872 const elem_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");5879 const elem_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");
5873 const llvm_elem = try self.resolveInst(elem);5880 const llvm_elem = try self.resolveInst(elem);
5874 const store_inst = self.builder.buildStore(llvm_elem, elem_ptr);5881 var elem_ptr_payload: Type.Payload.Pointer = .{
5875 store_inst.setAlignment(elem_ty.abiAlignment(target));5882 .data = .{
5883 .pointee_type = elem_ty,
5884 .@"addrspace" = .generic,
5885 },
5886 };
5887 const elem_ptr_ty = Type.initPayload(&elem_ptr_payload.base);
5888 self.store(elem_ptr, elem_ptr_ty, llvm_elem, .NotAtomic);
5876 }5889 }
58775890
5878 return alloca_inst;5891 return alloca_inst;
test/behavior/align.zig+4-6
...@@ -193,11 +193,9 @@ test "function alignment" {...@@ -193,11 +193,9 @@ test "function alignment" {
193}193}
194194
195test "implicitly decreasing fn alignment" {195test "implicitly decreasing fn alignment" {
196 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
197 if (builtin.zig_backend == .stage1) return error.SkipZigTest;196 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
198 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
199 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;197 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
200 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;198 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
201 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;199 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
202200
203 // function alignment is a compile error on wasm32/wasm64201 // function alignment is a compile error on wasm32/wasm64
...@@ -321,7 +319,6 @@ const DefaultAligned = struct {...@@ -321,7 +319,6 @@ const DefaultAligned = struct {
321319
322test "read 128-bit field from default aligned struct in stack memory" {320test "read 128-bit field from default aligned struct in stack memory" {
323 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;321 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
324 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
325 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;322 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
326 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;323 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
327 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;324 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -353,10 +350,10 @@ test "read 128-bit field from default aligned struct in global memory" {...@@ -353,10 +350,10 @@ test "read 128-bit field from default aligned struct in global memory" {
353350
354test "struct field explicit alignment" {351test "struct field explicit alignment" {
355 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;352 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
356 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
357 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;353 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
358 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;354 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
359 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;355 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
356 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
360357
361 const S = struct {358 const S = struct {
362 const Node = struct {359 const Node = struct {
...@@ -402,8 +399,9 @@ test "align(@alignOf(T)) T does not force resolution of T" {...@@ -402,8 +399,9 @@ test "align(@alignOf(T)) T does not force resolution of T" {
402}399}
403400
404test "align(N) on functions" {401test "align(N) on functions" {
405 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
406 if (builtin.zig_backend == .stage1) return error.SkipZigTest;402 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
403
404 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
407 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;405 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
408 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;406 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
409 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;407 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
test/behavior/array.zig+2-3
...@@ -553,12 +553,11 @@ test "type coercion of anon struct literal to array" {...@@ -553,12 +553,11 @@ test "type coercion of anon struct literal to array" {
553}553}
554554
555test "type coercion of pointer to anon struct literal to pointer to array" {555test "type coercion of pointer to anon struct literal to pointer to array" {
556 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
557 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
558 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO556 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
559 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO557 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
560 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO558 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
561 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO559 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
560 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
562561
563 const S = struct {562 const S = struct {
564 const U = union {563 const U = union {
test/behavior/basic.zig+2-1
...@@ -760,7 +760,8 @@ test "pointer to thread local array" {...@@ -760,7 +760,8 @@ test "pointer to thread local array" {
760threadlocal var buffer: [11]u8 = undefined;760threadlocal var buffer: [11]u8 = undefined;
761761
762test "auto created variables have correct alignment" {762test "auto created variables have correct alignment" {
763 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO763 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
764 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
764765
765 const S = struct {766 const S = struct {
766 fn foo(str: [*]const u8) u32 {767 fn foo(str: [*]const u8) u32 {
test/behavior/slice.zig+6-4
...@@ -518,7 +518,11 @@ test "slice syntax resulting in pointer-to-array" {...@@ -518,7 +518,11 @@ test "slice syntax resulting in pointer-to-array" {
518}518}
519519
520test "type coercion of pointer to anon struct literal to pointer to slice" {520test "type coercion of pointer to anon struct literal to pointer to slice" {
521 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO521 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
522 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
523 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
524 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
525 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
522526
523 const S = struct {527 const S = struct {
524 const U = union {528 const U = union {
...@@ -546,7 +550,7 @@ test "type coercion of pointer to anon struct literal to pointer to slice" {...@@ -546,7 +550,7 @@ test "type coercion of pointer to anon struct literal to pointer to slice" {
546 try expect(mem.eql(u8, slice2[2], "world!"));550 try expect(mem.eql(u8, slice2[2], "world!"));
547 }551 }
548 };552 };
549 // try S.doTheTest();553 try S.doTheTest();
550 comptime try S.doTheTest();554 comptime try S.doTheTest();
551}555}
552556
...@@ -578,8 +582,6 @@ test "slice bounds in comptime concatenation" {...@@ -578,8 +582,6 @@ test "slice bounds in comptime concatenation" {
578}582}
579583
580test "slice sentinel access at comptime" {584test "slice sentinel access at comptime" {
581 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
582
583 {585 {
584 const str0 = &[_:0]u8{ '1', '2', '3' };586 const str0 = &[_:0]u8{ '1', '2', '3' };
585 const slice0: [:0]const u8 = str0;587 const slice0: [:0]const u8 = str0;
test/behavior/struct.zig+1-1
...@@ -426,7 +426,7 @@ test "packed struct 24bits" {...@@ -426,7 +426,7 @@ test "packed struct 24bits" {
426 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO426 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
427 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO427 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
428 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO428 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
429 if (builtin.zig_backend == .stage2_llvm and builtin.stage2_arch == .wasm32) return error.SkipZigTest; // TODO429 if (builtin.cpu.arch == .wasm32) return error.SkipZigTest; // TODO
430430
431 comptime {431 comptime {
432 try expect(@sizeOf(Foo24Bits) == 4);432 try expect(@sizeOf(Foo24Bits) == 4);
test/behavior/while.zig+4-4
...@@ -289,9 +289,8 @@ test "while bool 2 break statements and an else" {...@@ -289,9 +289,8 @@ test "while bool 2 break statements and an else" {
289}289}
290290
291test "while optional 2 break statements and an else" {291test "while optional 2 break statements and an else" {
292 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;292 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
293 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;293 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
294 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
295294
296 const S = struct {295 const S = struct {
297 fn entry(opt_t: ?bool, f: bool) !void {296 fn entry(opt_t: ?bool, f: bool) !void {
...@@ -308,7 +307,8 @@ test "while optional 2 break statements and an else" {...@@ -308,7 +307,8 @@ test "while optional 2 break statements and an else" {
308}307}
309308
310test "while error 2 break statements and an else" {309test "while error 2 break statements and an else" {
311 if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO310 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
311 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
312312
313 const S = struct {313 const S = struct {
314 fn entry(opt_t: anyerror!bool, f: bool) !void {314 fn entry(opt_t: anyerror!bool, f: bool) !void {