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 {...@@ -559,6 +559,7 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
559 .container_field,559 .container_field,
560 => {560 => {
561 const name_token = main_tokens[n];561 const name_token = main_tokens[n];
562 if (token_tags[name_token + 1] != .colon) return name_token - end_offset;
562 if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) {563 if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) {
563 end_offset += 1;564 end_offset += 1;
564 }565 }
...@@ -1320,33 +1321,39 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField {...@@ -1320,33 +1321,39 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField {
1320 assert(tree.nodes.items(.tag)[node] == .container_field);1321 assert(tree.nodes.items(.tag)[node] == .container_field);
1321 const data = tree.nodes.items(.data)[node];1322 const data = tree.nodes.items(.data)[node];
1322 const extra = tree.extraData(data.rhs, Node.ContainerField);1323 const extra = tree.extraData(data.rhs, Node.ContainerField);
1324 const main_token = tree.nodes.items(.main_token)[node];
1323 return tree.fullContainerField(.{1325 return tree.fullContainerField(.{
1324 .name_token = tree.nodes.items(.main_token)[node],1326 .main_token = main_token,
1325 .type_expr = data.lhs,1327 .type_expr = data.lhs,
1326 .value_expr = extra.value_expr,1328 .value_expr = extra.value_expr,
1327 .align_expr = extra.align_expr,1329 .align_expr = extra.align_expr,
1330 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,
1328 });1331 });
1329}1332}
13301333
1331pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField {1334pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField {
1332 assert(tree.nodes.items(.tag)[node] == .container_field_init);1335 assert(tree.nodes.items(.tag)[node] == .container_field_init);
1333 const data = tree.nodes.items(.data)[node];1336 const data = tree.nodes.items(.data)[node];
1337 const main_token = tree.nodes.items(.main_token)[node];
1334 return tree.fullContainerField(.{1338 return tree.fullContainerField(.{
1335 .name_token = tree.nodes.items(.main_token)[node],1339 .main_token = main_token,
1336 .type_expr = data.lhs,1340 .type_expr = data.lhs,
1337 .value_expr = data.rhs,1341 .value_expr = data.rhs,
1338 .align_expr = 0,1342 .align_expr = 0,
1343 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,
1339 });1344 });
1340}1345}
13411346
1342pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField {1347pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField {
1343 assert(tree.nodes.items(.tag)[node] == .container_field_align);1348 assert(tree.nodes.items(.tag)[node] == .container_field_align);
1344 const data = tree.nodes.items(.data)[node];1349 const data = tree.nodes.items(.data)[node];
1350 const main_token = tree.nodes.items(.main_token)[node];
1345 return tree.fullContainerField(.{1351 return tree.fullContainerField(.{
1346 .name_token = tree.nodes.items(.main_token)[node],1352 .main_token = main_token,
1347 .type_expr = data.lhs,1353 .type_expr = data.lhs,
1348 .value_expr = 0,1354 .value_expr = 0,
1349 .align_expr = data.rhs,1355 .align_expr = data.rhs,
1356 .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon,
1350 });1357 });
1351}1358}
13521359
...@@ -1944,10 +1951,14 @@ fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.Cont...@@ -1944,10 +1951,14 @@ fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.Cont
1944 .ast = info,1951 .ast = info,
1945 .comptime_token = null,1952 .comptime_token = null,
1946 };1953 };
1947 // comptime name: type = init,1954 if (token_tags[info.main_token] == .keyword_comptime) {
1948 // ^1955 // comptime type = init,
1949 if (info.name_token > 0 and token_tags[info.name_token - 1] == .keyword_comptime) {1956 // ^
1950 result.comptime_token = info.name_token - 1;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;
1951 }1962 }
1952 return result;1963 return result;
1953}1964}
...@@ -2256,14 +2267,26 @@ pub const full = struct {...@@ -2256,14 +2267,26 @@ pub const full = struct {
2256 ast: Components,2267 ast: Components,
22572268
2258 pub const Components = struct {2269 pub const Components = struct {
2259 name_token: TokenIndex,2270 main_token: TokenIndex,
2260 type_expr: Node.Index,2271 type_expr: Node.Index,
2261 value_expr: Node.Index,2272 value_expr: Node.Index,
2262 align_expr: Node.Index,2273 align_expr: Node.Index,
2274 tuple_like: bool,
2263 };2275 };
22642276
2265 pub fn firstToken(cf: ContainerField) TokenIndex {2277 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;
2267 }2290 }
2268 };2291 };
22692292
lib/std/zig/parse.zig+107-113
...@@ -272,53 +272,6 @@ const Parser = struct {...@@ -272,53 +272,6 @@ const Parser = struct {
272 trailing = false;272 trailing = false;
273 },273 },
274 .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {274 .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 },
322 .l_brace => {275 .l_brace => {
323 if (doc_comment) |some| {276 if (doc_comment) |some| {
324 try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });277 try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });
...@@ -349,53 +302,15 @@ const Parser = struct {...@@ -349,53 +302,15 @@ const Parser = struct {
349 },302 },
350 else => {303 else => {
351 p.tok_i += 1;304 p.tok_i += 1;
352 try p.warn(.expected_block_or_field);305 const identifier = p.tok_i;
353 },306 defer last_field = identifier;
354 },307 const container_field = p.expectContainerField() catch |err| switch (err) {
355 .keyword_pub => {308 error.OutOfMemory => return error.OutOfMemory,
356 p.tok_i += 1;309 error.ParseError => {
357 const top_level_decl = try p.expectTopLevelDeclRecoverable();310 p.findNextContainerMember();
358 if (top_level_decl != 0) {311 continue;
359 if (field_state == .seen) {312 },
360 field_state = .{ .end = top_level_decl };313 };
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) {
399 switch (field_state) {314 switch (field_state) {
400 .none => field_state = .seen,315 .none => field_state = .seen,
401 .err, .seen => {},316 .err, .seen => {},
...@@ -435,7 +350,46 @@ const Parser = struct {...@@ -435,7 +350,46 @@ const Parser = struct {
435 // Report error but recover parser.350 // Report error but recover parser.
436 try p.warn(.expected_comma_after_field);351 try p.warn(.expected_comma_after_field);
437 p.findNextContainerMember();352 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);
438 }391 }
392 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
439 },393 },
440 .eof, .r_brace => {394 .eof, .r_brace => {
441 if (doc_comment) |tok| {395 if (doc_comment) |tok| {
...@@ -451,11 +405,57 @@ const Parser = struct {...@@ -451,11 +405,57 @@ const Parser = struct {
451 error.OutOfMemory => return error.OutOfMemory,405 error.OutOfMemory => return error.OutOfMemory,
452 error.ParseError => false,406 error.ParseError => false,
453 };407 };
454 if (!c_container) {408 if (c_container) continue;
455 try p.warn(.expected_container_members);409
456 // This was likely not supposed to end yet; try to find the next declaration.410 const identifier = p.tok_i;
457 p.findNextContainerMember();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 },
458 }440 }
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;
459 },459 },
460 }460 }
461 }461 }
...@@ -875,12 +875,16 @@ const Parser = struct {...@@ -875,12 +875,16 @@ const Parser = struct {
875875
876 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?876 /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?
877 fn expectContainerField(p: *Parser) !Node.Index {877 fn expectContainerField(p: *Parser) !Node.Index {
878 var main_token = p.tok_i;
878 _ = p.eatToken(.keyword_comptime);879 _ = 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
881 var align_expr: Node.Index = 0;885 var align_expr: Node.Index = 0;
882 var type_expr: Node.Index = 0;886 var type_expr: Node.Index = 0;
883 if (p.eatToken(.colon)) |_| {887 if (p.eatToken(.colon) != null or tuple_like) {
884 type_expr = try p.expectTypeExpr();888 type_expr = try p.expectTypeExpr();
885 align_expr = try p.parseByteAlign();889 align_expr = try p.parseByteAlign();
886 }890 }
...@@ -890,7 +894,7 @@ const Parser = struct {...@@ -890,7 +894,7 @@ const Parser = struct {
890 if (align_expr == 0) {894 if (align_expr == 0) {
891 return p.addNode(.{895 return p.addNode(.{
892 .tag = .container_field_init,896 .tag = .container_field_init,
893 .main_token = name_token,897 .main_token = main_token,
894 .data = .{898 .data = .{
895 .lhs = type_expr,899 .lhs = type_expr,
896 .rhs = value_expr,900 .rhs = value_expr,
...@@ -899,7 +903,7 @@ const Parser = struct {...@@ -899,7 +903,7 @@ const Parser = struct {
899 } else if (value_expr == 0) {903 } else if (value_expr == 0) {
900 return p.addNode(.{904 return p.addNode(.{
901 .tag = .container_field_align,905 .tag = .container_field_align,
902 .main_token = name_token,906 .main_token = main_token,
903 .data = .{907 .data = .{
904 .lhs = type_expr,908 .lhs = type_expr,
905 .rhs = align_expr,909 .rhs = align_expr,
...@@ -908,7 +912,7 @@ const Parser = struct {...@@ -908,7 +912,7 @@ const Parser = struct {
908 } else {912 } else {
909 return p.addNode(.{913 return p.addNode(.{
910 .tag = .container_field,914 .tag = .container_field,
911 .main_token = name_token,915 .main_token = main_token,
912 .data = .{916 .data = .{
913 .lhs = type_expr,917 .lhs = type_expr,
914 .rhs = try p.addExtra(Node.ContainerField{918 .rhs = try p.addExtra(Node.ContainerField{
...@@ -920,16 +924,6 @@ const Parser = struct {...@@ -920,16 +924,6 @@ const Parser = struct {
920 }924 }
921 }925 }
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
933 /// Statement927 /// Statement
934 /// <- KEYWORD_comptime? VarDecl928 /// <- KEYWORD_comptime? VarDecl
935 /// / KEYWORD_comptime BlockExprStatement929 /// / KEYWORD_comptime BlockExprStatement
lib/std/zig/parser_test.zig+16-12
...@@ -1,3 +1,15 @@...@@ -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
1test "zig fmt: preserves clobbers in inline asm with stray comma" {13test "zig fmt: preserves clobbers in inline asm with stray comma" {
2 try testCanonical(14 try testCanonical(
3 \\fn foo() void {15 \\fn foo() void {
...@@ -265,14 +277,6 @@ test "zig fmt: decl between fields" {...@@ -265,14 +277,6 @@ test "zig fmt: decl between fields" {
265 });277 });
266}278}
267279
268test "zig fmt: eof after missing comma" {
269 try testError(
270 \\foo()
271 , &[_]Error{
272 .expected_comma_after_field,
273 });
274}
275
276test "zig fmt: errdefer with payload" {280test "zig fmt: errdefer with payload" {
277 try testCanonical(281 try testCanonical(
278 \\pub fn main() anyerror!void {282 \\pub fn main() anyerror!void {
...@@ -5732,8 +5736,8 @@ test "recovery: missing semicolon" {...@@ -5732,8 +5736,8 @@ test "recovery: missing semicolon" {
5732test "recovery: invalid container members" {5736test "recovery: invalid container members" {
5733 try testError(5737 try testError(
5734 \\usingnamespace;5738 \\usingnamespace;
5735 \\foo+5739 \\@foo()+
5736 \\bar@,5740 \\@bar()@,
5737 \\while (a == 2) { test "" {}}5741 \\while (a == 2) { test "" {}}
5738 \\test "" {5742 \\test "" {
5739 \\ a & b5743 \\ a & b
...@@ -5741,7 +5745,7 @@ test "recovery: invalid container members" {...@@ -5741,7 +5745,7 @@ test "recovery: invalid container members" {
5741 , &[_]Error{5745 , &[_]Error{
5742 .expected_expr,5746 .expected_expr,
5743 .expected_comma_after_field,5747 .expected_comma_after_field,
5744 .expected_container_members,5748 .expected_type_expr,
5745 .expected_semi_after_stmt,5749 .expected_semi_after_stmt,
5746 });5750 });
5747}5751}
...@@ -5820,7 +5824,7 @@ test "recovery: invalid comptime" {...@@ -5820,7 +5824,7 @@ test "recovery: invalid comptime" {
5820 try testError(5824 try testError(
5821 \\comptime5825 \\comptime
5822 , &[_]Error{5826 , &[_]Error{
5823 .expected_block_or_field,5827 .expected_type_expr,
5824 });5828 });
5825}5829}
58265830
lib/std/zig/render.zig+78-18
...@@ -40,14 +40,34 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void {...@@ -40,14 +40,34 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void {
40/// Render all members in the given slice, keeping empty lines where appropriate40/// Render all members in the given slice, keeping empty lines where appropriate
41fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void {41fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void {
42 if (members.len == 0) return;42 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);
44 for (members[1..]) |member| {57 for (members[1..]) |member| {
45 try renderExtraNewline(ais, tree, member);58 try renderExtraNewline(ais, tree, member);
46 try renderMember(gpa, ais, tree, member, .newline);59 try renderMember(gpa, ais, tree, member, is_tuple, .newline);
47 }60 }
48}61}
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 {
51 const token_tags = tree.tokens.items(.tag);71 const token_tags = tree.tokens.items(.tag);
52 const main_tokens = tree.nodes.items(.main_token);72 const main_tokens = tree.nodes.items(.main_token);
53 const datas = tree.nodes.items(.data);73 const datas = tree.nodes.items(.data);
...@@ -161,9 +181,9 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac...@@ -161,9 +181,9 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac
161 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);181 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
162 },182 },
163183
164 .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space),184 .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), is_tuple, space),
165 .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space),185 .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), is_tuple, space),
166 .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space),186 .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), is_tuple, space),
167 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),187 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),
168188
169 .root => unreachable,189 .root => unreachable,
...@@ -1158,18 +1178,34 @@ fn renderContainerField(...@@ -1158,18 +1178,34 @@ fn renderContainerField(
1158 gpa: Allocator,1178 gpa: Allocator,
1159 ais: *Ais,1179 ais: *Ais,
1160 tree: Ast,1180 tree: Ast,
1161 field: Ast.full.ContainerField,1181 field_param: Ast.full.ContainerField,
1182 is_tuple: bool,
1162 space: Space,1183 space: Space,
1163) Error!void {1184) Error!void {
1185 var field = field_param;
1186 if (!is_tuple) field.convertToNonTupleLike(tree.nodes);
1187
1164 if (field.comptime_token) |t| {1188 if (field.comptime_token) |t| {
1165 try renderToken(ais, tree, t, .space); // comptime1189 try renderToken(ais, tree, t, .space); // comptime
1166 }1190 }
1167 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {1191 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
1168 return renderIdentifierComma(ais, tree, field.ast.name_token, space, .eagerly_unquote); // name1192 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
1169 }1203 }
1170 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {1204 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
1171 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name1205 if (!field.ast.tuple_like) {
1172 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :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
1174 if (field.ast.align_expr != 0) {1210 if (field.ast.align_expr != 0) {
1175 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type1211 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
...@@ -1184,13 +1220,23 @@ fn renderContainerField(...@@ -1184,13 +1220,23 @@ fn renderContainerField(
1184 }1220 }
1185 }1221 }
1186 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {1222 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1187 try renderIdentifier(ais, tree, field.ast.name_token, .space, .eagerly_unquote); // name1223 try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
1188 try renderToken(ais, tree, field.ast.name_token + 1, .space); // =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); // =
1189 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value1234 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1190 }1235 }
11911236 if (!field.ast.tuple_like) {
1192 try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name1237 try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name
1193 try renderToken(ais, tree, field.ast.name_token + 1, .space); // :1238 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
1239 }
1194 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type1240 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
11951241
1196 if (field.ast.align_expr != 0) {1242 if (field.ast.align_expr != 0) {
...@@ -1901,6 +1947,20 @@ fn renderContainerDecl(...@@ -1901,6 +1947,20 @@ fn renderContainerDecl(
1901 try renderToken(ais, tree, layout_token, .space);1947 try renderToken(ais, tree, layout_token, .space);
1902 }1948 }
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
1904 var lbrace: Ast.TokenIndex = undefined;1964 var lbrace: Ast.TokenIndex = undefined;
1905 if (container_decl.ast.enum_token) |enum_token| {1965 if (container_decl.ast.enum_token) |enum_token| {
1906 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union1966 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
...@@ -1967,7 +2027,7 @@ fn renderContainerDecl(...@@ -1967,7 +2027,7 @@ fn renderContainerDecl(
1967 // Print all the declarations on the same line.2027 // Print all the declarations on the same line.
1968 try renderToken(ais, tree, lbrace, .space); // lbrace2028 try renderToken(ais, tree, lbrace, .space); // lbrace
1969 for (container_decl.ast.members) |member| {2029 for (container_decl.ast.members) |member| {
1970 try renderMember(gpa, ais, tree, member, .space);2030 try renderMember(gpa, ais, tree, member, is_tuple, .space);
1971 }2031 }
1972 return renderToken(ais, tree, rbrace, space); // rbrace2032 return renderToken(ais, tree, rbrace, space); // rbrace
1973 }2033 }
...@@ -1985,9 +2045,9 @@ fn renderContainerDecl(...@@ -1985,9 +2045,9 @@ fn renderContainerDecl(
1985 .container_field_init,2045 .container_field_init,
1986 .container_field_align,2046 .container_field_align,
1987 .container_field,2047 .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),
1991 }2051 }
1992 }2052 }
1993 ais.popIndent();2053 ais.popIndent();
src/AstGen.zig+57-12
...@@ -4386,6 +4386,7 @@ fn structDeclInner(...@@ -4386,6 +4386,7 @@ fn structDeclInner(
4386 .backing_int_body_len = 0,4386 .backing_int_body_len = 0,
4387 .known_non_opv = false,4387 .known_non_opv = false,
4388 .known_comptime_only = false,4388 .known_comptime_only = false,
4389 .is_tuple = false,
4389 });4390 });
4390 return indexToRef(decl_inst);4391 return indexToRef(decl_inst);
4391 }4392 }
...@@ -4467,22 +4468,53 @@ fn structDeclInner(...@@ -4467,22 +4468,53 @@ fn structDeclInner(
4467 // No defer needed here because it is handled by `wip_members.deinit()` above.4468 // No defer needed here because it is handled by `wip_members.deinit()` above.
4468 const bodies_start = astgen.scratch.items.len;4469 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
4470 var known_non_opv = false;4495 var known_non_opv = false;
4471 var known_comptime_only = false;4496 var known_comptime_only = false;
4472 for (container_decl.ast.members) |member_node| {4497 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)) {
4474 .decl => continue,4499 .decl => continue,
4475 .field => |field| field,4500 .field => |field| field,
4476 };4501 };
44774502
4478 const field_name = try astgen.identAsString(member.ast.name_token);4503 if (!is_tuple) {
4479 wip_members.appendToField(field_name);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
4481 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());4513 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
4482 wip_members.appendToField(doc_comment_index);4514 wip_members.appendToField(doc_comment_index);
44834515
4484 if (member.ast.type_expr == 0) {4516 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", .{});
4486 }4518 }
44874519
4488 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);4520 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
...@@ -4562,6 +4594,7 @@ fn structDeclInner(...@@ -4562,6 +4594,7 @@ fn structDeclInner(
4562 .backing_int_body_len = @intCast(u32, backing_int_body_len),4594 .backing_int_body_len = @intCast(u32, backing_int_body_len),
4563 .known_non_opv = known_non_opv,4595 .known_non_opv = known_non_opv,
4564 .known_comptime_only = known_comptime_only,4596 .known_comptime_only = known_comptime_only,
4597 .is_tuple = is_tuple,
4565 });4598 });
45664599
4567 wip_members.finishBits(bits_per_field);4600 wip_members.finishBits(bits_per_field);
...@@ -4640,15 +4673,19 @@ fn unionDeclInner(...@@ -4640,15 +4673,19 @@ fn unionDeclInner(
4640 defer wip_members.deinit();4673 defer wip_members.deinit();
46414674
4642 for (members) |member_node| {4675 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)) {
4644 .decl => continue,4677 .decl => continue,
4645 .field => |field| field,4678 .field => |field| field,
4646 };4679 };
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 }
4647 if (member.comptime_token) |comptime_token| {4684 if (member.comptime_token) |comptime_token| {
4648 return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});4685 return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
4649 }4686 }
46504687
4651 const field_name = try astgen.identAsString(member.ast.name_token);4688 const field_name = try astgen.identAsString(member.ast.main_token);
4652 wip_members.appendToField(field_name);4689 wip_members.appendToField(field_name);
46534690
4654 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());4691 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
...@@ -4787,7 +4824,7 @@ fn containerDecl(...@@ -4787,7 +4824,7 @@ fn containerDecl(
4787 var nonexhaustive_node: Ast.Node.Index = 0;4824 var nonexhaustive_node: Ast.Node.Index = 0;
4788 var nonfinal_nonexhaustive = false;4825 var nonfinal_nonexhaustive = false;
4789 for (container_decl.ast.members) |member_node| {4826 for (container_decl.ast.members) |member_node| {
4790 const member = switch (node_tags[member_node]) {4827 var member = switch (node_tags[member_node]) {
4791 .container_field_init => tree.containerFieldInit(member_node),4828 .container_field_init => tree.containerFieldInit(member_node),
4792 .container_field_align => tree.containerFieldAlign(member_node),4829 .container_field_align => tree.containerFieldAlign(member_node),
4793 .container_field => tree.containerField(member_node),4830 .container_field => tree.containerField(member_node),
...@@ -4796,6 +4833,10 @@ fn containerDecl(...@@ -4796,6 +4833,10 @@ fn containerDecl(
4796 continue;4833 continue;
4797 },4834 },
4798 };4835 };
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 }
4799 if (member.comptime_token) |comptime_token| {4840 if (member.comptime_token) |comptime_token| {
4800 return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});4841 return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
4801 }4842 }
...@@ -4813,10 +4854,11 @@ fn containerDecl(...@@ -4813,10 +4854,11 @@ fn containerDecl(
4813 },4854 },
4814 );4855 );
4815 }4856 }
4816 // Alignment expressions in enums are caught by the parser.4857 if (member.ast.align_expr != 0) {
4817 assert(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;
4820 if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {4862 if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
4821 if (nonexhaustive_node != 0) {4863 if (nonexhaustive_node != 0) {
4822 return astgen.failNodeNotes(4864 return astgen.failNodeNotes(
...@@ -4915,15 +4957,16 @@ fn containerDecl(...@@ -4915,15 +4957,16 @@ fn containerDecl(
4915 for (container_decl.ast.members) |member_node| {4957 for (container_decl.ast.members) |member_node| {
4916 if (member_node == counts.nonexhaustive_node)4958 if (member_node == counts.nonexhaustive_node)
4917 continue;4959 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)) {
4919 .decl => continue,4961 .decl => continue,
4920 .field => |field| field,4962 .field => |field| field,
4921 };4963 };
4964 member.convertToNonTupleLike(astgen.tree.nodes);
4922 assert(member.comptime_token == null);4965 assert(member.comptime_token == null);
4923 assert(member.ast.type_expr == 0);4966 assert(member.ast.type_expr == 0);
4924 assert(member.ast.align_expr == 0);4967 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);
4927 wip_members.appendToField(field_name);4970 wip_members.appendToField(field_name);
49284971
4929 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());4972 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
...@@ -11786,6 +11829,7 @@ const GenZir = struct {...@@ -11786,6 +11829,7 @@ const GenZir = struct {
11786 layout: std.builtin.Type.ContainerLayout,11829 layout: std.builtin.Type.ContainerLayout,
11787 known_non_opv: bool,11830 known_non_opv: bool,
11788 known_comptime_only: bool,11831 known_comptime_only: bool,
11832 is_tuple: bool,
11789 }) !void {11833 }) !void {
11790 const astgen = gz.astgen;11834 const astgen = gz.astgen;
11791 const gpa = astgen.gpa;11835 const gpa = astgen.gpa;
...@@ -11820,6 +11864,7 @@ const GenZir = struct {...@@ -11820,6 +11864,7 @@ const GenZir = struct {
11820 .has_backing_int = args.backing_int_ref != .none,11864 .has_backing_int = args.backing_int_ref != .none,
11821 .known_non_opv = args.known_non_opv,11865 .known_non_opv = args.known_non_opv,
11822 .known_comptime_only = args.known_comptime_only,11866 .known_comptime_only = args.known_comptime_only,
11867 .is_tuple = args.is_tuple,
11823 .name_strategy = gz.anon_name_strategy,11868 .name_strategy = gz.anon_name_strategy,
11824 .layout = args.layout,11869 .layout = args.layout,
11825 }),11870 }),
src/Module.zig+6-1
...@@ -938,6 +938,7 @@ pub const Struct = struct {...@@ -938,6 +938,7 @@ pub const Struct = struct {
938 known_non_opv: bool,938 known_non_opv: bool,
939 requires_comptime: PropertyBoolean = .unknown,939 requires_comptime: PropertyBoolean = .unknown,
940 have_field_inits: bool = false,940 have_field_inits: bool = false,
941 is_tuple: bool,
941942
942 pub const Fields = std.StringArrayHashMapUnmanaged(Field);943 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
943944
...@@ -4458,6 +4459,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {...@@ -4458,6 +4459,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
4458 .layout = .Auto,4459 .layout = .Auto,
4459 .status = .none,4460 .status = .none,
4460 .known_non_opv = undefined,4461 .known_non_opv = undefined,
4462 .is_tuple = undefined, // set below
4461 .namespace = .{4463 .namespace = .{
4462 .parent = null,4464 .parent = null,
4463 .ty = struct_ty,4465 .ty = struct_ty,
...@@ -4489,6 +4491,9 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {...@@ -4489,6 +4491,9 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
4489 assert(file.zir_loaded);4491 assert(file.zir_loaded);
4490 const main_struct_inst = Zir.main_struct_inst;4492 const main_struct_inst = Zir.main_struct_inst;
4491 struct_obj.zir_index = main_struct_inst;4493 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
4493 var sema_arena = std.heap.ArenaAllocator.init(gpa);4498 var sema_arena = std.heap.ArenaAllocator.init(gpa);
4494 defer sema_arena.deinit();4499 defer sema_arena.deinit();
...@@ -6138,7 +6143,7 @@ fn queryFieldSrc(...@@ -6138,7 +6143,7 @@ fn queryFieldSrc(
6138 .name => .{6143 .name => .{
6139 .file_scope = file_scope,6144 .file_scope = file_scope,
6140 .parent_decl_node = 0,6145 .parent_decl_node = 0,
6141 .lazy = .{ .token_abs = field.ast.name_token },6146 .lazy = .{ .token_abs = field.ast.main_token },
6142 },6147 },
6143 .type => .{6148 .type => .{
6144 .file_scope = file_scope,6149 .file_scope = file_scope,
src/Sema.zig+146-172
...@@ -2519,6 +2519,7 @@ fn zirStructDecl(...@@ -2519,6 +2519,7 @@ fn zirStructDecl(
2519 .layout = small.layout,2519 .layout = small.layout,
2520 .status = .none,2520 .status = .none,
2521 .known_non_opv = undefined,2521 .known_non_opv = undefined,
2522 .is_tuple = small.is_tuple,
2522 .namespace = .{2523 .namespace = .{
2523 .parent = block.namespace,2524 .parent = block.namespace,
2524 .ty = struct_ty,2525 .ty = struct_ty,
...@@ -4291,13 +4292,12 @@ fn zirValidateArrayInit(...@@ -4291,13 +4292,12 @@ fn zirValidateArrayInit(
42914292
4292 if (instrs.len != array_len) switch (array_ty.zigTypeTag()) {4293 if (instrs.len != array_len) switch (array_ty.zigTypeTag()) {
4293 .Struct => {4294 .Struct => {
4294 const struct_obj = array_ty.castTag(.tuple).?.data;
4295 var root_msg: ?*Module.ErrorMsg = null;4295 var root_msg: ?*Module.ErrorMsg = null;
4296 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);4296 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
42974297
4298 for (struct_obj.values) |default_val, i| {4298 var i = instrs.len;
4299 if (i < instrs.len) continue;4299 while (i < array_len) : (i += 1) {
43004300 const default_val = array_ty.structFieldDefaultValue(i);
4301 if (default_val.tag() == .unreachable_value) {4301 if (default_val.tag() == .unreachable_value) {
4302 const template = "missing tuple field with index {d}";4302 const template = "missing tuple field with index {d}";
4303 if (root_msg) |msg| {4303 if (root_msg) |msg| {
...@@ -7230,7 +7230,7 @@ fn instantiateGenericCall(...@@ -7230,7 +7230,7 @@ fn instantiateGenericCall(
7230}7230}
72317231
7232fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {7232fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
7233 if (!ty.isTuple()) return;7233 if (!ty.isSimpleTuple()) return;
7234 const tuple = ty.tupleFields();7234 const tuple = ty.tupleFields();
7235 for (tuple.values) |field_val, i| {7235 for (tuple.values) |field_val, i| {
7236 try sema.resolveTupleLazyValues(block, src, tuple.types[i]);7236 try sema.resolveTupleLazyValues(block, src, tuple.types[i]);
...@@ -7295,7 +7295,7 @@ fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -7295,7 +7295,7 @@ fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
7295 const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs);7295 const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs);
7296 assert(indexable_ty.isIndexable()); // validated by a previous instruction7296 assert(indexable_ty.isIndexable()); // validated by a previous instruction
7297 if (indexable_ty.zigTypeTag() == .Struct) {7297 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));
7299 return sema.addType(elem_type);7299 return sema.addType(elem_type);
7300 } else {7300 } else {
7301 const elem_type = indexable_ty.elemType2();7301 const elem_type = indexable_ty.elemType2();
...@@ -11827,13 +11827,19 @@ fn analyzeTupleCat(...@@ -11827,13 +11827,19 @@ fn analyzeTupleCat(
11827 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };11827 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
11828 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };11828 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
1182911829
11830 const lhs_tuple = lhs_ty.tupleFields();11830 const lhs_len = lhs_ty.structFieldCount();
11831 const rhs_tuple = rhs_ty.tupleFields();11831 const rhs_len = rhs_ty.structFieldCount();
11832 const dest_fields = lhs_tuple.types.len + rhs_tuple.types.len;11832 const dest_fields = lhs_len + rhs_len;
1183311833
11834 if (dest_fields == 0) {11834 if (dest_fields == 0) {
11835 return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));11835 return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
11836 }11836 }
11837 if (lhs_len == 0) {
11838 return rhs;
11839 }
11840 if (rhs_len == 0) {
11841 return lhs;
11842 }
11837 const final_len = try sema.usizeCast(block, rhs_src, dest_fields);11843 const final_len = try sema.usizeCast(block, rhs_src, dest_fields);
1183811844
11839 const types = try sema.arena.alloc(Type, final_len);11845 const types = try sema.arena.alloc(Type, final_len);
...@@ -11841,20 +11847,23 @@ fn analyzeTupleCat(...@@ -11841,20 +11847,23 @@ fn analyzeTupleCat(
1184111847
11842 const opt_runtime_src = rs: {11848 const opt_runtime_src = rs: {
11843 var runtime_src: ?LazySrcLoc = null;11849 var runtime_src: ?LazySrcLoc = null;
11844 for (lhs_tuple.types) |ty, i| {11850 var i: u32 = 0;
11845 types[i] = ty;11851 while (i < lhs_len) : (i += 1) {
11846 values[i] = lhs_tuple.values[i];11852 types[i] = lhs_ty.structFieldType(i);
11853 const default_val = lhs_ty.structFieldDefaultValue(i);
11854 values[i] = default_val;
11847 const operand_src = lhs_src; // TODO better source location11855 const operand_src = lhs_src; // TODO better source location
11848 if (values[i].tag() == .unreachable_value) {11856 if (default_val.tag() == .unreachable_value) {
11849 runtime_src = operand_src;11857 runtime_src = operand_src;
11850 }11858 }
11851 }11859 }
11852 const offset = lhs_tuple.types.len;11860 i = 0;
11853 for (rhs_tuple.types) |ty, i| {11861 while (i < rhs_len) : (i += 1) {
11854 types[i + offset] = ty;11862 types[i + lhs_len] = rhs_ty.structFieldType(i);
11855 values[i + offset] = rhs_tuple.values[i];11863 const default_val = rhs_ty.structFieldDefaultValue(i);
11864 values[i + lhs_len] = default_val;
11856 const operand_src = rhs_src; // TODO better source location11865 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) {
11858 runtime_src = operand_src;11867 runtime_src = operand_src;
11859 }11868 }
11860 }11869 }
...@@ -11874,15 +11883,16 @@ fn analyzeTupleCat(...@@ -11874,15 +11883,16 @@ fn analyzeTupleCat(
11874 try sema.requireRuntimeBlock(block, src, runtime_src);11883 try sema.requireRuntimeBlock(block, src, runtime_src);
1187511884
11876 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);11885 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) {
11878 const operand_src = lhs_src; // TODO better source location11888 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);
11880 }11890 }
11881 const offset = lhs_tuple.types.len;11891 i = 0;
11882 for (rhs_tuple.types) |_, i| {11892 while (i < rhs_len) : (i += 1) {
11883 const operand_src = rhs_src; // TODO better source location11893 const operand_src = rhs_src; // TODO better source location
11884 element_refs[i + offset] =11894 element_refs[i + lhs_len] =
11885 try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty);11895 try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty);
11886 }11896 }
1188711897
11888 return block.addAggregateInit(tuple_ty, element_refs);11898 return block.addAggregateInit(tuple_ty, element_refs);
...@@ -12107,12 +12117,11 @@ fn analyzeTupleMul(...@@ -12107,12 +12117,11 @@ fn analyzeTupleMul(
12107 factor: u64,12117 factor: u64,
12108) CompileError!Air.Inst.Ref {12118) CompileError!Air.Inst.Ref {
12109 const operand_ty = sema.typeOf(operand);12119 const operand_ty = sema.typeOf(operand);
12110 const operand_tuple = operand_ty.tupleFields();
12111 const src = LazySrcLoc.nodeOffset(src_node);12120 const src = LazySrcLoc.nodeOffset(src_node);
12112 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };12121 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
12113 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };12122 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();
12116 const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch12125 const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch
12117 return sema.fail(block, rhs_src, "operation results in overflow", .{});12126 return sema.fail(block, rhs_src, "operation results in overflow", .{});
1211812127
...@@ -12126,18 +12135,19 @@ fn analyzeTupleMul(...@@ -12126,18 +12135,19 @@ fn analyzeTupleMul(
1212612135
12127 const opt_runtime_src = rs: {12136 const opt_runtime_src = rs: {
12128 var runtime_src: ?LazySrcLoc = null;12137 var runtime_src: ?LazySrcLoc = null;
12129 for (operand_tuple.types) |ty, i| {12138 var i: u32 = 0;
12130 types[i] = ty;12139 while (i < tuple_len) : (i += 1) {
12131 values[i] = operand_tuple.values[i];12140 types[i] = operand_ty.structFieldType(i);
12141 values[i] = operand_ty.structFieldDefaultValue(i);
12132 const operand_src = lhs_src; // TODO better source location12142 const operand_src = lhs_src; // TODO better source location
12133 if (values[i].tag() == .unreachable_value) {12143 if (values[i].tag() == .unreachable_value) {
12134 runtime_src = operand_src;12144 runtime_src = operand_src;
12135 }12145 }
12136 }12146 }
12137 var i: usize = 1;12147 i = 0;
12138 while (i < factor) : (i += 1) {12148 while (i < factor) : (i += 1) {
12139 mem.copy(Type, types[tuple_len * i ..], operand_tuple.types);12149 mem.copy(Type, types[tuple_len * i ..], types[0..tuple_len]);
12140 mem.copy(Value, values[tuple_len * i ..], operand_tuple.values);12150 mem.copy(Value, values[tuple_len * i ..], values[0..tuple_len]);
12141 }12151 }
12142 break :rs runtime_src;12152 break :rs runtime_src;
12143 };12153 };
...@@ -12155,11 +12165,12 @@ fn analyzeTupleMul(...@@ -12155,11 +12165,12 @@ fn analyzeTupleMul(
12155 try sema.requireRuntimeBlock(block, src, runtime_src);12165 try sema.requireRuntimeBlock(block, src, runtime_src);
1215612166
12157 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);12167 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) {
12159 const operand_src = lhs_src; // TODO better source location12170 const operand_src = lhs_src; // TODO better source location
12160 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(u32, i), operand_ty);12171 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(u32, i), operand_ty);
12161 }12172 }
12162 var i: usize = 1;12173 i = 1;
12163 while (i < factor) : (i += 1) {12174 while (i < factor) : (i += 1) {
12164 mem.copy(Air.Inst.Ref, element_refs[tuple_len * i ..], element_refs[0..tuple_len]);12175 mem.copy(Air.Inst.Ref, element_refs[tuple_len * i ..], element_refs[0..tuple_len]);
12165 }12176 }
...@@ -15593,7 +15604,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -15593,7 +15604,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
15593 const layout = struct_ty.containerLayout();15604 const layout = struct_ty.containerLayout();
1559415605
15595 const struct_field_vals = fv: {15606 const struct_field_vals = fv: {
15596 if (struct_ty.isTupleOrAnonStruct()) {15607 if (struct_ty.isSimpleTupleOrAnonStruct()) {
15597 const tuple = struct_ty.tupleFields();15608 const tuple = struct_ty.tupleFields();
15598 const field_types = tuple.types;15609 const field_types = tuple.types;
15599 const struct_field_vals = try fields_anon_decl.arena().alloc(Value, field_types.len);15610 const struct_field_vals = try fields_anon_decl.arena().alloc(Value, field_types.len);
...@@ -17063,10 +17074,12 @@ fn finishStructInit(...@@ -17063,10 +17074,12 @@ fn finishStructInit(
17063 }17074 }
17064 }17075 }
17065 } else if (struct_ty.isTuple()) {17076 } else if (struct_ty.isTuple()) {
17066 const struct_obj = struct_ty.castTag(.tuple).?.data;17077 var i: u32 = 0;
17067 for (struct_obj.values) |default_val, i| {17078 const len = struct_ty.structFieldCount();
17079 while (i < len) : (i += 1) {
17068 if (field_inits[i] != .none) continue;17080 if (field_inits[i] != .none) continue;
1706917081
17082 const default_val = struct_ty.structFieldDefaultValue(i);
17070 if (default_val.tag() == .unreachable_value) {17083 if (default_val.tag() == .unreachable_value) {
17071 const template = "missing tuple field with index {d}";17084 const template = "missing tuple field with index {d}";
17072 if (root_msg) |msg| {17085 if (root_msg) |msg| {
...@@ -17075,7 +17088,7 @@ fn finishStructInit(...@@ -17075,7 +17088,7 @@ fn finishStructInit(
17075 root_msg = try sema.errMsg(block, init_src, template, .{i});17088 root_msg = try sema.errMsg(block, init_src, template, .{i});
17076 }17089 }
17077 } else {17090 } 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);
17079 }17092 }
17080 }17093 }
17081 } else {17094 } else {
...@@ -17297,7 +17310,7 @@ fn zirArrayInit(...@@ -17297,7 +17310,7 @@ fn zirArrayInit(
17297 const resolved_arg = try sema.resolveInst(arg);17310 const resolved_arg = try sema.resolveInst(arg);
17298 const arg_src = src; // TODO better source location17311 const arg_src = src; // TODO better source location
17299 const elem_ty = if (array_ty.zigTypeTag() == .Struct)17312 const elem_ty = if (array_ty.zigTypeTag() == .Struct)
17300 array_ty.tupleFields().types[i]17313 array_ty.structFieldType(i)
17301 else17314 else
17302 array_ty.elemType2();17315 array_ty.elemType2();
17303 resolved_args[i] = try sema.coerce(block, elem_ty, resolved_arg, arg_src);17316 resolved_args[i] = try sema.coerce(block, elem_ty, resolved_arg, arg_src);
...@@ -17337,12 +17350,11 @@ fn zirArrayInit(...@@ -17337,12 +17350,11 @@ fn zirArrayInit(
17337 const alloc = try block.addTy(.alloc, alloc_ty);17350 const alloc = try block.addTy(.alloc, alloc_ty);
1733817351
17339 if (array_ty.isTuple()) {17352 if (array_ty.isTuple()) {
17340 const types = array_ty.tupleFields().types;
17341 for (resolved_args) |arg, i| {17353 for (resolved_args) |arg, i| {
17342 const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{17354 const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
17343 .mutable = true,17355 .mutable = true,
17344 .@"addrspace" = target_util.defaultAddressSpace(target, .local),17356 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
17345 .pointee_type = types[i],17357 .pointee_type = array_ty.structFieldType(i),
17346 });17358 });
17347 const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);17359 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...@@ -18015,10 +18027,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
18015 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});18027 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});
18016 }18028 }
1801718029
18018 return if (is_tuple_val.toBool())18030 return try sema.reifyStruct(block, inst, src, layout, backing_int_val, fields_val, name_strategy, 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);
18022 },18031 },
18023 .Enum => {18032 .Enum => {
18024 const struct_val: []const Value = union_val.val.castTag(.aggregate).?.data;18033 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...@@ -18432,84 +18441,6 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
18432 }18441 }
18433}18442}
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
18513fn reifyStruct(18444fn reifyStruct(
18514 sema: *Sema,18445 sema: *Sema,
18515 block: *Block,18446 block: *Block,
...@@ -18519,6 +18450,7 @@ fn reifyStruct(...@@ -18519,6 +18450,7 @@ fn reifyStruct(
18519 backing_int_val: Value,18450 backing_int_val: Value,
18520 fields_val: Value,18451 fields_val: Value,
18521 name_strategy: Zir.Inst.NameStrategy,18452 name_strategy: Zir.Inst.NameStrategy,
18453 is_tuple: bool,
18522) CompileError!Air.Inst.Ref {18454) CompileError!Air.Inst.Ref {
18523 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);18455 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
18524 errdefer new_decl_arena.deinit();18456 errdefer new_decl_arena.deinit();
...@@ -18542,6 +18474,7 @@ fn reifyStruct(...@@ -18542,6 +18474,7 @@ fn reifyStruct(
18542 .layout = layout,18474 .layout = layout,
18543 .status = .have_field_types,18475 .status = .have_field_types,
18544 .known_non_opv = false,18476 .known_non_opv = false,
18477 .is_tuple = is_tuple,
18545 .namespace = .{18478 .namespace = .{
18546 .parent = block.namespace,18479 .parent = block.namespace,
18547 .ty = struct_ty,18480 .ty = struct_ty,
...@@ -18575,8 +18508,12 @@ fn reifyStruct(...@@ -18575,8 +18508,12 @@ fn reifyStruct(
18575 }18508 }
18576 const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?);18509 const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?);
1857718510
18578 if (layout == .Packed and abi_align != 0) {18511 if (layout == .Packed) {
18579 return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});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", .{});
18580 }18517 }
1858118518
18582 const field_name = try name_val.toAllocatedBytes(18519 const field_name = try name_val.toAllocatedBytes(
...@@ -18585,6 +18522,25 @@ fn reifyStruct(...@@ -18585,6 +18522,25 @@ fn reifyStruct(
18585 mod,18522 mod,
18586 );18523 );
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 }
18588 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);18544 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
18589 if (gop.found_existing) {18545 if (gop.found_existing) {
18590 // TODO: better source location18546 // TODO: better source location
...@@ -18598,6 +18554,9 @@ fn reifyStruct(...@@ -18598,6 +18554,9 @@ fn reifyStruct(
18598 opt_val;18554 opt_val;
18599 break :blk try payload_val.copy(new_decl_arena_allocator);18555 break :blk try payload_val.copy(new_decl_arena_allocator);
18600 } else Value.initTag(.unreachable_value);18556 } 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
18602 var buffer: Value.ToTypeBuffer = undefined;18561 var buffer: Value.ToTypeBuffer = undefined;
18603 gop.value_ptr.* = .{18562 gop.value_ptr.* = .{
...@@ -23177,6 +23136,7 @@ fn structFieldVal(...@@ -23177,6 +23136,7 @@ fn structFieldVal(
23177 },23136 },
23178 .@"struct" => {23137 .@"struct" => {
23179 const struct_obj = struct_ty.castTag(.@"struct").?.data;23138 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
23181 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse23141 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
23182 return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);23142 return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
...@@ -23249,11 +23209,10 @@ fn tupleFieldValByIndex(...@@ -23249,11 +23209,10 @@ fn tupleFieldValByIndex(
23249 field_index: u32,23209 field_index: u32,
23250 tuple_ty: Type,23210 tuple_ty: Type,
23251) CompileError!Air.Inst.Ref {23211) CompileError!Air.Inst.Ref {
23252 const tuple = tuple_ty.tupleFields();23212 const field_ty = tuple_ty.structFieldType(field_index);
23253 const field_ty = tuple.types[field_index];
2325423213
23255 if (tuple.values[field_index].tag() != .unreachable_value) {23214 if (tuple_ty.structFieldValueComptime(field_index)) |default_value| {
23256 return sema.addConstant(field_ty, tuple.values[field_index]);23215 return sema.addConstant(field_ty, default_value);
23257 }23216 }
2325823217
23259 if (try sema.resolveMaybeUndefVal(tuple_byval)) |tuple_val| {23218 if (try sema.resolveMaybeUndefVal(tuple_byval)) |tuple_val| {
...@@ -23601,19 +23560,20 @@ fn tupleFieldPtr(...@@ -23601,19 +23560,20 @@ fn tupleFieldPtr(
23601) CompileError!Air.Inst.Ref {23560) CompileError!Air.Inst.Ref {
23602 const tuple_ptr_ty = sema.typeOf(tuple_ptr);23561 const tuple_ptr_ty = sema.typeOf(tuple_ptr);
23603 const tuple_ty = tuple_ptr_ty.childType();23562 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) {
23607 return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});23567 return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
23608 }23568 }
2360923569
23610 if (field_index >= tuple_fields.types.len) {23570 if (field_index >= field_count) {
23611 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{23571 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,
23613 });23573 });
23614 }23574 }
2361523575
23616 const field_ty = tuple_fields.types[field_index];23576 const field_ty = tuple_ty.structFieldType(field_index);
23617 const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{23577 const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{
23618 .pointee_type = field_ty,23578 .pointee_type = field_ty,
23619 .mutable = tuple_ptr_ty.ptrIsMutable(),23579 .mutable = tuple_ptr_ty.ptrIsMutable(),
...@@ -23656,24 +23616,23 @@ fn tupleField(...@@ -23656,24 +23616,23 @@ fn tupleField(
23656 field_index_src: LazySrcLoc,23616 field_index_src: LazySrcLoc,
23657 field_index: u32,23617 field_index: u32,
23658) CompileError!Air.Inst.Ref {23618) CompileError!Air.Inst.Ref {
23659 const tuple_ty = sema.typeOf(tuple);23619 const tuple_ty = try sema.resolveTypeFields(sema.typeOf(tuple));
23660 const tuple_fields = tuple_ty.tupleFields();23620 const field_count = tuple_ty.structFieldCount();
2366123621
23662 if (tuple_fields.types.len == 0) {23622 if (field_count == 0) {
23663 return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});23623 return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});
23664 }23624 }
2366523625
23666 if (field_index >= tuple_fields.types.len) {23626 if (field_index >= field_count) {
23667 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{23627 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,
23669 });23629 });
23670 }23630 }
2367123631
23672 const field_ty = tuple_fields.types[field_index];23632 const field_ty = tuple_ty.structFieldType(field_index);
23673 const field_val = tuple_fields.values[field_index];
2367423633
23675 if (field_val.tag() != .unreachable_value) {23634 if (tuple_ty.structFieldValueComptime(field_index)) |default_value| {
23676 return sema.addConstant(field_ty, field_val); // comptime field23635 return sema.addConstant(field_ty, default_value); // comptime field
23677 }23636 }
2367823637
23679 if (try sema.resolveMaybeUndefVal(tuple)) |tuple_val| {23638 if (try sema.resolveMaybeUndefVal(tuple)) |tuple_val| {
...@@ -24223,7 +24182,10 @@ fn coerceExtra(...@@ -24223,7 +24182,10 @@ fn coerceExtra(
24223 inst_ty.childType().isAnonStruct() and24182 inst_ty.childType().isAnonStruct() and
24224 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))24183 sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result))
24225 {24184 {
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 };
24227 }24189 }
24228 },24190 },
24229 .Array => {24191 .Array => {
...@@ -24253,7 +24215,7 @@ fn coerceExtra(...@@ -24253,7 +24215,7 @@ fn coerceExtra(
2425324215
24254 // empty tuple to zero-length slice24216 // empty tuple to zero-length slice
24255 // note that this allows coercing to a mutable slice.24217 // 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) {
24257 const slice_val = try Value.Tag.slice.create(sema.arena, .{24219 const slice_val = try Value.Tag.slice.create(sema.arena, .{
24258 .ptr = Value.undef,24220 .ptr = Value.undef,
24259 .len = Value.zero,24221 .len = Value.zero,
...@@ -24536,12 +24498,15 @@ fn coerceExtra(...@@ -24536,12 +24498,15 @@ fn coerceExtra(
24536 },24498 },
24537 else => {},24499 else => {},
24538 },24500 },
24539 .Struct => {24501 .Struct => blk: {
24540 if (inst == .empty_struct) {24502 if (inst == .empty_struct) {
24541 return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src);24503 return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src);
24542 }24504 }
24543 if (inst_ty.isTupleOrAnonStruct()) {24505 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 };
24545 }24510 }
24546 },24511 },
24547 else => {},24512 else => {},
...@@ -25563,9 +25528,9 @@ fn storePtr2(...@@ -25563,9 +25528,9 @@ fn storePtr2(
25563 // fields.25528 // fields.
25564 const operand_ty = sema.typeOf(uncasted_operand);25529 const operand_ty = sema.typeOf(uncasted_operand);
25565 if (operand_ty.isTuple() and elem_ty.zigTypeTag() == .Array) {25530 if (operand_ty.isTuple() and elem_ty.zigTypeTag() == .Array) {
25566 const tuple = operand_ty.tupleFields();25531 const field_count = operand_ty.structFieldCount();
25567 for (tuple.types) |_, i_usize| {25532 var i: u32 = 0;
25568 const i = @intCast(u32, i_usize);25533 while (i < field_count) : (i += 1) {
25569 const elem_src = operand_src; // TODO better source location25534 const elem_src = operand_src; // TODO better source location
25570 const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);25535 const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);
25571 const elem_index = try sema.addIntUnsigned(Type.usize, i);25536 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...@@ -26657,7 +26622,7 @@ fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_resul
26657 const inst_info = inst_ty.ptrInfo().data;26622 const inst_info = inst_ty.ptrInfo().data;
26658 const len0 = (inst_info.pointee_type.zigTypeTag() == .Array and (inst_info.pointee_type.arrayLenIncludingSentinel() == 0 or26623 const len0 = (inst_info.pointee_type.zigTypeTag() == .Array and (inst_info.pointee_type.arrayLenIncludingSentinel() == 0 or
26659 (inst_info.pointee_type.arrayLen() == 0 and dest_info.sentinel == null and dest_info.size != .C and dest_info.size != .Many))) or26624 (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
26662 const ok_cv_qualifiers =26627 const ok_cv_qualifiers =
26663 ((inst_info.mutable or !dest_info.mutable) or len0) and26628 ((inst_info.mutable or !dest_info.mutable) or len0) and
...@@ -27142,18 +27107,18 @@ fn coerceTupleToStruct(...@@ -27142,18 +27107,18 @@ fn coerceTupleToStruct(
27142 mem.set(Air.Inst.Ref, field_refs, .none);27107 mem.set(Air.Inst.Ref, field_refs, .none);
2714327108
27144 const inst_ty = sema.typeOf(inst);27109 const inst_ty = sema.typeOf(inst);
27145 const tuple = inst_ty.tupleFields();
27146 var runtime_src: ?LazySrcLoc = null;27110 var runtime_src: ?LazySrcLoc = null;
27147 for (tuple.types) |_, i_usize| {27111 const field_count = inst_ty.structFieldCount();
27148 const i = @intCast(u32, i_usize);27112 var field_i: u32 = 0;
27113 while (field_i < field_count) : (field_i += 1) {
27149 const field_src = inst_src; // TODO better source location27114 const field_src = inst_src; // TODO better source location
27150 const field_name = if (inst_ty.castTag(.anon_struct)) |payload|27115 const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
27151 payload.data.names[i]27116 payload.data.names[field_i]
27152 else27117 else
27153 try std.fmt.allocPrint(sema.arena, "{d}", .{i});27118 try std.fmt.allocPrint(sema.arena, "{d}", .{field_i});
27154 const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);27119 const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
27155 const field = fields.values()[field_index];27120 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);
27157 const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);27122 const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
27158 field_refs[field_index] = coerced;27123 field_refs[field_index] = coerced;
27159 if (field.is_comptime) {27124 if (field.is_comptime) {
...@@ -27162,7 +27127,7 @@ fn coerceTupleToStruct(...@@ -27162,7 +27127,7 @@ fn coerceTupleToStruct(
27162 };27127 };
2716327128
27164 if (!init_val.eql(field.default_val, field.ty, sema.mod)) {27129 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);
27166 }27131 }
27167 }27132 }
27168 if (runtime_src == null) {27133 if (runtime_src == null) {
...@@ -27225,21 +27190,23 @@ fn coerceTupleToTuple(...@@ -27225,21 +27190,23 @@ fn coerceTupleToTuple(
27225 inst: Air.Inst.Ref,27190 inst: Air.Inst.Ref,
27226 inst_src: LazySrcLoc,27191 inst_src: LazySrcLoc,
27227) !Air.Inst.Ref {27192) !Air.Inst.Ref {
27228 const field_count = tuple_ty.structFieldCount();27193 const dest_field_count = tuple_ty.structFieldCount();
27229 const field_vals = try sema.arena.alloc(Value, field_count);27194 const field_vals = try sema.arena.alloc(Value, dest_field_count);
27230 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);27195 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
27231 mem.set(Air.Inst.Ref, field_refs, .none);27196 mem.set(Air.Inst.Ref, field_refs, .none);
2723227197
27233 const inst_ty = sema.typeOf(inst);27198 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
27235 var runtime_src: ?LazySrcLoc = null;27202 var runtime_src: ?LazySrcLoc = null;
27236 for (tuple.types) |_, i_usize| {27203 var field_i: u32 = 0;
27237 const i = @intCast(u32, i_usize);27204 while (field_i < inst_field_count) : (field_i += 1) {
27238 const field_src = inst_src; // TODO better source location27205 const field_src = inst_src; // TODO better source location
27239 const field_name = if (inst_ty.castTag(.anon_struct)) |payload|27206 const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
27240 payload.data.names[i]27207 payload.data.names[field_i]
27241 else27208 else
27242 try std.fmt.allocPrint(sema.arena, "{d}", .{i});27209 try std.fmt.allocPrint(sema.arena, "{d}", .{field_i});
2724327210
27244 if (mem.eql(u8, field_name, "len")) {27211 if (mem.eql(u8, field_name, "len")) {
27245 return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{});27212 return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{});
...@@ -27247,9 +27214,9 @@ fn coerceTupleToTuple(...@@ -27247,9 +27214,9 @@ fn coerceTupleToTuple(
2724727214
27248 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src);27215 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src);
2724927216
27250 const field_ty = tuple_ty.structFieldType(i);27217 const field_ty = tuple_ty.structFieldType(field_i);
27251 const default_val = tuple_ty.structFieldDefaultValue(i);27218 const default_val = tuple_ty.structFieldDefaultValue(field_i);
27252 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, i);27219 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
27253 const coerced = try sema.coerce(block, field_ty, elem_ref, field_src);27220 const coerced = try sema.coerce(block, field_ty, elem_ref, field_src);
27254 field_refs[field_index] = coerced;27221 field_refs[field_index] = coerced;
27255 if (default_val.tag() != .unreachable_value) {27222 if (default_val.tag() != .unreachable_value) {
...@@ -27258,7 +27225,7 @@ fn coerceTupleToTuple(...@@ -27258,7 +27225,7 @@ fn coerceTupleToTuple(
27258 };27225 };
2725927226
27260 if (!init_val.eql(default_val, field_ty, sema.mod)) {27227 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);
27262 }27229 }
27263 }27230 }
27264 if (runtime_src == null) {27231 if (runtime_src == null) {
...@@ -29641,6 +29608,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void...@@ -29641,6 +29608,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
29641 block_scope.params.deinit(gpa);29608 block_scope.params.deinit(gpa);
29642 }29609 }
2964329610
29611 struct_obj.fields = .{};
29644 try struct_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len);29612 try struct_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len);
2964529613
29646 const Field = struct {29614 const Field = struct {
...@@ -29675,8 +29643,11 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void...@@ -29675,8 +29643,11 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
29675 const has_type_body = @truncate(u1, cur_bit_bag) != 0;29643 const has_type_body = @truncate(u1, cur_bit_bag) != 0;
29676 cur_bit_bag >>= 1;29644 cur_bit_bag >>= 1;
2967729645
29678 const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);29646 var field_name_zir: ?[:0]const u8 = null;
29679 extra_index += 1;29647 if (!small.is_tuple) {
29648 field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
29649 extra_index += 1;
29650 }
29680 extra_index += 1; // doc_comment29651 extra_index += 1; // doc_comment
2968129652
29682 fields[field_i] = .{};29653 fields[field_i] = .{};
...@@ -29689,7 +29660,10 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void...@@ -29689,7 +29660,10 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
29689 extra_index += 1;29660 extra_index += 1;
2969029661
29691 // This string needs to outlive the ZIR code.29662 // 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
29694 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);29668 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
29695 if (gop.found_existing) {29669 if (gop.found_existing) {
src/Zir.zig+3-2
...@@ -3166,7 +3166,7 @@ pub const Inst = struct {...@@ -3166,7 +3166,7 @@ pub const Inst = struct {
3166 /// 0b0X00: whether corresponding field is comptime3166 /// 0b0X00: whether corresponding field is comptime
3167 /// 0bX000: whether corresponding field has a type expression3167 /// 0bX000: whether corresponding field has a type expression
3168 /// 9. fields: { // for every fields_len3168 /// 9. fields: { // for every fields_len
3169 /// field_name: u32,3169 /// field_name: u32, // if !is_tuple
3170 /// doc_comment: u32, // 0 if no doc comment3170 /// doc_comment: u32, // 0 if no doc comment
3171 /// field_type: Ref, // if corresponding bit is not set. none means anytype.3171 /// field_type: Ref, // if corresponding bit is not set. none means anytype.
3172 /// field_type_body_len: u32, // if corresponding bit is set3172 /// field_type_body_len: u32, // if corresponding bit is set
...@@ -3186,9 +3186,10 @@ pub const Inst = struct {...@@ -3186,9 +3186,10 @@ pub const Inst = struct {
3186 has_backing_int: bool,3186 has_backing_int: bool,
3187 known_non_opv: bool,3187 known_non_opv: bool,
3188 known_comptime_only: bool,3188 known_comptime_only: bool,
3189 is_tuple: bool,
3189 name_strategy: NameStrategy,3190 name_strategy: NameStrategy,
3190 layout: std.builtin.Type.ContainerLayout,3191 layout: std.builtin.Type.ContainerLayout,
3191 _: u6 = undefined,3192 _: u5 = undefined,
3192 };3193 };
3193 };3194 };
31943195
src/arch/x86_64/abi.zig+1
...@@ -552,6 +552,7 @@ test "C_C_D" {...@@ -552,6 +552,7 @@ test "C_C_D" {
552 .layout = .Extern,552 .layout = .Extern,
553 .status = .fully_resolved,553 .status = .fully_resolved,
554 .known_non_opv = true,554 .known_non_opv = true,
555 .is_tuple = false,
555 };556 };
556 var C_C_D = Type.Payload.Struct{ .data = &C_C_D_struct };557 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 {...@@ -1796,7 +1796,7 @@ pub const DeclGen = struct {
1796 },1796 },
1797 .Struct, .Union => |tag| if (tag == .Struct and t.containerLayout() == .Packed)1797 .Struct, .Union => |tag| if (tag == .Struct and t.containerLayout() == .Packed)
1798 try dg.renderType(w, t.castTag(.@"struct").?.data.backing_int_ty, kind)1798 try dg.renderType(w, t.castTag(.@"struct").?.data.backing_int_ty, kind)
1799 else if (t.isTupleOrAnonStruct()) {1799 else if (t.isSimpleTupleOrAnonStruct()) {
1800 const ExpectedContents = struct { types: [8]Type, values: [8]Value };1800 const ExpectedContents = struct { types: [8]Type, values: [8]Value };
1801 var stack align(@alignOf(ExpectedContents)) =1801 var stack align(@alignOf(ExpectedContents)) =
1802 std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa);1802 std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa);
src/codegen/llvm.zig+5-5
...@@ -1956,7 +1956,7 @@ pub const Object = struct {...@@ -1956,7 +1956,7 @@ pub const Object = struct {
1956 break :blk fwd_decl;1956 break :blk fwd_decl;
1957 };1957 };
19581958
1959 if (ty.isTupleOrAnonStruct()) {1959 if (ty.isSimpleTupleOrAnonStruct()) {
1960 const tuple = ty.tupleFields();1960 const tuple = ty.tupleFields();
19611961
1962 var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{};1962 var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{};
...@@ -2890,7 +2890,7 @@ pub const DeclGen = struct {...@@ -2890,7 +2890,7 @@ pub const DeclGen = struct {
2890 // reference, we need to copy it here.2890 // reference, we need to copy it here.
2891 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());2891 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
28922892
2893 if (t.isTupleOrAnonStruct()) {2893 if (t.isSimpleTupleOrAnonStruct()) {
2894 const tuple = t.tupleFields();2894 const tuple = t.tupleFields();
2895 const llvm_struct_ty = dg.context.structCreateNamed("");2895 const llvm_struct_ty = dg.context.structCreateNamed("");
2896 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls2896 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
...@@ -3584,7 +3584,7 @@ pub const DeclGen = struct {...@@ -3584,7 +3584,7 @@ pub const DeclGen = struct {
3584 const field_vals = tv.val.castTag(.aggregate).?.data;3584 const field_vals = tv.val.castTag(.aggregate).?.data;
3585 const gpa = dg.gpa;3585 const gpa = dg.gpa;
35863586
3587 if (tv.ty.isTupleOrAnonStruct()) {3587 if (tv.ty.isSimpleTupleOrAnonStruct()) {
3588 const tuple = tv.ty.tupleFields();3588 const tuple = tv.ty.tupleFields();
3589 var llvm_fields: std.ArrayListUnmanaged(*llvm.Value) = .{};3589 var llvm_fields: std.ArrayListUnmanaged(*llvm.Value) = .{};
3590 defer llvm_fields.deinit(gpa);3590 defer llvm_fields.deinit(gpa);
...@@ -10210,7 +10210,7 @@ fn llvmFieldIndex(...@@ -10210,7 +10210,7 @@ fn llvmFieldIndex(
10210 var offset: u64 = 0;10210 var offset: u64 = 0;
10211 var big_align: u32 = 0;10211 var big_align: u32 = 0;
1021210212
10213 if (ty.isTupleOrAnonStruct()) {10213 if (ty.isSimpleTupleOrAnonStruct()) {
10214 const tuple = ty.tupleFields();10214 const tuple = ty.tupleFields();
10215 var llvm_field_index: c_uint = 0;10215 var llvm_field_index: c_uint = 0;
10216 for (tuple.types) |field_ty, i| {10216 for (tuple.types) |field_ty, i| {
...@@ -10773,7 +10773,7 @@ fn isByRef(ty: Type) bool {...@@ -10773,7 +10773,7 @@ fn isByRef(ty: Type) bool {
10773 .Struct => {10773 .Struct => {
10774 // Packed structs are represented to LLVM as integers.10774 // Packed structs are represented to LLVM as integers.
10775 if (ty.containerLayout() == .Packed) return false;10775 if (ty.containerLayout() == .Packed) return false;
10776 if (ty.isTupleOrAnonStruct()) {10776 if (ty.isSimpleTupleOrAnonStruct()) {
10777 const tuple = ty.tupleFields();10777 const tuple = ty.tupleFields();
10778 var count: usize = 0;10778 var count: usize = 0;
10779 for (tuple.values) |field_val, i| {10779 for (tuple.values) |field_val, i| {
src/print_zir.zig+13-6
...@@ -1262,6 +1262,7 @@ const Writer = struct {...@@ -1262,6 +1262,7 @@ const Writer = struct {
12621262
1263 try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv);1263 try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv);
1264 try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only);1264 try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only);
1265 try self.writeFlag(stream, "tuple, ", small.is_tuple);
12651266
1266 try stream.print("{s}, ", .{@tagName(small.name_strategy)});1267 try stream.print("{s}, ", .{@tagName(small.name_strategy)});
12671268
...@@ -1335,8 +1336,11 @@ const Writer = struct {...@@ -1335,8 +1336,11 @@ const Writer = struct {
1335 const has_type_body = @truncate(u1, cur_bit_bag) != 0;1336 const has_type_body = @truncate(u1, cur_bit_bag) != 0;
1336 cur_bit_bag >>= 1;1337 cur_bit_bag >>= 1;
13371338
1338 const field_name = self.code.extra[extra_index];1339 var field_name: u32 = 0;
1339 extra_index += 1;1340 if (!small.is_tuple) {
1341 field_name = self.code.extra[extra_index];
1342 extra_index += 1;
1343 }
1340 const doc_comment_index = self.code.extra[extra_index];1344 const doc_comment_index = self.code.extra[extra_index];
1341 extra_index += 1;1345 extra_index += 1;
13421346
...@@ -1370,13 +1374,16 @@ const Writer = struct {...@@ -1370,13 +1374,16 @@ const Writer = struct {
1370 try stream.writeAll("{\n");1374 try stream.writeAll("{\n");
1371 self.indent += 2;1375 self.indent += 2;
13721376
1373 for (fields) |field| {1377 for (fields) |field, i| {
1374 const field_name = self.code.nullTerminatedString(field.name);
1375
1376 try self.writeDocComment(stream, field.doc_comment_index);1378 try self.writeDocComment(stream, field.doc_comment_index);
1377 try stream.writeByteNTimes(' ', self.indent);1379 try stream.writeByteNTimes(' ', self.indent);
1378 try self.writeFlag(stream, "comptime ", field.is_comptime);1380 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 }
1380 if (field.field_type != .none) {1387 if (field.field_type != .none) {
1381 try self.writeInstRef(stream, field.field_type);1388 try self.writeInstRef(stream, field.field_type);
1382 }1389 }
src/type.zig+19-1
...@@ -804,7 +804,7 @@ pub const Type = extern union {...@@ -804,7 +804,7 @@ pub const Type = extern union {
804 return a_struct_obj == b_struct_obj;804 return a_struct_obj == b_struct_obj;
805 },805 },
806 .tuple, .empty_struct_literal => {806 .tuple, .empty_struct_literal => {
807 if (!b.isTuple()) return false;807 if (!b.isSimpleTuple()) return false;
808808
809 const a_tuple = a.tupleFields();809 const a_tuple = a.tupleFields();
810 const b_tuple = b.tupleFields();810 const b_tuple = b.tupleFields();
...@@ -4494,6 +4494,7 @@ pub const Type = extern union {...@@ -4494,6 +4494,7 @@ pub const Type = extern union {
4494 .mut_slice,4494 .mut_slice,
4495 .tuple,4495 .tuple,
4496 .empty_struct_literal,4496 .empty_struct_literal,
4497 .@"struct",
4497 => return null,4498 => return null,
44984499
4499 .pointer => return self.castTag(.pointer).?.data.sentinel,4500 .pointer => return self.castTag(.pointer).?.data.sentinel,
...@@ -6178,6 +6179,7 @@ pub const Type = extern union {...@@ -6178,6 +6179,7 @@ pub const Type = extern union {
6178 pub fn isTuple(ty: Type) bool {6179 pub fn isTuple(ty: Type) bool {
6179 return switch (ty.tag()) {6180 return switch (ty.tag()) {
6180 .tuple, .empty_struct_literal => true,6181 .tuple, .empty_struct_literal => true,
6182 .@"struct" => ty.castTag(.@"struct").?.data.is_tuple,
6181 else => false,6183 else => false,
6182 };6184 };
6183 }6185 }
...@@ -6190,12 +6192,28 @@ pub const Type = extern union {...@@ -6190,12 +6192,28 @@ pub const Type = extern union {
6190 }6192 }
61916193
6192 pub fn isTupleOrAnonStruct(ty: Type) bool {6194 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 {
6193 return switch (ty.tag()) {6210 return switch (ty.tag()) {
6194 .tuple, .empty_struct_literal, .anon_struct => true,6211 .tuple, .empty_struct_literal, .anon_struct => true,
6195 else => false,6212 else => false,
6196 };6213 };
6197 }6214 }
61986215
6216 // Only allowed for simple tuple types
6199 pub fn tupleFields(ty: Type) Payload.Tuple.Data {6217 pub fn tupleFields(ty: Type) Payload.Tuple.Data {
6200 return switch (ty.tag()) {6218 return switch (ty.tag()) {
6201 .tuple => ty.castTag(.tuple).?.data,6219 .tuple => ty.castTag(.tuple).?.data,
src/value.zig+2-2
...@@ -2209,7 +2209,7 @@ pub const Value = extern union {...@@ -2209,7 +2209,7 @@ pub const Value = extern union {
2209 const b_field_vals = b.castTag(.aggregate).?.data;2209 const b_field_vals = b.castTag(.aggregate).?.data;
2210 assert(a_field_vals.len == b_field_vals.len);2210 assert(a_field_vals.len == b_field_vals.len);
22112211
2212 if (ty.isTupleOrAnonStruct()) {2212 if (ty.isSimpleTupleOrAnonStruct()) {
2213 const types = ty.tupleFields().types;2213 const types = ty.tupleFields().types;
2214 assert(types.len == a_field_vals.len);2214 assert(types.len == a_field_vals.len);
2215 for (types) |field_ty, i| {2215 for (types) |field_ty, i| {
...@@ -3004,7 +3004,7 @@ pub const Value = extern union {...@@ -3004,7 +3004,7 @@ pub const Value = extern union {
3004 .the_only_possible_value => return ty.onePossibleValue().?,3004 .the_only_possible_value => return ty.onePossibleValue().?,
30053005
3006 .empty_struct_value => {3006 .empty_struct_value => {
3007 if (ty.isTupleOrAnonStruct()) {3007 if (ty.isSimpleTupleOrAnonStruct()) {
3008 const tuple = ty.tupleFields();3008 const tuple = ty.tupleFields();
3009 return tuple.values[index];3009 return tuple.values[index];
3010 }3010 }
test/behavior.zig+1
...@@ -200,6 +200,7 @@ test {...@@ -200,6 +200,7 @@ test {
200 _ = @import("behavior/packed_struct_explicit_backing_int.zig");200 _ = @import("behavior/packed_struct_explicit_backing_int.zig");
201 _ = @import("behavior/empty_union.zig");201 _ = @import("behavior/empty_union.zig");
202 _ = @import("behavior/inline_switch.zig");202 _ = @import("behavior/inline_switch.zig");
203 _ = @import("behavior/tuple_declarations.zig");
203 _ = @import("behavior/bugs/12723.zig");204 _ = @import("behavior/bugs/12723.zig");
204 _ = @import("behavior/bugs/12776.zig");205 _ = @import("behavior/bugs/12776.zig");
205 }206 }
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 {...@@ -11,4 +11,4 @@ export fn entry1() void {
11// backend=stage211// backend=stage2
12// target=native12// target=native
13//13//
14// :3:7: error: expected ',' after field14// :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 {...@@ -7,4 +7,4 @@ export fn entry() void {
7// backend=stage27// backend=stage2
8// target=native8// target=native
9//9//
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 {...@@ -213,7 +213,7 @@ pub fn addCases(ctx: *TestContext) !void {
213 case.backend = .stage2;213 case.backend = .stage2;
214214
215 case.addSourceFile("b.zig",215 case.addSourceFile("b.zig",
216 \\bad216 \\+
217 );217 );
218218
219 case.addError(219 case.addError(
...@@ -221,7 +221,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -221,7 +221,7 @@ pub fn addCases(ctx: *TestContext) !void {
221 \\ _ = (@sizeOf(@import("b.zig")));221 \\ _ = (@sizeOf(@import("b.zig")));
222 \\}222 \\}
223 , &[_][]const u8{223 , &[_][]const u8{
224 ":1:1: error: struct field missing type",224 ":1:1: error: expected type expression, found '+'",
225 });225 });
226 }226 }
227227
test/stage2/cbe.zig+1-1
...@@ -670,7 +670,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -670,7 +670,7 @@ pub fn addCases(ctx: *TestContext) !void {
670 \\ _ = E1.a;670 \\ _ = E1.a;
671 \\}671 \\}
672 , &.{672 , &.{
673 ":3:7: error: expected ',' after field",673 ":3:13: error: enum fields cannot be aligned",
674 });674 });
675675
676 // Redundant non-exhaustive enum mark.676 // Redundant non-exhaustive enum mark.