| author | |
| committer | |
| log | 61b70778bdf975957d45432987dde16029aca69a |
| tree | c9cff38d49849c519aa79f8cdcd3a63a02c8c58e |
| parent | 94529ffb621fa633437ac48d8f90003e26e8ce5b |
| parent | f366d9f8793fc297e581321fbbd6242089f07440 |
| signature |
compiler: implement destructuring syntax19 files changed, 1208 insertions(+), 233 deletions(-)
build.zig+3-1| ... | ... | @@ -204,7 +204,9 @@ pub fn build(b: *std.Build) !void { |
| 204 | 204 | "Request creation of '.note.gnu.build-id' section", |
| 205 | 205 | ); |
| 206 | 206 | |
| 207 | if (!no_bin) { | |
| 207 | if (no_bin) { | |
| 208 | b.getInstallStep().dependOn(&exe.step); | |
| 209 | } else { | |
| 208 | 210 | const install_exe = b.addInstallArtifact(exe, .{ |
| 209 | 211 | .dest_dir = if (flat) .{ .override = .prefix } else .default, |
| 210 | 212 | }); |
doc/langref.html.in+35-15| ... | ... | @@ -12382,21 +12382,22 @@ ComptimeDecl <- KEYWORD_comptime Block |
| 12382 | 12382 | |
| 12383 | 12383 | Decl |
| 12384 | 12384 | <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) |
| 12385 | / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl | |
| 12385 | / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl | |
| 12386 | 12386 | / KEYWORD_usingnamespace Expr SEMICOLON |
| 12387 | 12387 | |
| 12388 | 12388 | FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr |
| 12389 | 12389 | |
| 12390 | VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON | |
| 12390 | VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? | |
| 12391 | ||
| 12392 | GlobalVarDecl <- VarDeclProto (EQUAL Expr)? SEMICOLON | |
| 12391 | 12393 | |
| 12392 | 12394 | ContainerField |
| 12393 | <- doc_comment? KEYWORD_comptime? IDENTIFIER (COLON TypeExpr)? ByteAlign? (EQUAL Expr)? | |
| 12394 | / doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? !KEYWORD_fn TypeExpr ByteAlign? (EQUAL Expr)? | |
| 12395 | <- doc_comment? KEYWORD_comptime? IDENTIFIER (COLON TypeExpr)? ByteAlign? (EQUAL Expr)? | |
| 12396 | / doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? !KEYWORD_fn TypeExpr ByteAlign? (EQUAL Expr)? | |
| 12395 | 12397 | |
| 12396 | 12398 | # *** Block Level *** |
| 12397 | 12399 | Statement |
| 12398 | <- KEYWORD_comptime? VarDecl | |
| 12399 | / KEYWORD_comptime BlockExprStatement | |
| 12400 | <- KEYWORD_comptime ComptimeStatement | |
| 12400 | 12401 | / KEYWORD_nosuspend BlockExprStatement |
| 12401 | 12402 | / KEYWORD_suspend BlockExprStatement |
| 12402 | 12403 | / KEYWORD_defer BlockExprStatement |
| ... | ... | @@ -12404,7 +12405,11 @@ Statement |
| 12404 | 12405 | / IfStatement |
| 12405 | 12406 | / LabeledStatement |
| 12406 | 12407 | / SwitchExpr |
| 12407 | / AssignExpr SEMICOLON | |
| 12408 | / VarDeclExprStatement | |
| 12409 | ||
| 12410 | ComptimeStatement | |
| 12411 | <- BlockExpr | |
| 12412 | / VarDeclExprStatement | |
| 12408 | 12413 | |
| 12409 | 12414 | IfStatement |
| 12410 | 12415 | <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? |
| ... | ... | @@ -12428,8 +12433,17 @@ BlockExprStatement |
| 12428 | 12433 | |
| 12429 | 12434 | BlockExpr <- BlockLabel? Block |
| 12430 | 12435 | |
| 12436 | # An expression, assignment, or any destructure, as a statement. | |
| 12437 | VarDeclExprStatement | |
| 12438 | <- VarDeclProto (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON | |
| 12439 | / Expr (AssignOp Expr / (COMMA (VarDeclProto / Expr))+ EQUAL Expr)? SEMICOLON | |
| 12440 | ||
| 12431 | 12441 | # *** Expression Level *** |
| 12432 | AssignExpr <- Expr (AssignOp Expr)? | |
| 12442 | ||
| 12443 | # An assignment or a destructure whose LHS are all lvalue expressions. | |
| 12444 | AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr)? | |
| 12445 | ||
| 12446 | SingleAssignExpr <- Expr (AssignOp Expr)? | |
| 12433 | 12447 | |
| 12434 | 12448 | Expr <- BoolOrExpr |
| 12435 | 12449 | |
| ... | ... | @@ -12570,7 +12584,7 @@ IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? |
| 12570 | 12584 | |
| 12571 | 12585 | WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? |
| 12572 | 12586 | |
| 12573 | ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | |
| 12587 | ForPrefix <- KEYWORD_for LPAREN ForArgumentsList RPAREN PtrListPayload | |
| 12574 | 12588 | |
| 12575 | 12589 | # Payloads |
| 12576 | 12590 | Payload <- PIPE IDENTIFIER PIPE |
| ... | ... | @@ -12579,9 +12593,10 @@ PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE |
| 12579 | 12593 | |
| 12580 | 12594 | PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE |
| 12581 | 12595 | |
| 12596 | PtrListPayload <- PIPE ASTERISK? IDENTIFIER (COMMA ASTERISK? IDENTIFIER)* COMMA? PIPE | |
| 12582 | 12597 | |
| 12583 | 12598 | # Switch specific |
| 12584 | SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr | |
| 12599 | SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? SingleAssignExpr | |
| 12585 | 12600 | |
| 12586 | 12601 | SwitchCase |
| 12587 | 12602 | <- SwitchItem (COMMA SwitchItem)* COMMA? |
| ... | ... | @@ -12589,6 +12604,11 @@ SwitchCase |
| 12589 | 12604 | |
| 12590 | 12605 | SwitchItem <- Expr (DOT3 Expr)? |
| 12591 | 12606 | |
| 12607 | # For specific | |
| 12608 | ForArgumentsList <- ForItem (COMMA ForItem)* COMMA? | |
| 12609 | ||
| 12610 | ForItem <- Expr (DOT2 Expr?)? | |
| 12611 | ||
| 12592 | 12612 | # Operators |
| 12593 | 12613 | AssignOp |
| 12594 | 12614 | <- ASTERISKEQUAL |
| ... | ... | @@ -12799,7 +12819,7 @@ STRINGLITERAL |
| 12799 | 12819 | / (line_string skip)+ |
| 12800 | 12820 | IDENTIFIER |
| 12801 | 12821 | <- !keyword [A-Za-z_] [A-Za-z0-9_]* skip |
| 12802 | / "@\"" string_char* "\"" skip | |
| 12822 | / "@" STRINGLITERALSINGLE | |
| 12803 | 12823 | BUILTINIDENTIFIER <- "@"[A-Za-z_][A-Za-z0-9_]* skip |
| 12804 | 12824 | |
| 12805 | 12825 | |
| ... | ... | @@ -12895,7 +12915,6 @@ KEYWORD_fn <- 'fn' end_of_word |
| 12895 | 12915 | KEYWORD_for <- 'for' end_of_word |
| 12896 | 12916 | KEYWORD_if <- 'if' end_of_word |
| 12897 | 12917 | KEYWORD_inline <- 'inline' end_of_word |
| 12898 | KEYWORD_linksection <- 'linksection' end_of_word | |
| 12899 | 12918 | KEYWORD_noalias <- 'noalias' end_of_word |
| 12900 | 12919 | KEYWORD_nosuspend <- 'nosuspend' end_of_word |
| 12901 | 12920 | KEYWORD_noinline <- 'noinline' end_of_word |
| ... | ... | @@ -12906,6 +12925,7 @@ KEYWORD_packed <- 'packed' end_of_word |
| 12906 | 12925 | KEYWORD_pub <- 'pub' end_of_word |
| 12907 | 12926 | KEYWORD_resume <- 'resume' end_of_word |
| 12908 | 12927 | KEYWORD_return <- 'return' end_of_word |
| 12928 | KEYWORD_linksection <- 'linksection' end_of_word | |
| 12909 | 12929 | KEYWORD_struct <- 'struct' end_of_word |
| 12910 | 12930 | KEYWORD_suspend <- 'suspend' end_of_word |
| 12911 | 12931 | KEYWORD_switch <- 'switch' end_of_word |
| ... | ... | @@ -12925,9 +12945,9 @@ keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and |
| 12925 | 12945 | / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer |
| 12926 | 12946 | / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export |
| 12927 | 12947 | / KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if |
| 12928 | / KEYWORD_inline / KEYWORD_linksection / KEYWORD_noalias / KEYWORD_noinline | |
| 12929 | 	 / KEYWORD_nosuspend / KEYWORD_opaque / KEYWORD_or / KEYWORD_orelse | |
| 12930 | 	 / KEYWORD_packed / KEYWORD_pub / KEYWORD_resume / KEYWORD_return | |
| 12948 | / KEYWORD_inline / KEYWORD_noalias / KEYWORD_nosuspend / KEYWORD_noinline | |
| 12949 | / KEYWORD_opaque / KEYWORD_or / KEYWORD_orelse / KEYWORD_packed | |
| 12950 | / KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection | |
| 12931 | 12951 | / KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test |
| 12932 | 12952 | / KEYWORD_threadlocal / KEYWORD_try / KEYWORD_union / KEYWORD_unreachable |
| 12933 | 12953 | / KEYWORD_usingnamespace / KEYWORD_var / KEYWORD_volatile / KEYWORD_while |
lib/std/zig/Ast.zig+28| ... | ... | @@ -241,6 +241,11 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { |
| 241 | 241 | token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), |
| 242 | 242 | }); |
| 243 | 243 | }, |
| 244 | .expected_expr_or_var_decl => { | |
| 245 | return stream.print("expected expression or var decl, found '{s}'", .{ | |
| 246 | token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), | |
| 247 | }); | |
| 248 | }, | |
| 244 | 249 | .expected_fn => { |
| 245 | 250 | return stream.print("expected function, found '{s}'", .{ |
| 246 | 251 | token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(), |
| ... | ... | @@ -584,6 +589,13 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex { |
| 584 | 589 | .error_union, |
| 585 | 590 | => n = datas[n].lhs, |
| 586 | 591 | |
| 592 | .assign_destructure => { | |
| 593 | const extra_idx = datas[n].lhs; | |
| 594 | const lhs_len = tree.extra_data[extra_idx]; | |
| 595 | assert(lhs_len > 0); | |
| 596 | n = tree.extra_data[extra_idx + 1]; | |
| 597 | }, | |
| 598 | ||
| 587 | 599 | .fn_decl, |
| 588 | 600 | .fn_proto_simple, |
| 589 | 601 | .fn_proto_multi, |
| ... | ... | @@ -816,6 +828,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { |
| 816 | 828 | .assign_add_sat, |
| 817 | 829 | .assign_sub_sat, |
| 818 | 830 | .assign, |
| 831 | .assign_destructure, | |
| 819 | 832 | .merge_error_sets, |
| 820 | 833 | .mul, |
| 821 | 834 | .div, |
| ... | ... | @@ -2846,6 +2859,7 @@ pub const Error = struct { |
| 2846 | 2859 | expected_container_members, |
| 2847 | 2860 | expected_expr, |
| 2848 | 2861 | expected_expr_or_assignment, |
| 2862 | expected_expr_or_var_decl, | |
| 2849 | 2863 | expected_fn, |
| 2850 | 2864 | expected_inlinable, |
| 2851 | 2865 | expected_labelable, |
| ... | ... | @@ -3006,6 +3020,20 @@ pub const Node = struct { |
| 3006 | 3020 | assign_sub_sat, |
| 3007 | 3021 | /// `lhs = rhs`. main_token is op. |
| 3008 | 3022 | assign, |
| 3023 | /// `a, b, ... = rhs`. main_token is op. lhs is index into `extra_data` | |
| 3024 | /// of an lhs elem count followed by an array of that many `Node.Index`, | |
| 3025 | /// with each node having one of the following types: | |
| 3026 | /// * `global_var_decl` | |
| 3027 | /// * `local_var_decl` | |
| 3028 | /// * `simple_var_decl` | |
| 3029 | /// * `aligned_var_decl` | |
| 3030 | /// * Any expression node | |
| 3031 | /// The first 3 types correspond to a `var` or `const` lhs node (note | |
| 3032 | /// that their `rhs` is always 0). An expression node corresponds to a | |
| 3033 | /// standard assignment LHS (which must be evaluated as an lvalue). | |
| 3034 | /// There may be a preceding `comptime` token, which does not create a | |
| 3035 | /// corresponding `comptime` node so must be manually detected. | |
| 3036 | assign_destructure, | |
| 3009 | 3037 | /// `lhs || rhs`. main_token is the `||`. |
| 3010 | 3038 | merge_error_sets, |
| 3011 | 3039 | /// `lhs * rhs`. main_token is the `*`. |
lib/std/zig/Parse.zig+278-72| ... | ... | @@ -658,9 +658,8 @@ fn expectTopLevelDecl(p: *Parse) !Node.Index { |
| 658 | 658 | } |
| 659 | 659 | |
| 660 | 660 | const thread_local_token = p.eatToken(.keyword_threadlocal); |
| 661 | const var_decl = try p.parseVarDecl(); | |
| 661 | const var_decl = try p.parseGlobalVarDecl(); | |
| 662 | 662 | if (var_decl != 0) { |
| 663 | try p.expectSemicolon(.expected_semi_after_decl, false); | |
| 664 | 663 | return var_decl; |
| 665 | 664 | } |
| 666 | 665 | if (thread_local_token != null) { |
| ... | ... | @@ -792,8 +791,9 @@ fn parseFnProto(p: *Parse) !Node.Index { |
| 792 | 791 | } |
| 793 | 792 | } |
| 794 | 793 | |
| 795 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON | |
| 796 | fn parseVarDecl(p: *Parse) !Node.Index { | |
| 794 | /// VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? | |
| 795 | /// Returns a `*_var_decl` node with its rhs (init expression) initialized to 0. | |
| 796 | fn parseVarDeclProto(p: *Parse) !Node.Index { | |
| 797 | 797 | const mut_token = p.eatToken(.keyword_const) orelse |
| 798 | 798 | p.eatToken(.keyword_var) orelse |
| 799 | 799 | return null_node; |
| ... | ... | @@ -803,18 +803,7 @@ fn parseVarDecl(p: *Parse) !Node.Index { |
| 803 | 803 | const align_node = try p.parseByteAlign(); |
| 804 | 804 | const addrspace_node = try p.parseAddrSpace(); |
| 805 | 805 | const section_node = try p.parseLinkSection(); |
| 806 | const init_node: Node.Index = switch (p.token_tags[p.tok_i]) { | |
| 807 | .equal_equal => blk: { | |
| 808 | try p.warn(.wrong_equal_var_decl); | |
| 809 | p.tok_i += 1; | |
| 810 | break :blk try p.expectExpr(); | |
| 811 | }, | |
| 812 | .equal => blk: { | |
| 813 | p.tok_i += 1; | |
| 814 | break :blk try p.expectExpr(); | |
| 815 | }, | |
| 816 | else => 0, | |
| 817 | }; | |
| 806 | ||
| 818 | 807 | if (section_node == 0 and addrspace_node == 0) { |
| 819 | 808 | if (align_node == 0) { |
| 820 | 809 | return p.addNode(.{ |
| ... | ... | @@ -822,31 +811,33 @@ fn parseVarDecl(p: *Parse) !Node.Index { |
| 822 | 811 | .main_token = mut_token, |
| 823 | 812 | .data = .{ |
| 824 | 813 | .lhs = type_node, |
| 825 | .rhs = init_node, | |
| 814 | .rhs = 0, | |
| 826 | 815 | }, |
| 827 | 816 | }); |
| 828 | } else if (type_node == 0) { | |
| 817 | } | |
| 818 | ||
| 819 | if (type_node == 0) { | |
| 829 | 820 | return p.addNode(.{ |
| 830 | 821 | .tag = .aligned_var_decl, |
| 831 | 822 | .main_token = mut_token, |
| 832 | 823 | .data = .{ |
| 833 | 824 | .lhs = align_node, |
| 834 | .rhs = init_node, | |
| 835 | }, | |
| 836 | }); | |
| 837 | } else { | |
| 838 | return p.addNode(.{ | |
| 839 | .tag = .local_var_decl, | |
| 840 | .main_token = mut_token, | |
| 841 | .data = .{ | |
| 842 | .lhs = try p.addExtra(Node.LocalVarDecl{ | |
| 843 | .type_node = type_node, | |
| 844 | .align_node = align_node, | |
| 845 | }), | |
| 846 | .rhs = init_node, | |
| 825 | .rhs = 0, | |
| 847 | 826 | }, |
| 848 | 827 | }); |
| 849 | 828 | } |
| 829 | ||
| 830 | return p.addNode(.{ | |
| 831 | .tag = .local_var_decl, | |
| 832 | .main_token = mut_token, | |
| 833 | .data = .{ | |
| 834 | .lhs = try p.addExtra(Node.LocalVarDecl{ | |
| 835 | .type_node = type_node, | |
| 836 | .align_node = align_node, | |
| 837 | }), | |
| 838 | .rhs = 0, | |
| 839 | }, | |
| 840 | }); | |
| 850 | 841 | } else { |
| 851 | 842 | return p.addNode(.{ |
| 852 | 843 | .tag = .global_var_decl, |
| ... | ... | @@ -858,12 +849,38 @@ fn parseVarDecl(p: *Parse) !Node.Index { |
| 858 | 849 | .addrspace_node = addrspace_node, |
| 859 | 850 | .section_node = section_node, |
| 860 | 851 | }), |
| 861 | .rhs = init_node, | |
| 852 | .rhs = 0, | |
| 862 | 853 | }, |
| 863 | 854 | }); |
| 864 | 855 | } |
| 865 | 856 | } |
| 866 | 857 | |
| 858 | /// GlobalVarDecl <- VarDeclProto (EQUAL Expr?) SEMICOLON | |
| 859 | fn parseGlobalVarDecl(p: *Parse) !Node.Index { | |
| 860 | const var_decl = try p.parseVarDeclProto(); | |
| 861 | if (var_decl == 0) { | |
| 862 | return null_node; | |
| 863 | } | |
| 864 | ||
| 865 | const init_node: Node.Index = switch (p.token_tags[p.tok_i]) { | |
| 866 | .equal_equal => blk: { | |
| 867 | try p.warn(.wrong_equal_var_decl); | |
| 868 | p.tok_i += 1; | |
| 869 | break :blk try p.expectExpr(); | |
| 870 | }, | |
| 871 | .equal => blk: { | |
| 872 | p.tok_i += 1; | |
| 873 | break :blk try p.expectExpr(); | |
| 874 | }, | |
| 875 | else => 0, | |
| 876 | }; | |
| 877 | ||
| 878 | p.nodes.items(.data)[var_decl].rhs = init_node; | |
| 879 | ||
| 880 | try p.expectSemicolon(.expected_semi_after_decl, false); | |
| 881 | return var_decl; | |
| 882 | } | |
| 883 | ||
| 867 | 884 | /// ContainerField |
| 868 | 885 | /// <- doc_comment? KEYWORD_comptime? IDENTIFIER (COLON TypeExpr)? ByteAlign? (EQUAL Expr)? |
| 869 | 886 | /// / doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? !KEYWORD_fn TypeExpr ByteAlign? (EQUAL Expr)? |
| ... | ... | @@ -918,8 +935,7 @@ fn expectContainerField(p: *Parse) !Node.Index { |
| 918 | 935 | } |
| 919 | 936 | |
| 920 | 937 | /// Statement |
| 921 | /// <- KEYWORD_comptime? VarDecl | |
| 922 | /// / KEYWORD_comptime BlockExprStatement | |
| 938 | /// <- KEYWORD_comptime ComptimeStatement | |
| 923 | 939 | /// / KEYWORD_nosuspend BlockExprStatement |
| 924 | 940 | /// / KEYWORD_suspend BlockExprStatement |
| 925 | 941 | /// / KEYWORD_defer BlockExprStatement |
| ... | ... | @@ -927,27 +943,28 @@ fn expectContainerField(p: *Parse) !Node.Index { |
| 927 | 943 | /// / IfStatement |
| 928 | 944 | /// / LabeledStatement |
| 929 | 945 | /// / SwitchExpr |
| 930 | /// / AssignExpr SEMICOLON | |
| 931 | fn parseStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { | |
| 932 | const comptime_token = p.eatToken(.keyword_comptime); | |
| 933 | ||
| 934 | if (allow_defer_var) { | |
| 935 | const var_decl = try p.parseVarDecl(); | |
| 936 | if (var_decl != 0) { | |
| 937 | try p.expectSemicolon(.expected_semi_after_decl, true); | |
| 938 | return var_decl; | |
| 946 | /// / VarDeclExprStatement | |
| 947 | fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { | |
| 948 | if (p.eatToken(.keyword_comptime)) |comptime_token| { | |
| 949 | const block_expr = try p.parseBlockExpr(); | |
| 950 | if (block_expr != 0) { | |
| 951 | return p.addNode(.{ | |
| 952 | .tag = .@"comptime", | |
| 953 | .main_token = comptime_token, | |
| 954 | .data = .{ | |
| 955 | .lhs = block_expr, | |
| 956 | .rhs = undefined, | |
| 957 | }, | |
| 958 | }); | |
| 939 | 959 | } |
| 940 | } | |
| 941 | 960 | |
| 942 | if (comptime_token) |token| { | |
| 943 | return p.addNode(.{ | |
| 944 | .tag = .@"comptime", | |
| 945 | .main_token = token, | |
| 946 | .data = .{ | |
| 947 | .lhs = try p.expectBlockExprStatement(), | |
| 948 | .rhs = undefined, | |
| 949 | }, | |
| 950 | }); | |
| 961 | if (allow_defer_var) { | |
| 962 | return p.expectVarDeclExprStatement(comptime_token); | |
| 963 | } else { | |
| 964 | const assign = try p.expectAssignExpr(); | |
| 965 | try p.expectSemicolon(.expected_semi_after_stmt, true); | |
| 966 | return assign; | |
| 967 | } | |
| 951 | 968 | } |
| 952 | 969 | |
| 953 | 970 | switch (p.token_tags[p.tok_i]) { |
| ... | ... | @@ -1011,21 +1028,145 @@ fn parseStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { |
| 1011 | 1028 | const labeled_statement = try p.parseLabeledStatement(); |
| 1012 | 1029 | if (labeled_statement != 0) return labeled_statement; |
| 1013 | 1030 | |
| 1014 | const assign_expr = try p.parseAssignExpr(); | |
| 1015 | if (assign_expr != 0) { | |
| 1031 | if (allow_defer_var) { | |
| 1032 | return p.expectVarDeclExprStatement(null); | |
| 1033 | } else { | |
| 1034 | const assign = try p.expectAssignExpr(); | |
| 1016 | 1035 | try p.expectSemicolon(.expected_semi_after_stmt, true); |
| 1017 | return assign_expr; | |
| 1036 | return assign; | |
| 1018 | 1037 | } |
| 1038 | } | |
| 1019 | 1039 | |
| 1020 | return null_node; | |
| 1040 | /// ComptimeStatement | |
| 1041 | /// <- BlockExpr | |
| 1042 | /// / VarDeclExprStatement | |
| 1043 | fn expectComptimeStatement(p: *Parse, comptime_token: TokenIndex) !Node.Index { | |
| 1044 | const block_expr = try p.parseBlockExpr(); | |
| 1045 | if (block_expr != 0) { | |
| 1046 | return p.addNode(.{ | |
| 1047 | .tag = .@"comptime", | |
| 1048 | .main_token = comptime_token, | |
| 1049 | .data = .{ .lhs = block_expr, .rhs = undefined }, | |
| 1050 | }); | |
| 1051 | } | |
| 1052 | return p.expectVarDeclExprStatement(comptime_token); | |
| 1021 | 1053 | } |
| 1022 | 1054 | |
| 1023 | fn expectStatement(p: *Parse, allow_defer_var: bool) !Node.Index { | |
| 1024 | const statement = try p.parseStatement(allow_defer_var); | |
| 1025 | if (statement == 0) { | |
| 1026 | return p.fail(.expected_statement); | |
| 1055 | /// VarDeclExprStatement | |
| 1056 | /// <- VarDeclProto (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON | |
| 1057 | /// / Expr (AssignOp Expr / (COMMA (VarDeclProto / Expr))+ EQUAL Expr)? SEMICOLON | |
| 1058 | fn expectVarDeclExprStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Index { | |
| 1059 | const scratch_top = p.scratch.items.len; | |
| 1060 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 1061 | ||
| 1062 | while (true) { | |
| 1063 | const var_decl_proto = try p.parseVarDeclProto(); | |
| 1064 | if (var_decl_proto != 0) { | |
| 1065 | try p.scratch.append(p.gpa, var_decl_proto); | |
| 1066 | } else { | |
| 1067 | const expr = try p.parseExpr(); | |
| 1068 | if (expr == 0) { | |
| 1069 | if (p.scratch.items.len == scratch_top) { | |
| 1070 | // We parsed nothing | |
| 1071 | return p.fail(.expected_statement); | |
| 1072 | } else { | |
| 1073 | // We've had at least one LHS, but had a bad comma | |
| 1074 | return p.fail(.expected_expr_or_var_decl); | |
| 1075 | } | |
| 1076 | } | |
| 1077 | try p.scratch.append(p.gpa, expr); | |
| 1078 | } | |
| 1079 | _ = p.eatToken(.comma) orelse break; | |
| 1080 | } | |
| 1081 | ||
| 1082 | const lhs_count = p.scratch.items.len - scratch_top; | |
| 1083 | assert(lhs_count > 0); | |
| 1084 | ||
| 1085 | const equal_token = p.eatToken(.equal) orelse eql: { | |
| 1086 | if (lhs_count > 1) { | |
| 1087 | // Definitely a destructure, so allow recovering from == | |
| 1088 | if (p.eatToken(.equal_equal)) |tok| { | |
| 1089 | try p.warnMsg(.{ .tag = .wrong_equal_var_decl, .token = tok }); | |
| 1090 | break :eql tok; | |
| 1091 | } | |
| 1092 | return p.failExpected(.equal); | |
| 1093 | } | |
| 1094 | const lhs = p.scratch.items[scratch_top]; | |
| 1095 | switch (p.nodes.items(.tag)[lhs]) { | |
| 1096 | .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => { | |
| 1097 | // Definitely a var decl, so allow recovering from == | |
| 1098 | if (p.eatToken(.equal_equal)) |tok| { | |
| 1099 | try p.warnMsg(.{ .tag = .wrong_equal_var_decl, .token = tok }); | |
| 1100 | break :eql tok; | |
| 1101 | } | |
| 1102 | return p.failExpected(.equal); | |
| 1103 | }, | |
| 1104 | else => {}, | |
| 1105 | } | |
| 1106 | ||
| 1107 | const expr = try p.finishAssignExpr(lhs); | |
| 1108 | try p.expectSemicolon(.expected_semi_after_stmt, true); | |
| 1109 | if (comptime_token) |t| { | |
| 1110 | return p.addNode(.{ | |
| 1111 | .tag = .@"comptime", | |
| 1112 | .main_token = t, | |
| 1113 | .data = .{ | |
| 1114 | .lhs = expr, | |
| 1115 | .rhs = undefined, | |
| 1116 | }, | |
| 1117 | }); | |
| 1118 | } else { | |
| 1119 | return expr; | |
| 1120 | } | |
| 1121 | }; | |
| 1122 | ||
| 1123 | const rhs = try p.expectExpr(); | |
| 1124 | try p.expectSemicolon(.expected_semi_after_stmt, true); | |
| 1125 | ||
| 1126 | if (lhs_count == 1) { | |
| 1127 | const lhs = p.scratch.items[scratch_top]; | |
| 1128 | switch (p.nodes.items(.tag)[lhs]) { | |
| 1129 | .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => { | |
| 1130 | p.nodes.items(.data)[lhs].rhs = rhs; | |
| 1131 | // Don't need to wrap in comptime | |
| 1132 | return lhs; | |
| 1133 | }, | |
| 1134 | else => {}, | |
| 1135 | } | |
| 1136 | const expr = try p.addNode(.{ | |
| 1137 | .tag = .assign, | |
| 1138 | .main_token = equal_token, | |
| 1139 | .data = .{ .lhs = lhs, .rhs = rhs }, | |
| 1140 | }); | |
| 1141 | if (comptime_token) |t| { | |
| 1142 | return p.addNode(.{ | |
| 1143 | .tag = .@"comptime", | |
| 1144 | .main_token = t, | |
| 1145 | .data = .{ | |
| 1146 | .lhs = expr, | |
| 1147 | .rhs = undefined, | |
| 1148 | }, | |
| 1149 | }); | |
| 1150 | } else { | |
| 1151 | return expr; | |
| 1152 | } | |
| 1027 | 1153 | } |
| 1028 | return statement; | |
| 1154 | ||
| 1155 | // An actual destructure! No need for any `comptime` wrapper here. | |
| 1156 | ||
| 1157 | const extra_start = p.extra_data.items.len; | |
| 1158 | try p.extra_data.ensureUnusedCapacity(p.gpa, lhs_count + 1); | |
| 1159 | p.extra_data.appendAssumeCapacity(@intCast(lhs_count)); | |
| 1160 | p.extra_data.appendSliceAssumeCapacity(p.scratch.items[scratch_top..]); | |
| 1161 | ||
| 1162 | return p.addNode(.{ | |
| 1163 | .tag = .assign_destructure, | |
| 1164 | .main_token = equal_token, | |
| 1165 | .data = .{ | |
| 1166 | .lhs = @intCast(extra_start), | |
| 1167 | .rhs = rhs, | |
| 1168 | }, | |
| 1169 | }); | |
| 1029 | 1170 | } |
| 1030 | 1171 | |
| 1031 | 1172 | /// If a parse error occurs, reports an error, but then finds the next statement |
| ... | ... | @@ -1345,7 +1486,7 @@ fn parseBlockExpr(p: *Parse) Error!Node.Index { |
| 1345 | 1486 | } |
| 1346 | 1487 | } |
| 1347 | 1488 | |
| 1348 | /// AssignExpr <- Expr (AssignOp Expr)? | |
| 1489 | /// AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr)? | |
| 1349 | 1490 | /// |
| 1350 | 1491 | /// AssignOp |
| 1351 | 1492 | /// <- ASTERISKEQUAL |
| ... | ... | @@ -1369,8 +1510,40 @@ fn parseBlockExpr(p: *Parse) Error!Node.Index { |
| 1369 | 1510 | fn parseAssignExpr(p: *Parse) !Node.Index { |
| 1370 | 1511 | const expr = try p.parseExpr(); |
| 1371 | 1512 | if (expr == 0) return null_node; |
| 1513 | return p.finishAssignExpr(expr); | |
| 1514 | } | |
| 1372 | 1515 | |
| 1373 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { | |
| 1516 | /// SingleAssignExpr <- Expr (AssignOp Expr)? | |
| 1517 | fn parseSingleAssignExpr(p: *Parse) !Node.Index { | |
| 1518 | const lhs = try p.parseExpr(); | |
| 1519 | if (lhs == 0) return null_node; | |
| 1520 | const tag = assignOpNode(p.token_tags[p.tok_i]) orelse return lhs; | |
| 1521 | return p.addNode(.{ | |
| 1522 | .tag = tag, | |
| 1523 | .main_token = p.nextToken(), | |
| 1524 | .data = .{ | |
| 1525 | .lhs = lhs, | |
| 1526 | .rhs = try p.expectExpr(), | |
| 1527 | }, | |
| 1528 | }); | |
| 1529 | } | |
| 1530 | ||
| 1531 | fn finishAssignExpr(p: *Parse, lhs: Node.Index) !Node.Index { | |
| 1532 | const tok = p.token_tags[p.tok_i]; | |
| 1533 | if (tok == .comma) return p.finishAssignDestructureExpr(lhs); | |
| 1534 | const tag = assignOpNode(tok) orelse return lhs; | |
| 1535 | return p.addNode(.{ | |
| 1536 | .tag = tag, | |
| 1537 | .main_token = p.nextToken(), | |
| 1538 | .data = .{ | |
| 1539 | .lhs = lhs, | |
| 1540 | .rhs = try p.expectExpr(), | |
| 1541 | }, | |
| 1542 | }); | |
| 1543 | } | |
| 1544 | ||
| 1545 | fn assignOpNode(tok: Token.Tag) ?Node.Tag { | |
| 1546 | return switch (tok) { | |
| 1374 | 1547 | .asterisk_equal => .assign_mul, |
| 1375 | 1548 | .slash_equal => .assign_div, |
| 1376 | 1549 | .percent_equal => .assign_mod, |
| ... | ... | @@ -1389,18 +1562,51 @@ fn parseAssignExpr(p: *Parse) !Node.Index { |
| 1389 | 1562 | .plus_pipe_equal => .assign_add_sat, |
| 1390 | 1563 | .minus_pipe_equal => .assign_sub_sat, |
| 1391 | 1564 | .equal => .assign, |
| 1392 | else => return expr, | |
| 1565 | else => null, | |
| 1393 | 1566 | }; |
| 1567 | } | |
| 1568 | ||
| 1569 | fn finishAssignDestructureExpr(p: *Parse, first_lhs: Node.Index) !Node.Index { | |
| 1570 | const scratch_top = p.scratch.items.len; | |
| 1571 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 1572 | ||
| 1573 | try p.scratch.append(p.gpa, first_lhs); | |
| 1574 | ||
| 1575 | while (p.eatToken(.comma)) |_| { | |
| 1576 | const expr = try p.expectExpr(); | |
| 1577 | try p.scratch.append(p.gpa, expr); | |
| 1578 | } | |
| 1579 | ||
| 1580 | const equal_token = try p.expectToken(.equal); | |
| 1581 | ||
| 1582 | const rhs = try p.expectExpr(); | |
| 1583 | ||
| 1584 | const lhs_count = p.scratch.items.len - scratch_top; | |
| 1585 | assert(lhs_count > 1); // we already had first_lhs, and must have at least one more lvalue | |
| 1586 | ||
| 1587 | const extra_start = p.extra_data.items.len; | |
| 1588 | try p.extra_data.ensureUnusedCapacity(p.gpa, lhs_count + 1); | |
| 1589 | p.extra_data.appendAssumeCapacity(@intCast(lhs_count)); | |
| 1590 | p.extra_data.appendSliceAssumeCapacity(p.scratch.items[scratch_top..]); | |
| 1591 | ||
| 1394 | 1592 | return p.addNode(.{ |
| 1395 | .tag = tag, | |
| 1396 | .main_token = p.nextToken(), | |
| 1593 | .tag = .assign_destructure, | |
| 1594 | .main_token = equal_token, | |
| 1397 | 1595 | .data = .{ |
| 1398 | .lhs = expr, | |
| 1399 | .rhs = try p.expectExpr(), | |
| 1596 | .lhs = @intCast(extra_start), | |
| 1597 | .rhs = rhs, | |
| 1400 | 1598 | }, |
| 1401 | 1599 | }); |
| 1402 | 1600 | } |
| 1403 | 1601 | |
| 1602 | fn expectSingleAssignExpr(p: *Parse) !Node.Index { | |
| 1603 | const expr = try p.parseSingleAssignExpr(); | |
| 1604 | if (expr == 0) { | |
| 1605 | return p.fail(.expected_expr_or_assignment); | |
| 1606 | } | |
| 1607 | return expr; | |
| 1608 | } | |
| 1609 | ||
| 1404 | 1610 | fn expectAssignExpr(p: *Parse) !Node.Index { |
| 1405 | 1611 | const expr = try p.parseAssignExpr(); |
| 1406 | 1612 | if (expr == 0) { |
| ... | ... | @@ -3260,7 +3466,7 @@ fn parseSwitchProng(p: *Parse) !Node.Index { |
| 3260 | 3466 | .main_token = arrow_token, |
| 3261 | 3467 | .data = .{ |
| 3262 | 3468 | .lhs = 0, |
| 3263 | .rhs = try p.expectAssignExpr(), | |
| 3469 | .rhs = try p.expectSingleAssignExpr(), | |
| 3264 | 3470 | }, |
| 3265 | 3471 | }), |
| 3266 | 3472 | 1 => return p.addNode(.{ |
| ... | ... | @@ -3268,7 +3474,7 @@ fn parseSwitchProng(p: *Parse) !Node.Index { |
| 3268 | 3474 | .main_token = arrow_token, |
| 3269 | 3475 | .data = .{ |
| 3270 | 3476 | .lhs = items[0], |
| 3271 | .rhs = try p.expectAssignExpr(), | |
| 3477 | .rhs = try p.expectSingleAssignExpr(), | |
| 3272 | 3478 | }, |
| 3273 | 3479 | }), |
| 3274 | 3480 | else => return p.addNode(.{ |
| ... | ... | @@ -3276,7 +3482,7 @@ fn parseSwitchProng(p: *Parse) !Node.Index { |
| 3276 | 3482 | .main_token = arrow_token, |
| 3277 | 3483 | .data = .{ |
| 3278 | 3484 | .lhs = try p.addExtra(try p.listToSpan(items)), |
| 3279 | .rhs = try p.expectAssignExpr(), | |
| 3485 | .rhs = try p.expectSingleAssignExpr(), | |
| 3280 | 3486 | }, |
| 3281 | 3487 | }), |
| 3282 | 3488 | } |
lib/std/zig/parser_test.zig+7-7| ... | ... | @@ -4348,12 +4348,12 @@ test "zig fmt: invalid else branch statement" { |
| 4348 | 4348 | \\ for ("") |_| {} else defer {} |
| 4349 | 4349 | \\} |
| 4350 | 4350 | , &[_]Error{ |
| 4351 | .expected_statement, | |
| 4352 | .expected_statement, | |
| 4353 | .expected_statement, | |
| 4354 | .expected_statement, | |
| 4355 | .expected_statement, | |
| 4356 | .expected_statement, | |
| 4351 | .expected_expr_or_assignment, | |
| 4352 | .expected_expr_or_assignment, | |
| 4353 | .expected_expr_or_assignment, | |
| 4354 | .expected_expr_or_assignment, | |
| 4355 | .expected_expr_or_assignment, | |
| 4356 | .expected_expr_or_assignment, | |
| 4357 | 4357 | }); |
| 4358 | 4358 | } |
| 4359 | 4359 | |
| ... | ... | @@ -6078,7 +6078,7 @@ test "recovery: missing for payload" { |
| 6078 | 6078 | try testError( |
| 6079 | 6079 | \\comptime { |
| 6080 | 6080 | \\ const a = for(a) {}; |
| 6081 | \\ const a: for(a) blk: {}; | |
| 6081 | \\ const a: for(a) blk: {} = {}; | |
| 6082 | 6082 | \\ for(a) {} |
| 6083 | 6083 | \\} |
| 6084 | 6084 | , &[_]Error{ |
lib/std/zig/render.zig+82-32| ... | ... | @@ -164,7 +164,7 @@ fn renderMember( |
| 164 | 164 | .local_var_decl, |
| 165 | 165 | .simple_var_decl, |
| 166 | 166 | .aligned_var_decl, |
| 167 | => return renderVarDecl(gpa, ais, tree, tree.fullVarDecl(decl).?), | |
| 167 | => return renderVarDecl(gpa, ais, tree, tree.fullVarDecl(decl).?, false, .semicolon), | |
| 168 | 168 | |
| 169 | 169 | .test_decl => { |
| 170 | 170 | const test_token = main_tokens[decl]; |
| ... | ... | @@ -427,6 +427,42 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 427 | 427 | return renderExpression(gpa, ais, tree, infix.rhs, space); |
| 428 | 428 | }, |
| 429 | 429 | |
| 430 | .assign_destructure => { | |
| 431 | const lhs_count = tree.extra_data[datas[node].lhs]; | |
| 432 | assert(lhs_count > 1); | |
| 433 | const lhs_exprs = tree.extra_data[datas[node].lhs + 1 ..][0..lhs_count]; | |
| 434 | const rhs = datas[node].rhs; | |
| 435 | ||
| 436 | const maybe_comptime_token = tree.firstToken(node) - 1; | |
| 437 | if (token_tags[maybe_comptime_token] == .keyword_comptime) { | |
| 438 | try renderToken(ais, tree, maybe_comptime_token, .space); | |
| 439 | } | |
| 440 | ||
| 441 | for (lhs_exprs, 0..) |lhs_node, i| { | |
| 442 | const lhs_space: Space = if (i == lhs_exprs.len - 1) .space else .comma_space; | |
| 443 | switch (node_tags[lhs_node]) { | |
| 444 | .global_var_decl, | |
| 445 | .local_var_decl, | |
| 446 | .simple_var_decl, | |
| 447 | .aligned_var_decl, | |
| 448 | => { | |
| 449 | try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(lhs_node).?, true, lhs_space); | |
| 450 | }, | |
| 451 | else => try renderExpression(gpa, ais, tree, lhs_node, lhs_space), | |
| 452 | } | |
| 453 | } | |
| 454 | const equal_token = main_tokens[node]; | |
| 455 | if (tree.tokensOnSameLine(equal_token, equal_token + 1)) { | |
| 456 | try renderToken(ais, tree, equal_token, .space); | |
| 457 | } else { | |
| 458 | ais.pushIndent(); | |
| 459 | try renderToken(ais, tree, equal_token, .newline); | |
| 460 | ais.popIndent(); | |
| 461 | } | |
| 462 | ais.pushIndentOneShot(); | |
| 463 | return renderExpression(gpa, ais, tree, rhs, space); | |
| 464 | }, | |
| 465 | ||
| 430 | 466 | .bit_not, |
| 431 | 467 | .bool_not, |
| 432 | 468 | .negation, |
| ... | ... | @@ -943,7 +979,16 @@ fn renderAsmInput( |
| 943 | 979 | return renderToken(ais, tree, datas[asm_input].rhs, space); // rparen |
| 944 | 980 | } |
| 945 | 981 | |
| 946 | fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDecl) Error!void { | |
| 982 | fn renderVarDecl( | |
| 983 | gpa: Allocator, | |
| 984 | ais: *Ais, | |
| 985 | tree: Ast, | |
| 986 | var_decl: Ast.full.VarDecl, | |
| 987 | /// Destructures intentionally ignore leading `comptime` tokens. | |
| 988 | ignore_comptime_token: bool, | |
| 989 | /// `comma_space` and `space` are used for destructure LHS decls. | |
| 990 | space: Space, | |
| 991 | ) Error!void { | |
| 947 | 992 | if (var_decl.visib_token) |visib_token| { |
| 948 | 993 | try renderToken(ais, tree, visib_token, Space.space); // pub |
| 949 | 994 | } |
| ... | ... | @@ -960,21 +1005,31 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec |
| 960 | 1005 | try renderToken(ais, tree, thread_local_token, Space.space); // threadlocal |
| 961 | 1006 | } |
| 962 | 1007 | |
| 963 | if (var_decl.comptime_token) |comptime_token| { | |
| 964 | try renderToken(ais, tree, comptime_token, Space.space); // comptime | |
| 1008 | if (!ignore_comptime_token) { | |
| 1009 | if (var_decl.comptime_token) |comptime_token| { | |
| 1010 | try renderToken(ais, tree, comptime_token, Space.space); // comptime | |
| 1011 | } | |
| 965 | 1012 | } |
| 966 | 1013 | |
| 967 | 1014 | try renderToken(ais, tree, var_decl.ast.mut_token, .space); // var |
| 968 | 1015 | |
| 969 | const name_space = if (var_decl.ast.type_node == 0 and | |
| 970 | (var_decl.ast.align_node != 0 or | |
| 971 | var_decl.ast.addrspace_node != 0 or | |
| 972 | var_decl.ast.section_node != 0 or | |
| 973 | var_decl.ast.init_node != 0)) | |
| 974 | Space.space | |
| 975 | else | |
| 976 | Space.none; | |
| 977 | try renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name | |
| 1016 | if (var_decl.ast.type_node != 0 or var_decl.ast.align_node != 0 or | |
| 1017 | var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or | |
| 1018 | var_decl.ast.init_node != 0) | |
| 1019 | { | |
| 1020 | const name_space = if (var_decl.ast.type_node == 0 and | |
| 1021 | (var_decl.ast.align_node != 0 or | |
| 1022 | var_decl.ast.addrspace_node != 0 or | |
| 1023 | var_decl.ast.section_node != 0 or | |
| 1024 | var_decl.ast.init_node != 0)) | |
| 1025 | Space.space | |
| 1026 | else | |
| 1027 | Space.none; | |
| 1028 | ||
| 1029 | try renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name | |
| 1030 | } else { | |
| 1031 | return renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name | |
| 1032 | } | |
| 978 | 1033 | |
| 979 | 1034 | if (var_decl.ast.type_node != 0) { |
| 980 | 1035 | try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // : |
| ... | ... | @@ -983,9 +1038,7 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec |
| 983 | 1038 | { |
| 984 | 1039 | try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space); |
| 985 | 1040 | } else { |
| 986 | try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .none); | |
| 987 | const semicolon = tree.lastToken(var_decl.ast.type_node) + 1; | |
| 988 | return renderToken(ais, tree, semicolon, Space.newline); // ; | |
| 1041 | return renderExpression(gpa, ais, tree, var_decl.ast.type_node, space); | |
| 989 | 1042 | } |
| 990 | 1043 | } |
| 991 | 1044 | |
| ... | ... | @@ -1001,8 +1054,7 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec |
| 1001 | 1054 | { |
| 1002 | 1055 | try renderToken(ais, tree, rparen, .space); // ) |
| 1003 | 1056 | } else { |
| 1004 | try renderToken(ais, tree, rparen, .none); // ) | |
| 1005 | return renderToken(ais, tree, rparen + 1, Space.newline); // ; | |
| 1057 | return renderToken(ais, tree, rparen, space); // ) | |
| 1006 | 1058 | } |
| 1007 | 1059 | } |
| 1008 | 1060 | |
| ... | ... | @@ -1031,23 +1083,21 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec |
| 1031 | 1083 | if (var_decl.ast.init_node != 0) { |
| 1032 | 1084 | try renderToken(ais, tree, rparen, .space); // ) |
| 1033 | 1085 | } else { |
| 1034 | try renderToken(ais, tree, rparen, .none); // ) | |
| 1035 | return renderToken(ais, tree, rparen + 1, Space.newline); // ; | |
| 1086 | return renderToken(ais, tree, rparen, space); // ) | |
| 1036 | 1087 | } |
| 1037 | 1088 | } |
| 1038 | 1089 | |
| 1039 | if (var_decl.ast.init_node != 0) { | |
| 1040 | const eq_token = tree.firstToken(var_decl.ast.init_node) - 1; | |
| 1041 | const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline; | |
| 1042 | { | |
| 1043 | ais.pushIndent(); | |
| 1044 | try renderToken(ais, tree, eq_token, eq_space); // = | |
| 1045 | ais.popIndent(); | |
| 1046 | } | |
| 1047 | ais.pushIndentOneShot(); | |
| 1048 | return renderExpression(gpa, ais, tree, var_decl.ast.init_node, .semicolon); // ; | |
| 1090 | assert(var_decl.ast.init_node != 0); | |
| 1091 | ||
| 1092 | const eq_token = tree.firstToken(var_decl.ast.init_node) - 1; | |
| 1093 | const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline; | |
| 1094 | { | |
| 1095 | ais.pushIndent(); | |
| 1096 | try renderToken(ais, tree, eq_token, eq_space); // = | |
| 1097 | ais.popIndent(); | |
| 1049 | 1098 | } |
| 1050 | return renderToken(ais, tree, var_decl.ast.mut_token + 2, .newline); // ; | |
| 1099 | ais.pushIndentOneShot(); | |
| 1100 | return renderExpression(gpa, ais, tree, var_decl.ast.init_node, space); // ; | |
| 1051 | 1101 | } |
| 1052 | 1102 | |
| 1053 | 1103 | fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: Space) Error!void { |
| ... | ... | @@ -1825,7 +1875,7 @@ fn renderBlock( |
| 1825 | 1875 | .local_var_decl, |
| 1826 | 1876 | .simple_var_decl, |
| 1827 | 1877 | .aligned_var_decl, |
| 1828 | => try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(stmt).?), | |
| 1878 | => try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(stmt).?, false, .semicolon), | |
| 1829 | 1879 | else => try renderExpression(gpa, ais, tree, stmt, .semicolon), |
| 1830 | 1880 | } |
| 1831 | 1881 | } |
src/AstGen.zig+432-44| ... | ... | @@ -280,6 +280,20 @@ const ResultInfo = struct { |
| 280 | 280 | /// The result instruction from the expression must be ignored. |
| 281 | 281 | /// Always an instruction with tag `alloc_inferred`. |
| 282 | 282 | inferred_ptr: Zir.Inst.Ref, |
| 283 | /// The expression has a sequence of pointers to store its results into due to a destructure | |
| 284 | /// operation. Each of these pointers may or may not have an inferred type. | |
| 285 | destructure: struct { | |
| 286 | /// The AST node of the destructure operation itself. | |
| 287 | src_node: Ast.Node.Index, | |
| 288 | /// The pointers to store results into. | |
| 289 | components: []const DestructureComponent, | |
| 290 | }, | |
| 291 | ||
| 292 | const DestructureComponent = union(enum) { | |
| 293 | typed_ptr: PtrResultLoc, | |
| 294 | inferred_ptr: Zir.Inst.Ref, | |
| 295 | discard, | |
| 296 | }; | |
| 283 | 297 | |
| 284 | 298 | const PtrResultLoc = struct { |
| 285 | 299 | inst: Zir.Inst.Ref, |
| ... | ... | @@ -298,6 +312,12 @@ const ResultInfo = struct { |
| 298 | 312 | const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node); |
| 299 | 313 | return gz.addUnNode(.elem_type, ptr_ty, node); |
| 300 | 314 | }, |
| 315 | .destructure => |destructure| { | |
| 316 | return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ | |
| 317 | try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}), | |
| 318 | try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), | |
| 319 | }); | |
| 320 | }, | |
| 301 | 321 | } |
| 302 | 322 | |
| 303 | 323 | return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ |
| ... | ... | @@ -399,6 +419,7 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins |
| 399 | 419 | .asm_input => unreachable, |
| 400 | 420 | |
| 401 | 421 | .assign, |
| 422 | .assign_destructure, | |
| 402 | 423 | .assign_bit_and, |
| 403 | 424 | .assign_bit_or, |
| 404 | 425 | .assign_shl, |
| ... | ... | @@ -621,6 +642,13 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 621 | 642 | return rvalue(gz, ri, .void_value, node); |
| 622 | 643 | }, |
| 623 | 644 | |
| 645 | .assign_destructure => { | |
| 646 | // Note that this variant does not declare any new var/const: that | |
| 647 | // variant is handled by `blockExprStmts`. | |
| 648 | try assignDestructure(gz, scope, node); | |
| 649 | return rvalue(gz, ri, .void_value, node); | |
| 650 | }, | |
| 651 | ||
| 624 | 652 | .assign_shl => { |
| 625 | 653 | try assignShift(gz, scope, node, .shl); |
| 626 | 654 | return rvalue(gz, ri, .void_value, node); |
| ... | ... | @@ -1364,14 +1392,8 @@ fn arrayInitExpr( |
| 1364 | 1392 | |
| 1365 | 1393 | assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init. |
| 1366 | 1394 | |
| 1367 | const types: struct { | |
| 1368 | array: Zir.Inst.Ref, | |
| 1369 | elem: Zir.Inst.Ref, | |
| 1370 | } = inst: { | |
| 1371 | if (array_init.ast.type_expr == 0) break :inst .{ | |
| 1372 | .array = .none, | |
| 1373 | .elem = .none, | |
| 1374 | }; | |
| 1395 | const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: { | |
| 1396 | if (array_init.ast.type_expr == 0) break :inst .{ .none, .none }; | |
| 1375 | 1397 | |
| 1376 | 1398 | infer: { |
| 1377 | 1399 | const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer; |
| ... | ... | @@ -1386,10 +1408,7 @@ fn arrayInitExpr( |
| 1386 | 1408 | .lhs = len_inst, |
| 1387 | 1409 | .rhs = elem_type, |
| 1388 | 1410 | }); |
| 1389 | break :inst .{ | |
| 1390 | .array = array_type_inst, | |
| 1391 | .elem = elem_type, | |
| 1392 | }; | |
| 1411 | break :inst .{ array_type_inst, elem_type }; | |
| 1393 | 1412 | } else { |
| 1394 | 1413 | const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel); |
| 1395 | 1414 | const array_type_inst = try gz.addPlNode( |
| ... | ... | @@ -1401,10 +1420,7 @@ fn arrayInitExpr( |
| 1401 | 1420 | .sentinel = sentinel, |
| 1402 | 1421 | }, |
| 1403 | 1422 | ); |
| 1404 | break :inst .{ | |
| 1405 | .array = array_type_inst, | |
| 1406 | .elem = elem_type, | |
| 1407 | }; | |
| 1423 | break :inst .{ array_type_inst, elem_type }; | |
| 1408 | 1424 | } |
| 1409 | 1425 | } |
| 1410 | 1426 | } |
| ... | ... | @@ -1413,29 +1429,26 @@ fn arrayInitExpr( |
| 1413 | 1429 | .ty = array_type_inst, |
| 1414 | 1430 | .init_count = @intCast(array_init.ast.elements.len), |
| 1415 | 1431 | }); |
| 1416 | break :inst .{ | |
| 1417 | .array = array_type_inst, | |
| 1418 | .elem = .none, | |
| 1419 | }; | |
| 1432 | break :inst .{ array_type_inst, .none }; | |
| 1420 | 1433 | }; |
| 1421 | 1434 | |
| 1422 | 1435 | switch (ri.rl) { |
| 1423 | 1436 | .discard => { |
| 1424 | if (types.elem != .none) { | |
| 1425 | const elem_ri: ResultInfo = .{ .rl = .{ .ty = types.elem } }; | |
| 1437 | if (elem_ty != .none) { | |
| 1438 | const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } }; | |
| 1426 | 1439 | for (array_init.ast.elements) |elem_init| { |
| 1427 | 1440 | _ = try expr(gz, scope, elem_ri, elem_init); |
| 1428 | 1441 | } |
| 1429 | } else if (types.array != .none) { | |
| 1442 | } else if (array_ty != .none) { | |
| 1430 | 1443 | for (array_init.ast.elements, 0..) |elem_init, i| { |
| 1431 | const elem_ty = try gz.add(.{ | |
| 1444 | const this_elem_ty = try gz.add(.{ | |
| 1432 | 1445 | .tag = .elem_type_index, |
| 1433 | 1446 | .data = .{ .bin = .{ |
| 1434 | .lhs = types.array, | |
| 1447 | .lhs = array_ty, | |
| 1435 | 1448 | .rhs = @enumFromInt(i), |
| 1436 | 1449 | } }, |
| 1437 | 1450 | }); |
| 1438 | _ = try expr(gz, scope, .{ .rl = .{ .ty = elem_ty } }, elem_init); | |
| 1451 | _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init); | |
| 1439 | 1452 | } |
| 1440 | 1453 | } else { |
| 1441 | 1454 | for (array_init.ast.elements) |elem_init| { |
| ... | ... | @@ -1445,15 +1458,15 @@ fn arrayInitExpr( |
| 1445 | 1458 | return Zir.Inst.Ref.void_value; |
| 1446 | 1459 | }, |
| 1447 | 1460 | .ref => { |
| 1448 | const tag: Zir.Inst.Tag = if (types.array != .none) .array_init_ref else .array_init_anon_ref; | |
| 1449 | return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag); | |
| 1461 | const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init_ref else .array_init_anon_ref; | |
| 1462 | return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, tag); | |
| 1450 | 1463 | }, |
| 1451 | 1464 | .none => { |
| 1452 | const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon; | |
| 1453 | return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag); | |
| 1465 | const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init else .array_init_anon; | |
| 1466 | return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, tag); | |
| 1454 | 1467 | }, |
| 1455 | 1468 | .ty, .coerced_ty => |ty_inst| { |
| 1456 | const arr_ty = if (types.array != .none) types.array else blk: { | |
| 1469 | const arr_ty = if (array_ty != .none) array_ty else blk: { | |
| 1457 | 1470 | const arr_ty = try gz.addUnNode(.opt_eu_base_ty, ty_inst, node); |
| 1458 | 1471 | _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{ |
| 1459 | 1472 | .ty = arr_ty, |
| ... | ... | @@ -1461,22 +1474,49 @@ fn arrayInitExpr( |
| 1461 | 1474 | }); |
| 1462 | 1475 | break :blk arr_ty; |
| 1463 | 1476 | }; |
| 1464 | const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, arr_ty, types.elem, .array_init); | |
| 1477 | const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, arr_ty, elem_ty, .array_init); | |
| 1465 | 1478 | return rvalue(gz, ri, result, node); |
| 1466 | 1479 | }, |
| 1467 | 1480 | .ptr => |ptr_res| { |
| 1468 | return arrayInitExprRlPtr(gz, scope, node, ptr_res.inst, array_init.ast.elements, types.array); | |
| 1481 | return arrayInitExprRlPtr(gz, scope, node, ptr_res.inst, array_init.ast.elements, array_ty); | |
| 1469 | 1482 | }, |
| 1470 | 1483 | .inferred_ptr => |ptr_inst| { |
| 1471 | if (types.array == .none) { | |
| 1484 | if (array_ty == .none) { | |
| 1472 | 1485 | // We treat this case differently so that we don't get a crash when |
| 1473 | 1486 | // analyzing array_base_ptr against an alloc_inferred_mut. |
| 1474 | 1487 | // See corresponding logic in structInitExpr. |
| 1475 | 1488 | const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); |
| 1476 | 1489 | return rvalue(gz, ri, result, node); |
| 1477 | 1490 | } else { |
| 1478 | return arrayInitExprRlPtr(gz, scope, node, ptr_inst, array_init.ast.elements, types.array); | |
| 1491 | return arrayInitExprRlPtr(gz, scope, node, ptr_inst, array_init.ast.elements, array_ty); | |
| 1492 | } | |
| 1493 | }, | |
| 1494 | .destructure => |destructure| { | |
| 1495 | if (array_ty != .none) { | |
| 1496 | // We have a specific type, so there may be things like default | |
| 1497 | // field values messing with us. Do this as a standard typed | |
| 1498 | // init followed by an rvalue destructure. | |
| 1499 | const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, .array_init); | |
| 1500 | return rvalue(gz, ri, result, node); | |
| 1479 | 1501 | } |
| 1502 | // Untyped init - destructure directly into result pointers | |
| 1503 | if (array_init.ast.elements.len != destructure.components.len) { | |
| 1504 | return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{ | |
| 1505 | destructure.components.len, | |
| 1506 | array_init.ast.elements.len, | |
| 1507 | }, &.{ | |
| 1508 | try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), | |
| 1509 | }); | |
| 1510 | } | |
| 1511 | for (array_init.ast.elements, destructure.components) |elem_init, ds_comp| { | |
| 1512 | const elem_ri: ResultInfo = .{ .rl = switch (ds_comp) { | |
| 1513 | .typed_ptr => |ptr_rl| .{ .ptr = ptr_rl }, | |
| 1514 | .inferred_ptr => |ptr_inst| .{ .inferred_ptr = ptr_inst }, | |
| 1515 | .discard => .discard, | |
| 1516 | } }; | |
| 1517 | _ = try expr(gz, scope, elem_ri, elem_init); | |
| 1518 | } | |
| 1519 | return .void_value; | |
| 1480 | 1520 | }, |
| 1481 | 1521 | } |
| 1482 | 1522 | } |
| ... | ... | @@ -1707,6 +1747,23 @@ fn structInitExpr( |
| 1707 | 1747 | return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst); |
| 1708 | 1748 | } |
| 1709 | 1749 | }, |
| 1750 | .destructure => |destructure| { | |
| 1751 | if (struct_init.ast.type_expr == 0) { | |
| 1752 | // This is an untyped init, so is an actual struct, which does | |
| 1753 | // not support destructuring. | |
| 1754 | return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{ | |
| 1755 | try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), | |
| 1756 | }); | |
| 1757 | } | |
| 1758 | // You can init tuples using struct init syntax and numeric field | |
| 1759 | // names, but as with array inits, we could be bitten by default | |
| 1760 | // fields. Therefore, we do a normal typed init then an rvalue | |
| 1761 | // destructure. | |
| 1762 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); | |
| 1763 | _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); | |
| 1764 | const result = try structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); | |
| 1765 | return rvalue(gz, ri, result, node); | |
| 1766 | }, | |
| 1710 | 1767 | } |
| 1711 | 1768 | } |
| 1712 | 1769 | |
| ... | ... | @@ -1968,6 +2025,7 @@ fn restoreErrRetIndex( |
| 1968 | 2025 | // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it. |
| 1969 | 2026 | break :blk .none; |
| 1970 | 2027 | }, |
| 2028 | .destructure => return, // value must be a tuple or array, so never restore/pop | |
| 1971 | 2029 | else => result, |
| 1972 | 2030 | }, |
| 1973 | 2031 | else => .none, // always restore/pop |
| ... | ... | @@ -2340,6 +2398,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod |
| 2340 | 2398 | .simple_var_decl, |
| 2341 | 2399 | .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?), |
| 2342 | 2400 | |
| 2401 | .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator), | |
| 2402 | ||
| 2343 | 2403 | .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal), |
| 2344 | 2404 | .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error), |
| 2345 | 2405 | |
| ... | ... | @@ -2481,6 +2541,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2481 | 2541 | .elem_ptr_node, |
| 2482 | 2542 | .elem_ptr_imm, |
| 2483 | 2543 | .elem_val_node, |
| 2544 | .elem_val_imm, | |
| 2484 | 2545 | .field_ptr, |
| 2485 | 2546 | .field_ptr_init, |
| 2486 | 2547 | .field_val, |
| ... | ... | @@ -2686,6 +2747,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2686 | 2747 | .validate_array_init_ty, |
| 2687 | 2748 | .validate_struct_init_ty, |
| 2688 | 2749 | .validate_deref, |
| 2750 | .validate_destructure, | |
| 2689 | 2751 | .save_err_ret_index, |
| 2690 | 2752 | .restore_err_ret_index, |
| 2691 | 2753 | => break :b true, |
| ... | ... | @@ -3100,10 +3162,7 @@ fn varDecl( |
| 3100 | 3162 | .keyword_var => { |
| 3101 | 3163 | const is_comptime = var_decl.comptime_token != null or gz.is_comptime; |
| 3102 | 3164 | var resolve_inferred_alloc: Zir.Inst.Ref = .none; |
| 3103 | const var_data: struct { | |
| 3104 | result_info: ResultInfo, | |
| 3105 | alloc: Zir.Inst.Ref, | |
| 3106 | } = if (var_decl.ast.type_node != 0) a: { | |
| 3165 | const alloc: Zir.Inst.Ref, const result_info: ResultInfo = if (var_decl.ast.type_node != 0) a: { | |
| 3107 | 3166 | const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node); |
| 3108 | 3167 | const alloc = alloc: { |
| 3109 | 3168 | if (align_inst == .none) { |
| ... | ... | @@ -3122,7 +3181,7 @@ fn varDecl( |
| 3122 | 3181 | }); |
| 3123 | 3182 | } |
| 3124 | 3183 | }; |
| 3125 | break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .ptr = .{ .inst = alloc } } } }; | |
| 3184 | break :a .{ alloc, .{ .rl = .{ .ptr = .{ .inst = alloc } } } }; | |
| 3126 | 3185 | } else a: { |
| 3127 | 3186 | const alloc = alloc: { |
| 3128 | 3187 | if (align_inst == .none) { |
| ... | ... | @@ -3142,24 +3201,24 @@ fn varDecl( |
| 3142 | 3201 | } |
| 3143 | 3202 | }; |
| 3144 | 3203 | resolve_inferred_alloc = alloc; |
| 3145 | break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .inferred_ptr = alloc } } }; | |
| 3204 | break :a .{ alloc, .{ .rl = .{ .inferred_ptr = alloc } } }; | |
| 3146 | 3205 | }; |
| 3147 | 3206 | const prev_anon_name_strategy = gz.anon_name_strategy; |
| 3148 | 3207 | gz.anon_name_strategy = .dbg_var; |
| 3149 | _ = try reachableExprComptime(gz, scope, var_data.result_info, var_decl.ast.init_node, node, is_comptime); | |
| 3208 | _ = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, is_comptime); | |
| 3150 | 3209 | gz.anon_name_strategy = prev_anon_name_strategy; |
| 3151 | 3210 | if (resolve_inferred_alloc != .none) { |
| 3152 | 3211 | _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); |
| 3153 | 3212 | } |
| 3154 | 3213 | |
| 3155 | try gz.addDbgVar(.dbg_var_ptr, ident_name, var_data.alloc); | |
| 3214 | try gz.addDbgVar(.dbg_var_ptr, ident_name, alloc); | |
| 3156 | 3215 | |
| 3157 | 3216 | const sub_scope = try block_arena.create(Scope.LocalPtr); |
| 3158 | 3217 | sub_scope.* = .{ |
| 3159 | 3218 | .parent = scope, |
| 3160 | 3219 | .gen_zir = gz, |
| 3161 | 3220 | .name = ident_name, |
| 3162 | .ptr = var_data.alloc, | |
| 3221 | .ptr = alloc, | |
| 3163 | 3222 | .token_src = name_token, |
| 3164 | 3223 | .maybe_comptime = is_comptime, |
| 3165 | 3224 | .id_cat = .@"local variable", |
| ... | ... | @@ -3227,6 +3286,301 @@ fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!voi |
| 3227 | 3286 | } } }, rhs); |
| 3228 | 3287 | } |
| 3229 | 3288 | |
| 3289 | /// Handles destructure assignments where no LHS is a `const` or `var` decl. | |
| 3290 | fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!void { | |
| 3291 | try emitDbgNode(gz, node); | |
| 3292 | const astgen = gz.astgen; | |
| 3293 | const tree = astgen.tree; | |
| 3294 | const token_tags = tree.tokens.items(.tag); | |
| 3295 | const node_datas = tree.nodes.items(.data); | |
| 3296 | const main_tokens = tree.nodes.items(.main_token); | |
| 3297 | const node_tags = tree.nodes.items(.tag); | |
| 3298 | ||
| 3299 | const extra_index = node_datas[node].lhs; | |
| 3300 | const lhs_count = tree.extra_data[extra_index]; | |
| 3301 | const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]); | |
| 3302 | const rhs = node_datas[node].rhs; | |
| 3303 | ||
| 3304 | const maybe_comptime_token = tree.firstToken(node) - 1; | |
| 3305 | const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime; | |
| 3306 | ||
| 3307 | if (declared_comptime and gz.is_comptime) { | |
| 3308 | return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{}); | |
| 3309 | } | |
| 3310 | ||
| 3311 | // If this expression is marked comptime, we must wrap the whole thing in a comptime block. | |
| 3312 | var gz_buf: GenZir = undefined; | |
| 3313 | const inner_gz = if (declared_comptime) bs: { | |
| 3314 | gz_buf = gz.makeSubBlock(scope); | |
| 3315 | gz_buf.is_comptime = true; | |
| 3316 | break :bs &gz_buf; | |
| 3317 | } else gz; | |
| 3318 | defer if (declared_comptime) inner_gz.unstack(); | |
| 3319 | ||
| 3320 | const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len); | |
| 3321 | for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| { | |
| 3322 | if (node_tags[lhs_node] == .identifier) { | |
| 3323 | // This intentionally does not support `@"_"` syntax. | |
| 3324 | const ident_name = tree.tokenSlice(main_tokens[lhs_node]); | |
| 3325 | if (mem.eql(u8, ident_name, "_")) { | |
| 3326 | lhs_rl.* = .discard; | |
| 3327 | continue; | |
| 3328 | } | |
| 3329 | } | |
| 3330 | lhs_rl.* = .{ .typed_ptr = .{ | |
| 3331 | .inst = try lvalExpr(inner_gz, scope, lhs_node), | |
| 3332 | .src_node = lhs_node, | |
| 3333 | } }; | |
| 3334 | } | |
| 3335 | ||
| 3336 | const ri: ResultInfo = .{ .rl = .{ .destructure = .{ | |
| 3337 | .src_node = node, | |
| 3338 | .components = rl_components, | |
| 3339 | } } }; | |
| 3340 | ||
| 3341 | _ = try expr(inner_gz, scope, ri, rhs); | |
| 3342 | ||
| 3343 | if (declared_comptime) { | |
| 3344 | const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); | |
| 3345 | _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value); | |
| 3346 | try inner_gz.setBlockBody(comptime_block_inst); | |
| 3347 | try gz.instructions.append(gz.astgen.gpa, comptime_block_inst); | |
| 3348 | } | |
| 3349 | } | |
| 3350 | ||
| 3351 | /// Handles destructure assignments where the LHS may contain `const` or `var` decls. | |
| 3352 | fn assignDestructureMaybeDecls( | |
| 3353 | gz: *GenZir, | |
| 3354 | scope: *Scope, | |
| 3355 | node: Ast.Node.Index, | |
| 3356 | block_arena: Allocator, | |
| 3357 | ) InnerError!*Scope { | |
| 3358 | try emitDbgNode(gz, node); | |
| 3359 | const astgen = gz.astgen; | |
| 3360 | const tree = astgen.tree; | |
| 3361 | const token_tags = tree.tokens.items(.tag); | |
| 3362 | const node_datas = tree.nodes.items(.data); | |
| 3363 | const main_tokens = tree.nodes.items(.main_token); | |
| 3364 | const node_tags = tree.nodes.items(.tag); | |
| 3365 | ||
| 3366 | const extra_index = node_datas[node].lhs; | |
| 3367 | const lhs_count = tree.extra_data[extra_index]; | |
| 3368 | const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]); | |
| 3369 | const rhs = node_datas[node].rhs; | |
| 3370 | ||
| 3371 | const maybe_comptime_token = tree.firstToken(node) - 1; | |
| 3372 | const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime; | |
| 3373 | if (declared_comptime and gz.is_comptime) { | |
| 3374 | return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{}); | |
| 3375 | } | |
| 3376 | ||
| 3377 | const is_comptime = declared_comptime or gz.is_comptime; | |
| 3378 | const rhs_is_comptime = tree.nodes.items(.tag)[rhs] == .@"comptime"; | |
| 3379 | ||
| 3380 | // When declaring consts via a destructure, we always use a result pointer. | |
| 3381 | // This avoids the need to create tuple types, and is also likely easier to | |
| 3382 | // optimize, since it's a bit tricky for the optimizer to "split up" the | |
| 3383 | // value into individual pointer writes down the line. | |
| 3384 | ||
| 3385 | // We know this rl information won't live past the evaluation of this | |
| 3386 | // expression, so it may as well go in the block arena. | |
| 3387 | const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len); | |
| 3388 | var any_non_const_lhs = false; | |
| 3389 | var any_lvalue_expr = false; | |
| 3390 | for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| { | |
| 3391 | switch (node_tags[lhs_node]) { | |
| 3392 | .identifier => { | |
| 3393 | // This intentionally does not support `@"_"` syntax. | |
| 3394 | const ident_name = tree.tokenSlice(main_tokens[lhs_node]); | |
| 3395 | if (mem.eql(u8, ident_name, "_")) { | |
| 3396 | any_non_const_lhs = true; | |
| 3397 | lhs_rl.* = .discard; | |
| 3398 | continue; | |
| 3399 | } | |
| 3400 | }, | |
| 3401 | .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => { | |
| 3402 | const full = tree.fullVarDecl(lhs_node).?; | |
| 3403 | ||
| 3404 | const name_token = full.ast.mut_token + 1; | |
| 3405 | const ident_name_raw = tree.tokenSlice(name_token); | |
| 3406 | if (mem.eql(u8, ident_name_raw, "_")) { | |
| 3407 | return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{}); | |
| 3408 | } | |
| 3409 | ||
| 3410 | // We detect shadowing in the second pass over these, while we're creating scopes. | |
| 3411 | ||
| 3412 | if (full.ast.addrspace_node != 0) { | |
| 3413 | return astgen.failTok(main_tokens[full.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw}); | |
| 3414 | } | |
| 3415 | if (full.ast.section_node != 0) { | |
| 3416 | return astgen.failTok(main_tokens[full.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw}); | |
| 3417 | } | |
| 3418 | ||
| 3419 | const is_const = switch (token_tags[full.ast.mut_token]) { | |
| 3420 | .keyword_var => false, | |
| 3421 | .keyword_const => true, | |
| 3422 | else => unreachable, | |
| 3423 | }; | |
| 3424 | if (!is_const) any_non_const_lhs = true; | |
| 3425 | ||
| 3426 | // We also mark `const`s as comptime if the RHS is definitely comptime-known. | |
| 3427 | const this_lhs_comptime = is_comptime or (is_const and rhs_is_comptime); | |
| 3428 | ||
| 3429 | const align_inst: Zir.Inst.Ref = if (full.ast.align_node != 0) | |
| 3430 | try expr(gz, scope, align_ri, full.ast.align_node) | |
| 3431 | else | |
| 3432 | .none; | |
| 3433 | ||
| 3434 | if (full.ast.type_node != 0) { | |
| 3435 | // Typed alloc | |
| 3436 | const type_inst = try typeExpr(gz, scope, full.ast.type_node); | |
| 3437 | const ptr = if (align_inst == .none) ptr: { | |
| 3438 | const tag: Zir.Inst.Tag = if (is_const) | |
| 3439 | .alloc | |
| 3440 | else if (this_lhs_comptime) | |
| 3441 | .alloc_comptime_mut | |
| 3442 | else | |
| 3443 | .alloc_mut; | |
| 3444 | break :ptr try gz.addUnNode(tag, type_inst, node); | |
| 3445 | } else try gz.addAllocExtended(.{ | |
| 3446 | .node = node, | |
| 3447 | .type_inst = type_inst, | |
| 3448 | .align_inst = align_inst, | |
| 3449 | .is_const = is_const, | |
| 3450 | .is_comptime = this_lhs_comptime, | |
| 3451 | }); | |
| 3452 | lhs_rl.* = .{ .typed_ptr = .{ .inst = ptr } }; | |
| 3453 | } else { | |
| 3454 | // Inferred alloc | |
| 3455 | const ptr = if (align_inst == .none) ptr: { | |
| 3456 | const tag: Zir.Inst.Tag = if (is_const) tag: { | |
| 3457 | break :tag if (this_lhs_comptime) .alloc_inferred_comptime else .alloc_inferred; | |
| 3458 | } else tag: { | |
| 3459 | break :tag if (this_lhs_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut; | |
| 3460 | }; | |
| 3461 | break :ptr try gz.addNode(tag, node); | |
| 3462 | } else try gz.addAllocExtended(.{ | |
| 3463 | .node = node, | |
| 3464 | .type_inst = .none, | |
| 3465 | .align_inst = align_inst, | |
| 3466 | .is_const = is_const, | |
| 3467 | .is_comptime = this_lhs_comptime, | |
| 3468 | }); | |
| 3469 | lhs_rl.* = .{ .inferred_ptr = ptr }; | |
| 3470 | } | |
| 3471 | ||
| 3472 | continue; | |
| 3473 | }, | |
| 3474 | else => {}, | |
| 3475 | } | |
| 3476 | // This LHS is just an lvalue expression. | |
| 3477 | // We will fill in its result pointer later, inside a comptime block. | |
| 3478 | any_non_const_lhs = true; | |
| 3479 | any_lvalue_expr = true; | |
| 3480 | lhs_rl.* = .{ .typed_ptr = .{ | |
| 3481 | .inst = undefined, | |
| 3482 | .src_node = lhs_node, | |
| 3483 | } }; | |
| 3484 | } | |
| 3485 | ||
| 3486 | if (declared_comptime and !any_non_const_lhs) { | |
| 3487 | try astgen.appendErrorTok(maybe_comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); | |
| 3488 | } | |
| 3489 | ||
| 3490 | // If this expression is marked comptime, we must wrap it in a comptime block. | |
| 3491 | var gz_buf: GenZir = undefined; | |
| 3492 | const inner_gz = if (declared_comptime) bs: { | |
| 3493 | gz_buf = gz.makeSubBlock(scope); | |
| 3494 | gz_buf.is_comptime = true; | |
| 3495 | break :bs &gz_buf; | |
| 3496 | } else gz; | |
| 3497 | defer if (declared_comptime) inner_gz.unstack(); | |
| 3498 | ||
| 3499 | if (any_lvalue_expr) { | |
| 3500 | // At least one LHS was an lvalue expr. Iterate again in order to | |
| 3501 | // evaluate the lvalues from within the possible block_comptime. | |
| 3502 | for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| { | |
| 3503 | if (lhs_rl.* != .typed_ptr) continue; | |
| 3504 | switch (node_tags[lhs_node]) { | |
| 3505 | .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue, | |
| 3506 | else => {}, | |
| 3507 | } | |
| 3508 | lhs_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, lhs_node); | |
| 3509 | } | |
| 3510 | } | |
| 3511 | ||
| 3512 | // We can't give a reasonable anon name strategy for destructured inits, so | |
| 3513 | // leave it at its default of `.anon`. | |
| 3514 | _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{ | |
| 3515 | .src_node = node, | |
| 3516 | .components = rl_components, | |
| 3517 | } } }, rhs, node); | |
| 3518 | ||
| 3519 | if (declared_comptime) { | |
| 3520 | // Finish the block_comptime. Inferred alloc resolution etc will occur | |
| 3521 | // in the parent block. | |
| 3522 | const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node); | |
| 3523 | _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value); | |
| 3524 | try inner_gz.setBlockBody(comptime_block_inst); | |
| 3525 | try gz.instructions.append(gz.astgen.gpa, comptime_block_inst); | |
| 3526 | } | |
| 3527 | ||
| 3528 | // Now, iterate over the LHS exprs to construct any new scopes. | |
| 3529 | // If there were any inferred allocations, resolve them. | |
| 3530 | // If there were any `const` decls, make the pointer constant. | |
| 3531 | var cur_scope = scope; | |
| 3532 | for (rl_components, lhs_nodes) |lhs_rl, lhs_node| { | |
| 3533 | switch (node_tags[lhs_node]) { | |
| 3534 | .local_var_decl, .simple_var_decl, .aligned_var_decl => {}, | |
| 3535 | else => continue, // We were mutating an existing lvalue - nothing to do | |
| 3536 | } | |
| 3537 | const full = tree.fullVarDecl(lhs_node).?; | |
| 3538 | const raw_ptr = switch (lhs_rl) { | |
| 3539 | .discard => unreachable, | |
| 3540 | .typed_ptr => |typed_ptr| typed_ptr.inst, | |
| 3541 | .inferred_ptr => |ptr_inst| ptr_inst, | |
| 3542 | }; | |
| 3543 | // If the alloc was inferred, resolve it. | |
| 3544 | if (full.ast.type_node == 0) { | |
| 3545 | _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, lhs_node); | |
| 3546 | } | |
| 3547 | const is_const = switch (token_tags[full.ast.mut_token]) { | |
| 3548 | .keyword_var => false, | |
| 3549 | .keyword_const => true, | |
| 3550 | else => unreachable, | |
| 3551 | }; | |
| 3552 | // If the alloc was const, make it const. | |
| 3553 | const var_ptr = if (is_const) make_const: { | |
| 3554 | break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node); | |
| 3555 | } else raw_ptr; | |
| 3556 | const name_token = full.ast.mut_token + 1; | |
| 3557 | const ident_name_raw = tree.tokenSlice(name_token); | |
| 3558 | const ident_name = try astgen.identAsString(name_token); | |
| 3559 | try astgen.detectLocalShadowing( | |
| 3560 | cur_scope, | |
| 3561 | ident_name, | |
| 3562 | name_token, | |
| 3563 | ident_name_raw, | |
| 3564 | if (is_const) .@"local constant" else .@"local variable", | |
| 3565 | ); | |
| 3566 | try gz.addDbgVar(.dbg_var_ptr, ident_name, var_ptr); | |
| 3567 | // Finally, create the scope. | |
| 3568 | const sub_scope = try block_arena.create(Scope.LocalPtr); | |
| 3569 | sub_scope.* = .{ | |
| 3570 | .parent = cur_scope, | |
| 3571 | .gen_zir = gz, | |
| 3572 | .name = ident_name, | |
| 3573 | .ptr = var_ptr, | |
| 3574 | .token_src = name_token, | |
| 3575 | .maybe_comptime = is_const or is_comptime, | |
| 3576 | .id_cat = if (is_const) .@"local constant" else .@"local variable", | |
| 3577 | }; | |
| 3578 | cur_scope = &sub_scope.base; | |
| 3579 | } | |
| 3580 | ||
| 3581 | return cur_scope; | |
| 3582 | } | |
| 3583 | ||
| 3230 | 3584 | fn assignOp( |
| 3231 | 3585 | gz: *GenZir, |
| 3232 | 3586 | scope: *Scope, |
| ... | ... | @@ -9059,6 +9413,7 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev |
| 9059 | 9413 | .array_cat, |
| 9060 | 9414 | .array_mult, |
| 9061 | 9415 | .assign, |
| 9416 | .assign_destructure, | |
| 9062 | 9417 | .assign_bit_and, |
| 9063 | 9418 | .assign_bit_or, |
| 9064 | 9419 | .assign_shl, |
| ... | ... | @@ -9237,6 +9592,7 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In |
| 9237 | 9592 | .array_cat, |
| 9238 | 9593 | .array_mult, |
| 9239 | 9594 | .assign, |
| 9595 | .assign_destructure, | |
| 9240 | 9596 | .assign_bit_and, |
| 9241 | 9597 | .assign_bit_or, |
| 9242 | 9598 | .assign_shl, |
| ... | ... | @@ -9483,6 +9839,7 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { |
| 9483 | 9839 | .array_cat, |
| 9484 | 9840 | .array_mult, |
| 9485 | 9841 | .assign, |
| 9842 | .assign_destructure, | |
| 9486 | 9843 | .assign_bit_and, |
| 9487 | 9844 | .assign_bit_or, |
| 9488 | 9845 | .assign_shl, |
| ... | ... | @@ -9830,6 +10187,37 @@ fn rvalue( |
| 9830 | 10187 | _ = try gz.addBin(.store_to_inferred_ptr, alloc, result); |
| 9831 | 10188 | return .void_value; |
| 9832 | 10189 | }, |
| 10190 | .destructure => |destructure| { | |
| 10191 | const components = destructure.components; | |
| 10192 | _ = try gz.addPlNode(.validate_destructure, src_node, Zir.Inst.ValidateDestructure{ | |
| 10193 | .operand = result, | |
| 10194 | .destructure_node = gz.nodeIndexToRelative(destructure.src_node), | |
| 10195 | .expect_len = @intCast(components.len), | |
| 10196 | }); | |
| 10197 | for (components, 0..) |component, i| { | |
| 10198 | if (component == .discard) continue; | |
| 10199 | const elem_val = try gz.add(.{ | |
| 10200 | .tag = .elem_val_imm, | |
| 10201 | .data = .{ .elem_val_imm = .{ | |
| 10202 | .operand = result, | |
| 10203 | .idx = @intCast(i), | |
| 10204 | } }, | |
| 10205 | }); | |
| 10206 | switch (component) { | |
| 10207 | .typed_ptr => |ptr_res| { | |
| 10208 | _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{ | |
| 10209 | .lhs = ptr_res.inst, | |
| 10210 | .rhs = elem_val, | |
| 10211 | }); | |
| 10212 | }, | |
| 10213 | .inferred_ptr => |ptr_inst| { | |
| 10214 | _ = try gz.addBin(.store_to_inferred_ptr, ptr_inst, elem_val); | |
| 10215 | }, | |
| 10216 | .discard => unreachable, | |
| 10217 | } | |
| 10218 | } | |
| 10219 | return .void_value; | |
| 10220 | }, | |
| 9833 | 10221 | } |
| 9834 | 10222 | } |
| 9835 | 10223 |
src/AstRlAnnotate.zig+10| ... | ... | @@ -203,6 +203,16 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI |
| 203 | 203 | else => unreachable, |
| 204 | 204 | } |
| 205 | 205 | }, |
| 206 | .assign_destructure => { | |
| 207 | const lhs_count = tree.extra_data[node_datas[node].lhs]; | |
| 208 | const all_lhs = tree.extra_data[node_datas[node].lhs + 1 ..][0..lhs_count]; | |
| 209 | for (all_lhs) |lhs| { | |
| 210 | _ = try astrl.expr(lhs, block, ResultInfo.none); | |
| 211 | } | |
| 212 | // We don't need to gather any meaningful data here, because destructures always use RLS | |
| 213 | _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none); | |
| 214 | return false; | |
| 215 | }, | |
| 206 | 216 | .assign => { |
| 207 | 217 | _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none); |
| 208 | 218 | _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.typed_ptr); |
src/Sema.zig+115-62| ... | ... | @@ -1018,6 +1018,7 @@ fn analyzeBodyInner( |
| 1018 | 1018 | .elem_ptr_imm => try sema.zirElemPtrImm(block, inst), |
| 1019 | 1019 | .elem_val => try sema.zirElemVal(block, inst), |
| 1020 | 1020 | .elem_val_node => try sema.zirElemValNode(block, inst), |
| 1021 | .elem_val_imm => try sema.zirElemValImm(block, inst), | |
| 1021 | 1022 | .elem_type_index => try sema.zirElemTypeIndex(block, inst), |
| 1022 | 1023 | .elem_type => try sema.zirElemType(block, inst), |
| 1023 | 1024 | .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), |
| ... | ... | @@ -1379,6 +1380,11 @@ fn analyzeBodyInner( |
| 1379 | 1380 | i += 1; |
| 1380 | 1381 | continue; |
| 1381 | 1382 | }, |
| 1383 | .validate_destructure => { | |
| 1384 | try sema.zirValidateDestructure(block, inst); | |
| 1385 | i += 1; | |
| 1386 | continue; | |
| 1387 | }, | |
| 1382 | 1388 | .@"export" => { |
| 1383 | 1389 | try sema.zirExport(block, inst); |
| 1384 | 1390 | i += 1; |
| ... | ... | @@ -3780,6 +3786,21 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro |
| 3780 | 3786 | return sema.analyzeDeclRef(try anon_decl.finish(elem_ty, store_val, ptr_info.flags.alignment)); |
| 3781 | 3787 | } |
| 3782 | 3788 | |
| 3789 | // If this is already a comptime-mutable allocation, we don't want to emit an error - the stores | |
| 3790 | // were already performed at comptime! Just make the pointer constant as normal. | |
| 3791 | implicit_ct: { | |
| 3792 | const ptr_val = try sema.resolveMaybeUndefVal(alloc) orelse break :implicit_ct; | |
| 3793 | if (ptr_val.isComptimeMutablePtr(mod)) break :implicit_ct; | |
| 3794 | return sema.makePtrConst(block, alloc); | |
| 3795 | } | |
| 3796 | ||
| 3797 | if (try sema.typeRequiresComptime(elem_ty)) { | |
| 3798 | // The value was initialized through RLS, so we didn't detect the runtime condition earlier. | |
| 3799 | // TODO: source location of runtime control flow | |
| 3800 | const init_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | |
| 3801 | return sema.fail(block, init_src, "value with comptime-only type '{}' depends on runtime control flow", .{elem_ty.fmt(mod)}); | |
| 3802 | } | |
| 3803 | ||
| 3783 | 3804 | return sema.makePtrConst(block, alloc); |
| 3784 | 3805 | } |
| 3785 | 3806 | |
| ... | ... | @@ -5163,6 +5184,43 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr |
| 5163 | 5184 | } |
| 5164 | 5185 | } |
| 5165 | 5186 | |
| 5187 | fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | |
| 5188 | const mod = sema.mod; | |
| 5189 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 5190 | const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data; | |
| 5191 | const src = inst_data.src(); | |
| 5192 | const destructure_src = LazySrcLoc.nodeOffset(extra.destructure_node); | |
| 5193 | const operand = try sema.resolveInst(extra.operand); | |
| 5194 | const operand_ty = sema.typeOf(operand); | |
| 5195 | ||
| 5196 | const can_destructure = switch (operand_ty.zigTypeTag(mod)) { | |
| 5197 | .Array => true, | |
| 5198 | .Struct => operand_ty.isTuple(mod), | |
| 5199 | else => false, | |
| 5200 | }; | |
| 5201 | ||
| 5202 | if (!can_destructure) { | |
| 5203 | return sema.failWithOwnedErrorMsg(block, msg: { | |
| 5204 | const msg = try sema.errMsg(block, src, "type '{}' cannot be destructured", .{operand_ty.fmt(mod)}); | |
| 5205 | errdefer msg.destroy(sema.gpa); | |
| 5206 | try sema.errNote(block, destructure_src, msg, "result destructured here", .{}); | |
| 5207 | break :msg msg; | |
| 5208 | }); | |
| 5209 | } | |
| 5210 | ||
| 5211 | if (operand_ty.arrayLen(mod) != extra.expect_len) { | |
| 5212 | return sema.failWithOwnedErrorMsg(block, msg: { | |
| 5213 | const msg = try sema.errMsg(block, src, "expected {} elements for destructure, found {}", .{ | |
| 5214 | extra.expect_len, | |
| 5215 | operand_ty.arrayLen(mod), | |
| 5216 | }); | |
| 5217 | errdefer msg.destroy(sema.gpa); | |
| 5218 | try sema.errNote(block, destructure_src, msg, "result destructured here", .{}); | |
| 5219 | break :msg msg; | |
| 5220 | }); | |
| 5221 | } | |
| 5222 | } | |
| 5223 | ||
| 5166 | 5224 | fn failWithBadMemberAccess( |
| 5167 | 5225 | sema: *Sema, |
| 5168 | 5226 | block: *Block, |
| ... | ... | @@ -10289,6 +10347,17 @@ fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 10289 | 10347 | return sema.elemVal(block, src, array, elem_index, elem_index_src, true); |
| 10290 | 10348 | } |
| 10291 | 10349 | |
| 10350 | fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 10351 | const tracy = trace(@src()); | |
| 10352 | defer tracy.end(); | |
| 10353 | ||
| 10354 | const mod = sema.mod; | |
| 10355 | const inst_data = sema.code.instructions.items(.data)[inst].elem_val_imm; | |
| 10356 | const array = try sema.resolveInst(inst_data.operand); | |
| 10357 | const elem_index = try mod.intRef(Type.usize, inst_data.idx); | |
| 10358 | return sema.elemVal(block, .unneeded, array, elem_index, .unneeded, false); | |
| 10359 | } | |
| 10360 | ||
| 10292 | 10361 | fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 10293 | 10362 | const tracy = trace(@src()); |
| 10294 | 10363 | defer tracy.end(); |
| ... | ... | @@ -11023,17 +11092,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11023 | 11092 | const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset }; |
| 11024 | 11093 | const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); |
| 11025 | 11094 | |
| 11026 | const raw_operand: struct { val: Air.Inst.Ref, ptr: Air.Inst.Ref } = blk: { | |
| 11095 | const raw_operand_val: Air.Inst.Ref, const raw_operand_ptr: Air.Inst.Ref = blk: { | |
| 11027 | 11096 | const maybe_ptr = try sema.resolveInst(extra.data.operand); |
| 11028 | 11097 | if (operand_is_ref) { |
| 11029 | 11098 | const val = try sema.analyzeLoad(block, src, maybe_ptr, operand_src); |
| 11030 | break :blk .{ .val = val, .ptr = maybe_ptr }; | |
| 11099 | break :blk .{ val, maybe_ptr }; | |
| 11031 | 11100 | } else { |
| 11032 | break :blk .{ .val = maybe_ptr, .ptr = undefined }; | |
| 11101 | break :blk .{ maybe_ptr, undefined }; | |
| 11033 | 11102 | } |
| 11034 | 11103 | }; |
| 11035 | 11104 | |
| 11036 | const operand = try sema.switchCond(block, operand_src, raw_operand.val); | |
| 11105 | const operand = try sema.switchCond(block, operand_src, raw_operand_val); | |
| 11037 | 11106 | |
| 11038 | 11107 | // AstGen guarantees that the instruction immediately preceding |
| 11039 | 11108 | // switch_block(_ref) is a dbg_stmt |
| ... | ... | @@ -11091,7 +11160,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11091 | 11160 | }, |
| 11092 | 11161 | }; |
| 11093 | 11162 | |
| 11094 | const maybe_union_ty = sema.typeOf(raw_operand.val); | |
| 11163 | const maybe_union_ty = sema.typeOf(raw_operand_val); | |
| 11095 | 11164 | const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union; |
| 11096 | 11165 | |
| 11097 | 11166 | // Duplicate checking variables later also used for `inline else`. |
| ... | ... | @@ -11642,8 +11711,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11642 | 11711 | const spa: SwitchProngAnalysis = .{ |
| 11643 | 11712 | .sema = sema, |
| 11644 | 11713 | .parent_block = block, |
| 11645 | .operand = raw_operand.val, | |
| 11646 | .operand_ptr = raw_operand.ptr, | |
| 11714 | .operand = raw_operand_val, | |
| 11715 | .operand_ptr = raw_operand_ptr, | |
| 11647 | 11716 | .cond = operand, |
| 11648 | 11717 | .else_error_ty = else_error_ty, |
| 11649 | 11718 | .switch_block_inst = inst, |
| ... | ... | @@ -15431,11 +15500,7 @@ fn analyzeArithmetic( |
| 15431 | 15500 | |
| 15432 | 15501 | const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs); |
| 15433 | 15502 | const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(casted_rhs); |
| 15434 | const rs: struct { | |
| 15435 | src: LazySrcLoc, | |
| 15436 | air_tag: Air.Inst.Tag, | |
| 15437 | air_tag_safe: Air.Inst.Tag, | |
| 15438 | } = rs: { | |
| 15503 | const runtime_src: LazySrcLoc, const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag = rs: { | |
| 15439 | 15504 | switch (zir_tag) { |
| 15440 | 15505 | .add, .add_unsafe => { |
| 15441 | 15506 | // For integers:intAddSat |
| ... | ... | @@ -15482,8 +15547,8 @@ fn analyzeArithmetic( |
| 15482 | 15547 | } else { |
| 15483 | 15548 | return Air.internedToRef((try Value.floatAdd(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern()); |
| 15484 | 15549 | } |
| 15485 | } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .add_safe }; | |
| 15486 | } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .add_safe }; | |
| 15550 | } else break :rs .{ rhs_src, air_tag, .add_safe }; | |
| 15551 | } else break :rs .{ lhs_src, air_tag, .add_safe }; | |
| 15487 | 15552 | }, |
| 15488 | 15553 | .addwrap => { |
| 15489 | 15554 | // Integers only; floats are checked above. |
| ... | ... | @@ -15503,8 +15568,8 @@ fn analyzeArithmetic( |
| 15503 | 15568 | } |
| 15504 | 15569 | if (maybe_lhs_val) |lhs_val| { |
| 15505 | 15570 | return Air.internedToRef((try sema.numberAddWrapScalar(lhs_val, rhs_val, resolved_type)).toIntern()); |
| 15506 | } else break :rs .{ .src = lhs_src, .air_tag = .add_wrap, .air_tag_safe = .add_wrap }; | |
| 15507 | } else break :rs .{ .src = rhs_src, .air_tag = .add_wrap, .air_tag_safe = .add_wrap }; | |
| 15571 | } else break :rs .{ lhs_src, .add_wrap, .add_wrap }; | |
| 15572 | } else break :rs .{ rhs_src, .add_wrap, .add_wrap }; | |
| 15508 | 15573 | }, |
| 15509 | 15574 | .add_sat => { |
| 15510 | 15575 | // Integers only; floats are checked above. |
| ... | ... | @@ -15530,14 +15595,14 @@ fn analyzeArithmetic( |
| 15530 | 15595 | |
| 15531 | 15596 | return Air.internedToRef(val.toIntern()); |
| 15532 | 15597 | } else break :rs .{ |
| 15533 | .src = lhs_src, | |
| 15534 | .air_tag = .add_sat, | |
| 15535 | .air_tag_safe = .add_sat, | |
| 15598 | lhs_src, | |
| 15599 | .add_sat, | |
| 15600 | .add_sat, | |
| 15536 | 15601 | }; |
| 15537 | 15602 | } else break :rs .{ |
| 15538 | .src = rhs_src, | |
| 15539 | .air_tag = .add_sat, | |
| 15540 | .air_tag_safe = .add_sat, | |
| 15603 | rhs_src, | |
| 15604 | .add_sat, | |
| 15605 | .add_sat, | |
| 15541 | 15606 | }; |
| 15542 | 15607 | }, |
| 15543 | 15608 | .sub => { |
| ... | ... | @@ -15580,8 +15645,8 @@ fn analyzeArithmetic( |
| 15580 | 15645 | } else { |
| 15581 | 15646 | return Air.internedToRef((try Value.floatSub(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern()); |
| 15582 | 15647 | } |
| 15583 | } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .sub_safe }; | |
| 15584 | } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .sub_safe }; | |
| 15648 | } else break :rs .{ rhs_src, air_tag, .sub_safe }; | |
| 15649 | } else break :rs .{ lhs_src, air_tag, .sub_safe }; | |
| 15585 | 15650 | }, |
| 15586 | 15651 | .subwrap => { |
| 15587 | 15652 | // Integers only; floats are checked above. |
| ... | ... | @@ -15601,8 +15666,8 @@ fn analyzeArithmetic( |
| 15601 | 15666 | } |
| 15602 | 15667 | if (maybe_rhs_val) |rhs_val| { |
| 15603 | 15668 | return Air.internedToRef((try sema.numberSubWrapScalar(lhs_val, rhs_val, resolved_type)).toIntern()); |
| 15604 | } else break :rs .{ .src = rhs_src, .air_tag = .sub_wrap, .air_tag_safe = .sub_wrap }; | |
| 15605 | } else break :rs .{ .src = lhs_src, .air_tag = .sub_wrap, .air_tag_safe = .sub_wrap }; | |
| 15669 | } else break :rs .{ rhs_src, .sub_wrap, .sub_wrap }; | |
| 15670 | } else break :rs .{ lhs_src, .sub_wrap, .sub_wrap }; | |
| 15606 | 15671 | }, |
| 15607 | 15672 | .sub_sat => { |
| 15608 | 15673 | // Integers only; floats are checked above. |
| ... | ... | @@ -15627,8 +15692,8 @@ fn analyzeArithmetic( |
| 15627 | 15692 | try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod); |
| 15628 | 15693 | |
| 15629 | 15694 | return Air.internedToRef(val.toIntern()); |
| 15630 | } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat, .air_tag_safe = .sub_sat }; | |
| 15631 | } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat, .air_tag_safe = .sub_sat }; | |
| 15695 | } else break :rs .{ rhs_src, .sub_sat, .sub_sat }; | |
| 15696 | } else break :rs .{ lhs_src, .sub_sat, .sub_sat }; | |
| 15632 | 15697 | }, |
| 15633 | 15698 | .mul => { |
| 15634 | 15699 | // For integers: |
| ... | ... | @@ -15720,8 +15785,8 @@ fn analyzeArithmetic( |
| 15720 | 15785 | } else { |
| 15721 | 15786 | return Air.internedToRef((try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, mod)).toIntern()); |
| 15722 | 15787 | } |
| 15723 | } else break :rs .{ .src = lhs_src, .air_tag = air_tag, .air_tag_safe = .mul_safe }; | |
| 15724 | } else break :rs .{ .src = rhs_src, .air_tag = air_tag, .air_tag_safe = .mul_safe }; | |
| 15788 | } else break :rs .{ lhs_src, air_tag, .mul_safe }; | |
| 15789 | } else break :rs .{ rhs_src, air_tag, .mul_safe }; | |
| 15725 | 15790 | }, |
| 15726 | 15791 | .mulwrap => { |
| 15727 | 15792 | // Integers only; floats are handled above. |
| ... | ... | @@ -15765,8 +15830,8 @@ fn analyzeArithmetic( |
| 15765 | 15830 | return mod.undefRef(resolved_type); |
| 15766 | 15831 | } |
| 15767 | 15832 | return Air.internedToRef((try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, mod)).toIntern()); |
| 15768 | } else break :rs .{ .src = lhs_src, .air_tag = .mul_wrap, .air_tag_safe = .mul_wrap }; | |
| 15769 | } else break :rs .{ .src = rhs_src, .air_tag = .mul_wrap, .air_tag_safe = .mul_wrap }; | |
| 15833 | } else break :rs .{ lhs_src, .mul_wrap, .mul_wrap }; | |
| 15834 | } else break :rs .{ rhs_src, .mul_wrap, .mul_wrap }; | |
| 15770 | 15835 | }, |
| 15771 | 15836 | .mul_sat => { |
| 15772 | 15837 | // Integers only; floats are checked above. |
| ... | ... | @@ -15816,20 +15881,20 @@ fn analyzeArithmetic( |
| 15816 | 15881 | try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, mod); |
| 15817 | 15882 | |
| 15818 | 15883 | return Air.internedToRef(val.toIntern()); |
| 15819 | } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat, .air_tag_safe = .mul_sat }; | |
| 15820 | } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat, .air_tag_safe = .mul_sat }; | |
| 15884 | } else break :rs .{ lhs_src, .mul_sat, .mul_sat }; | |
| 15885 | } else break :rs .{ rhs_src, .mul_sat, .mul_sat }; | |
| 15821 | 15886 | }, |
| 15822 | 15887 | else => unreachable, |
| 15823 | 15888 | } |
| 15824 | 15889 | }; |
| 15825 | 15890 | |
| 15826 | try sema.requireRuntimeBlock(block, src, rs.src); | |
| 15891 | try sema.requireRuntimeBlock(block, src, runtime_src); | |
| 15827 | 15892 | if (block.wantSafety() and want_safety and scalar_tag == .Int) { |
| 15828 | 15893 | if (mod.backendSupportsFeature(.safety_checked_instructions)) { |
| 15829 | 15894 | _ = try sema.preparePanicId(block, .integer_overflow); |
| 15830 | return block.addBinOp(rs.air_tag_safe, casted_lhs, casted_rhs); | |
| 15895 | return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs); | |
| 15831 | 15896 | } else { |
| 15832 | const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) { | |
| 15897 | const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) { | |
| 15833 | 15898 | .add => .add_with_overflow, |
| 15834 | 15899 | .sub => .sub_with_overflow, |
| 15835 | 15900 | .mul => .mul_with_overflow, |
| ... | ... | @@ -15866,7 +15931,7 @@ fn analyzeArithmetic( |
| 15866 | 15931 | } |
| 15867 | 15932 | } |
| 15868 | 15933 | } |
| 15869 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); | |
| 15934 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); | |
| 15870 | 15935 | } |
| 15871 | 15936 | |
| 15872 | 15937 | fn analyzePtrArithmetic( |
| ... | ... | @@ -32276,16 +32341,10 @@ fn compareIntsOnlyPossibleResult( |
| 32276 | 32341 | |
| 32277 | 32342 | // For any other comparison, we need to know if the LHS value is |
| 32278 | 32343 | // equal to the maximum or minimum possible value of the RHS type. |
| 32279 | const edge: struct { min: bool, max: bool } = edge: { | |
| 32280 | if (is_zero and rhs_info.signedness == .unsigned) break :edge .{ | |
| 32281 | .min = true, | |
| 32282 | .max = false, | |
| 32283 | }; | |
| 32344 | const is_min, const is_max = edge: { | |
| 32345 | if (is_zero and rhs_info.signedness == .unsigned) break :edge .{ true, false }; | |
| 32284 | 32346 | |
| 32285 | if (req_bits != rhs_info.bits) break :edge .{ | |
| 32286 | .min = false, | |
| 32287 | .max = false, | |
| 32288 | }; | |
| 32347 | if (req_bits != rhs_info.bits) break :edge .{ false, false }; | |
| 32289 | 32348 | |
| 32290 | 32349 | const ty = try mod.intType( |
| 32291 | 32350 | if (is_negative) .signed else .unsigned, |
| ... | ... | @@ -32294,24 +32353,18 @@ fn compareIntsOnlyPossibleResult( |
| 32294 | 32353 | const pop_count = lhs_val.popCount(ty, mod); |
| 32295 | 32354 | |
| 32296 | 32355 | if (is_negative) { |
| 32297 | break :edge .{ | |
| 32298 | .min = pop_count == 1, | |
| 32299 | .max = false, | |
| 32300 | }; | |
| 32356 | break :edge .{ pop_count == 1, false }; | |
| 32301 | 32357 | } else { |
| 32302 | break :edge .{ | |
| 32303 | .min = false, | |
| 32304 | .max = pop_count == req_bits - sign_adj, | |
| 32305 | }; | |
| 32358 | break :edge .{ false, pop_count == req_bits - sign_adj }; | |
| 32306 | 32359 | } |
| 32307 | 32360 | }; |
| 32308 | 32361 | |
| 32309 | 32362 | assert(fits); |
| 32310 | 32363 | return switch (op) { |
| 32311 | .lt => if (edge.max) false else null, | |
| 32312 | .lte => if (edge.min) true else null, | |
| 32313 | .gt => if (edge.min) false else null, | |
| 32314 | .gte => if (edge.max) true else null, | |
| 32364 | .lt => if (is_max) false else null, | |
| 32365 | .lte => if (is_min) true else null, | |
| 32366 | .gt => if (is_min) false else null, | |
| 32367 | .gte => if (is_max) true else null, | |
| 32315 | 32368 | .eq, .neq => unreachable, |
| 32316 | 32369 | }; |
| 32317 | 32370 | } |
| ... | ... | @@ -32548,7 +32601,7 @@ const PeerResolveStrategy = enum { |
| 32548 | 32601 | either, |
| 32549 | 32602 | }; |
| 32550 | 32603 | |
| 32551 | const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) { | |
| 32604 | const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) { | |
| 32552 | 32605 | .unknown => .{ .all_s1, s1 }, |
| 32553 | 32606 | .error_set => switch (s1) { |
| 32554 | 32607 | .error_set => .{ .either, .error_set }, |
| ... | ... | @@ -32616,7 +32669,7 @@ const PeerResolveStrategy = enum { |
| 32616 | 32669 | .exact => .{ .all_s0, .exact }, |
| 32617 | 32670 | }; |
| 32618 | 32671 | |
| 32619 | switch (res[0]) { | |
| 32672 | switch (reason_method) { | |
| 32620 | 32673 | .all_s0 => { |
| 32621 | 32674 | if (!s0_is_a) { |
| 32622 | 32675 | reason_peer.* = b_peer_idx; |
| ... | ... | @@ -32633,7 +32686,7 @@ const PeerResolveStrategy = enum { |
| 32633 | 32686 | }, |
| 32634 | 32687 | } |
| 32635 | 32688 | |
| 32636 | return res[1]; | |
| 32689 | return strat; | |
| 32637 | 32690 | } |
| 32638 | 32691 | |
| 32639 | 32692 | fn select(ty: Type, mod: *Module) PeerResolveStrategy { |
src/Zir.zig+29| ... | ... | @@ -434,6 +434,10 @@ pub const Inst = struct { |
| 434 | 434 | /// Payload is `Bin`. |
| 435 | 435 | /// No OOB safety check is emitted. |
| 436 | 436 | elem_val, |
| 437 | /// Same as `elem_val` but takes the index as an immediate value. | |
| 438 | /// No OOB safety check is emitted. A prior instruction must validate this operation. | |
| 439 | /// Uses the `elem_val_imm` union field. | |
| 440 | elem_val_imm, | |
| 437 | 441 | /// Emits a compile error if the operand is not `void`. |
| 438 | 442 | /// Uses the `un_node` field. |
| 439 | 443 | ensure_result_used, |
| ... | ... | @@ -725,6 +729,9 @@ pub const Inst = struct { |
| 725 | 729 | /// Check that operand type supports the dereference operand (.*). |
| 726 | 730 | /// Uses the `un_node` field. |
| 727 | 731 | validate_deref, |
| 732 | /// Check that the operand's type is an array or tuple with the given number of elements. | |
| 733 | /// Uses the `pl_node` field. Payload is `ValidateDestructure`. | |
| 734 | validate_destructure, | |
| 728 | 735 | /// A struct literal with a specified type, with no fields. |
| 729 | 736 | /// Uses the `un_node` field. |
| 730 | 737 | struct_init_empty, |
| ... | ... | @@ -1069,6 +1076,7 @@ pub const Inst = struct { |
| 1069 | 1076 | .elem_ptr_node, |
| 1070 | 1077 | .elem_ptr_imm, |
| 1071 | 1078 | .elem_val_node, |
| 1079 | .elem_val_imm, | |
| 1072 | 1080 | .ensure_result_used, |
| 1073 | 1081 | .ensure_result_non_error, |
| 1074 | 1082 | .ensure_err_union_payload_void, |
| ... | ... | @@ -1145,6 +1153,7 @@ pub const Inst = struct { |
| 1145 | 1153 | .validate_struct_init, |
| 1146 | 1154 | .validate_array_init, |
| 1147 | 1155 | .validate_deref, |
| 1156 | .validate_destructure, | |
| 1148 | 1157 | .struct_init_empty, |
| 1149 | 1158 | .struct_init, |
| 1150 | 1159 | .struct_init_ref, |
| ... | ... | @@ -1295,6 +1304,7 @@ pub const Inst = struct { |
| 1295 | 1304 | .validate_struct_init, |
| 1296 | 1305 | .validate_array_init, |
| 1297 | 1306 | .validate_deref, |
| 1307 | .validate_destructure, | |
| 1298 | 1308 | .@"export", |
| 1299 | 1309 | .export_value, |
| 1300 | 1310 | .set_runtime_safety, |
| ... | ... | @@ -1369,6 +1379,7 @@ pub const Inst = struct { |
| 1369 | 1379 | .elem_ptr_node, |
| 1370 | 1380 | .elem_ptr_imm, |
| 1371 | 1381 | .elem_val_node, |
| 1382 | .elem_val_imm, | |
| 1372 | 1383 | .field_ptr, |
| 1373 | 1384 | .field_ptr_init, |
| 1374 | 1385 | .field_val, |
| ... | ... | @@ -1615,6 +1626,7 @@ pub const Inst = struct { |
| 1615 | 1626 | .elem_ptr_imm = .pl_node, |
| 1616 | 1627 | .elem_val = .pl_node, |
| 1617 | 1628 | .elem_val_node = .pl_node, |
| 1629 | .elem_val_imm = .elem_val_imm, | |
| 1618 | 1630 | .ensure_result_used = .un_node, |
| 1619 | 1631 | .ensure_result_non_error = .un_node, |
| 1620 | 1632 | .ensure_err_union_payload_void = .un_node, |
| ... | ... | @@ -1689,6 +1701,7 @@ pub const Inst = struct { |
| 1689 | 1701 | .validate_struct_init = .pl_node, |
| 1690 | 1702 | .validate_array_init = .pl_node, |
| 1691 | 1703 | .validate_deref = .un_node, |
| 1704 | .validate_destructure = .pl_node, | |
| 1692 | 1705 | .struct_init_empty = .un_node, |
| 1693 | 1706 | .field_type = .pl_node, |
| 1694 | 1707 | .field_type_ref = .pl_node, |
| ... | ... | @@ -2295,6 +2308,12 @@ pub const Inst = struct { |
| 2295 | 2308 | block: Ref, // If restored, the index is from this block's entrypoint |
| 2296 | 2309 | operand: Ref, // If non-error (or .none), then restore the index |
| 2297 | 2310 | }, |
| 2311 | elem_val_imm: struct { | |
| 2312 | /// The indexable value being accessed. | |
| 2313 | operand: Ref, | |
| 2314 | /// The index being accessed. | |
| 2315 | idx: u32, | |
| 2316 | }, | |
| 2298 | 2317 | |
| 2299 | 2318 | // Make sure we don't accidentally add a field to make this union |
| 2300 | 2319 | // bigger than expected. Note that in Debug builds, Zig is allowed |
| ... | ... | @@ -2334,6 +2353,7 @@ pub const Inst = struct { |
| 2334 | 2353 | defer_err_code, |
| 2335 | 2354 | save_err_ret_index, |
| 2336 | 2355 | restore_err_ret_index, |
| 2356 | elem_val_imm, | |
| 2337 | 2357 | }; |
| 2338 | 2358 | }; |
| 2339 | 2359 | |
| ... | ... | @@ -3233,6 +3253,15 @@ pub const Inst = struct { |
| 3233 | 3253 | index: u32, |
| 3234 | 3254 | len: u32, |
| 3235 | 3255 | }; |
| 3256 | ||
| 3257 | pub const ValidateDestructure = struct { | |
| 3258 | /// The value being destructured. | |
| 3259 | operand: Ref, | |
| 3260 | /// The `destructure_assign` node. | |
| 3261 | destructure_node: i32, | |
| 3262 | /// The expected field count. | |
| 3263 | expect_len: u32, | |
| 3264 | }; | |
| 3236 | 3265 | }; |
| 3237 | 3266 | |
| 3238 | 3267 | pub const SpecialProng = enum { none, @"else", under }; |
src/print_zir.zig+23| ... | ... | @@ -242,6 +242,7 @@ const Writer = struct { |
| 242 | 242 | .bool_br_or, |
| 243 | 243 | => try self.writeBoolBr(stream, inst), |
| 244 | 244 | |
| 245 | .validate_destructure => try self.writeValidateDestructure(stream, inst), | |
| 245 | 246 | .validate_array_init_ty => try self.writeValidateArrayInitTy(stream, inst), |
| 246 | 247 | .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), |
| 247 | 248 | .ptr_type => try self.writePtrType(stream, inst), |
| ... | ... | @@ -357,6 +358,8 @@ const Writer = struct { |
| 357 | 358 | |
| 358 | 359 | .for_len => try self.writePlNodeMultiOp(stream, inst), |
| 359 | 360 | |
| 361 | .elem_val_imm => try self.writeElemValImm(stream, inst), | |
| 362 | ||
| 360 | 363 | .elem_ptr_imm => try self.writeElemPtrImm(stream, inst), |
| 361 | 364 | |
| 362 | 365 | .@"export" => try self.writePlNodeExport(stream, inst), |
| ... | ... | @@ -585,6 +588,20 @@ const Writer = struct { |
| 585 | 588 | try self.writeSrc(stream, inst_data.src()); |
| 586 | 589 | } |
| 587 | 590 | |
| 591 | fn writeValidateDestructure( | |
| 592 | self: *Writer, | |
| 593 | stream: anytype, | |
| 594 | inst: Zir.Inst.Index, | |
| 595 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 596 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 597 | const extra = self.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data; | |
| 598 | try self.writeInstRef(stream, extra.operand); | |
| 599 | try stream.print(", {d}) (destructure=", .{extra.expect_len}); | |
| 600 | try self.writeSrc(stream, LazySrcLoc.nodeOffset(extra.destructure_node)); | |
| 601 | try stream.writeAll(") "); | |
| 602 | try self.writeSrc(stream, inst_data.src()); | |
| 603 | } | |
| 604 | ||
| 588 | 605 | fn writeValidateArrayInitTy( |
| 589 | 606 | self: *Writer, |
| 590 | 607 | stream: anytype, |
| ... | ... | @@ -892,6 +909,12 @@ const Writer = struct { |
| 892 | 909 | try self.writeSrc(stream, inst_data.src()); |
| 893 | 910 | } |
| 894 | 911 | |
| 912 | fn writeElemValImm(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 913 | const inst_data = self.code.instructions.items(.data)[inst].elem_val_imm; | |
| 914 | try self.writeInstRef(stream, inst_data.operand); | |
| 915 | try stream.print(", {d})", .{inst_data.idx}); | |
| 916 | } | |
| 917 | ||
| 895 | 918 | fn writeElemPtrImm(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| 896 | 919 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 897 | 920 | const extra = self.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; |
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ |
test/behavior.zig+1| ... | ... | @@ -157,6 +157,7 @@ test { |
| 157 | 157 | _ = @import("behavior/decltest.zig"); |
| 158 | 158 | _ = @import("behavior/duplicated_test_names.zig"); |
| 159 | 159 | _ = @import("behavior/defer.zig"); |
| 160 | _ = @import("behavior/destructure.zig"); | |
| 160 | 161 | _ = @import("behavior/empty_tuple_fields.zig"); |
| 161 | 162 | _ = @import("behavior/empty_union.zig"); |
| 162 | 163 | _ = @import("behavior/enum.zig"); |
test/behavior/destructure.zig created+100| ... | ... | @@ -0,0 +1,100 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const expect = std.testing.expect; | |
| 4 | ||
| 5 | test "simple destructure" { | |
| 6 | const S = struct { | |
| 7 | fn doTheTest() !void { | |
| 8 | var x: u32 = undefined; | |
| 9 | x, const y, var z: u64 = .{ 1, @as(u16, 2), 3 }; | |
| 10 | ||
| 11 | comptime assert(@TypeOf(y) == u16); | |
| 12 | ||
| 13 | try expect(x == 1); | |
| 14 | try expect(y == 2); | |
| 15 | try expect(z == 3); | |
| 16 | } | |
| 17 | }; | |
| 18 | ||
| 19 | try S.doTheTest(); | |
| 20 | try comptime S.doTheTest(); | |
| 21 | } | |
| 22 | ||
| 23 | test "destructure with comptime syntax" { | |
| 24 | const S = struct { | |
| 25 | fn doTheTest() void { | |
| 26 | comptime var x: f32 = undefined; | |
| 27 | comptime x, const y, var z = .{ 0.5, 123, 456 }; // z is a comptime var | |
| 28 | ||
| 29 | comptime assert(@TypeOf(y) == comptime_int); | |
| 30 | comptime assert(@TypeOf(z) == comptime_int); | |
| 31 | comptime assert(x == 0.5); | |
| 32 | comptime assert(y == 123); | |
| 33 | comptime assert(z == 456); | |
| 34 | } | |
| 35 | }; | |
| 36 | ||
| 37 | S.doTheTest(); | |
| 38 | comptime S.doTheTest(); | |
| 39 | } | |
| 40 | ||
| 41 | test "destructure from labeled block" { | |
| 42 | const S = struct { | |
| 43 | fn doTheTest(rt_true: bool) !void { | |
| 44 | const x: u32, const y: u8, const z: i64 = blk: { | |
| 45 | if (rt_true) break :blk .{ 1, 2, 3 }; | |
| 46 | break :blk .{ 4, 5, 6 }; | |
| 47 | }; | |
| 48 | ||
| 49 | try expect(x == 1); | |
| 50 | try expect(y == 2); | |
| 51 | try expect(z == 3); | |
| 52 | } | |
| 53 | }; | |
| 54 | ||
| 55 | try S.doTheTest(true); | |
| 56 | try comptime S.doTheTest(true); | |
| 57 | } | |
| 58 | ||
| 59 | test "destructure tuple value" { | |
| 60 | const tup: struct { f32, u32, i64 } = .{ 10.0, 20, 30 }; | |
| 61 | const x, const y, const z = tup; | |
| 62 | ||
| 63 | comptime assert(@TypeOf(x) == f32); | |
| 64 | comptime assert(@TypeOf(y) == u32); | |
| 65 | comptime assert(@TypeOf(z) == i64); | |
| 66 | ||
| 67 | try expect(x == 10.0); | |
| 68 | try expect(y == 20); | |
| 69 | try expect(z == 30); | |
| 70 | } | |
| 71 | ||
| 72 | test "destructure array value" { | |
| 73 | const arr: [3]u32 = .{ 10, 20, 30 }; | |
| 74 | const x, const y, const z = arr; | |
| 75 | ||
| 76 | comptime assert(@TypeOf(x) == u32); | |
| 77 | comptime assert(@TypeOf(y) == u32); | |
| 78 | comptime assert(@TypeOf(z) == u32); | |
| 79 | ||
| 80 | try expect(x == 10); | |
| 81 | try expect(y == 20); | |
| 82 | try expect(z == 30); | |
| 83 | } | |
| 84 | ||
| 85 | test "destructure from struct init with named tuple fields" { | |
| 86 | const Tuple = struct { u8, u16, u32 }; | |
| 87 | const x, const y, const z = Tuple{ | |
| 88 | .@"0" = 100, | |
| 89 | .@"1" = 200, | |
| 90 | .@"2" = 300, | |
| 91 | }; | |
| 92 | ||
| 93 | comptime assert(@TypeOf(x) == u8); | |
| 94 | comptime assert(@TypeOf(y) == u16); | |
| 95 | comptime assert(@TypeOf(z) == u32); | |
| 96 | ||
| 97 | try expect(x == 100); | |
| 98 | try expect(y == 200); | |
| 99 | try expect(z == 300); | |
| 100 | } |
test/cases/compile_errors/cast_without_result_type.zig+7| ... | ... | @@ -13,6 +13,10 @@ export fn d() void { |
| 13 | 13 | var x: f32 = 0; |
| 14 | 14 | _ = x + @floatFromInt(123); |
| 15 | 15 | } |
| 16 | export fn e() void { | |
| 17 | const x: u32, const y: u64 = @intCast(123); | |
| 18 | _ = x + y; | |
| 19 | } | |
| 16 | 20 | |
| 17 | 21 | // error |
| 18 | 22 | // backend=stage2 |
| ... | ... | @@ -26,3 +30,6 @@ export fn d() void { |
| 26 | 30 | // :9:10: note: use @as to provide explicit result type |
| 27 | 31 | // :14:13: error: @floatFromInt must have a known result type |
| 28 | 32 | // :14:13: note: use @as to provide explicit result type |
| 33 | // :17:34: error: @intCast must have a known result type | |
| 34 | // :17:32: note: destructure expressions do not provide a single result type | |
| 35 | // :17:34: note: use @as to provide explicit result type |
test/cases/compile_errors/extra_comma_in_destructure.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn foo() void { | |
| 2 | const x, const y, = .{ 1, 2 }; | |
| 3 | _ = .{ x, y }; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // backend=stage2 | |
| 8 | // target=native | |
| 9 | // | |
| 10 | // :2:23: error: expected expression or var decl, found '=' |
test/cases/compile_errors/invalid_destructure_astgen.zig created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | export fn foo() void { | |
| 2 | const x, const y = .{ 1, 2, 3 }; | |
| 3 | _ = .{ x, y }; | |
| 4 | } | |
| 5 | ||
| 6 | export fn bar() void { | |
| 7 | var x: u32 = undefined; | |
| 8 | x, const y: u64 = blk: { | |
| 9 | if (true) break :blk .{ 1, 2 }; | |
| 10 | break :blk .{ .x = 123, .y = 456 }; | |
| 11 | }; | |
| 12 | _ = y; | |
| 13 | } | |
| 14 | ||
| 15 | // error | |
| 16 | // backend=stage2 | |
| 17 | // target=native | |
| 18 | // | |
| 19 | // :2:25: error: expected 2 elements for destructure, found 3 | |
| 20 | // :2:22: note: result destructured here | |
| 21 | // :10:21: error: struct value cannot be destructured | |
| 22 | // :8:21: note: result destructured here |
test/cases/compile_errors/invalid_destructure_sema.zig created+23| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | export fn foo() void { | |
| 2 | const x, const y = 123; | |
| 3 | _ = .{ x, y }; | |
| 4 | } | |
| 5 | ||
| 6 | export fn bar() void { | |
| 7 | var x: u32 = undefined; | |
| 8 | x, const y: u64 = blk: { | |
| 9 | if (false) break :blk .{ 1, 2 }; | |
| 10 | const val = .{ 3, 4, 5 }; | |
| 11 | break :blk val; | |
| 12 | }; | |
| 13 | _ = y; | |
| 14 | } | |
| 15 | ||
| 16 | // error | |
| 17 | // backend=stage2 | |
| 18 | // target=native | |
| 19 | // | |
| 20 | // :2:24: error: type 'comptime_int' cannot be destructured | |
| 21 | // :2:22: note: result destructured here | |
| 22 | // :11:20: error: expected 2 elements for destructure, found 3 | |
| 23 | // :8:21: note: result destructured here |
test/cases/unused_vars.zig+3| ... | ... | @@ -1,7 +1,10 @@ |
| 1 | 1 | pub fn main() void { |
| 2 | 2 | const x = 1; |
| 3 | const y, var z = .{ 2, 3 }; | |
| 3 | 4 | } |
| 4 | 5 | |
| 5 | 6 | // error |
| 6 | 7 | // |
| 8 | // :3:18: error: unused local variable | |
| 9 | // :3:11: error: unused local constant | |
| 7 | 10 | // :2:11: error: unused local constant |