authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-25 18:06:09-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-11-25 18:06:09-05:00
log30eb2a1753c41f348f7d5dcf2b9059a51afab5ad
tree9c89fa27e516c1575daea0d6c408acab75617f58
parenta2403d354fa3f93aa4b916f574393c12dff39e51
parent9f055e2eb0252d872c9111e60ef7c0802d91bea6
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13627 from Vexu/tuple-decls

Implement tuple type declarations

23 files changed, 676 insertions(+), 372 deletions(-)

lib/std/zig/Ast.zig+32-9
......@@ -559,6 +559,7 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
559559 .container_field,
560560 => {
561561 const name_token = main_tokens[n];
562 if (token_tags[name_token + 1] != .colon) return name_token - end_offset;
562563 if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) {
563564 end_offset += 1;
564565 }
......@@ -1320,33 +1321,39 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField {
13201321 assert(tree.nodes.items(.tag)[node] == .container_field);
13211322 const data = tree.nodes.items(.data)[node];
13221323 const extra = tree.extraData(data.rhs, Node.ContainerField);
1324 const main_token = tree.nodes.items(.main_token)[node];
13231325 return tree.fullContainerField(.{
1324 .name_token = tree.nodes.items(.main_token)[node],
1326 .main_token = main_token,
13251327 .type_expr = data.lhs,
13261328 .value_expr = extra.value_expr,
13271329 .align_expr = extra.align_expr,
1330 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,
13281331 });
13291332}
13301333
13311334pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField {
13321335 assert(tree.nodes.items(.tag)[node] == .container_field_init);
13331336 const data = tree.nodes.items(.data)[node];
1337 const main_token = tree.nodes.items(.main_token)[node];
13341338 return tree.fullContainerField(.{
1335 .name_token = tree.nodes.items(.main_token)[node],
1339 .main_token = main_token,
13361340 .type_expr = data.lhs,
13371341 .value_expr = data.rhs,
13381342 .align_expr = 0,
1343 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,
13391344 });
13401345}
13411346
13421347pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField {
13431348 assert(tree.nodes.items(.tag)[node] == .container_field_align);
13441349 const data = tree.nodes.items(.data)[node];
1350 const main_token = tree.nodes.items(.main_token)[node];
13451351 return tree.fullContainerField(.{
1346 .name_token = tree.nodes.items(.main_token)[node],
1352 .main_token = main_token,
13471353 .type_expr = data.lhs,
13481354 .value_expr = 0,
13491355 .align_expr = data.rhs,
1356 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,
13501357 });
13511358}
13521359
......@@ -1944,10 +1951,14 @@ fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.Cont
19441951 .ast = info,
19451952 .comptime_token = null,
19461953 };
1947 // comptime name: type = init,
1948 // ^
1949 if (info.name_token > 0 and token_tags[info.name_token - 1] == .keyword_comptime) {
1950 result.comptime_token = info.name_token - 1;
1954 if (token_tags[info.main_token] == .keyword_comptime) {
1955 // comptime type = init,
1956 // ^
1957 result.comptime_token = info.main_token;
1958 } else if (info.main_token > 0 and token_tags[info.main_token - 1] == .keyword_comptime) {
1959 // comptime name: type = init,
1960 // ^
1961 result.comptime_token = info.main_token - 1;
19511962 }
19521963 return result;
19531964}
......@@ -2256,14 +2267,26 @@ pub const full = struct {
22562267 ast: Components,
22572268
22582269 pub const Components = struct {
2259 name_token: TokenIndex,
2270 main_token: TokenIndex,
22602271 type_expr: Node.Index,
22612272 value_expr: Node.Index,
22622273 align_expr: Node.Index,
2274 tuple_like: bool,
22632275 };
22642276
22652277 pub fn firstToken(cf: ContainerField) TokenIndex {
2266 return cf.comptime_token orelse cf.ast.name_token;
2278 return cf.comptime_token orelse cf.ast.main_token;
2279 }
2280
2281 pub fn convertToNonTupleLike(cf: *ContainerField, nodes: NodeList.Slice) void {
2282 if (!cf.ast.tuple_like) return;
2283 if (cf.ast.type_expr == 0) return;
2284 if (nodes.items(.tag)[cf.ast.type_expr] != .identifier) return;
2285
2286 const ident = nodes.items(.main_token)[cf.ast.type_expr];
2287 cf.ast.tuple_like = false;
2288 cf.ast.main_token = ident;
2289 cf.ast.type_expr = 0;
22672290 }
22682291 };
22692292
lib/std/zig/parse.zig+107-113
......@@ -272,53 +272,6 @@ const Parser = struct {
272272 trailing = false;
273273 },
274274 .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
275 .identifier => {
276 p.tok_i += 1;
277 const identifier = p.tok_i;
278 defer last_field = identifier;
279 const container_field = try p.expectContainerFieldRecoverable();
280 if (container_field != 0) {
281 switch (field_state) {
282 .none => field_state = .seen,
283 .err, .seen => {},
284 .end => |node| {
285 try p.warnMsg(.{
286 .tag = .decl_between_fields,
287 .token = p.nodes.items(.main_token)[node],
288 });
289 try p.warnMsg(.{
290 .tag = .previous_field,
291 .is_note = true,
292 .token = last_field,
293 });
294 try p.warnMsg(.{
295 .tag = .next_field,
296 .is_note = true,
297 .token = identifier,
298 });
299 // Continue parsing; error will be reported later.
300 field_state = .err;
301 },
302 }
303 try p.scratch.append(p.gpa, container_field);
304 switch (p.token_tags[p.tok_i]) {
305 .comma => {
306 p.tok_i += 1;
307 trailing = true;
308 continue;
309 },
310 .r_brace, .eof => {
311 trailing = false;
312 break;
313 },
314 else => {},
315 }
316 // There is not allowed to be a decl after a field with no comma.
317 // Report error but recover parser.
318 try p.warn(.expected_comma_after_field);
319 p.findNextContainerMember();
320 }
321 },
322275 .l_brace => {
323276 if (doc_comment) |some| {
324277 try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });
......@@ -349,53 +302,15 @@ const Parser = struct {
349302 },
350303 else => {
351304 p.tok_i += 1;
352 try p.warn(.expected_block_or_field);
353 },
354 },
355 .keyword_pub => {
356 p.tok_i += 1;
357 const top_level_decl = try p.expectTopLevelDeclRecoverable();
358 if (top_level_decl != 0) {
359 if (field_state == .seen) {
360 field_state = .{ .end = top_level_decl };
361 }
362 try p.scratch.append(p.gpa, top_level_decl);
363 }
364 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
365 },
366 .keyword_usingnamespace => {
367 const node = try p.expectUsingNamespaceRecoverable();
368 if (node != 0) {
369 if (field_state == .seen) {
370 field_state = .{ .end = node };
371 }
372 try p.scratch.append(p.gpa, node);
373 }
374 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
375 },
376 .keyword_const,
377 .keyword_var,
378 .keyword_threadlocal,
379 .keyword_export,
380 .keyword_extern,
381 .keyword_inline,
382 .keyword_noinline,
383 .keyword_fn,
384 => {
385 const top_level_decl = try p.expectTopLevelDeclRecoverable();
386 if (top_level_decl != 0) {
387 if (field_state == .seen) {
388 field_state = .{ .end = top_level_decl };
389 }
390 try p.scratch.append(p.gpa, top_level_decl);
391 }
392 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
393 },
394 .identifier => {
395 const identifier = p.tok_i;
396 defer last_field = identifier;
397 const container_field = try p.expectContainerFieldRecoverable();
398 if (container_field != 0) {
305 const identifier = p.tok_i;
306 defer last_field = identifier;
307 const container_field = p.expectContainerField() catch |err| switch (err) {
308 error.OutOfMemory => return error.OutOfMemory,
309 error.ParseError => {
310 p.findNextContainerMember();
311 continue;
312 },
313 };
399314 switch (field_state) {
400315 .none => field_state = .seen,
401316 .err, .seen => {},
......@@ -435,7 +350,46 @@ const Parser = struct {
435350 // Report error but recover parser.
436351 try p.warn(.expected_comma_after_field);
437352 p.findNextContainerMember();
353 },
354 },
355 .keyword_pub => {
356 p.tok_i += 1;
357 const top_level_decl = try p.expectTopLevelDeclRecoverable();
358 if (top_level_decl != 0) {
359 if (field_state == .seen) {
360 field_state = .{ .end = top_level_decl };
361 }
362 try p.scratch.append(p.gpa, top_level_decl);
363 }
364 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
365 },
366 .keyword_usingnamespace => {
367 const node = try p.expectUsingNamespaceRecoverable();
368 if (node != 0) {
369 if (field_state == .seen) {
370 field_state = .{ .end = node };
371 }
372 try p.scratch.append(p.gpa, node);
373 }
374 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
375 },
376 .keyword_const,
377 .keyword_var,
378 .keyword_threadlocal,
379 .keyword_export,
380 .keyword_extern,
381 .keyword_inline,
382 .keyword_noinline,
383 .keyword_fn,
384 => {
385 const top_level_decl = try p.expectTopLevelDeclRecoverable();
386 if (top_level_decl != 0) {
387 if (field_state == .seen) {
388 field_state = .{ .end = top_level_decl };
389 }
390 try p.scratch.append(p.gpa, top_level_decl);
438391 }
392 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
439393 },
440394 .eof, .r_brace => {
441395 if (doc_comment) |tok| {
......@@ -451,11 +405,57 @@ const Parser = struct {
451405 error.OutOfMemory => return error.OutOfMemory,
452406 error.ParseError => false,
453407 };
454 if (!c_container) {
455 try p.warn(.expected_container_members);
456 // This was likely not supposed to end yet; try to find the next declaration.
457 p.findNextContainerMember();
408 if (c_container) continue;
409
410 const identifier = p.tok_i;
411 defer last_field = identifier;
412 const container_field = p.expectContainerField() catch |err| switch (err) {
413 error.OutOfMemory => return error.OutOfMemory,
414 error.ParseError => {
415 p.findNextContainerMember();
416 continue;
417 },
418 };
419 switch (field_state) {
420 .none => field_state = .seen,
421 .err, .seen => {},
422 .end => |node| {
423 try p.warnMsg(.{
424 .tag = .decl_between_fields,
425 .token = p.nodes.items(.main_token)[node],
426 });
427 try p.warnMsg(.{
428 .tag = .previous_field,
429 .is_note = true,
430 .token = last_field,
431 });
432 try p.warnMsg(.{
433 .tag = .next_field,
434 .is_note = true,
435 .token = identifier,
436 });
437 // Continue parsing; error will be reported later.
438 field_state = .err;
439 },
458440 }
441 try p.scratch.append(p.gpa, container_field);
442 switch (p.token_tags[p.tok_i]) {
443 .comma => {
444 p.tok_i += 1;
445 trailing = true;
446 continue;
447 },
448 .r_brace, .eof => {
449 trailing = false;
450 break;
451 },
452 else => {},
453 }
454 // There is not allowed to be a decl after a field with no comma.
455 // Report error but recover parser.
456 try p.warn(.expected_comma_after_field);
457 p.findNextContainerMember();
458 continue;
459459 },
460460 }
461461 }
......@@ -875,12 +875,16 @@ const Parser = struct {
875875
876876 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?
877877 fn expectContainerField(p: *Parser) !Node.Index {
878 var main_token = p.tok_i;
878879 _ = p.eatToken(.keyword_comptime);
879 const name_token = p.assertToken(.identifier);
880 const tuple_like = p.token_tags[p.tok_i] != .identifier or p.token_tags[p.tok_i + 1] != .colon;
881 if (!tuple_like) {
882 main_token = p.assertToken(.identifier);
883 }
880884
881885 var align_expr: Node.Index = 0;
882886 var type_expr: Node.Index = 0;
883 if (p.eatToken(.colon)) |_| {
887 if (p.eatToken(.colon) != null or tuple_like) {
884888 type_expr = try p.expectTypeExpr();
885889 align_expr = try p.parseByteAlign();
886890 }
......@@ -890,7 +894,7 @@ const Parser = struct {
890894 if (align_expr == 0) {
891895 return p.addNode(.{
892896 .tag = .container_field_init,
893 .main_token = name_token,
897 .main_token = main_token,
894898 .data = .{
895899 .lhs = type_expr,
896900 .rhs = value_expr,
......@@ -899,7 +903,7 @@ const Parser = struct {
899903 } else if (value_expr == 0) {
900904 return p.addNode(.{
901905 .tag = .container_field_align,
902 .main_token = name_token,
906 .main_token = main_token,
903907 .data = .{
904908 .lhs = type_expr,
905909 .rhs = align_expr,
......@@ -908,7 +912,7 @@ const Parser = struct {
908912 } else {
909913 return p.addNode(.{
910914 .tag = .container_field,
911 .main_token = name_token,
915 .main_token = main_token,
912916 .data = .{
913917 .lhs = type_expr,
914918 .rhs = try p.addExtra(Node.ContainerField{
......@@ -920,16 +924,6 @@ const Parser = struct {
920924 }
921925 }
922926
923 fn expectContainerFieldRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
924 return p.expectContainerField() catch |err| switch (err) {
925 error.OutOfMemory => return error.OutOfMemory,
926 error.ParseError => {
927 p.findNextContainerMember();
928 return null_node;
929 },
930 };
931 }
932
933927 /// Statement
934928 /// <- KEYWORD_comptime? VarDecl
935929 /// / KEYWORD_comptime BlockExprStatement
lib/std/zig/parser_test.zig+16-12
......@@ -1,3 +1,15 @@
1test "zig fmt: tuple struct" {
2 try testCanonical(
3 \\const T = struct {
4 \\ comptime u32,
5 \\ *u32 = 1,
6 \\ // needs to be wrapped in parentheses to not be parsed as a function decl
7 \\ (fn () void) align(1),
8 \\};
9 \\
10 );
11}
12
113test "zig fmt: preserves clobbers in inline asm with stray comma" {
214 try testCanonical(
315 \\fn foo() void {
......@@ -265,14 +277,6 @@ test "zig fmt: decl between fields" {
265277 });
266278}
267279
268test "zig fmt: eof after missing comma" {
269 try testError(
270 \\foo()
271 , &[_]Error{
272 .expected_comma_after_field,
273 });
274}
275
276280test "zig fmt: errdefer with payload" {
277281 try testCanonical(
278282 \\pub fn main() anyerror!void {
......@@ -5732,8 +5736,8 @@ test "recovery: missing semicolon" {
57325736test "recovery: invalid container members" {
57335737 try testError(
57345738 \\usingnamespace;
5735 \\foo+
5736 \\bar@,
5739 \\@foo()+
5740 \\@bar()@,
57375741 \\while (a == 2) { test "" {}}
57385742 \\test "" {
57395743 \\ a & b
......@@ -5741,7 +5745,7 @@ test "recovery: invalid container members" {
57415745 , &[_]Error{
57425746 .expected_expr,
57435747 .expected_comma_after_field,
5744 .expected_container_members,
5748 .expected_type_expr,
57455749 .expected_semi_after_stmt,
57465750 });
57475751}
......@@ -5820,7 +5824,7 @@ test "recovery: invalid comptime" {
58205824 try testError(
58215825 \\comptime
58225826 , &[_]Error{
5823 .expected_block_or_field,
5827 .expected_type_expr,
58245828 });
58255829}
58265830
lib/std/zig/render.zig+78-18
......@@ -40,14 +40,34 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void {
4040/// Render all members in the given slice, keeping empty lines where appropriate
4141fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void {
4242 if (members.len == 0) return;
43 try renderMember(gpa, ais, tree, members[0], .newline);
43 var is_tuple = true;
44 for (members) |member| {
45 const tuple_like = switch (tree.nodes.items(.tag)[member]) {
46 .container_field_init => tree.containerFieldInit(member).ast.tuple_like,
47 .container_field_align => tree.containerFieldAlign(member).ast.tuple_like,
48 .container_field => tree.containerField(member).ast.tuple_like,
49 else => continue,
50 };
51 if (!tuple_like) {
52 is_tuple = false;
53 break;
54 }
55 }
56 try renderMember(gpa, ais, tree, members[0], is_tuple, .newline);
4457 for (members[1..]) |member| {
4558 try renderExtraNewline(ais, tree, member);
46 try renderMember(gpa, ais, tree, member, .newline);
59 try renderMember(gpa, ais, tree, member, is_tuple, .newline);
4760 }
4861}
4962
50fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, space: Space) Error!void {
63fn renderMember(
64 gpa: Allocator,
65 ais: *Ais,
66 tree: Ast,
67 decl: Ast.Node.Index,
68 is_tuple: bool,
69 space: Space,
70) Error!void {
5171 const token_tags = tree.tokens.items(.tag);
5272 const main_tokens = tree.nodes.items(.main_token);
5373 const datas = tree.nodes.items(.data);
......@@ -161,9 +181,9 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac
161181 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
162182 },
163183
164 .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space),
165 .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space),
166 .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space),
184 .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), is_tuple, space),
185 .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), is_tuple, space),
186 .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), is_tuple, space),
167187 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),
168188
169189 .root => unreachable,
......@@ -1158,18 +1178,34 @@ fn renderContainerField(
11581178 gpa: Allocator,
11591179 ais: *Ais,
11601180 tree: Ast,
1161 field: Ast.full.ContainerField,
1181 field_param: Ast.full.ContainerField,
1182 is_tuple: bool,
11621183 space: Space,
11631184) Error!void {
1185 var field = field_param;
1186 if (!is_tuple) field.convertToNonTupleLike(tree.nodes);
1187
11641188 if (field.comptime_token) |t| {
11651189 try renderToken(ais, tree, t, .space); // comptime
11661190 }
11671191 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
1168 return renderIdentifierComma(ais, tree, field.ast.name_token, space, .eagerly_unquote); // name
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 }
1202 return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name
11691203 }
11701204 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
1171 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name
1172 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
1205 if (!field.ast.tuple_like) {
1206 try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name
1207 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
1208 }
11731209
11741210 if (field.ast.align_expr != 0) {
11751211 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
......@@ -1184,13 +1220,23 @@ fn renderContainerField(
11841220 }
11851221 }
11861222 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1187 try renderIdentifier(ais, tree, field.ast.name_token, .space, .eagerly_unquote); // name
1188 try renderToken(ais, tree, field.ast.name_token + 1, .space); // =
1223 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 }
1233 try renderToken(ais, tree, field.ast.main_token + 1, .space); // =
11891234 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
11901235 }
1191
1192 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name
1193 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
1236 if (!field.ast.tuple_like) {
1237 try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name
1238 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
1239 }
11941240 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
11951241
11961242 if (field.ast.align_expr != 0) {
......@@ -1901,6 +1947,20 @@ fn renderContainerDecl(
19011947 try renderToken(ais, tree, layout_token, .space);
19021948 }
19031949
1950 var is_tuple = token_tags[container_decl.ast.main_token] == .keyword_struct;
1951 if (is_tuple) for (container_decl.ast.members) |member| {
1952 const tuple_like = switch (tree.nodes.items(.tag)[member]) {
1953 .container_field_init => tree.containerFieldInit(member).ast.tuple_like,
1954 .container_field_align => tree.containerFieldAlign(member).ast.tuple_like,
1955 .container_field => tree.containerField(member).ast.tuple_like,
1956 else => continue,
1957 };
1958 if (!tuple_like) {
1959 is_tuple = false;
1960 break;
1961 }
1962 };
1963
19041964 var lbrace: Ast.TokenIndex = undefined;
19051965 if (container_decl.ast.enum_token) |enum_token| {
19061966 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
......@@ -1967,7 +2027,7 @@ fn renderContainerDecl(
19672027 // Print all the declarations on the same line.
19682028 try renderToken(ais, tree, lbrace, .space); // lbrace
19692029 for (container_decl.ast.members) |member| {
1970 try renderMember(gpa, ais, tree, member, .space);
2030 try renderMember(gpa, ais, tree, member, is_tuple, .space);
19712031 }
19722032 return renderToken(ais, tree, rbrace, space); // rbrace
19732033 }
......@@ -1985,9 +2045,9 @@ fn renderContainerDecl(
19852045 .container_field_init,
19862046 .container_field_align,
19872047 .container_field,
1988 => try renderMember(gpa, ais, tree, member, .comma),
2048 => try renderMember(gpa, ais, tree, member, is_tuple, .comma),
19892049
1990 else => try renderMember(gpa, ais, tree, member, .newline),
2050 else => try renderMember(gpa, ais, tree, member, is_tuple, .newline),
19912051 }
19922052 }
19932053 ais.popIndent();
src/AstGen.zig+57-12
......@@ -4386,6 +4386,7 @@ fn structDeclInner(
43864386 .backing_int_body_len = 0,
43874387 .known_non_opv = false,
43884388 .known_comptime_only = false,
4389 .is_tuple = false,
43894390 });
43904391 return indexToRef(decl_inst);
43914392 }
......@@ -4467,22 +4468,53 @@ fn structDeclInner(
44674468 // No defer needed here because it is handled by `wip_members.deinit()` above.
44684469 const bodies_start = astgen.scratch.items.len;
44694470
4471 var is_tuple = false;
4472 const node_tags = tree.nodes.items(.tag);
4473 for (container_decl.ast.members) |member_node| {
4474 switch (node_tags[member_node]) {
4475 .container_field_init => is_tuple = tree.containerFieldInit(member_node).ast.tuple_like,
4476 .container_field_align => is_tuple = tree.containerFieldAlign(member_node).ast.tuple_like,
4477 .container_field => is_tuple = tree.containerField(member_node).ast.tuple_like,
4478 else => continue,
4479 }
4480 if (is_tuple) break;
4481 }
4482 if (is_tuple) for (container_decl.ast.members) |member_node| {
4483 switch (node_tags[member_node]) {
4484 .container_field_init,
4485 .container_field_align,
4486 .container_field,
4487 .@"comptime",
4488 => continue,
4489 else => {
4490 return astgen.failNode(member_node, "tuple declarations cannot contain declarations", .{});
4491 },
4492 }
4493 };
4494
44704495 var known_non_opv = false;
44714496 var known_comptime_only = false;
44724497 for (container_decl.ast.members) |member_node| {
4473 const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
4498 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
44744499 .decl => continue,
44754500 .field => |field| field,
44764501 };
44774502
4478 const field_name = try astgen.identAsString(member.ast.name_token);
4479 wip_members.appendToField(field_name);
4503 if (!is_tuple) {
4504 member.convertToNonTupleLike(astgen.tree.nodes);
4505 assert(!member.ast.tuple_like);
4506
4507 const field_name = try astgen.identAsString(member.ast.main_token);
4508 wip_members.appendToField(field_name);
4509 } else if (!member.ast.tuple_like) {
4510 return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
4511 }
44804512
44814513 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
44824514 wip_members.appendToField(doc_comment_index);
44834515
44844516 if (member.ast.type_expr == 0) {
4485 return astgen.failTok(member.ast.name_token, "struct field missing type", .{});
4517 return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
44864518 }
44874519
44884520 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
......@@ -4562,6 +4594,7 @@ fn structDeclInner(
45624594 .backing_int_body_len = @intCast(u32, backing_int_body_len),
45634595 .known_non_opv = known_non_opv,
45644596 .known_comptime_only = known_comptime_only,
4597 .is_tuple = is_tuple,
45654598 });
45664599
45674600 wip_members.finishBits(bits_per_field);
......@@ -4640,15 +4673,19 @@ fn unionDeclInner(
46404673 defer wip_members.deinit();
46414674
46424675 for (members) |member_node| {
4643 const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
4676 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
46444677 .decl => continue,
46454678 .field => |field| field,
46464679 };
4680 member.convertToNonTupleLike(astgen.tree.nodes);
4681 if (member.ast.tuple_like) {
4682 return astgen.failTok(member.ast.main_token, "union field missing name", .{});
4683 }
46474684 if (member.comptime_token) |comptime_token| {
46484685 return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
46494686 }
46504687
4651 const field_name = try astgen.identAsString(member.ast.name_token);
4688 const field_name = try astgen.identAsString(member.ast.main_token);
46524689 wip_members.appendToField(field_name);
46534690
46544691 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
......@@ -4787,7 +4824,7 @@ fn containerDecl(
47874824 var nonexhaustive_node: Ast.Node.Index = 0;
47884825 var nonfinal_nonexhaustive = false;
47894826 for (container_decl.ast.members) |member_node| {
4790 const member = switch (node_tags[member_node]) {
4827 var member = switch (node_tags[member_node]) {
47914828 .container_field_init => tree.containerFieldInit(member_node),
47924829 .container_field_align => tree.containerFieldAlign(member_node),
47934830 .container_field => tree.containerField(member_node),
......@@ -4796,6 +4833,10 @@ fn containerDecl(
47964833 continue;
47974834 },
47984835 };
4836 member.convertToNonTupleLike(astgen.tree.nodes);
4837 if (member.ast.tuple_like) {
4838 return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
4839 }
47994840 if (member.comptime_token) |comptime_token| {
48004841 return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
48014842 }
......@@ -4813,10 +4854,11 @@ fn containerDecl(
48134854 },
48144855 );
48154856 }
4816 // Alignment expressions in enums are caught by the parser.
4817 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 }
48184860
4819 const name_token = member.ast.name_token;
4861 const name_token = member.ast.main_token;
48204862 if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
48214863 if (nonexhaustive_node != 0) {
48224864 return astgen.failNodeNotes(
......@@ -4915,15 +4957,16 @@ fn containerDecl(
49154957 for (container_decl.ast.members) |member_node| {
49164958 if (member_node == counts.nonexhaustive_node)
49174959 continue;
4918 const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
4960 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
49194961 .decl => continue,
49204962 .field => |field| field,
49214963 };
4964 member.convertToNonTupleLike(astgen.tree.nodes);
49224965 assert(member.comptime_token == null);
49234966 assert(member.ast.type_expr == 0);
49244967 assert(member.ast.align_expr == 0);
49254968
4926 const field_name = try astgen.identAsString(member.ast.name_token);
4969 const field_name = try astgen.identAsString(member.ast.main_token);
49274970 wip_members.appendToField(field_name);
49284971
49294972 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
......@@ -11786,6 +11829,7 @@ const GenZir = struct {
1178611829 layout: std.builtin.Type.ContainerLayout,
1178711830 known_non_opv: bool,
1178811831 known_comptime_only: bool,
11832 is_tuple: bool,
1178911833 }) !void {
1179011834 const astgen = gz.astgen;
1179111835 const gpa = astgen.gpa;
......@@ -11820,6 +11864,7 @@ const GenZir = struct {
1182011864 .has_backing_int = args.backing_int_ref != .none,
1182111865 .known_non_opv = args.known_non_opv,
1182211866 .known_comptime_only = args.known_comptime_only,
11867 .is_tuple = args.is_tuple,
1182311868 .name_strategy = gz.anon_name_strategy,
1182411869 .layout = args.layout,
1182511870 }),
src/Module.zig+6-1
......@@ -938,6 +938,7 @@ pub const Struct = struct {
938938 known_non_opv: bool,
939939 requires_comptime: PropertyBoolean = .unknown,
940940 have_field_inits: bool = false,
941 is_tuple: bool,
941942
942943 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
943944
......@@ -4458,6 +4459,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
44584459 .layout = .Auto,
44594460 .status = .none,
44604461 .known_non_opv = undefined,
4462 .is_tuple = undefined, // set below
44614463 .namespace = .{
44624464 .parent = null,
44634465 .ty = struct_ty,
......@@ -4489,6 +4491,9 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
44894491 assert(file.zir_loaded);
44904492 const main_struct_inst = Zir.main_struct_inst;
44914493 struct_obj.zir_index = main_struct_inst;
4494 const extended = file.zir.instructions.items(.data)[main_struct_inst].extended;
4495 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
4496 struct_obj.is_tuple = small.is_tuple;
44924497
44934498 var sema_arena = std.heap.ArenaAllocator.init(gpa);
44944499 defer sema_arena.deinit();
......@@ -6138,7 +6143,7 @@ fn queryFieldSrc(
61386143 .name => .{
61396144 .file_scope = file_scope,
61406145 .parent_decl_node = 0,
6141 .lazy = .{ .token_abs = field.ast.name_token },
6146 .lazy = .{ .token_abs = field.ast.main_token },
61426147 },
61436148 .type => .{
61446149 .file_scope = file_scope,
src/Sema.zig+146-172
......@@ -2519,6 +2519,7 @@ fn zirStructDecl(
25192519 .layout = small.layout,
25202520 .status = .none,
25212521 .known_non_opv = undefined,
2522 .is_tuple = small.is_tuple,
25222523 .namespace = .{
25232524 .parent = block.namespace,
25242525 .ty = struct_ty,
......@@ -4291,13 +4292,12 @@ fn zirValidateArrayInit(
42914292
42924293 if (instrs.len != array_len) switch (array_ty.zigTypeTag()) {
42934294 .Struct => {
4294 const struct_obj = array_ty.castTag(.tuple).?.data;
42954295 var root_msg: ?*Module.ErrorMsg = null;
42964296 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
42974297
4298 for (struct_obj.values) |default_val, i| {
4299 if (i < instrs.len) continue;
4300
4298 var i = instrs.len;
4299 while (i < array_len) : (i += 1) {
4300 const default_val = array_ty.structFieldDefaultValue(i);
43014301 if (default_val.tag() == .unreachable_value) {
43024302 const template = "missing tuple field with index {d}";
43034303 if (root_msg) |msg| {
......@@ -7230,7 +7230,7 @@ fn instantiateGenericCall(
72307230}
72317231
72327232fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
7233 if (!ty.isTuple()) return;
7233 if (!ty.isSimpleTuple()) return;
72347234 const tuple = ty.tupleFields();
72357235 for (tuple.values) |field_val, i| {
72367236 try sema.resolveTupleLazyValues(block, src, tuple.types[i]);
......@@ -7295,7 +7295,7 @@ fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
72957295 const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs);
72967296 assert(indexable_ty.isIndexable()); // validated by a previous instruction
72977297 if (indexable_ty.zigTypeTag() == .Struct) {
7298 const elem_type = indexable_ty.tupleFields().types[@enumToInt(bin.rhs)];
7298 const elem_type = indexable_ty.structFieldType(@enumToInt(bin.rhs));
72997299 return sema.addType(elem_type);
73007300 } else {
73017301 const elem_type = indexable_ty.elemType2();
......@@ -11827,13 +11827,19 @@ fn analyzeTupleCat(
1182711827 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
1182811828 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
1182911829
11830 const lhs_tuple = lhs_ty.tupleFields();
11831 const rhs_tuple = rhs_ty.tupleFields();
11832 const dest_fields = lhs_tuple.types.len + rhs_tuple.types.len;
11830 const lhs_len = lhs_ty.structFieldCount();
11831 const rhs_len = rhs_ty.structFieldCount();
11832 const dest_fields = lhs_len + rhs_len;
1183311833
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);
......@@ -11841,20 +11847,23 @@ fn analyzeTupleCat(
1184111847
1184211848 const opt_runtime_src = rs: {
1184311849 var runtime_src: ?LazySrcLoc = null;
11844 for (lhs_tuple.types) |ty, i| {
11845 types[i] = ty;
11846 values[i] = lhs_tuple.values[i];
11850 var i: u32 = 0;
11851 while (i < lhs_len) : (i += 1) {
11852 types[i] = lhs_ty.structFieldType(i);
11853 const default_val = lhs_ty.structFieldDefaultValue(i);
11854 values[i] = default_val;
1184711855 const operand_src = lhs_src; // TODO better source location
11848 if (values[i].tag() == .unreachable_value) {
11856 if (default_val.tag() == .unreachable_value) {
1184911857 runtime_src = operand_src;
1185011858 }
1185111859 }
11852 const offset = lhs_tuple.types.len;
11853 for (rhs_tuple.types) |ty, i| {
11854 types[i + offset] = ty;
11855 values[i + offset] = rhs_tuple.values[i];
11860 i = 0;
11861 while (i < rhs_len) : (i += 1) {
11862 types[i + lhs_len] = rhs_ty.structFieldType(i);
11863 const default_val = rhs_ty.structFieldDefaultValue(i);
11864 values[i + lhs_len] = default_val;
1185611865 const operand_src = rhs_src; // TODO better source location
11857 if (rhs_tuple.values[i].tag() == .unreachable_value) {
11866 if (default_val.tag() == .unreachable_value) {
1185811867 runtime_src = operand_src;
1185911868 }
1186011869 }
......@@ -11874,15 +11883,16 @@ fn analyzeTupleCat(
1187411883 try sema.requireRuntimeBlock(block, src, runtime_src);
1187511884
1187611885 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
11877 for (lhs_tuple.types) |_, i| {
11886 var i: u32 = 0;
11887 while (i < lhs_len) : (i += 1) {
1187811888 const operand_src = lhs_src; // TODO better source location
11879 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);
1188011890 }
11881 const offset = lhs_tuple.types.len;
11882 for (rhs_tuple.types) |_, i| {
11891 i = 0;
11892 while (i < rhs_len) : (i += 1) {
1188311893 const operand_src = rhs_src; // TODO better source location
11884 element_refs[i + offset] =
11885 try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty);
11894 element_refs[i + lhs_len] =
11895 try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty);
1188611896 }
1188711897
1188811898 return block.addAggregateInit(tuple_ty, element_refs);
......@@ -12107,12 +12117,11 @@ fn analyzeTupleMul(
1210712117 factor: u64,
1210812118) CompileError!Air.Inst.Ref {
1210912119 const operand_ty = sema.typeOf(operand);
12110 const operand_tuple = operand_ty.tupleFields();
1211112120 const src = LazySrcLoc.nodeOffset(src_node);
1211212121 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
1211312122 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
1211412123
12115 const tuple_len = operand_tuple.types.len;
12124 const tuple_len = operand_ty.structFieldCount();
1211612125 const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch
1211712126 return sema.fail(block, rhs_src, "operation results in overflow", .{});
1211812127
......@@ -12126,18 +12135,19 @@ fn analyzeTupleMul(
1212612135
1212712136 const opt_runtime_src = rs: {
1212812137 var runtime_src: ?LazySrcLoc = null;
12129 for (operand_tuple.types) |ty, i| {
12130 types[i] = ty;
12131 values[i] = operand_tuple.values[i];
12138 var i: u32 = 0;
12139 while (i < tuple_len) : (i += 1) {
12140 types[i] = operand_ty.structFieldType(i);
12141 values[i] = operand_ty.structFieldDefaultValue(i);
1213212142 const operand_src = lhs_src; // TODO better source location
1213312143 if (values[i].tag() == .unreachable_value) {
1213412144 runtime_src = operand_src;
1213512145 }
1213612146 }
12137 var i: usize = 1;
12147 i = 0;
1213812148 while (i < factor) : (i += 1) {
12139 mem.copy(Type, types[tuple_len * i ..], operand_tuple.types);
12140 mem.copy(Value, values[tuple_len * i ..], operand_tuple.values);
12149 mem.copy(Type, types[tuple_len * i ..], types[0..tuple_len]);
12150 mem.copy(Value, values[tuple_len * i ..], values[0..tuple_len]);
1214112151 }
1214212152 break :rs runtime_src;
1214312153 };
......@@ -12155,11 +12165,12 @@ fn analyzeTupleMul(
1215512165 try sema.requireRuntimeBlock(block, src, runtime_src);
1215612166
1215712167 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
12158 for (operand_tuple.types) |_, i| {
12168 var i: u32 = 0;
12169 while (i < tuple_len) : (i += 1) {
1215912170 const operand_src = lhs_src; // TODO better source location
1216012171 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(u32, i), operand_ty);
1216112172 }
12162 var i: usize = 1;
12173 i = 1;
1216312174 while (i < factor) : (i += 1) {
1216412175 mem.copy(Air.Inst.Ref, element_refs[tuple_len * i ..], element_refs[0..tuple_len]);
1216512176 }
......@@ -15593,7 +15604,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1559315604 const layout = struct_ty.containerLayout();
1559415605
1559515606 const struct_field_vals = fv: {
15596 if (struct_ty.isTupleOrAnonStruct()) {
15607 if (struct_ty.isSimpleTupleOrAnonStruct()) {
1559715608 const tuple = struct_ty.tupleFields();
1559815609 const field_types = tuple.types;
1559915610 const struct_field_vals = try fields_anon_decl.arena().alloc(Value, field_types.len);
......@@ -17063,10 +17074,12 @@ fn finishStructInit(
1706317074 }
1706417075 }
1706517076 } else if (struct_ty.isTuple()) {
17066 const struct_obj = struct_ty.castTag(.tuple).?.data;
17067 for (struct_obj.values) |default_val, i| {
17077 var i: u32 = 0;
17078 const len = struct_ty.structFieldCount();
17079 while (i < len) : (i += 1) {
1706817080 if (field_inits[i] != .none) continue;
1706917081
17082 const default_val = struct_ty.structFieldDefaultValue(i);
1707017083 if (default_val.tag() == .unreachable_value) {
1707117084 const template = "missing tuple field with index {d}";
1707217085 if (root_msg) |msg| {
......@@ -17075,7 +17088,7 @@ fn finishStructInit(
1707517088 root_msg = try sema.errMsg(block, init_src, template, .{i});
1707617089 }
1707717090 } else {
17078 field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val);
17091 field_inits[i] = try sema.addConstant(struct_ty.structFieldType(i), default_val);
1707917092 }
1708017093 }
1708117094 } else {
......@@ -17297,7 +17310,7 @@ fn zirArrayInit(
1729717310 const resolved_arg = try sema.resolveInst(arg);
1729817311 const arg_src = src; // TODO better source location
1729917312 const elem_ty = if (array_ty.zigTypeTag() == .Struct)
17300 array_ty.tupleFields().types[i]
17313 array_ty.structFieldType(i)
1730117314 else
1730217315 array_ty.elemType2();
1730317316 resolved_args[i] = try sema.coerce(block, elem_ty, resolved_arg, arg_src);
......@@ -17337,12 +17350,11 @@ fn zirArrayInit(
1733717350 const alloc = try block.addTy(.alloc, alloc_ty);
1733817351
1733917352 if (array_ty.isTuple()) {
17340 const types = array_ty.tupleFields().types;
1734117353 for (resolved_args) |arg, i| {
1734217354 const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
1734317355 .mutable = true,
1734417356 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
17345 .pointee_type = types[i],
17357 .pointee_type = array_ty.structFieldType(i),
1734617358 });
1734717359 const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
1734817360
......@@ -18015,10 +18027,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
1801518027 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});
1801618028 }
1801718029
18018 return if (is_tuple_val.toBool())
18019 try sema.reifyTuple(block, src, fields_val)
18020 else
18021 try sema.reifyStruct(block, inst, src, layout, backing_int_val, fields_val, name_strategy);
18030 return try sema.reifyStruct(block, inst, src, layout, backing_int_val, fields_val, name_strategy, is_tuple_val.toBool());
1802218031 },
1802318032 .Enum => {
1802418033 const struct_val: []const Value = union_val.val.castTag(.aggregate).?.data;
......@@ -18432,84 +18441,6 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
1843218441 }
1843318442}
1843418443
18435fn reifyTuple(
18436 sema: *Sema,
18437 block: *Block,
18438 src: LazySrcLoc,
18439 fields_val: Value,
18440) CompileError!Air.Inst.Ref {
18441 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(sema.mod));
18442 if (fields_len == 0) return sema.addType(Type.initTag(.empty_struct_literal));
18443
18444 const types = try sema.arena.alloc(Type, fields_len);
18445 const values = try sema.arena.alloc(Value, fields_len);
18446
18447 var used_fields: std.AutoArrayHashMapUnmanaged(u32, void) = .{};
18448 defer used_fields.deinit(sema.gpa);
18449 try used_fields.ensureTotalCapacity(sema.gpa, fields_len);
18450
18451 var i: usize = 0;
18452 while (i < fields_len) : (i += 1) {
18453 const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
18454 const field_struct_val = elem_val.castTag(.aggregate).?.data;
18455 // TODO use reflection instead of magic numbers here
18456 // name: []const u8
18457 const name_val = field_struct_val[0];
18458 // field_type: type,
18459 const field_type_val = field_struct_val[1];
18460 //default_value: ?*const anyopaque,
18461 const default_value_val = field_struct_val[2];
18462
18463 const field_name = try name_val.toAllocatedBytes(
18464 Type.initTag(.const_slice_u8),
18465 sema.arena,
18466 sema.mod,
18467 );
18468
18469 const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
18470 return sema.fail(
18471 block,
18472 src,
18473 "tuple cannot have non-numeric field '{s}': {}",
18474 .{ field_name, err },
18475 );
18476 };
18477
18478 if (field_index >= fields_len) {
18479 return sema.fail(
18480 block,
18481 src,
18482 "tuple field {} exceeds tuple field count",
18483 .{field_index},
18484 );
18485 }
18486
18487 const gop = used_fields.getOrPutAssumeCapacity(field_index);
18488 if (gop.found_existing) {
18489 // TODO: better source location
18490 return sema.fail(block, src, "duplicate tuple field {}", .{field_index});
18491 }
18492
18493 const default_val = if (default_value_val.optionalValue()) |opt_val| blk: {
18494 const payload_val = if (opt_val.pointerDecl()) |opt_decl|
18495 sema.mod.declPtr(opt_decl).val
18496 else
18497 opt_val;
18498 break :blk try payload_val.copy(sema.arena);
18499 } else Value.initTag(.unreachable_value);
18500
18501 var buffer: Value.ToTypeBuffer = undefined;
18502 types[field_index] = try field_type_val.toType(&buffer).copy(sema.arena);
18503 values[field_index] = default_val;
18504 }
18505
18506 const ty = try Type.Tag.tuple.create(sema.arena, .{
18507 .types = types,
18508 .values = values,
18509 });
18510 return sema.addType(ty);
18511}
18512
1851318444fn reifyStruct(
1851418445 sema: *Sema,
1851518446 block: *Block,
......@@ -18519,6 +18450,7 @@ fn reifyStruct(
1851918450 backing_int_val: Value,
1852018451 fields_val: Value,
1852118452 name_strategy: Zir.Inst.NameStrategy,
18453 is_tuple: bool,
1852218454) CompileError!Air.Inst.Ref {
1852318455 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
1852418456 errdefer new_decl_arena.deinit();
......@@ -18542,6 +18474,7 @@ fn reifyStruct(
1854218474 .layout = layout,
1854318475 .status = .have_field_types,
1854418476 .known_non_opv = false,
18477 .is_tuple = is_tuple,
1854518478 .namespace = .{
1854618479 .parent = block.namespace,
1854718480 .ty = struct_ty,
......@@ -18575,8 +18508,12 @@ fn reifyStruct(
1857518508 }
1857618509 const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?);
1857718510
18578 if (layout == .Packed and abi_align != 0) {
18579 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", .{});
1858018517 }
1858118518
1858218519 const field_name = try name_val.toAllocatedBytes(
......@@ -18585,6 +18522,25 @@ fn reifyStruct(
1858518522 mod,
1858618523 );
1858718524
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 }
1858818544 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
1858918545 if (gop.found_existing) {
1859018546 // TODO: better source location
......@@ -18598,6 +18554,9 @@ fn reifyStruct(
1859818554 opt_val;
1859918555 break :blk try payload_val.copy(new_decl_arena_allocator);
1860018556 } 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 }
1860118560
1860218561 var buffer: Value.ToTypeBuffer = undefined;
1860318562 gop.value_ptr.* = .{
......@@ -23177,6 +23136,7 @@ fn structFieldVal(
2317723136 },
2317823137 .@"struct" => {
2317923138 const struct_obj = struct_ty.castTag(.@"struct").?.data;
23139 if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty);
2318023140
2318123141 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
2318223142 return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
......@@ -23249,11 +23209,10 @@ fn tupleFieldValByIndex(
2324923209 field_index: u32,
2325023210 tuple_ty: Type,
2325123211) CompileError!Air.Inst.Ref {
23252 const tuple = tuple_ty.tupleFields();
23253 const field_ty = tuple.types[field_index];
23212 const field_ty = tuple_ty.structFieldType(field_index);
2325423213
23255 if (tuple.values[field_index].tag() != .unreachable_value) {
23256 return sema.addConstant(field_ty, tuple.values[field_index]);
23214 if (tuple_ty.structFieldValueComptime(field_index)) |default_value| {
23215 return sema.addConstant(field_ty, default_value);
2325723216 }
2325823217
2325923218 if (try sema.resolveMaybeUndefVal(tuple_byval)) |tuple_val| {
......@@ -23601,19 +23560,20 @@ fn tupleFieldPtr(
2360123560) CompileError!Air.Inst.Ref {
2360223561 const tuple_ptr_ty = sema.typeOf(tuple_ptr);
2360323562 const tuple_ty = tuple_ptr_ty.childType();
23604 const tuple_fields = tuple_ty.tupleFields();
23563 _ = try sema.resolveTypeFields(tuple_ty);
23564 const field_count = tuple_ty.structFieldCount();
2360523565
23606 if (tuple_fields.types.len == 0) {
23566 if (field_count == 0) {
2360723567 return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
2360823568 }
2360923569
23610 if (field_index >= tuple_fields.types.len) {
23570 if (field_index >= field_count) {
2361123571 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
23612 field_index, tuple_fields.types.len,
23572 field_index, field_count,
2361323573 });
2361423574 }
2361523575
23616 const field_ty = tuple_fields.types[field_index];
23576 const field_ty = tuple_ty.structFieldType(field_index);
2361723577 const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{
2361823578 .pointee_type = field_ty,
2361923579 .mutable = tuple_ptr_ty.ptrIsMutable(),
......@@ -23656,24 +23616,23 @@ fn tupleField(
2365623616 field_index_src: LazySrcLoc,
2365723617 field_index: u32,
2365823618) CompileError!Air.Inst.Ref {
23659 const tuple_ty = sema.typeOf(tuple);
23660 const tuple_fields = tuple_ty.tupleFields();
23619 const tuple_ty = try sema.resolveTypeFields(sema.typeOf(tuple));
23620 const field_count = tuple_ty.structFieldCount();
2366123621
23662 if (tuple_fields.types.len == 0) {
23622 if (field_count == 0) {
2366323623 return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});
2366423624 }
2366523625
23666 if (field_index >= tuple_fields.types.len) {
23626 if (field_index >= field_count) {
2366723627 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
23668 field_index, tuple_fields.types.len,
23628 field_index, field_count,
2366923629 });
2367023630 }
2367123631
23672 const field_ty = tuple_fields.types[field_index];
23673 const field_val = tuple_fields.values[field_index];
23632 const field_ty = tuple_ty.structFieldType(field_index);
2367423633
23675 if (field_val.tag() != .unreachable_value) {
23676 return sema.addConstant(field_ty, field_val); // comptime field
23634 if (tuple_ty.structFieldValueComptime(field_index)) |default_value| {
23635 return sema.addConstant(field_ty, default_value); // comptime field
2367723636 }
2367823637
2367923638 if (try sema.resolveMaybeUndefVal(tuple)) |tuple_val| {
......@@ -24223,7 +24182,10 @@ fn coerceExtra(
2422324182 inst_ty.childType().isAnonStruct() and
2422424183 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
2422524184 {
24226 return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
24185 return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) {
24186 error.NotCoercible => break :pointer,
24187 else => |e| return e,
24188 };
2422724189 }
2422824190 },
2422924191 .Array => {
......@@ -24253,7 +24215,7 @@ fn coerceExtra(
2425324215
2425424216 // empty tuple to zero-length slice
2425524217 // note that this allows coercing to a mutable slice.
24256 if (inst_child_ty.tupleFields().types.len == 0) {
24218 if (inst_child_ty.structFieldCount() == 0) {
2425724219 const slice_val = try Value.Tag.slice.create(sema.arena, .{
2425824220 .ptr = Value.undef,
2425924221 .len = Value.zero,
......@@ -24536,12 +24498,15 @@ fn coerceExtra(
2453624498 },
2453724499 else => {},
2453824500 },
24539 .Struct => {
24501 .Struct => blk: {
2454024502 if (inst == .empty_struct) {
2454124503 return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src);
2454224504 }
2454324505 if (inst_ty.isTupleOrAnonStruct()) {
24544 return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src);
24506 return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src) catch |err| switch (err) {
24507 error.NotCoercible => break :blk,
24508 else => |e| return e,
24509 };
2454524510 }
2454624511 },
2454724512 else => {},
......@@ -25563,9 +25528,9 @@ fn storePtr2(
2556325528 // fields.
2556425529 const operand_ty = sema.typeOf(uncasted_operand);
2556525530 if (operand_ty.isTuple() and elem_ty.zigTypeTag() == .Array) {
25566 const tuple = operand_ty.tupleFields();
25567 for (tuple.types) |_, i_usize| {
25568 const i = @intCast(u32, i_usize);
25531 const field_count = operand_ty.structFieldCount();
25532 var i: u32 = 0;
25533 while (i < field_count) : (i += 1) {
2556925534 const elem_src = operand_src; // TODO better source location
2557025535 const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);
2557125536 const elem_index = try sema.addIntUnsigned(Type.usize, i);
......@@ -26657,7 +26622,7 @@ fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_resul
2665726622 const inst_info = inst_ty.ptrInfo().data;
2665826623 const len0 = (inst_info.pointee_type.zigTypeTag() == .Array and (inst_info.pointee_type.arrayLenIncludingSentinel() == 0 or
2665926624 (inst_info.pointee_type.arrayLen() == 0 and dest_info.sentinel == null and dest_info.size != .C and dest_info.size != .Many))) or
26660 (inst_info.pointee_type.isTuple() and inst_info.pointee_type.tupleFields().types.len == 0);
26625 (inst_info.pointee_type.isTuple() and inst_info.pointee_type.structFieldCount() == 0);
2666126626
2666226627 const ok_cv_qualifiers =
2666326628 ((inst_info.mutable or !dest_info.mutable) or len0) and
......@@ -27142,18 +27107,18 @@ fn coerceTupleToStruct(
2714227107 mem.set(Air.Inst.Ref, field_refs, .none);
2714327108
2714427109 const inst_ty = sema.typeOf(inst);
27145 const tuple = inst_ty.tupleFields();
2714627110 var runtime_src: ?LazySrcLoc = null;
27147 for (tuple.types) |_, i_usize| {
27148 const i = @intCast(u32, i_usize);
27111 const field_count = inst_ty.structFieldCount();
27112 var field_i: u32 = 0;
27113 while (field_i < field_count) : (field_i += 1) {
2714927114 const field_src = inst_src; // TODO better source location
2715027115 const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
27151 payload.data.names[i]
27116 payload.data.names[field_i]
2715227117 else
27153 try std.fmt.allocPrint(sema.arena, "{d}", .{i});
27118 try std.fmt.allocPrint(sema.arena, "{d}", .{field_i});
2715427119 const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
2715527120 const field = fields.values()[field_index];
27156 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, i);
27121 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
2715727122 const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
2715827123 field_refs[field_index] = coerced;
2715927124 if (field.is_comptime) {
......@@ -27162,7 +27127,7 @@ fn coerceTupleToStruct(
2716227127 };
2716327128
2716427129 if (!init_val.eql(field.default_val, field.ty, sema.mod)) {
27165 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, i);
27130 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
2716627131 }
2716727132 }
2716827133 if (runtime_src == null) {
......@@ -27225,21 +27190,23 @@ fn coerceTupleToTuple(
2722527190 inst: Air.Inst.Ref,
2722627191 inst_src: LazySrcLoc,
2722727192) !Air.Inst.Ref {
27228 const field_count = tuple_ty.structFieldCount();
27229 const field_vals = try sema.arena.alloc(Value, field_count);
27193 const dest_field_count = tuple_ty.structFieldCount();
27194 const field_vals = try sema.arena.alloc(Value, dest_field_count);
2723027195 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
2723127196 mem.set(Air.Inst.Ref, field_refs, .none);
2723227197
2723327198 const inst_ty = sema.typeOf(inst);
27234 const tuple = inst_ty.tupleFields();
27199 const inst_field_count = inst_ty.structFieldCount();
27200 if (inst_field_count > dest_field_count) return error.NotCoercible;
27201
2723527202 var runtime_src: ?LazySrcLoc = null;
27236 for (tuple.types) |_, i_usize| {
27237 const i = @intCast(u32, i_usize);
27203 var field_i: u32 = 0;
27204 while (field_i < inst_field_count) : (field_i += 1) {
2723827205 const field_src = inst_src; // TODO better source location
2723927206 const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
27240 payload.data.names[i]
27207 payload.data.names[field_i]
2724127208 else
27242 try std.fmt.allocPrint(sema.arena, "{d}", .{i});
27209 try std.fmt.allocPrint(sema.arena, "{d}", .{field_i});
2724327210
2724427211 if (mem.eql(u8, field_name, "len")) {
2724527212 return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{});
......@@ -27247,9 +27214,9 @@ fn coerceTupleToTuple(
2724727214
2724827215 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src);
2724927216
27250 const field_ty = tuple_ty.structFieldType(i);
27251 const default_val = tuple_ty.structFieldDefaultValue(i);
27252 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, i);
27217 const field_ty = tuple_ty.structFieldType(field_i);
27218 const default_val = tuple_ty.structFieldDefaultValue(field_i);
27219 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
2725327220 const coerced = try sema.coerce(block, field_ty, elem_ref, field_src);
2725427221 field_refs[field_index] = coerced;
2725527222 if (default_val.tag() != .unreachable_value) {
......@@ -27258,7 +27225,7 @@ fn coerceTupleToTuple(
2725827225 };
2725927226
2726027227 if (!init_val.eql(default_val, field_ty, sema.mod)) {
27261 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, i);
27228 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
2726227229 }
2726327230 }
2726427231 if (runtime_src == null) {
......@@ -29641,6 +29608,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
2964129608 block_scope.params.deinit(gpa);
2964229609 }
2964329610
29611 struct_obj.fields = .{};
2964429612 try struct_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len);
2964529613
2964629614 const Field = struct {
......@@ -29675,8 +29643,11 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
2967529643 const has_type_body = @truncate(u1, cur_bit_bag) != 0;
2967629644 cur_bit_bag >>= 1;
2967729645
29678 const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
29679 extra_index += 1;
29646 var field_name_zir: ?[:0]const u8 = null;
29647 if (!small.is_tuple) {
29648 field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
29649 extra_index += 1;
29650 }
2968029651 extra_index += 1; // doc_comment
2968129652
2968229653 fields[field_i] = .{};
......@@ -29689,7 +29660,10 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
2968929660 extra_index += 1;
2969029661
2969129662 // This string needs to outlive the ZIR code.
29692 const field_name = try decl_arena_allocator.dupe(u8, field_name_zir);
29663 const field_name = if (field_name_zir) |some|
29664 try decl_arena_allocator.dupe(u8, some)
29665 else
29666 try std.fmt.allocPrint(decl_arena_allocator, "{d}", .{field_i});
2969329667
2969429668 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
2969529669 if (gop.found_existing) {
src/Zir.zig+3-2
......@@ -3166,7 +3166,7 @@ pub const Inst = struct {
31663166 /// 0b0X00: whether corresponding field is comptime
31673167 /// 0bX000: whether corresponding field has a type expression
31683168 /// 9. fields: { // for every fields_len
3169 /// field_name: u32,
3169 /// field_name: u32, // if !is_tuple
31703170 /// doc_comment: u32, // 0 if no doc comment
31713171 /// field_type: Ref, // if corresponding bit is not set. none means anytype.
31723172 /// field_type_body_len: u32, // if corresponding bit is set
......@@ -3186,9 +3186,10 @@ pub const Inst = struct {
31863186 has_backing_int: bool,
31873187 known_non_opv: bool,
31883188 known_comptime_only: bool,
3189 is_tuple: bool,
31893190 name_strategy: NameStrategy,
31903191 layout: std.builtin.Type.ContainerLayout,
3191 _: u6 = undefined,
3192 _: u5 = undefined,
31923193 };
31933194 };
31943195
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/codegen/c.zig+1-1
......@@ -1796,7 +1796,7 @@ pub const DeclGen = struct {
17961796 },
17971797 .Struct, .Union => |tag| if (tag == .Struct and t.containerLayout() == .Packed)
17981798 try dg.renderType(w, t.castTag(.@"struct").?.data.backing_int_ty, kind)
1799 else if (t.isTupleOrAnonStruct()) {
1799 else if (t.isSimpleTupleOrAnonStruct()) {
18001800 const ExpectedContents = struct { types: [8]Type, values: [8]Value };
18011801 var stack align(@alignOf(ExpectedContents)) =
18021802 std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa);
src/codegen/llvm.zig+5-5
......@@ -1956,7 +1956,7 @@ pub const Object = struct {
19561956 break :blk fwd_decl;
19571957 };
19581958
1959 if (ty.isTupleOrAnonStruct()) {
1959 if (ty.isSimpleTupleOrAnonStruct()) {
19601960 const tuple = ty.tupleFields();
19611961
19621962 var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{};
......@@ -2890,7 +2890,7 @@ pub const DeclGen = struct {
28902890 // reference, we need to copy it here.
28912891 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
28922892
2893 if (t.isTupleOrAnonStruct()) {
2893 if (t.isSimpleTupleOrAnonStruct()) {
28942894 const tuple = t.tupleFields();
28952895 const llvm_struct_ty = dg.context.structCreateNamed("");
28962896 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
......@@ -3584,7 +3584,7 @@ pub const DeclGen = struct {
35843584 const field_vals = tv.val.castTag(.aggregate).?.data;
35853585 const gpa = dg.gpa;
35863586
3587 if (tv.ty.isTupleOrAnonStruct()) {
3587 if (tv.ty.isSimpleTupleOrAnonStruct()) {
35883588 const tuple = tv.ty.tupleFields();
35893589 var llvm_fields: std.ArrayListUnmanaged(*llvm.Value) = .{};
35903590 defer llvm_fields.deinit(gpa);
......@@ -10210,7 +10210,7 @@ fn llvmFieldIndex(
1021010210 var offset: u64 = 0;
1021110211 var big_align: u32 = 0;
1021210212
10213 if (ty.isTupleOrAnonStruct()) {
10213 if (ty.isSimpleTupleOrAnonStruct()) {
1021410214 const tuple = ty.tupleFields();
1021510215 var llvm_field_index: c_uint = 0;
1021610216 for (tuple.types) |field_ty, i| {
......@@ -10773,7 +10773,7 @@ fn isByRef(ty: Type) bool {
1077310773 .Struct => {
1077410774 // Packed structs are represented to LLVM as integers.
1077510775 if (ty.containerLayout() == .Packed) return false;
10776 if (ty.isTupleOrAnonStruct()) {
10776 if (ty.isSimpleTupleOrAnonStruct()) {
1077710777 const tuple = ty.tupleFields();
1077810778 var count: usize = 0;
1077910779 for (tuple.values) |field_val, i| {
src/print_zir.zig+13-6
......@@ -1262,6 +1262,7 @@ const Writer = struct {
12621262
12631263 try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv);
12641264 try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only);
1265 try self.writeFlag(stream, "tuple, ", small.is_tuple);
12651266
12661267 try stream.print("{s}, ", .{@tagName(small.name_strategy)});
12671268
......@@ -1335,8 +1336,11 @@ const Writer = struct {
13351336 const has_type_body = @truncate(u1, cur_bit_bag) != 0;
13361337 cur_bit_bag >>= 1;
13371338
1338 const field_name = self.code.extra[extra_index];
1339 extra_index += 1;
1339 var field_name: u32 = 0;
1340 if (!small.is_tuple) {
1341 field_name = self.code.extra[extra_index];
1342 extra_index += 1;
1343 }
13401344 const doc_comment_index = self.code.extra[extra_index];
13411345 extra_index += 1;
13421346
......@@ -1370,13 +1374,16 @@ const Writer = struct {
13701374 try stream.writeAll("{\n");
13711375 self.indent += 2;
13721376
1373 for (fields) |field| {
1374 const field_name = self.code.nullTerminatedString(field.name);
1375
1377 for (fields) |field, i| {
13761378 try self.writeDocComment(stream, field.doc_comment_index);
13771379 try stream.writeByteNTimes(' ', self.indent);
13781380 try self.writeFlag(stream, "comptime ", field.is_comptime);
1379 try stream.print("{}: ", .{std.zig.fmtId(field_name)});
1381 if (field.name != 0) {
1382 const field_name = self.code.nullTerminatedString(field.name);
1383 try stream.print("{}: ", .{std.zig.fmtId(field_name)});
1384 } else {
1385 try stream.print("@\"{d}\": ", .{i});
1386 }
13801387 if (field.field_type != .none) {
13811388 try self.writeInstRef(stream, field.field_type);
13821389 }
src/type.zig+19-1
......@@ -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();
......@@ -4494,6 +4494,7 @@ pub const Type = extern union {
44944494 .mut_slice,
44954495 .tuple,
44964496 .empty_struct_literal,
4497 .@"struct",
44974498 => return null,
44984499
44994500 .pointer => return self.castTag(.pointer).?.data.sentinel,
......@@ -6178,6 +6179,7 @@ pub const Type = extern union {
61786179 pub fn isTuple(ty: Type) bool {
61796180 return switch (ty.tag()) {
61806181 .tuple, .empty_struct_literal => true,
6182 .@"struct" => ty.castTag(.@"struct").?.data.is_tuple,
61816183 else => false,
61826184 };
61836185 }
......@@ -6190,12 +6192,28 @@ pub const Type = extern union {
61906192 }
61916193
61926194 pub fn isTupleOrAnonStruct(ty: Type) bool {
6195 return switch (ty.tag()) {
6196 .tuple, .empty_struct_literal, .anon_struct => true,
6197 .@"struct" => ty.castTag(.@"struct").?.data.is_tuple,
6198 else => false,
6199 };
6200 }
6201
6202 pub fn isSimpleTuple(ty: Type) bool {
6203 return switch (ty.tag()) {
6204 .tuple, .empty_struct_literal => true,
6205 else => false,
6206 };
6207 }
6208
6209 pub fn isSimpleTupleOrAnonStruct(ty: Type) bool {
61936210 return switch (ty.tag()) {
61946211 .tuple, .empty_struct_literal, .anon_struct => true,
61956212 else => false,
61966213 };
61976214 }
61986215
6216 // Only allowed for simple tuple types
61996217 pub fn tupleFields(ty: Type) Payload.Tuple.Data {
62006218 return switch (ty.tag()) {
62016219 .tuple => ty.castTag(.tuple).?.data,
src/value.zig+2-2
......@@ -2209,7 +2209,7 @@ pub const Value = extern union {
22092209 const b_field_vals = b.castTag(.aggregate).?.data;
22102210 assert(a_field_vals.len == b_field_vals.len);
22112211
2212 if (ty.isTupleOrAnonStruct()) {
2212 if (ty.isSimpleTupleOrAnonStruct()) {
22132213 const types = ty.tupleFields().types;
22142214 assert(types.len == a_field_vals.len);
22152215 for (types) |field_ty, i| {
......@@ -3004,7 +3004,7 @@ pub const Value = extern union {
30043004 .the_only_possible_value => return ty.onePossibleValue().?,
30053005
30063006 .empty_struct_value => {
3007 if (ty.isTupleOrAnonStruct()) {
3007 if (ty.isSimpleTupleOrAnonStruct()) {
30083008 const tuple = ty.tupleFields();
30093009 return tuple.values[index];
30103010 }
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/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig+1-1
......@@ -7,4 +7,4 @@ export fn entry() void {
77// backend=stage2
88// target=native
99//
10// :3:11: error: index '0' out of bounds of tuple '@TypeOf(.{})'
10// :3:11: error: expected type '@TypeOf(.{})', found 'tuple{comptime comptime_int = 1, comptime comptime_int = 2, comptime comptime_int = 3}'
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.