| author | |
| committer | |
| log | 30eb2a1753c41f348f7d5dcf2b9059a51afab5ad |
| tree | 9c89fa27e516c1575daea0d6c408acab75617f58 |
| parent | a2403d354fa3f93aa4b916f574393c12dff39e51 |
| parent | 9f055e2eb0252d872c9111e60ef7c0802d91bea6 |
| signature |
Implement tuple type declarations23 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 | 559 | .container_field, |
| 560 | 560 | => { |
| 561 | 561 | const name_token = main_tokens[n]; |
| 562 | if (token_tags[name_token + 1] != .colon) return name_token - end_offset; | |
| 562 | 563 | if (name_token > 0 and token_tags[name_token - 1] == .keyword_comptime) { |
| 563 | 564 | end_offset += 1; |
| 564 | 565 | } |
| ... | ... | @@ -1320,33 +1321,39 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField { |
| 1320 | 1321 | assert(tree.nodes.items(.tag)[node] == .container_field); |
| 1321 | 1322 | const data = tree.nodes.items(.data)[node]; |
| 1322 | 1323 | const extra = tree.extraData(data.rhs, Node.ContainerField); |
| 1324 | const main_token = tree.nodes.items(.main_token)[node]; | |
| 1323 | 1325 | return tree.fullContainerField(.{ |
| 1324 | .name_token = tree.nodes.items(.main_token)[node], | |
| 1326 | .main_token = main_token, | |
| 1325 | 1327 | .type_expr = data.lhs, |
| 1326 | 1328 | .value_expr = extra.value_expr, |
| 1327 | 1329 | .align_expr = extra.align_expr, |
| 1330 | .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, | |
| 1328 | 1331 | }); |
| 1329 | 1332 | } |
| 1330 | 1333 | |
| 1331 | 1334 | pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField { |
| 1332 | 1335 | assert(tree.nodes.items(.tag)[node] == .container_field_init); |
| 1333 | 1336 | const data = tree.nodes.items(.data)[node]; |
| 1337 | const main_token = tree.nodes.items(.main_token)[node]; | |
| 1334 | 1338 | return tree.fullContainerField(.{ |
| 1335 | .name_token = tree.nodes.items(.main_token)[node], | |
| 1339 | .main_token = main_token, | |
| 1336 | 1340 | .type_expr = data.lhs, |
| 1337 | 1341 | .value_expr = data.rhs, |
| 1338 | 1342 | .align_expr = 0, |
| 1343 | .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, | |
| 1339 | 1344 | }); |
| 1340 | 1345 | } |
| 1341 | 1346 | |
| 1342 | 1347 | pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField { |
| 1343 | 1348 | assert(tree.nodes.items(.tag)[node] == .container_field_align); |
| 1344 | 1349 | const data = tree.nodes.items(.data)[node]; |
| 1350 | const main_token = tree.nodes.items(.main_token)[node]; | |
| 1345 | 1351 | return tree.fullContainerField(.{ |
| 1346 | .name_token = tree.nodes.items(.main_token)[node], | |
| 1352 | .main_token = main_token, | |
| 1347 | 1353 | .type_expr = data.lhs, |
| 1348 | 1354 | .value_expr = 0, |
| 1349 | 1355 | .align_expr = data.rhs, |
| 1356 | .tuple_like = tree.tokens.items(.tag)[main_token + 1] != .colon, | |
| 1350 | 1357 | }); |
| 1351 | 1358 | } |
| 1352 | 1359 | |
| ... | ... | @@ -1944,10 +1951,14 @@ fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.Cont |
| 1944 | 1951 | .ast = info, |
| 1945 | 1952 | .comptime_token = null, |
| 1946 | 1953 | }; |
| 1947 | // comptime name: type = init, | |
| 1948 | // ^ | |
| 1949 | if (info.name_token > 0 and token_tags[info.name_token - 1] == .keyword_comptime) { | |
| 1950 | result.comptime_token = info.name_token - 1; | |
| 1954 | if (token_tags[info.main_token] == .keyword_comptime) { | |
| 1955 | // comptime type = init, | |
| 1956 | // ^ | |
| 1957 | result.comptime_token = info.main_token; | |
| 1958 | } else if (info.main_token > 0 and token_tags[info.main_token - 1] == .keyword_comptime) { | |
| 1959 | // comptime name: type = init, | |
| 1960 | // ^ | |
| 1961 | result.comptime_token = info.main_token - 1; | |
| 1951 | 1962 | } |
| 1952 | 1963 | return result; |
| 1953 | 1964 | } |
| ... | ... | @@ -2256,14 +2267,26 @@ pub const full = struct { |
| 2256 | 2267 | ast: Components, |
| 2257 | 2268 | |
| 2258 | 2269 | pub const Components = struct { |
| 2259 | name_token: TokenIndex, | |
| 2270 | main_token: TokenIndex, | |
| 2260 | 2271 | type_expr: Node.Index, |
| 2261 | 2272 | value_expr: Node.Index, |
| 2262 | 2273 | align_expr: Node.Index, |
| 2274 | tuple_like: bool, | |
| 2263 | 2275 | }; |
| 2264 | 2276 | |
| 2265 | 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 | }; |
| 2269 | 2292 |
lib/std/zig/parse.zig+107-113| ... | ... | @@ -272,53 +272,6 @@ const Parser = struct { |
| 272 | 272 | trailing = false; |
| 273 | 273 | }, |
| 274 | 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 | 275 | .l_brace => { |
| 323 | 276 | if (doc_comment) |some| { |
| 324 | 277 | try p.warnMsg(.{ .tag = .test_doc_comment, .token = some }); |
| ... | ... | @@ -349,53 +302,15 @@ const Parser = struct { |
| 349 | 302 | }, |
| 350 | 303 | else => { |
| 351 | 304 | p.tok_i += 1; |
| 352 | try p.warn(.expected_block_or_field); | |
| 353 | }, | |
| 354 | }, | |
| 355 | .keyword_pub => { | |
| 356 | p.tok_i += 1; | |
| 357 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | |
| 358 | if (top_level_decl != 0) { | |
| 359 | if (field_state == .seen) { | |
| 360 | field_state = .{ .end = top_level_decl }; | |
| 361 | } | |
| 362 | try p.scratch.append(p.gpa, top_level_decl); | |
| 363 | } | |
| 364 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | |
| 365 | }, | |
| 366 | .keyword_usingnamespace => { | |
| 367 | const node = try p.expectUsingNamespaceRecoverable(); | |
| 368 | if (node != 0) { | |
| 369 | if (field_state == .seen) { | |
| 370 | field_state = .{ .end = node }; | |
| 371 | } | |
| 372 | try p.scratch.append(p.gpa, node); | |
| 373 | } | |
| 374 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | |
| 375 | }, | |
| 376 | .keyword_const, | |
| 377 | .keyword_var, | |
| 378 | .keyword_threadlocal, | |
| 379 | .keyword_export, | |
| 380 | .keyword_extern, | |
| 381 | .keyword_inline, | |
| 382 | .keyword_noinline, | |
| 383 | .keyword_fn, | |
| 384 | => { | |
| 385 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | |
| 386 | if (top_level_decl != 0) { | |
| 387 | if (field_state == .seen) { | |
| 388 | field_state = .{ .end = top_level_decl }; | |
| 389 | } | |
| 390 | try p.scratch.append(p.gpa, top_level_decl); | |
| 391 | } | |
| 392 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | |
| 393 | }, | |
| 394 | .identifier => { | |
| 395 | const identifier = p.tok_i; | |
| 396 | defer last_field = identifier; | |
| 397 | const container_field = try p.expectContainerFieldRecoverable(); | |
| 398 | if (container_field != 0) { | |
| 305 | const identifier = p.tok_i; | |
| 306 | defer last_field = identifier; | |
| 307 | const container_field = p.expectContainerField() catch |err| switch (err) { | |
| 308 | error.OutOfMemory => return error.OutOfMemory, | |
| 309 | error.ParseError => { | |
| 310 | p.findNextContainerMember(); | |
| 311 | continue; | |
| 312 | }, | |
| 313 | }; | |
| 399 | 314 | switch (field_state) { |
| 400 | 315 | .none => field_state = .seen, |
| 401 | 316 | .err, .seen => {}, |
| ... | ... | @@ -435,7 +350,46 @@ const Parser = struct { |
| 435 | 350 | // Report error but recover parser. |
| 436 | 351 | try p.warn(.expected_comma_after_field); |
| 437 | 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 | 394 | .eof, .r_brace => { |
| 441 | 395 | if (doc_comment) |tok| { |
| ... | ... | @@ -451,11 +405,57 @@ const Parser = struct { |
| 451 | 405 | error.OutOfMemory => return error.OutOfMemory, |
| 452 | 406 | error.ParseError => false, |
| 453 | 407 | }; |
| 454 | if (!c_container) { | |
| 455 | try p.warn(.expected_container_members); | |
| 456 | // This was likely not supposed to end yet; try to find the next declaration. | |
| 457 | p.findNextContainerMember(); | |
| 408 | if (c_container) continue; | |
| 409 | ||
| 410 | const identifier = p.tok_i; | |
| 411 | defer last_field = identifier; | |
| 412 | const container_field = p.expectContainerField() catch |err| switch (err) { | |
| 413 | error.OutOfMemory => return error.OutOfMemory, | |
| 414 | error.ParseError => { | |
| 415 | p.findNextContainerMember(); | |
| 416 | continue; | |
| 417 | }, | |
| 418 | }; | |
| 419 | switch (field_state) { | |
| 420 | .none => field_state = .seen, | |
| 421 | .err, .seen => {}, | |
| 422 | .end => |node| { | |
| 423 | try p.warnMsg(.{ | |
| 424 | .tag = .decl_between_fields, | |
| 425 | .token = p.nodes.items(.main_token)[node], | |
| 426 | }); | |
| 427 | try p.warnMsg(.{ | |
| 428 | .tag = .previous_field, | |
| 429 | .is_note = true, | |
| 430 | .token = last_field, | |
| 431 | }); | |
| 432 | try p.warnMsg(.{ | |
| 433 | .tag = .next_field, | |
| 434 | .is_note = true, | |
| 435 | .token = identifier, | |
| 436 | }); | |
| 437 | // Continue parsing; error will be reported later. | |
| 438 | field_state = .err; | |
| 439 | }, | |
| 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 | 875 | |
| 876 | 876 | /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? |
| 877 | 877 | fn expectContainerField(p: *Parser) !Node.Index { |
| 878 | var main_token = p.tok_i; | |
| 878 | 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 | } | |
| 880 | 884 | |
| 881 | 885 | var align_expr: Node.Index = 0; |
| 882 | 886 | var type_expr: Node.Index = 0; |
| 883 | if (p.eatToken(.colon)) |_| { | |
| 887 | if (p.eatToken(.colon) != null or tuple_like) { | |
| 884 | 888 | type_expr = try p.expectTypeExpr(); |
| 885 | 889 | align_expr = try p.parseByteAlign(); |
| 886 | 890 | } |
| ... | ... | @@ -890,7 +894,7 @@ const Parser = struct { |
| 890 | 894 | if (align_expr == 0) { |
| 891 | 895 | return p.addNode(.{ |
| 892 | 896 | .tag = .container_field_init, |
| 893 | .main_token = name_token, | |
| 897 | .main_token = main_token, | |
| 894 | 898 | .data = .{ |
| 895 | 899 | .lhs = type_expr, |
| 896 | 900 | .rhs = value_expr, |
| ... | ... | @@ -899,7 +903,7 @@ const Parser = struct { |
| 899 | 903 | } else if (value_expr == 0) { |
| 900 | 904 | return p.addNode(.{ |
| 901 | 905 | .tag = .container_field_align, |
| 902 | .main_token = name_token, | |
| 906 | .main_token = main_token, | |
| 903 | 907 | .data = .{ |
| 904 | 908 | .lhs = type_expr, |
| 905 | 909 | .rhs = align_expr, |
| ... | ... | @@ -908,7 +912,7 @@ const Parser = struct { |
| 908 | 912 | } else { |
| 909 | 913 | return p.addNode(.{ |
| 910 | 914 | .tag = .container_field, |
| 911 | .main_token = name_token, | |
| 915 | .main_token = main_token, | |
| 912 | 916 | .data = .{ |
| 913 | 917 | .lhs = type_expr, |
| 914 | 918 | .rhs = try p.addExtra(Node.ContainerField{ |
| ... | ... | @@ -920,16 +924,6 @@ const Parser = struct { |
| 920 | 924 | } |
| 921 | 925 | } |
| 922 | 926 | |
| 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 | 927 | /// Statement |
| 934 | 928 | /// <- KEYWORD_comptime? VarDecl |
| 935 | 929 | /// / KEYWORD_comptime BlockExprStatement |
lib/std/zig/parser_test.zig+16-12| ... | ... | @@ -1,3 +1,15 @@ |
| 1 | test "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 | ||
| 1 | 13 | test "zig fmt: preserves clobbers in inline asm with stray comma" { |
| 2 | 14 | try testCanonical( |
| 3 | 15 | \\fn foo() void { |
| ... | ... | @@ -265,14 +277,6 @@ test "zig fmt: decl between fields" { |
| 265 | 277 | }); |
| 266 | 278 | } |
| 267 | 279 | |
| 268 | test "zig fmt: eof after missing comma" { | |
| 269 | try testError( | |
| 270 | \\foo() | |
| 271 | , &[_]Error{ | |
| 272 | .expected_comma_after_field, | |
| 273 | }); | |
| 274 | } | |
| 275 | ||
| 276 | 280 | test "zig fmt: errdefer with payload" { |
| 277 | 281 | try testCanonical( |
| 278 | 282 | \\pub fn main() anyerror!void { |
| ... | ... | @@ -5732,8 +5736,8 @@ test "recovery: missing semicolon" { |
| 5732 | 5736 | test "recovery: invalid container members" { |
| 5733 | 5737 | try testError( |
| 5734 | 5738 | \\usingnamespace; |
| 5735 | \\foo+ | |
| 5736 | \\bar@, | |
| 5739 | \\@foo()+ | |
| 5740 | \\@bar()@, | |
| 5737 | 5741 | \\while (a == 2) { test "" {}} |
| 5738 | 5742 | \\test "" { |
| 5739 | 5743 | \\ a & b |
| ... | ... | @@ -5741,7 +5745,7 @@ test "recovery: invalid container members" { |
| 5741 | 5745 | , &[_]Error{ |
| 5742 | 5746 | .expected_expr, |
| 5743 | 5747 | .expected_comma_after_field, |
| 5744 | .expected_container_members, | |
| 5748 | .expected_type_expr, | |
| 5745 | 5749 | .expected_semi_after_stmt, |
| 5746 | 5750 | }); |
| 5747 | 5751 | } |
| ... | ... | @@ -5820,7 +5824,7 @@ test "recovery: invalid comptime" { |
| 5820 | 5824 | try testError( |
| 5821 | 5825 | \\comptime |
| 5822 | 5826 | , &[_]Error{ |
| 5823 | .expected_block_or_field, | |
| 5827 | .expected_type_expr, | |
| 5824 | 5828 | }); |
| 5825 | 5829 | } |
| 5826 | 5830 |
lib/std/zig/render.zig+78-18| ... | ... | @@ -40,14 +40,34 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void { |
| 40 | 40 | /// Render all members in the given slice, keeping empty lines where appropriate |
| 41 | 41 | fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void { |
| 42 | 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 | 57 | for (members[1..]) |member| { |
| 45 | 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 | } |
| 49 | 62 | |
| 50 | fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, space: Space) Error!void { | |
| 63 | fn 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 | 71 | const token_tags = tree.tokens.items(.tag); |
| 52 | 72 | const main_tokens = tree.nodes.items(.main_token); |
| 53 | 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 | 181 | try renderExpression(gpa, ais, tree, datas[decl].rhs, space); |
| 162 | 182 | }, |
| 163 | 183 | |
| 164 | .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space), | |
| 165 | .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space), | |
| 166 | .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space), | |
| 184 | .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), is_tuple, space), | |
| 185 | .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), is_tuple, space), | |
| 186 | .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), is_tuple, space), | |
| 167 | 187 | .@"comptime" => return renderExpression(gpa, ais, tree, decl, space), |
| 168 | 188 | |
| 169 | 189 | .root => unreachable, |
| ... | ... | @@ -1158,18 +1178,34 @@ fn renderContainerField( |
| 1158 | 1178 | gpa: Allocator, |
| 1159 | 1179 | ais: *Ais, |
| 1160 | 1180 | tree: Ast, |
| 1161 | field: Ast.full.ContainerField, | |
| 1181 | field_param: Ast.full.ContainerField, | |
| 1182 | is_tuple: bool, | |
| 1162 | 1183 | space: Space, |
| 1163 | 1184 | ) Error!void { |
| 1185 | var field = field_param; | |
| 1186 | if (!is_tuple) field.convertToNonTupleLike(tree.nodes); | |
| 1187 | ||
| 1164 | 1188 | if (field.comptime_token) |t| { |
| 1165 | 1189 | try renderToken(ais, tree, t, .space); // comptime |
| 1166 | 1190 | } |
| 1167 | 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); // name | |
| 1192 | if (field.ast.align_expr != 0) { | |
| 1193 | try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name | |
| 1194 | const lparen_token = tree.firstToken(field.ast.align_expr) - 1; | |
| 1195 | const align_kw = lparen_token - 1; | |
| 1196 | const rparen_token = tree.lastToken(field.ast.align_expr) + 1; | |
| 1197 | try renderToken(ais, tree, align_kw, .none); // align | |
| 1198 | try renderToken(ais, tree, lparen_token, .none); // ( | |
| 1199 | try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment | |
| 1200 | return renderToken(ais, tree, rparen_token, .space); // ) | |
| 1201 | } | |
| 1202 | return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name | |
| 1169 | 1203 | } |
| 1170 | 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); // name | |
| 1172 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : | |
| 1205 | if (!field.ast.tuple_like) { | |
| 1206 | try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name | |
| 1207 | try renderToken(ais, tree, field.ast.main_token + 1, .space); // : | |
| 1208 | } | |
| 1173 | 1209 | |
| 1174 | 1210 | if (field.ast.align_expr != 0) { |
| 1175 | 1211 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type |
| ... | ... | @@ -1184,13 +1220,23 @@ fn renderContainerField( |
| 1184 | 1220 | } |
| 1185 | 1221 | } |
| 1186 | 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); // name | |
| 1188 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // = | |
| 1223 | try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name | |
| 1224 | if (field.ast.align_expr != 0) { | |
| 1225 | const lparen_token = tree.firstToken(field.ast.align_expr) - 1; | |
| 1226 | const align_kw = lparen_token - 1; | |
| 1227 | const rparen_token = tree.lastToken(field.ast.align_expr) + 1; | |
| 1228 | try renderToken(ais, tree, align_kw, .none); // align | |
| 1229 | try renderToken(ais, tree, lparen_token, .none); // ( | |
| 1230 | try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment | |
| 1231 | try renderToken(ais, tree, rparen_token, .space); // ) | |
| 1232 | } | |
| 1233 | try renderToken(ais, tree, field.ast.main_token + 1, .space); // = | |
| 1189 | 1234 | return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value |
| 1190 | 1235 | } |
| 1191 | ||
| 1192 | try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name | |
| 1193 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : | |
| 1236 | if (!field.ast.tuple_like) { | |
| 1237 | try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name | |
| 1238 | try renderToken(ais, tree, field.ast.main_token + 1, .space); // : | |
| 1239 | } | |
| 1194 | 1240 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type |
| 1195 | 1241 | |
| 1196 | 1242 | if (field.ast.align_expr != 0) { |
| ... | ... | @@ -1901,6 +1947,20 @@ fn renderContainerDecl( |
| 1901 | 1947 | try renderToken(ais, tree, layout_token, .space); |
| 1902 | 1948 | } |
| 1903 | 1949 | |
| 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 | 1964 | var lbrace: Ast.TokenIndex = undefined; |
| 1905 | 1965 | if (container_decl.ast.enum_token) |enum_token| { |
| 1906 | 1966 | try renderToken(ais, tree, container_decl.ast.main_token, .none); // union |
| ... | ... | @@ -1967,7 +2027,7 @@ fn renderContainerDecl( |
| 1967 | 2027 | // Print all the declarations on the same line. |
| 1968 | 2028 | try renderToken(ais, tree, lbrace, .space); // lbrace |
| 1969 | 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 | 2032 | return renderToken(ais, tree, rbrace, space); // rbrace |
| 1973 | 2033 | } |
| ... | ... | @@ -1985,9 +2045,9 @@ fn renderContainerDecl( |
| 1985 | 2045 | .container_field_init, |
| 1986 | 2046 | .container_field_align, |
| 1987 | 2047 | .container_field, |
| 1988 | => try renderMember(gpa, ais, tree, member, .comma), | |
| 2048 | => try renderMember(gpa, ais, tree, member, is_tuple, .comma), | |
| 1989 | 2049 | |
| 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 | 2053 | ais.popIndent(); |
src/AstGen.zig+57-12| ... | ... | @@ -4386,6 +4386,7 @@ fn structDeclInner( |
| 4386 | 4386 | .backing_int_body_len = 0, |
| 4387 | 4387 | .known_non_opv = false, |
| 4388 | 4388 | .known_comptime_only = false, |
| 4389 | .is_tuple = false, | |
| 4389 | 4390 | }); |
| 4390 | 4391 | return indexToRef(decl_inst); |
| 4391 | 4392 | } |
| ... | ... | @@ -4467,22 +4468,53 @@ fn structDeclInner( |
| 4467 | 4468 | // No defer needed here because it is handled by `wip_members.deinit()` above. |
| 4468 | 4469 | const bodies_start = astgen.scratch.items.len; |
| 4469 | 4470 | |
| 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 | 4495 | var known_non_opv = false; |
| 4471 | 4496 | var known_comptime_only = false; |
| 4472 | 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 | 4499 | .decl => continue, |
| 4475 | 4500 | .field => |field| field, |
| 4476 | 4501 | }; |
| 4477 | 4502 | |
| 4478 | const field_name = try astgen.identAsString(member.ast.name_token); | |
| 4479 | wip_members.appendToField(field_name); | |
| 4503 | if (!is_tuple) { | |
| 4504 | member.convertToNonTupleLike(astgen.tree.nodes); | |
| 4505 | assert(!member.ast.tuple_like); | |
| 4506 | ||
| 4507 | const field_name = try astgen.identAsString(member.ast.main_token); | |
| 4508 | wip_members.appendToField(field_name); | |
| 4509 | } else if (!member.ast.tuple_like) { | |
| 4510 | return astgen.failTok(member.ast.main_token, "tuple field has a name", .{}); | |
| 4511 | } | |
| 4480 | 4512 | |
| 4481 | 4513 | const doc_comment_index = try astgen.docCommentAsString(member.firstToken()); |
| 4482 | 4514 | wip_members.appendToField(doc_comment_index); |
| 4483 | 4515 | |
| 4484 | 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 | } |
| 4487 | 4519 | |
| 4488 | 4520 | const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr); |
| ... | ... | @@ -4562,6 +4594,7 @@ fn structDeclInner( |
| 4562 | 4594 | .backing_int_body_len = @intCast(u32, backing_int_body_len), |
| 4563 | 4595 | .known_non_opv = known_non_opv, |
| 4564 | 4596 | .known_comptime_only = known_comptime_only, |
| 4597 | .is_tuple = is_tuple, | |
| 4565 | 4598 | }); |
| 4566 | 4599 | |
| 4567 | 4600 | wip_members.finishBits(bits_per_field); |
| ... | ... | @@ -4640,15 +4673,19 @@ fn unionDeclInner( |
| 4640 | 4673 | defer wip_members.deinit(); |
| 4641 | 4674 | |
| 4642 | 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 | 4677 | .decl => continue, |
| 4645 | 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 | 4684 | if (member.comptime_token) |comptime_token| { |
| 4648 | 4685 | return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{}); |
| 4649 | 4686 | } |
| 4650 | 4687 | |
| 4651 | const field_name = try astgen.identAsString(member.ast.name_token); | |
| 4688 | const field_name = try astgen.identAsString(member.ast.main_token); | |
| 4652 | 4689 | wip_members.appendToField(field_name); |
| 4653 | 4690 | |
| 4654 | 4691 | const doc_comment_index = try astgen.docCommentAsString(member.firstToken()); |
| ... | ... | @@ -4787,7 +4824,7 @@ fn containerDecl( |
| 4787 | 4824 | var nonexhaustive_node: Ast.Node.Index = 0; |
| 4788 | 4825 | var nonfinal_nonexhaustive = false; |
| 4789 | 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 | 4828 | .container_field_init => tree.containerFieldInit(member_node), |
| 4792 | 4829 | .container_field_align => tree.containerFieldAlign(member_node), |
| 4793 | 4830 | .container_field => tree.containerField(member_node), |
| ... | ... | @@ -4796,6 +4833,10 @@ fn containerDecl( |
| 4796 | 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 | 4840 | if (member.comptime_token) |comptime_token| { |
| 4800 | 4841 | return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{}); |
| 4801 | 4842 | } |
| ... | ... | @@ -4813,10 +4854,11 @@ fn containerDecl( |
| 4813 | 4854 | }, |
| 4814 | 4855 | ); |
| 4815 | 4856 | } |
| 4816 | // Alignment expressions in enums are caught by the parser. | |
| 4817 | assert(member.ast.align_expr == 0); | |
| 4857 | if (member.ast.align_expr != 0) { | |
| 4858 | return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{}); | |
| 4859 | } | |
| 4818 | 4860 | |
| 4819 | const name_token = member.ast.name_token; | |
| 4861 | const name_token = member.ast.main_token; | |
| 4820 | 4862 | if (mem.eql(u8, tree.tokenSlice(name_token), "_")) { |
| 4821 | 4863 | if (nonexhaustive_node != 0) { |
| 4822 | 4864 | return astgen.failNodeNotes( |
| ... | ... | @@ -4915,15 +4957,16 @@ fn containerDecl( |
| 4915 | 4957 | for (container_decl.ast.members) |member_node| { |
| 4916 | 4958 | if (member_node == counts.nonexhaustive_node) |
| 4917 | 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 | 4961 | .decl => continue, |
| 4920 | 4962 | .field => |field| field, |
| 4921 | 4963 | }; |
| 4964 | member.convertToNonTupleLike(astgen.tree.nodes); | |
| 4922 | 4965 | assert(member.comptime_token == null); |
| 4923 | 4966 | assert(member.ast.type_expr == 0); |
| 4924 | 4967 | assert(member.ast.align_expr == 0); |
| 4925 | 4968 | |
| 4926 | const field_name = try astgen.identAsString(member.ast.name_token); | |
| 4969 | const field_name = try astgen.identAsString(member.ast.main_token); | |
| 4927 | 4970 | wip_members.appendToField(field_name); |
| 4928 | 4971 | |
| 4929 | 4972 | const doc_comment_index = try astgen.docCommentAsString(member.firstToken()); |
| ... | ... | @@ -11786,6 +11829,7 @@ const GenZir = struct { |
| 11786 | 11829 | layout: std.builtin.Type.ContainerLayout, |
| 11787 | 11830 | known_non_opv: bool, |
| 11788 | 11831 | known_comptime_only: bool, |
| 11832 | is_tuple: bool, | |
| 11789 | 11833 | }) !void { |
| 11790 | 11834 | const astgen = gz.astgen; |
| 11791 | 11835 | const gpa = astgen.gpa; |
| ... | ... | @@ -11820,6 +11864,7 @@ const GenZir = struct { |
| 11820 | 11864 | .has_backing_int = args.backing_int_ref != .none, |
| 11821 | 11865 | .known_non_opv = args.known_non_opv, |
| 11822 | 11866 | .known_comptime_only = args.known_comptime_only, |
| 11867 | .is_tuple = args.is_tuple, | |
| 11823 | 11868 | .name_strategy = gz.anon_name_strategy, |
| 11824 | 11869 | .layout = args.layout, |
| 11825 | 11870 | }), |
src/Module.zig+6-1| ... | ... | @@ -938,6 +938,7 @@ pub const Struct = struct { |
| 938 | 938 | known_non_opv: bool, |
| 939 | 939 | requires_comptime: PropertyBoolean = .unknown, |
| 940 | 940 | have_field_inits: bool = false, |
| 941 | is_tuple: bool, | |
| 941 | 942 | |
| 942 | 943 | pub const Fields = std.StringArrayHashMapUnmanaged(Field); |
| 943 | 944 | |
| ... | ... | @@ -4458,6 +4459,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { |
| 4458 | 4459 | .layout = .Auto, |
| 4459 | 4460 | .status = .none, |
| 4460 | 4461 | .known_non_opv = undefined, |
| 4462 | .is_tuple = undefined, // set below | |
| 4461 | 4463 | .namespace = .{ |
| 4462 | 4464 | .parent = null, |
| 4463 | 4465 | .ty = struct_ty, |
| ... | ... | @@ -4489,6 +4491,9 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { |
| 4489 | 4491 | assert(file.zir_loaded); |
| 4490 | 4492 | const main_struct_inst = Zir.main_struct_inst; |
| 4491 | 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; | |
| 4492 | 4497 | |
| 4493 | 4498 | var sema_arena = std.heap.ArenaAllocator.init(gpa); |
| 4494 | 4499 | defer sema_arena.deinit(); |
| ... | ... | @@ -6138,7 +6143,7 @@ fn queryFieldSrc( |
| 6138 | 6143 | .name => .{ |
| 6139 | 6144 | .file_scope = file_scope, |
| 6140 | 6145 | .parent_decl_node = 0, |
| 6141 | .lazy = .{ .token_abs = field.ast.name_token }, | |
| 6146 | .lazy = .{ .token_abs = field.ast.main_token }, | |
| 6142 | 6147 | }, |
| 6143 | 6148 | .type => .{ |
| 6144 | 6149 | .file_scope = file_scope, |
src/Sema.zig+146-172| ... | ... | @@ -2519,6 +2519,7 @@ fn zirStructDecl( |
| 2519 | 2519 | .layout = small.layout, |
| 2520 | 2520 | .status = .none, |
| 2521 | 2521 | .known_non_opv = undefined, |
| 2522 | .is_tuple = small.is_tuple, | |
| 2522 | 2523 | .namespace = .{ |
| 2523 | 2524 | .parent = block.namespace, |
| 2524 | 2525 | .ty = struct_ty, |
| ... | ... | @@ -4291,13 +4292,12 @@ fn zirValidateArrayInit( |
| 4291 | 4292 | |
| 4292 | 4293 | if (instrs.len != array_len) switch (array_ty.zigTypeTag()) { |
| 4293 | 4294 | .Struct => { |
| 4294 | const struct_obj = array_ty.castTag(.tuple).?.data; | |
| 4295 | 4295 | var root_msg: ?*Module.ErrorMsg = null; |
| 4296 | 4296 | errdefer if (root_msg) |msg| msg.destroy(sema.gpa); |
| 4297 | 4297 | |
| 4298 | for (struct_obj.values) |default_val, i| { | |
| 4299 | if (i < instrs.len) continue; | |
| 4300 | ||
| 4298 | var i = instrs.len; | |
| 4299 | while (i < array_len) : (i += 1) { | |
| 4300 | const default_val = array_ty.structFieldDefaultValue(i); | |
| 4301 | 4301 | if (default_val.tag() == .unreachable_value) { |
| 4302 | 4302 | const template = "missing tuple field with index {d}"; |
| 4303 | 4303 | if (root_msg) |msg| { |
| ... | ... | @@ -7230,7 +7230,7 @@ fn instantiateGenericCall( |
| 7230 | 7230 | } |
| 7231 | 7231 | |
| 7232 | 7232 | fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { |
| 7233 | if (!ty.isTuple()) return; | |
| 7233 | if (!ty.isSimpleTuple()) return; | |
| 7234 | 7234 | const tuple = ty.tupleFields(); |
| 7235 | 7235 | for (tuple.values) |field_val, i| { |
| 7236 | 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 | 7295 | const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs); |
| 7296 | 7296 | assert(indexable_ty.isIndexable()); // validated by a previous instruction |
| 7297 | 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 | 7299 | return sema.addType(elem_type); |
| 7300 | 7300 | } else { |
| 7301 | 7301 | const elem_type = indexable_ty.elemType2(); |
| ... | ... | @@ -11827,13 +11827,19 @@ fn analyzeTupleCat( |
| 11827 | 11827 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; |
| 11828 | 11828 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; |
| 11829 | 11829 | |
| 11830 | const lhs_tuple = lhs_ty.tupleFields(); | |
| 11831 | const rhs_tuple = rhs_ty.tupleFields(); | |
| 11832 | const dest_fields = lhs_tuple.types.len + rhs_tuple.types.len; | |
| 11830 | const lhs_len = lhs_ty.structFieldCount(); | |
| 11831 | const rhs_len = rhs_ty.structFieldCount(); | |
| 11832 | const dest_fields = lhs_len + rhs_len; | |
| 11833 | 11833 | |
| 11834 | 11834 | if (dest_fields == 0) { |
| 11835 | 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 | 11843 | const final_len = try sema.usizeCast(block, rhs_src, dest_fields); |
| 11838 | 11844 | |
| 11839 | 11845 | const types = try sema.arena.alloc(Type, final_len); |
| ... | ... | @@ -11841,20 +11847,23 @@ fn analyzeTupleCat( |
| 11841 | 11847 | |
| 11842 | 11848 | const opt_runtime_src = rs: { |
| 11843 | 11849 | var runtime_src: ?LazySrcLoc = null; |
| 11844 | for (lhs_tuple.types) |ty, i| { | |
| 11845 | types[i] = ty; | |
| 11846 | values[i] = lhs_tuple.values[i]; | |
| 11850 | var i: u32 = 0; | |
| 11851 | while (i < lhs_len) : (i += 1) { | |
| 11852 | types[i] = lhs_ty.structFieldType(i); | |
| 11853 | const default_val = lhs_ty.structFieldDefaultValue(i); | |
| 11854 | values[i] = default_val; | |
| 11847 | 11855 | const operand_src = lhs_src; // TODO better source location |
| 11848 | if (values[i].tag() == .unreachable_value) { | |
| 11856 | if (default_val.tag() == .unreachable_value) { | |
| 11849 | 11857 | runtime_src = operand_src; |
| 11850 | 11858 | } |
| 11851 | 11859 | } |
| 11852 | const offset = lhs_tuple.types.len; | |
| 11853 | for (rhs_tuple.types) |ty, i| { | |
| 11854 | types[i + offset] = ty; | |
| 11855 | values[i + offset] = rhs_tuple.values[i]; | |
| 11860 | i = 0; | |
| 11861 | while (i < rhs_len) : (i += 1) { | |
| 11862 | types[i + lhs_len] = rhs_ty.structFieldType(i); | |
| 11863 | const default_val = rhs_ty.structFieldDefaultValue(i); | |
| 11864 | values[i + lhs_len] = default_val; | |
| 11856 | 11865 | 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 | 11867 | runtime_src = operand_src; |
| 11859 | 11868 | } |
| 11860 | 11869 | } |
| ... | ... | @@ -11874,15 +11883,16 @@ fn analyzeTupleCat( |
| 11874 | 11883 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 11875 | 11884 | |
| 11876 | 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 | 11888 | 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; | |
| 11882 | for (rhs_tuple.types) |_, i| { | |
| 11891 | i = 0; | |
| 11892 | while (i < rhs_len) : (i += 1) { | |
| 11883 | 11893 | const operand_src = rhs_src; // TODO better source location |
| 11884 | element_refs[i + offset] = | |
| 11885 | try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty); | |
| 11894 | element_refs[i + lhs_len] = | |
| 11895 | try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty); | |
| 11886 | 11896 | } |
| 11887 | 11897 | |
| 11888 | 11898 | return block.addAggregateInit(tuple_ty, element_refs); |
| ... | ... | @@ -12107,12 +12117,11 @@ fn analyzeTupleMul( |
| 12107 | 12117 | factor: u64, |
| 12108 | 12118 | ) CompileError!Air.Inst.Ref { |
| 12109 | 12119 | const operand_ty = sema.typeOf(operand); |
| 12110 | const operand_tuple = operand_ty.tupleFields(); | |
| 12111 | 12120 | const src = LazySrcLoc.nodeOffset(src_node); |
| 12112 | 12121 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; |
| 12113 | 12122 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; |
| 12114 | 12123 | |
| 12115 | const tuple_len = operand_tuple.types.len; | |
| 12124 | const tuple_len = operand_ty.structFieldCount(); | |
| 12116 | 12125 | const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch |
| 12117 | 12126 | return sema.fail(block, rhs_src, "operation results in overflow", .{}); |
| 12118 | 12127 | |
| ... | ... | @@ -12126,18 +12135,19 @@ fn analyzeTupleMul( |
| 12126 | 12135 | |
| 12127 | 12136 | const opt_runtime_src = rs: { |
| 12128 | 12137 | var runtime_src: ?LazySrcLoc = null; |
| 12129 | for (operand_tuple.types) |ty, i| { | |
| 12130 | types[i] = ty; | |
| 12131 | values[i] = operand_tuple.values[i]; | |
| 12138 | var i: u32 = 0; | |
| 12139 | while (i < tuple_len) : (i += 1) { | |
| 12140 | types[i] = operand_ty.structFieldType(i); | |
| 12141 | values[i] = operand_ty.structFieldDefaultValue(i); | |
| 12132 | 12142 | const operand_src = lhs_src; // TODO better source location |
| 12133 | 12143 | if (values[i].tag() == .unreachable_value) { |
| 12134 | 12144 | runtime_src = operand_src; |
| 12135 | 12145 | } |
| 12136 | 12146 | } |
| 12137 | var i: usize = 1; | |
| 12147 | i = 0; | |
| 12138 | 12148 | while (i < factor) : (i += 1) { |
| 12139 | mem.copy(Type, types[tuple_len * i ..], operand_tuple.types); | |
| 12140 | mem.copy(Value, values[tuple_len * i ..], operand_tuple.values); | |
| 12149 | mem.copy(Type, types[tuple_len * i ..], types[0..tuple_len]); | |
| 12150 | mem.copy(Value, values[tuple_len * i ..], values[0..tuple_len]); | |
| 12141 | 12151 | } |
| 12142 | 12152 | break :rs runtime_src; |
| 12143 | 12153 | }; |
| ... | ... | @@ -12155,11 +12165,12 @@ fn analyzeTupleMul( |
| 12155 | 12165 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 12156 | 12166 | |
| 12157 | 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 | 12170 | const operand_src = lhs_src; // TODO better source location |
| 12160 | 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 | 12174 | while (i < factor) : (i += 1) { |
| 12164 | 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 | 15604 | const layout = struct_ty.containerLayout(); |
| 15594 | 15605 | |
| 15595 | 15606 | const struct_field_vals = fv: { |
| 15596 | if (struct_ty.isTupleOrAnonStruct()) { | |
| 15607 | if (struct_ty.isSimpleTupleOrAnonStruct()) { | |
| 15597 | 15608 | const tuple = struct_ty.tupleFields(); |
| 15598 | 15609 | const field_types = tuple.types; |
| 15599 | 15610 | const struct_field_vals = try fields_anon_decl.arena().alloc(Value, field_types.len); |
| ... | ... | @@ -17063,10 +17074,12 @@ fn finishStructInit( |
| 17063 | 17074 | } |
| 17064 | 17075 | } |
| 17065 | 17076 | } else if (struct_ty.isTuple()) { |
| 17066 | const struct_obj = struct_ty.castTag(.tuple).?.data; | |
| 17067 | for (struct_obj.values) |default_val, i| { | |
| 17077 | var i: u32 = 0; | |
| 17078 | const len = struct_ty.structFieldCount(); | |
| 17079 | while (i < len) : (i += 1) { | |
| 17068 | 17080 | if (field_inits[i] != .none) continue; |
| 17069 | 17081 | |
| 17082 | const default_val = struct_ty.structFieldDefaultValue(i); | |
| 17070 | 17083 | if (default_val.tag() == .unreachable_value) { |
| 17071 | 17084 | const template = "missing tuple field with index {d}"; |
| 17072 | 17085 | if (root_msg) |msg| { |
| ... | ... | @@ -17075,7 +17088,7 @@ fn finishStructInit( |
| 17075 | 17088 | root_msg = try sema.errMsg(block, init_src, template, .{i}); |
| 17076 | 17089 | } |
| 17077 | 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 | 17094 | } else { |
| ... | ... | @@ -17297,7 +17310,7 @@ fn zirArrayInit( |
| 17297 | 17310 | const resolved_arg = try sema.resolveInst(arg); |
| 17298 | 17311 | const arg_src = src; // TODO better source location |
| 17299 | 17312 | const elem_ty = if (array_ty.zigTypeTag() == .Struct) |
| 17300 | array_ty.tupleFields().types[i] | |
| 17313 | array_ty.structFieldType(i) | |
| 17301 | 17314 | else |
| 17302 | 17315 | array_ty.elemType2(); |
| 17303 | 17316 | resolved_args[i] = try sema.coerce(block, elem_ty, resolved_arg, arg_src); |
| ... | ... | @@ -17337,12 +17350,11 @@ fn zirArrayInit( |
| 17337 | 17350 | const alloc = try block.addTy(.alloc, alloc_ty); |
| 17338 | 17351 | |
| 17339 | 17352 | if (array_ty.isTuple()) { |
| 17340 | const types = array_ty.tupleFields().types; | |
| 17341 | 17353 | for (resolved_args) |arg, i| { |
| 17342 | 17354 | const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ |
| 17343 | 17355 | .mutable = true, |
| 17344 | 17356 | .@"addrspace" = target_util.defaultAddressSpace(target, .local), |
| 17345 | .pointee_type = types[i], | |
| 17357 | .pointee_type = array_ty.structFieldType(i), | |
| 17346 | 17358 | }); |
| 17347 | 17359 | const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); |
| 17348 | 17360 | |
| ... | ... | @@ -18015,10 +18027,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in |
| 18015 | 18027 | return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); |
| 18016 | 18028 | } |
| 18017 | 18029 | |
| 18018 | return if (is_tuple_val.toBool()) | |
| 18019 | try sema.reifyTuple(block, src, fields_val) | |
| 18020 | else | |
| 18021 | try sema.reifyStruct(block, inst, src, layout, backing_int_val, fields_val, name_strategy); | |
| 18030 | return try sema.reifyStruct(block, inst, src, layout, backing_int_val, fields_val, name_strategy, is_tuple_val.toBool()); | |
| 18022 | 18031 | }, |
| 18023 | 18032 | .Enum => { |
| 18024 | 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 | 18441 | } |
| 18433 | 18442 | } |
| 18434 | 18443 | |
| 18435 | fn 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 | ||
| 18513 | 18444 | fn reifyStruct( |
| 18514 | 18445 | sema: *Sema, |
| 18515 | 18446 | block: *Block, |
| ... | ... | @@ -18519,6 +18450,7 @@ fn reifyStruct( |
| 18519 | 18450 | backing_int_val: Value, |
| 18520 | 18451 | fields_val: Value, |
| 18521 | 18452 | name_strategy: Zir.Inst.NameStrategy, |
| 18453 | is_tuple: bool, | |
| 18522 | 18454 | ) CompileError!Air.Inst.Ref { |
| 18523 | 18455 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); |
| 18524 | 18456 | errdefer new_decl_arena.deinit(); |
| ... | ... | @@ -18542,6 +18474,7 @@ fn reifyStruct( |
| 18542 | 18474 | .layout = layout, |
| 18543 | 18475 | .status = .have_field_types, |
| 18544 | 18476 | .known_non_opv = false, |
| 18477 | .is_tuple = is_tuple, | |
| 18545 | 18478 | .namespace = .{ |
| 18546 | 18479 | .parent = block.namespace, |
| 18547 | 18480 | .ty = struct_ty, |
| ... | ... | @@ -18575,8 +18508,12 @@ fn reifyStruct( |
| 18575 | 18508 | } |
| 18576 | 18509 | const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?); |
| 18577 | 18510 | |
| 18578 | if (layout == .Packed and abi_align != 0) { | |
| 18579 | return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); | |
| 18511 | if (layout == .Packed) { | |
| 18512 | if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); | |
| 18513 | if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}); | |
| 18514 | } | |
| 18515 | if (layout == .Extern and is_comptime_val.toBool()) { | |
| 18516 | return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}); | |
| 18580 | 18517 | } |
| 18581 | 18518 | |
| 18582 | 18519 | const field_name = try name_val.toAllocatedBytes( |
| ... | ... | @@ -18585,6 +18522,25 @@ fn reifyStruct( |
| 18585 | 18522 | mod, |
| 18586 | 18523 | ); |
| 18587 | 18524 | |
| 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 | 18544 | const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); |
| 18589 | 18545 | if (gop.found_existing) { |
| 18590 | 18546 | // TODO: better source location |
| ... | ... | @@ -18598,6 +18554,9 @@ fn reifyStruct( |
| 18598 | 18554 | opt_val; |
| 18599 | 18555 | break :blk try payload_val.copy(new_decl_arena_allocator); |
| 18600 | 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 | } | |
| 18601 | 18560 | |
| 18602 | 18561 | var buffer: Value.ToTypeBuffer = undefined; |
| 18603 | 18562 | gop.value_ptr.* = .{ |
| ... | ... | @@ -23177,6 +23136,7 @@ fn structFieldVal( |
| 23177 | 23136 | }, |
| 23178 | 23137 | .@"struct" => { |
| 23179 | 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); | |
| 23180 | 23140 | |
| 23181 | 23141 | const field_index_usize = struct_obj.fields.getIndex(field_name) orelse |
| 23182 | 23142 | return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); |
| ... | ... | @@ -23249,11 +23209,10 @@ fn tupleFieldValByIndex( |
| 23249 | 23209 | field_index: u32, |
| 23250 | 23210 | tuple_ty: Type, |
| 23251 | 23211 | ) CompileError!Air.Inst.Ref { |
| 23252 | const tuple = tuple_ty.tupleFields(); | |
| 23253 | const field_ty = tuple.types[field_index]; | |
| 23212 | const field_ty = tuple_ty.structFieldType(field_index); | |
| 23254 | 23213 | |
| 23255 | if (tuple.values[field_index].tag() != .unreachable_value) { | |
| 23256 | return sema.addConstant(field_ty, tuple.values[field_index]); | |
| 23214 | if (tuple_ty.structFieldValueComptime(field_index)) |default_value| { | |
| 23215 | return sema.addConstant(field_ty, default_value); | |
| 23257 | 23216 | } |
| 23258 | 23217 | |
| 23259 | 23218 | if (try sema.resolveMaybeUndefVal(tuple_byval)) |tuple_val| { |
| ... | ... | @@ -23601,19 +23560,20 @@ fn tupleFieldPtr( |
| 23601 | 23560 | ) CompileError!Air.Inst.Ref { |
| 23602 | 23561 | const tuple_ptr_ty = sema.typeOf(tuple_ptr); |
| 23603 | 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(); | |
| 23605 | 23565 | |
| 23606 | if (tuple_fields.types.len == 0) { | |
| 23566 | if (field_count == 0) { | |
| 23607 | 23567 | return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{}); |
| 23608 | 23568 | } |
| 23609 | 23569 | |
| 23610 | if (field_index >= tuple_fields.types.len) { | |
| 23570 | if (field_index >= field_count) { | |
| 23611 | 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 | } |
| 23615 | 23575 | |
| 23616 | const field_ty = tuple_fields.types[field_index]; | |
| 23576 | const field_ty = tuple_ty.structFieldType(field_index); | |
| 23617 | 23577 | const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{ |
| 23618 | 23578 | .pointee_type = field_ty, |
| 23619 | 23579 | .mutable = tuple_ptr_ty.ptrIsMutable(), |
| ... | ... | @@ -23656,24 +23616,23 @@ fn tupleField( |
| 23656 | 23616 | field_index_src: LazySrcLoc, |
| 23657 | 23617 | field_index: u32, |
| 23658 | 23618 | ) CompileError!Air.Inst.Ref { |
| 23659 | const tuple_ty = sema.typeOf(tuple); | |
| 23660 | const tuple_fields = tuple_ty.tupleFields(); | |
| 23619 | const tuple_ty = try sema.resolveTypeFields(sema.typeOf(tuple)); | |
| 23620 | const field_count = tuple_ty.structFieldCount(); | |
| 23661 | 23621 | |
| 23662 | if (tuple_fields.types.len == 0) { | |
| 23622 | if (field_count == 0) { | |
| 23663 | 23623 | return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{}); |
| 23664 | 23624 | } |
| 23665 | 23625 | |
| 23666 | if (field_index >= tuple_fields.types.len) { | |
| 23626 | if (field_index >= field_count) { | |
| 23667 | 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 | } |
| 23671 | 23631 | |
| 23672 | const field_ty = tuple_fields.types[field_index]; | |
| 23673 | const field_val = tuple_fields.values[field_index]; | |
| 23632 | const field_ty = tuple_ty.structFieldType(field_index); | |
| 23674 | 23633 | |
| 23675 | if (field_val.tag() != .unreachable_value) { | |
| 23676 | return sema.addConstant(field_ty, field_val); // comptime field | |
| 23634 | if (tuple_ty.structFieldValueComptime(field_index)) |default_value| { | |
| 23635 | return sema.addConstant(field_ty, default_value); // comptime field | |
| 23677 | 23636 | } |
| 23678 | 23637 | |
| 23679 | 23638 | if (try sema.resolveMaybeUndefVal(tuple)) |tuple_val| { |
| ... | ... | @@ -24223,7 +24182,10 @@ fn coerceExtra( |
| 24223 | 24182 | inst_ty.childType().isAnonStruct() and |
| 24224 | 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 | 24191 | .Array => { |
| ... | ... | @@ -24253,7 +24215,7 @@ fn coerceExtra( |
| 24253 | 24215 | |
| 24254 | 24216 | // empty tuple to zero-length slice |
| 24255 | 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 | 24219 | const slice_val = try Value.Tag.slice.create(sema.arena, .{ |
| 24258 | 24220 | .ptr = Value.undef, |
| 24259 | 24221 | .len = Value.zero, |
| ... | ... | @@ -24536,12 +24498,15 @@ fn coerceExtra( |
| 24536 | 24498 | }, |
| 24537 | 24499 | else => {}, |
| 24538 | 24500 | }, |
| 24539 | .Struct => { | |
| 24501 | .Struct => blk: { | |
| 24540 | 24502 | if (inst == .empty_struct) { |
| 24541 | 24503 | return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src); |
| 24542 | 24504 | } |
| 24543 | 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 | 24512 | else => {}, |
| ... | ... | @@ -25563,9 +25528,9 @@ fn storePtr2( |
| 25563 | 25528 | // fields. |
| 25564 | 25529 | const operand_ty = sema.typeOf(uncasted_operand); |
| 25565 | 25530 | if (operand_ty.isTuple() and elem_ty.zigTypeTag() == .Array) { |
| 25566 | const tuple = operand_ty.tupleFields(); | |
| 25567 | for (tuple.types) |_, i_usize| { | |
| 25568 | const i = @intCast(u32, i_usize); | |
| 25531 | const field_count = operand_ty.structFieldCount(); | |
| 25532 | var i: u32 = 0; | |
| 25533 | while (i < field_count) : (i += 1) { | |
| 25569 | 25534 | const elem_src = operand_src; // TODO better source location |
| 25570 | 25535 | const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i); |
| 25571 | 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 | 26622 | const inst_info = inst_ty.ptrInfo().data; |
| 26658 | 26623 | const len0 = (inst_info.pointee_type.zigTypeTag() == .Array and (inst_info.pointee_type.arrayLenIncludingSentinel() == 0 or |
| 26659 | 26624 | (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); | |
| 26661 | 26626 | |
| 26662 | 26627 | const ok_cv_qualifiers = |
| 26663 | 26628 | ((inst_info.mutable or !dest_info.mutable) or len0) and |
| ... | ... | @@ -27142,18 +27107,18 @@ fn coerceTupleToStruct( |
| 27142 | 27107 | mem.set(Air.Inst.Ref, field_refs, .none); |
| 27143 | 27108 | |
| 27144 | 27109 | const inst_ty = sema.typeOf(inst); |
| 27145 | const tuple = inst_ty.tupleFields(); | |
| 27146 | 27110 | var runtime_src: ?LazySrcLoc = null; |
| 27147 | for (tuple.types) |_, i_usize| { | |
| 27148 | const i = @intCast(u32, i_usize); | |
| 27111 | const field_count = inst_ty.structFieldCount(); | |
| 27112 | var field_i: u32 = 0; | |
| 27113 | while (field_i < field_count) : (field_i += 1) { | |
| 27149 | 27114 | const field_src = inst_src; // TODO better source location |
| 27150 | 27115 | const field_name = if (inst_ty.castTag(.anon_struct)) |payload| |
| 27151 | payload.data.names[i] | |
| 27116 | payload.data.names[field_i] | |
| 27152 | 27117 | else |
| 27153 | try std.fmt.allocPrint(sema.arena, "{d}", .{i}); | |
| 27118 | try std.fmt.allocPrint(sema.arena, "{d}", .{field_i}); | |
| 27154 | 27119 | const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); |
| 27155 | 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 | 27122 | const coerced = try sema.coerce(block, field.ty, elem_ref, field_src); |
| 27158 | 27123 | field_refs[field_index] = coerced; |
| 27159 | 27124 | if (field.is_comptime) { |
| ... | ... | @@ -27162,7 +27127,7 @@ fn coerceTupleToStruct( |
| 27162 | 27127 | }; |
| 27163 | 27128 | |
| 27164 | 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 | 27133 | if (runtime_src == null) { |
| ... | ... | @@ -27225,21 +27190,23 @@ fn coerceTupleToTuple( |
| 27225 | 27190 | inst: Air.Inst.Ref, |
| 27226 | 27191 | inst_src: LazySrcLoc, |
| 27227 | 27192 | ) !Air.Inst.Ref { |
| 27228 | const field_count = tuple_ty.structFieldCount(); | |
| 27229 | const field_vals = try sema.arena.alloc(Value, field_count); | |
| 27193 | const dest_field_count = tuple_ty.structFieldCount(); | |
| 27194 | const field_vals = try sema.arena.alloc(Value, dest_field_count); | |
| 27230 | 27195 | const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); |
| 27231 | 27196 | mem.set(Air.Inst.Ref, field_refs, .none); |
| 27232 | 27197 | |
| 27233 | 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 | 27202 | var runtime_src: ?LazySrcLoc = null; |
| 27236 | for (tuple.types) |_, i_usize| { | |
| 27237 | const i = @intCast(u32, i_usize); | |
| 27203 | var field_i: u32 = 0; | |
| 27204 | while (field_i < inst_field_count) : (field_i += 1) { | |
| 27238 | 27205 | const field_src = inst_src; // TODO better source location |
| 27239 | 27206 | const field_name = if (inst_ty.castTag(.anon_struct)) |payload| |
| 27240 | payload.data.names[i] | |
| 27207 | payload.data.names[field_i] | |
| 27241 | 27208 | else |
| 27242 | try std.fmt.allocPrint(sema.arena, "{d}", .{i}); | |
| 27209 | try std.fmt.allocPrint(sema.arena, "{d}", .{field_i}); | |
| 27243 | 27210 | |
| 27244 | 27211 | if (mem.eql(u8, field_name, "len")) { |
| 27245 | 27212 | return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{}); |
| ... | ... | @@ -27247,9 +27214,9 @@ fn coerceTupleToTuple( |
| 27247 | 27214 | |
| 27248 | 27215 | const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src); |
| 27249 | 27216 | |
| 27250 | const field_ty = tuple_ty.structFieldType(i); | |
| 27251 | const default_val = tuple_ty.structFieldDefaultValue(i); | |
| 27252 | const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, i); | |
| 27217 | const field_ty = tuple_ty.structFieldType(field_i); | |
| 27218 | const default_val = tuple_ty.structFieldDefaultValue(field_i); | |
| 27219 | const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); | |
| 27253 | 27220 | const coerced = try sema.coerce(block, field_ty, elem_ref, field_src); |
| 27254 | 27221 | field_refs[field_index] = coerced; |
| 27255 | 27222 | if (default_val.tag() != .unreachable_value) { |
| ... | ... | @@ -27258,7 +27225,7 @@ fn coerceTupleToTuple( |
| 27258 | 27225 | }; |
| 27259 | 27226 | |
| 27260 | 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 | 27231 | if (runtime_src == null) { |
| ... | ... | @@ -29641,6 +29608,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void |
| 29641 | 29608 | block_scope.params.deinit(gpa); |
| 29642 | 29609 | } |
| 29643 | 29610 | |
| 29611 | struct_obj.fields = .{}; | |
| 29644 | 29612 | try struct_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len); |
| 29645 | 29613 | |
| 29646 | 29614 | const Field = struct { |
| ... | ... | @@ -29675,8 +29643,11 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void |
| 29675 | 29643 | const has_type_body = @truncate(u1, cur_bit_bag) != 0; |
| 29676 | 29644 | cur_bit_bag >>= 1; |
| 29677 | 29645 | |
| 29678 | const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); | |
| 29679 | extra_index += 1; | |
| 29646 | var field_name_zir: ?[:0]const u8 = null; | |
| 29647 | if (!small.is_tuple) { | |
| 29648 | field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); | |
| 29649 | extra_index += 1; | |
| 29650 | } | |
| 29680 | 29651 | extra_index += 1; // doc_comment |
| 29681 | 29652 | |
| 29682 | 29653 | fields[field_i] = .{}; |
| ... | ... | @@ -29689,7 +29660,10 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void |
| 29689 | 29660 | extra_index += 1; |
| 29690 | 29661 | |
| 29691 | 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}); | |
| 29693 | 29667 | |
| 29694 | 29668 | const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); |
| 29695 | 29669 | if (gop.found_existing) { |
src/Zir.zig+3-2| ... | ... | @@ -3166,7 +3166,7 @@ pub const Inst = struct { |
| 3166 | 3166 | /// 0b0X00: whether corresponding field is comptime |
| 3167 | 3167 | /// 0bX000: whether corresponding field has a type expression |
| 3168 | 3168 | /// 9. fields: { // for every fields_len |
| 3169 | /// field_name: u32, | |
| 3169 | /// field_name: u32, // if !is_tuple | |
| 3170 | 3170 | /// doc_comment: u32, // 0 if no doc comment |
| 3171 | 3171 | /// field_type: Ref, // if corresponding bit is not set. none means anytype. |
| 3172 | 3172 | /// field_type_body_len: u32, // if corresponding bit is set |
| ... | ... | @@ -3186,9 +3186,10 @@ pub const Inst = struct { |
| 3186 | 3186 | has_backing_int: bool, |
| 3187 | 3187 | known_non_opv: bool, |
| 3188 | 3188 | known_comptime_only: bool, |
| 3189 | is_tuple: bool, | |
| 3189 | 3190 | name_strategy: NameStrategy, |
| 3190 | 3191 | layout: std.builtin.Type.ContainerLayout, |
| 3191 | _: u6 = undefined, | |
| 3192 | _: u5 = undefined, | |
| 3192 | 3193 | }; |
| 3193 | 3194 | }; |
| 3194 | 3195 |
src/arch/x86_64/abi.zig+1| ... | ... | @@ -552,6 +552,7 @@ test "C_C_D" { |
| 552 | 552 | .layout = .Extern, |
| 553 | 553 | .status = .fully_resolved, |
| 554 | 554 | .known_non_opv = true, |
| 555 | .is_tuple = false, | |
| 555 | 556 | }; |
| 556 | 557 | var C_C_D = Type.Payload.Struct{ .data = &C_C_D_struct }; |
| 557 | 558 |
src/codegen/c.zig+1-1| ... | ... | @@ -1796,7 +1796,7 @@ pub const DeclGen = struct { |
| 1796 | 1796 | }, |
| 1797 | 1797 | .Struct, .Union => |tag| if (tag == .Struct and t.containerLayout() == .Packed) |
| 1798 | 1798 | try dg.renderType(w, t.castTag(.@"struct").?.data.backing_int_ty, kind) |
| 1799 | else if (t.isTupleOrAnonStruct()) { | |
| 1799 | else if (t.isSimpleTupleOrAnonStruct()) { | |
| 1800 | 1800 | const ExpectedContents = struct { types: [8]Type, values: [8]Value }; |
| 1801 | 1801 | var stack align(@alignOf(ExpectedContents)) = |
| 1802 | 1802 | std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa); |
src/codegen/llvm.zig+5-5| ... | ... | @@ -1956,7 +1956,7 @@ pub const Object = struct { |
| 1956 | 1956 | break :blk fwd_decl; |
| 1957 | 1957 | }; |
| 1958 | 1958 | |
| 1959 | if (ty.isTupleOrAnonStruct()) { | |
| 1959 | if (ty.isSimpleTupleOrAnonStruct()) { | |
| 1960 | 1960 | const tuple = ty.tupleFields(); |
| 1961 | 1961 | |
| 1962 | 1962 | var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; |
| ... | ... | @@ -2890,7 +2890,7 @@ pub const DeclGen = struct { |
| 2890 | 2890 | // reference, we need to copy it here. |
| 2891 | 2891 | gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); |
| 2892 | 2892 | |
| 2893 | if (t.isTupleOrAnonStruct()) { | |
| 2893 | if (t.isSimpleTupleOrAnonStruct()) { | |
| 2894 | 2894 | const tuple = t.tupleFields(); |
| 2895 | 2895 | const llvm_struct_ty = dg.context.structCreateNamed(""); |
| 2896 | 2896 | gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls |
| ... | ... | @@ -3584,7 +3584,7 @@ pub const DeclGen = struct { |
| 3584 | 3584 | const field_vals = tv.val.castTag(.aggregate).?.data; |
| 3585 | 3585 | const gpa = dg.gpa; |
| 3586 | 3586 | |
| 3587 | if (tv.ty.isTupleOrAnonStruct()) { | |
| 3587 | if (tv.ty.isSimpleTupleOrAnonStruct()) { | |
| 3588 | 3588 | const tuple = tv.ty.tupleFields(); |
| 3589 | 3589 | var llvm_fields: std.ArrayListUnmanaged(*llvm.Value) = .{}; |
| 3590 | 3590 | defer llvm_fields.deinit(gpa); |
| ... | ... | @@ -10210,7 +10210,7 @@ fn llvmFieldIndex( |
| 10210 | 10210 | var offset: u64 = 0; |
| 10211 | 10211 | var big_align: u32 = 0; |
| 10212 | 10212 | |
| 10213 | if (ty.isTupleOrAnonStruct()) { | |
| 10213 | if (ty.isSimpleTupleOrAnonStruct()) { | |
| 10214 | 10214 | const tuple = ty.tupleFields(); |
| 10215 | 10215 | var llvm_field_index: c_uint = 0; |
| 10216 | 10216 | for (tuple.types) |field_ty, i| { |
| ... | ... | @@ -10773,7 +10773,7 @@ fn isByRef(ty: Type) bool { |
| 10773 | 10773 | .Struct => { |
| 10774 | 10774 | // Packed structs are represented to LLVM as integers. |
| 10775 | 10775 | if (ty.containerLayout() == .Packed) return false; |
| 10776 | if (ty.isTupleOrAnonStruct()) { | |
| 10776 | if (ty.isSimpleTupleOrAnonStruct()) { | |
| 10777 | 10777 | const tuple = ty.tupleFields(); |
| 10778 | 10778 | var count: usize = 0; |
| 10779 | 10779 | for (tuple.values) |field_val, i| { |
src/print_zir.zig+13-6| ... | ... | @@ -1262,6 +1262,7 @@ const Writer = struct { |
| 1262 | 1262 | |
| 1263 | 1263 | try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv); |
| 1264 | 1264 | try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only); |
| 1265 | try self.writeFlag(stream, "tuple, ", small.is_tuple); | |
| 1265 | 1266 | |
| 1266 | 1267 | try stream.print("{s}, ", .{@tagName(small.name_strategy)}); |
| 1267 | 1268 | |
| ... | ... | @@ -1335,8 +1336,11 @@ const Writer = struct { |
| 1335 | 1336 | const has_type_body = @truncate(u1, cur_bit_bag) != 0; |
| 1336 | 1337 | cur_bit_bag >>= 1; |
| 1337 | 1338 | |
| 1338 | const field_name = self.code.extra[extra_index]; | |
| 1339 | extra_index += 1; | |
| 1339 | var field_name: u32 = 0; | |
| 1340 | if (!small.is_tuple) { | |
| 1341 | field_name = self.code.extra[extra_index]; | |
| 1342 | extra_index += 1; | |
| 1343 | } | |
| 1340 | 1344 | const doc_comment_index = self.code.extra[extra_index]; |
| 1341 | 1345 | extra_index += 1; |
| 1342 | 1346 | |
| ... | ... | @@ -1370,13 +1374,16 @@ const Writer = struct { |
| 1370 | 1374 | try stream.writeAll("{\n"); |
| 1371 | 1375 | self.indent += 2; |
| 1372 | 1376 | |
| 1373 | for (fields) |field| { | |
| 1374 | const field_name = self.code.nullTerminatedString(field.name); | |
| 1375 | ||
| 1377 | for (fields) |field, i| { | |
| 1376 | 1378 | try self.writeDocComment(stream, field.doc_comment_index); |
| 1377 | 1379 | try stream.writeByteNTimes(' ', self.indent); |
| 1378 | 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 | 1387 | if (field.field_type != .none) { |
| 1381 | 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 | 804 | return a_struct_obj == b_struct_obj; |
| 805 | 805 | }, |
| 806 | 806 | .tuple, .empty_struct_literal => { |
| 807 | if (!b.isTuple()) return false; | |
| 807 | if (!b.isSimpleTuple()) return false; | |
| 808 | 808 | |
| 809 | 809 | const a_tuple = a.tupleFields(); |
| 810 | 810 | const b_tuple = b.tupleFields(); |
| ... | ... | @@ -4494,6 +4494,7 @@ pub const Type = extern union { |
| 4494 | 4494 | .mut_slice, |
| 4495 | 4495 | .tuple, |
| 4496 | 4496 | .empty_struct_literal, |
| 4497 | .@"struct", | |
| 4497 | 4498 | => return null, |
| 4498 | 4499 | |
| 4499 | 4500 | .pointer => return self.castTag(.pointer).?.data.sentinel, |
| ... | ... | @@ -6178,6 +6179,7 @@ pub const Type = extern union { |
| 6178 | 6179 | pub fn isTuple(ty: Type) bool { |
| 6179 | 6180 | return switch (ty.tag()) { |
| 6180 | 6181 | .tuple, .empty_struct_literal => true, |
| 6182 | .@"struct" => ty.castTag(.@"struct").?.data.is_tuple, | |
| 6181 | 6183 | else => false, |
| 6182 | 6184 | }; |
| 6183 | 6185 | } |
| ... | ... | @@ -6190,12 +6192,28 @@ pub const Type = extern union { |
| 6190 | 6192 | } |
| 6191 | 6193 | |
| 6192 | 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 | 6210 | return switch (ty.tag()) { |
| 6194 | 6211 | .tuple, .empty_struct_literal, .anon_struct => true, |
| 6195 | 6212 | else => false, |
| 6196 | 6213 | }; |
| 6197 | 6214 | } |
| 6198 | 6215 | |
| 6216 | // Only allowed for simple tuple types | |
| 6199 | 6217 | pub fn tupleFields(ty: Type) Payload.Tuple.Data { |
| 6200 | 6218 | return switch (ty.tag()) { |
| 6201 | 6219 | .tuple => ty.castTag(.tuple).?.data, |
src/value.zig+2-2| ... | ... | @@ -2209,7 +2209,7 @@ pub const Value = extern union { |
| 2209 | 2209 | const b_field_vals = b.castTag(.aggregate).?.data; |
| 2210 | 2210 | assert(a_field_vals.len == b_field_vals.len); |
| 2211 | 2211 | |
| 2212 | if (ty.isTupleOrAnonStruct()) { | |
| 2212 | if (ty.isSimpleTupleOrAnonStruct()) { | |
| 2213 | 2213 | const types = ty.tupleFields().types; |
| 2214 | 2214 | assert(types.len == a_field_vals.len); |
| 2215 | 2215 | for (types) |field_ty, i| { |
| ... | ... | @@ -3004,7 +3004,7 @@ pub const Value = extern union { |
| 3004 | 3004 | .the_only_possible_value => return ty.onePossibleValue().?, |
| 3005 | 3005 | |
| 3006 | 3006 | .empty_struct_value => { |
| 3007 | if (ty.isTupleOrAnonStruct()) { | |
| 3007 | if (ty.isSimpleTupleOrAnonStruct()) { | |
| 3008 | 3008 | const tuple = ty.tupleFields(); |
| 3009 | 3009 | return tuple.values[index]; |
| 3010 | 3010 | } |
test/behavior.zig+1| ... | ... | @@ -200,6 +200,7 @@ test { |
| 200 | 200 | _ = @import("behavior/packed_struct_explicit_backing_int.zig"); |
| 201 | 201 | _ = @import("behavior/empty_union.zig"); |
| 202 | 202 | _ = @import("behavior/inline_switch.zig"); |
| 203 | _ = @import("behavior/tuple_declarations.zig"); | |
| 203 | 204 | _ = @import("behavior/bugs/12723.zig"); |
| 204 | 205 | _ = @import("behavior/bugs/12776.zig"); |
| 205 | 206 | } |
test/behavior/tuple_declarations.zig created+79| ... | ... | @@ -0,0 +1,79 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const testing = std.testing; | |
| 4 | const expect = testing.expect; | |
| 5 | const expectEqualStrings = testing.expectEqualStrings; | |
| 6 | ||
| 7 | test "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 | ||
| 57 | test "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 | 11 | // backend=stage2 |
| 12 | 12 | // target=native |
| 13 | 13 | // |
| 14 | // :3:7: error: expected ',' after field | |
| 14 | // :3:13: error: enum fields cannot be aligned |
test/cases/compile_errors/reify_struct.zig created+80| ... | ... | @@ -0,0 +1,80 @@ |
| 1 | comptime { | |
| 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 | } | |
| 15 | comptime { | |
| 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 | } | |
| 29 | comptime { | |
| 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 | } | |
| 43 | comptime { | |
| 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 | } | |
| 57 | comptime { | |
| 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 @@ |
| 1 | const Letter = struct { | |
| 2 | A, | |
| 3 | }; | |
| 4 | export 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 @@ |
| 1 | const E = enum { | |
| 2 | *u32, | |
| 3 | }; | |
| 4 | const U = union { | |
| 5 | *u32, | |
| 6 | }; | |
| 7 | const S = struct { | |
| 8 | a: u32, | |
| 9 | *u32, | |
| 10 | }; | |
| 11 | const 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 | 7 | // backend=stage2 |
| 8 | 8 | // 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 | 213 | case.backend = .stage2; |
| 214 | 214 | |
| 215 | 215 | case.addSourceFile("b.zig", |
| 216 | \\bad | |
| 216 | \\+ | |
| 217 | 217 | ); |
| 218 | 218 | |
| 219 | 219 | case.addError( |
| ... | ... | @@ -221,7 +221,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 221 | 221 | \\ _ = (@sizeOf(@import("b.zig"))); |
| 222 | 222 | \\} |
| 223 | 223 | , &[_][]const u8{ |
| 224 | ":1:1: error: struct field missing type", | |
| 224 | ":1:1: error: expected type expression, found '+'", | |
| 225 | 225 | }); |
| 226 | 226 | } |
| 227 | 227 |
test/stage2/cbe.zig+1-1| ... | ... | @@ -670,7 +670,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 670 | 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 | }); |
| 675 | 675 | |
| 676 | 676 | // Redundant non-exhaustive enum mark. |