authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-22 14:10:52+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-23 22:16:31+02:00
log8eea73fb922c1c7fab4b8bf1717588464691a66e
tree6b80dd1b1d2e1993e05e6d3c19cbc5ba4d424c5e
parent4cea15f12bdaf1603d60cf80e767dd8f2bb48b26

add tests for tuple declarations


14 files changed, 260 insertions(+), 30 deletions(-)

lib/std/zig/parse.zig+1-1
......@@ -884,7 +884,7 @@ const Parser = struct {
884884
885885 var align_expr: Node.Index = 0;
886886 var type_expr: Node.Index = 0;
887 if (p.eatToken(.colon) != null or tuple_like) |_| {
887 if (p.eatToken(.colon) != null or tuple_like) {
888888 type_expr = try p.expectTypeExpr();
889889 align_expr = try p.parseByteAlign();
890890 }
lib/std/zig/render.zig+19
......@@ -1189,6 +1189,16 @@ fn renderContainerField(
11891189 try renderToken(ais, tree, t, .space); // comptime
11901190 }
11911191 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
1192 if (field.ast.align_expr != 0) {
1193 try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
1194 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
1195 const align_kw = lparen_token - 1;
1196 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
1197 try renderToken(ais, tree, align_kw, .none); // align
1198 try renderToken(ais, tree, lparen_token, .none); // (
1199 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
1200 return renderToken(ais, tree, rparen_token, .space); // )
1201 }
11921202 return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name
11931203 }
11941204 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
......@@ -1211,6 +1221,15 @@ fn renderContainerField(
12111221 }
12121222 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
12131223 try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
1224 if (field.ast.align_expr != 0) {
1225 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
1226 const align_kw = lparen_token - 1;
1227 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
1228 try renderToken(ais, tree, align_kw, .none); // align
1229 try renderToken(ais, tree, lparen_token, .none); // (
1230 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
1231 try renderToken(ais, tree, rparen_token, .space); // )
1232 }
12141233 try renderToken(ais, tree, field.ast.main_token + 1, .space); // =
12151234 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
12161235 }
src/AstGen.zig+3-2
......@@ -4854,8 +4854,9 @@ fn containerDecl(
48544854 },
48554855 );
48564856 }
4857 // Alignment expressions in enums are caught by the parser.
4858 assert(member.ast.align_expr == 0);
4857 if (member.ast.align_expr != 0) {
4858 return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{});
4859 }
48594860
48604861 const name_token = member.ast.main_token;
48614862 if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
src/Sema.zig+41-8
......@@ -11834,6 +11834,12 @@ fn analyzeTupleCat(
1183411834 if (dest_fields == 0) {
1183511835 return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
1183611836 }
11837 if (lhs_len == 0) {
11838 return rhs;
11839 }
11840 if (rhs_len == 0) {
11841 return lhs;
11842 }
1183711843 const final_len = try sema.usizeCast(block, rhs_src, dest_fields);
1183811844
1183911845 const types = try sema.arena.alloc(Type, final_len);
......@@ -11880,13 +11886,13 @@ fn analyzeTupleCat(
1188011886 var i: u32 = 0;
1188111887 while (i < lhs_len) : (i += 1) {
1188211888 const operand_src = lhs_src; // TODO better source location
11883 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, @intCast(u32, i), lhs_ty);
11889 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, i, lhs_ty);
1188411890 }
1188511891 i = 0;
1188611892 while (i < rhs_len) : (i += 1) {
1188711893 const operand_src = rhs_src; // TODO better source location
1188811894 element_refs[i + lhs_len] =
11889 try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty);
11895 try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty);
1189011896 }
1189111897
1189211898 return block.addAggregateInit(tuple_ty, element_refs);
......@@ -18502,8 +18508,12 @@ fn reifyStruct(
1850218508 }
1850318509 const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?);
1850418510
18505 if (layout == .Packed and abi_align != 0) {
18506 return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
18511 if (layout == .Packed) {
18512 if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
18513 if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{});
18514 }
18515 if (layout == .Extern and is_comptime_val.toBool()) {
18516 return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{});
1850718517 }
1850818518
1850918519 const field_name = try name_val.toAllocatedBytes(
......@@ -18512,6 +18522,25 @@ fn reifyStruct(
1851218522 mod,
1851318523 );
1851418524
18525 if (is_tuple) {
18526 const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch {
18527 return sema.fail(
18528 block,
18529 src,
18530 "tuple cannot have non-numeric field '{s}'",
18531 .{field_name},
18532 );
18533 };
18534
18535 if (field_index >= fields_len) {
18536 return sema.fail(
18537 block,
18538 src,
18539 "tuple field {} exceeds tuple field count",
18540 .{field_index},
18541 );
18542 }
18543 }
1851518544 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
1851618545 if (gop.found_existing) {
1851718546 // TODO: better source location
......@@ -18525,6 +18554,9 @@ fn reifyStruct(
1852518554 opt_val;
1852618555 break :blk try payload_val.copy(new_decl_arena_allocator);
1852718556 } else Value.initTag(.unreachable_value);
18557 if (is_comptime_val.toBool() and default_val.tag() == .unreachable_value) {
18558 return sema.fail(block, src, "comptime field without default initialization value", .{});
18559 }
1852818560
1852918561 var buffer: Value.ToTypeBuffer = undefined;
1853018562 gop.value_ptr.* = .{
......@@ -27094,7 +27126,7 @@ fn coerceTupleToStruct(
2709427126
2709527127 const inst_ty = sema.typeOf(inst);
2709627128 var runtime_src: ?LazySrcLoc = null;
27097 const field_count = struct_ty.structFieldCount();
27129 const field_count = inst_ty.structFieldCount();
2709827130 var field_i: u32 = 0;
2709927131 while (field_i < field_count) : (field_i += 1) {
2710027132 const field_src = inst_src; // TODO better source location
......@@ -27176,15 +27208,16 @@ fn coerceTupleToTuple(
2717627208 inst: Air.Inst.Ref,
2717727209 inst_src: LazySrcLoc,
2717827210) !Air.Inst.Ref {
27179 const field_count = tuple_ty.structFieldCount();
27180 const field_vals = try sema.arena.alloc(Value, field_count);
27211 const dest_field_count = tuple_ty.structFieldCount();
27212 const field_vals = try sema.arena.alloc(Value, dest_field_count);
2718127213 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
2718227214 mem.set(Air.Inst.Ref, field_refs, .none);
2718327215
2718427216 const inst_ty = sema.typeOf(inst);
27217 const inst_field_count = inst_ty.structFieldCount();
2718527218 var runtime_src: ?LazySrcLoc = null;
2718627219 var field_i: u32 = 0;
27187 while (field_i < field_count) : (field_i += 1) {
27220 while (field_i < inst_field_count) : (field_i += 1) {
2718827221 const field_src = inst_src; // TODO better source location
2718927222 const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
2719027223 payload.data.names[field_i]
src/arch/x86_64/abi.zig+1
......@@ -552,6 +552,7 @@ test "C_C_D" {
552552 .layout = .Extern,
553553 .status = .fully_resolved,
554554 .known_non_opv = true,
555 .is_tuple = false,
555556 };
556557 var C_C_D = Type.Payload.Struct{ .data = &C_C_D_struct };
557558
src/type.zig+6-2
......@@ -804,7 +804,7 @@ pub const Type = extern union {
804804 return a_struct_obj == b_struct_obj;
805805 },
806806 .tuple, .empty_struct_literal => {
807 if (!b.isTuple()) return false;
807 if (!b.isSimpleTuple()) return false;
808808
809809 const a_tuple = a.tupleFields();
810810 const b_tuple = b.tupleFields();
......@@ -5572,7 +5572,11 @@ pub const Type = extern union {
55725572
55735573 pub fn structFieldCount(ty: Type) usize {
55745574 switch (ty.tag()) {
5575 .@"struct" => return ty.castTag(.@"struct").?.data.fields.count(),
5575 .@"struct" => {
5576 const struct_obj = ty.castTag(.@"struct").?.data;
5577 assert(struct_obj.haveFieldTypes());
5578 return struct_obj.fields.count();
5579 },
55765580 .empty_struct, .empty_struct_literal => return 0,
55775581 .tuple => return ty.castTag(.tuple).?.data.types.len,
55785582 .anon_struct => return ty.castTag(.anon_struct).?.data.types.len,
test/behavior.zig+1
......@@ -200,6 +200,7 @@ test {
200200 _ = @import("behavior/packed_struct_explicit_backing_int.zig");
201201 _ = @import("behavior/empty_union.zig");
202202 _ = @import("behavior/inline_switch.zig");
203 _ = @import("behavior/tuple_declarations.zig");
203204 _ = @import("behavior/bugs/12723.zig");
204205 _ = @import("behavior/bugs/12776.zig");
205206 }
test/behavior/tuple_declarations.zig created+79
......@@ -0,0 +1,79 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const testing = std.testing;
4const expect = testing.expect;
5const expectEqualStrings = testing.expectEqualStrings;
6
7test "tuple declaration type info" {
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
10 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
11
12 {
13 const T = struct { comptime u32 align(2) = 1, []const u8 };
14 const info = @typeInfo(T).Struct;
15
16 try expect(info.layout == .Auto);
17 try expect(info.backing_integer == null);
18 try expect(info.fields.len == 2);
19 try expect(info.decls.len == 0);
20 try expect(info.is_tuple);
21
22 try expectEqualStrings(info.fields[0].name, "0");
23 try expect(info.fields[0].field_type == u32);
24 try expect(@ptrCast(*const u32, @alignCast(@alignOf(u32), info.fields[0].default_value)).* == 1);
25 try expect(info.fields[0].is_comptime);
26 try expect(info.fields[0].alignment == 2);
27
28 try expectEqualStrings(info.fields[1].name, "1");
29 try expect(info.fields[1].field_type == []const u8);
30 try expect(info.fields[1].default_value == null);
31 try expect(!info.fields[1].is_comptime);
32 try expect(info.fields[1].alignment == @alignOf([]const u8));
33 }
34 {
35 const T = packed struct(u32) { u1, u30, u1 };
36 const info = @typeInfo(T).Struct;
37
38 try expect(std.mem.endsWith(u8, @typeName(T), "test.tuple declaration type info.T"));
39
40 try expect(info.layout == .Packed);
41 try expect(info.backing_integer == u32);
42 try expect(info.fields.len == 3);
43 try expect(info.decls.len == 0);
44 try expect(info.is_tuple);
45
46 try expectEqualStrings(info.fields[0].name, "0");
47 try expect(info.fields[0].field_type == u1);
48
49 try expectEqualStrings(info.fields[1].name, "1");
50 try expect(info.fields[1].field_type == u30);
51
52 try expectEqualStrings(info.fields[2].name, "2");
53 try expect(info.fields[2].field_type == u1);
54 }
55}
56
57test "Tuple declaration usage" {
58 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
59 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
60 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
61
62 const T = struct { u32, []const u8 };
63 var t: T = .{ 1, "foo" };
64 try expect(t[0] == 1);
65 try expectEqualStrings(t[1], "foo");
66
67 var mul = t ** 3;
68 try expect(@TypeOf(mul) != T);
69 try expect(mul.len == 6);
70 try expect(mul[2] == 1);
71 try expectEqualStrings(mul[3], "foo");
72
73 var t2: T = .{ 2, "bar" };
74 var cat = t ++ t2;
75 try expect(@TypeOf(cat) != T);
76 try expect(cat.len == 4);
77 try expect(cat[2] == 2);
78 try expectEqualStrings(cat[3], "bar");
79}
test/cases/compile_errors/alignment_of_enum_field_specified.zig+1-1
......@@ -11,4 +11,4 @@ export fn entry1() void {
1111// backend=stage2
1212// target=native
1313//
14// :3:7: error: expected ',' after field
14// :3:13: error: enum fields cannot be aligned
test/cases/compile_errors/reify_struct.zig created+80
......@@ -0,0 +1,80 @@
1comptime {
2 @Type(.{ .Struct = .{
3 .layout = .Auto,
4 .fields = &.{.{
5 .name = "foo",
6 .field_type = u32,
7 .default_value = null,
8 .is_comptime = false,
9 .alignment = 4,
10 }},
11 .decls = &.{},
12 .is_tuple = true,
13 } });
14}
15comptime {
16 @Type(.{ .Struct = .{
17 .layout = .Auto,
18 .fields = &.{.{
19 .name = "3",
20 .field_type = u32,
21 .default_value = null,
22 .is_comptime = false,
23 .alignment = 4,
24 }},
25 .decls = &.{},
26 .is_tuple = true,
27 } });
28}
29comptime {
30 @Type(.{ .Struct = .{
31 .layout = .Auto,
32 .fields = &.{.{
33 .name = "0",
34 .field_type = u32,
35 .default_value = null,
36 .is_comptime = true,
37 .alignment = 4,
38 }},
39 .decls = &.{},
40 .is_tuple = true,
41 } });
42}
43comptime {
44 @Type(.{ .Struct = .{
45 .layout = .Extern,
46 .fields = &.{.{
47 .name = "0",
48 .field_type = u32,
49 .default_value = null,
50 .is_comptime = true,
51 .alignment = 4,
52 }},
53 .decls = &.{},
54 .is_tuple = true,
55 } });
56}
57comptime {
58 @Type(.{ .Struct = .{
59 .layout = .Packed,
60 .fields = &.{.{
61 .name = "0",
62 .field_type = u32,
63 .default_value = null,
64 .is_comptime = true,
65 .alignment = 4,
66 }},
67 .decls = &.{},
68 .is_tuple = true,
69 } });
70}
71
72// error
73// backend=stage2
74// target=native
75//
76// :2:5: error: tuple cannot have non-numeric field 'foo'
77// :16:5: error: tuple field 3 exceeds tuple field count
78// :30:5: error: comptime field without default initialization value
79// :44:5: error: extern struct fields cannot be marked comptime
80// :58:5: error: alignment in a packed struct field must be set to 0
test/cases/compile_errors/struct_field_missing_type.zig deleted-13
......@@ -1,13 +0,0 @@
1const Letter = struct {
2 A,
3};
4export fn entry() void {
5 var a = Letter { .A = {} };
6 _ = a;
7}
8
9// error
10// backend=stage2
11// target=native
12//
13// :2:5: error: struct field missing type
test/cases/compile_errors/tuple_declarations.zig created+25
......@@ -0,0 +1,25 @@
1const E = enum {
2 *u32,
3};
4const U = union {
5 *u32,
6};
7const S = struct {
8 a: u32,
9 *u32,
10};
11const T = struct {
12 u32,
13 []const u8,
14
15 const a = 1;
16};
17
18// error
19// backend=stage2
20// target=native
21//
22// :2:5: error: enum field missing name
23// :5:5: error: union field missing name
24// :8:5: error: tuple field has a name
25// :15:5: error: tuple declarations cannot contain declarations
test/compile_errors.zig+2-2
......@@ -213,7 +213,7 @@ pub fn addCases(ctx: *TestContext) !void {
213213 case.backend = .stage2;
214214
215215 case.addSourceFile("b.zig",
216 \\bad
216 \\+
217217 );
218218
219219 case.addError(
......@@ -221,7 +221,7 @@ pub fn addCases(ctx: *TestContext) !void {
221221 \\ _ = (@sizeOf(@import("b.zig")));
222222 \\}
223223 , &[_][]const u8{
224 ":1:1: error: struct field missing type",
224 ":1:1: error: expected type expression, found '+'",
225225 });
226226 }
227227
test/stage2/cbe.zig+1-1
......@@ -670,7 +670,7 @@ pub fn addCases(ctx: *TestContext) !void {
670670 \\ _ = E1.a;
671671 \\}
672672 , &.{
673 ":3:7: error: expected ',' after field",
673 ":3:13: error: enum fields cannot be aligned",
674674 });
675675
676676 // Redundant non-exhaustive enum mark.