authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-09 17:33:01-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-09 17:33:01-07:00
logf32a77b30df48552897a5c9023c39d8ba6610821
tree0a12927beef0a93bbfeb6ab02989b48b94b30eb9
parent3b6e8fa59e6fb933e8279ba676ef986739665247

Sema: implement pointer-to-tuple coercion to slice and struct


5 files changed, 86 insertions(+), 23 deletions(-)

src/Sema.zig+75-13
...@@ -16052,13 +16052,38 @@ fn coerce(...@@ -16052,13 +16052,38 @@ fn coerce(
16052 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);16052 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
16053 }16053 }
1605416054
16055 // cast from pointer to anonymous struct to pointer to union16055 switch (dest_info.size) {
16056 if (dest_info.pointee_type.zigTypeTag() == .Union and16056 .C, .Many => {},
16057 inst_ty.zigTypeTag() == .Pointer and16057 .One => switch (dest_info.pointee_type.zigTypeTag()) {
16058 inst_ty.childType().tag() == .anon_struct and16058 .Union => {
16059 !dest_info.mutable)16059 // cast from pointer to anonymous struct to pointer to union
16060 {16060 if (inst_ty.isSinglePointer() and
16061 return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);16061 inst_ty.childType().isAnonStruct() and
16062 !dest_info.mutable)
16063 {
16064 return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
16065 }
16066 },
16067 .Struct => {
16068 // cast from pointer to anonymous struct to pointer to struct
16069 if (inst_ty.isSinglePointer() and
16070 inst_ty.childType().isAnonStruct() and
16071 !dest_info.mutable)
16072 {
16073 return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
16074 }
16075 },
16076 else => {},
16077 },
16078 .Slice => {
16079 // pointer to tuple to slice
16080 if (inst_ty.isSinglePointer() and
16081 inst_ty.childType().isTuple() and
16082 !dest_info.mutable and dest_info.size == .Slice)
16083 {
16084 return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
16085 }
16086 },
16062 }16087 }
1606316088
16064 // This will give an extra hint on top of what the bottom of this func would provide.16089 // This will give an extra hint on top of what the bottom of this func would provide.
...@@ -17365,6 +17390,20 @@ fn coerceAnonStructToUnionPtrs(...@@ -17365,6 +17390,20 @@ fn coerceAnonStructToUnionPtrs(
17365 return sema.analyzeRef(block, union_ty_src, union_inst);17390 return sema.analyzeRef(block, union_ty_src, union_inst);
17366}17391}
1736717392
17393fn coerceAnonStructToStructPtrs(
17394 sema: *Sema,
17395 block: *Block,
17396 ptr_struct_ty: Type,
17397 struct_ty_src: LazySrcLoc,
17398 ptr_anon_struct: Air.Inst.Ref,
17399 anon_struct_src: LazySrcLoc,
17400) !Air.Inst.Ref {
17401 const struct_ty = ptr_struct_ty.childType();
17402 const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
17403 const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, struct_ty_src, anon_struct, anon_struct_src);
17404 return sema.analyzeRef(block, struct_ty_src, struct_inst);
17405}
17406
17368/// If the lengths match, coerces element-wise.17407/// If the lengths match, coerces element-wise.
17369fn coerceArrayLike(17408fn coerceArrayLike(
17370 sema: *Sema,17409 sema: *Sema,
...@@ -17494,6 +17533,27 @@ fn coerceTupleToArray(...@@ -17494,6 +17533,27 @@ fn coerceTupleToArray(
17494 );17533 );
17495}17534}
1749617535
17536/// If the lengths match, coerces element-wise.
17537fn coerceTupleToSlicePtrs(
17538 sema: *Sema,
17539 block: *Block,
17540 slice_ty: Type,
17541 slice_ty_src: LazySrcLoc,
17542 ptr_tuple: Air.Inst.Ref,
17543 tuple_src: LazySrcLoc,
17544) !Air.Inst.Ref {
17545 const tuple_ty = sema.typeOf(ptr_tuple).childType();
17546 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
17547 const slice_info = slice_ty.ptrInfo().data;
17548 const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type);
17549 const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
17550 if (slice_info.@"align" != 0) {
17551 return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
17552 }
17553 const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst);
17554 return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
17555}
17556
17497/// Handles both tuples and anon struct literals. Coerces field-wise. Reports17557/// Handles both tuples and anon struct literals. Coerces field-wise. Reports
17498/// errors for both extra fields and missing fields.17558/// errors for both extra fields and missing fields.
17499fn coerceTupleToStruct(17559fn coerceTupleToStruct(
...@@ -17504,11 +17564,13 @@ fn coerceTupleToStruct(...@@ -17504,11 +17564,13 @@ fn coerceTupleToStruct(
17504 inst: Air.Inst.Ref,17564 inst: Air.Inst.Ref,
17505 inst_src: LazySrcLoc,17565 inst_src: LazySrcLoc,
17506) !Air.Inst.Ref {17566) !Air.Inst.Ref {
17507 if (dest_ty.isTupleOrAnonStruct()) {17567 const struct_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty);
17568
17569 if (struct_ty.isTupleOrAnonStruct()) {
17508 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{});17570 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{});
17509 }17571 }
1751017572
17511 const fields = dest_ty.structFields();17573 const fields = struct_ty.structFields();
17512 const field_vals = try sema.arena.alloc(Value, fields.count());17574 const field_vals = try sema.arena.alloc(Value, fields.count());
17513 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);17575 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
17514 mem.set(Air.Inst.Ref, field_refs, .none);17576 mem.set(Air.Inst.Ref, field_refs, .none);
...@@ -17523,7 +17585,7 @@ fn coerceTupleToStruct(...@@ -17523,7 +17585,7 @@ fn coerceTupleToStruct(
17523 payload.data.names[i]17585 payload.data.names[i]
17524 else17586 else
17525 try std.fmt.allocPrint(sema.arena, "{d}", .{i});17587 try std.fmt.allocPrint(sema.arena, "{d}", .{i});
17526 const field_index = try sema.structFieldIndex(block, dest_ty, field_name, field_src);17588 const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
17527 const field = fields.values()[field_index];17589 const field = fields.values()[field_index];
17528 if (field.is_comptime) {17590 if (field.is_comptime) {
17529 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});17591 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});
...@@ -17567,17 +17629,17 @@ fn coerceTupleToStruct(...@@ -17567,17 +17629,17 @@ fn coerceTupleToStruct(
17567 }17629 }
1756817630
17569 if (root_msg) |msg| {17631 if (root_msg) |msg| {
17570 try sema.addDeclaredHereNote(msg, dest_ty);17632 try sema.addDeclaredHereNote(msg, struct_ty);
17571 return sema.failWithOwnedErrorMsg(block, msg);17633 return sema.failWithOwnedErrorMsg(block, msg);
17572 }17634 }
1757317635
17574 if (runtime_src) |rs| {17636 if (runtime_src) |rs| {
17575 try sema.requireRuntimeBlock(block, rs);17637 try sema.requireRuntimeBlock(block, rs);
17576 return block.addAggregateInit(dest_ty, field_refs);17638 return block.addAggregateInit(struct_ty, field_refs);
17577 }17639 }
1757817640
17579 return sema.addConstant(17641 return sema.addConstant(
17580 dest_ty,17642 struct_ty,
17581 try Value.Tag.@"struct".create(sema.arena, field_vals),17643 try Value.Tag.@"struct".create(sema.arena, field_vals),
17582 );17644 );
17583}17645}
src/type.zig+2-1
...@@ -3502,6 +3502,7 @@ pub const Type = extern union {...@@ -3502,6 +3502,7 @@ pub const Type = extern union {
3502 .tuple => ty.castTag(.tuple).?.data.types.len,3502 .tuple => ty.castTag(.tuple).?.data.types.len,
3503 .anon_struct => ty.castTag(.anon_struct).?.data.types.len,3503 .anon_struct => ty.castTag(.anon_struct).?.data.types.len,
3504 .@"struct" => ty.castTag(.@"struct").?.data.fields.count(),3504 .@"struct" => ty.castTag(.@"struct").?.data.fields.count(),
3505 .empty_struct, .empty_struct_literal => 0,
35053506
3506 else => unreachable,3507 else => unreachable,
3507 };3508 };
...@@ -5070,7 +5071,7 @@ pub const Type = extern union {...@@ -5070,7 +5071,7 @@ pub const Type = extern union {
50705071
5071 pub fn isAnonStruct(ty: Type) bool {5072 pub fn isAnonStruct(ty: Type) bool {
5072 return switch (ty.tag()) {5073 return switch (ty.tag()) {
5073 .anon_struct => true,5074 .anon_struct, .empty_struct_literal => true,
5074 else => false,5075 else => false,
5075 };5076 };
5076 }5077 }
test/behavior/align.zig+2-6
...@@ -6,17 +6,15 @@ const native_arch = builtin.target.cpu.arch;...@@ -6,17 +6,15 @@ const native_arch = builtin.target.cpu.arch;
6var foo: u8 align(4) = 100;6var foo: u8 align(4) = 100;
77
8test "global variable alignment" {8test "global variable alignment" {
9 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
10
11 comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);9 comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
12 comptime try expect(@TypeOf(&foo) == *align(4) u8);10 comptime try expect(@TypeOf(&foo) == *align(4) u8);
13 {11 {
14 const slice = @as(*[1]u8, &foo)[0..];12 const slice = @as(*align(4) [1]u8, &foo)[0..];
15 comptime try expect(@TypeOf(slice) == *align(4) [1]u8);13 comptime try expect(@TypeOf(slice) == *align(4) [1]u8);
16 }14 }
17 {15 {
18 var runtime_zero: usize = 0;16 var runtime_zero: usize = 0;
19 const slice = @as(*[1]u8, &foo)[runtime_zero..];17 const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..];
20 comptime try expect(@TypeOf(slice) == []align(4) u8);18 comptime try expect(@TypeOf(slice) == []align(4) u8);
21 }19 }
22}20}
...@@ -86,8 +84,6 @@ test "size of extern struct with 128-bit field" {...@@ -86,8 +84,6 @@ test "size of extern struct with 128-bit field" {
86}84}
8785
88test "@ptrCast preserves alignment of bigger source" {86test "@ptrCast preserves alignment of bigger source" {
89 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
90
91 var x: u32 align(16) = 1234;87 var x: u32 align(16) = 1234;
92 const ptr = @ptrCast(*u8, &x);88 const ptr = @ptrCast(*u8, &x);
93 try expect(@TypeOf(ptr) == *align(16) u8);89 try expect(@TypeOf(ptr) == *align(16) u8);
test/behavior/struct.zig+5-1
...@@ -1072,7 +1072,11 @@ test "type coercion of anon struct literal to struct" {...@@ -1072,7 +1072,11 @@ test "type coercion of anon struct literal to struct" {
1072}1072}
10731073
1074test "type coercion of pointer to anon struct literal to pointer to struct" {1074test "type coercion of pointer to anon struct literal to pointer to struct" {
1075 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO1075 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1076 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1077 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1078 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1079 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10761080
1077 const S = struct {1081 const S = struct {
1078 const S2 = struct {1082 const S2 = struct {
test/behavior/tuple.zig+2-2
...@@ -159,8 +159,8 @@ test "array-like initializer for tuple types" {...@@ -159,8 +159,8 @@ test "array-like initializer for tuple types" {
159 const S = struct {159 const S = struct {
160 fn doTheTest() !void {160 fn doTheTest() !void {
161 var obj: T = .{ -1234, 128 };161 var obj: T = .{ -1234, 128 };
162 try testing.expectEqual(@as(i32, -1234), obj[0]);162 try expect(@as(i32, -1234) == obj[0]);
163 try testing.expectEqual(@as(u8, 128), obj[1]);163 try expect(@as(u8, 128) == obj[1]);
164 }164 }
165 };165 };
166166