authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-15 14:51:52-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-09-15 14:51:52-07:00
log61b70778bdf975957d45432987dde16029aca69a
treec9cff38d49849c519aa79f8cdcd3a63a02c8c58e
parent94529ffb621fa633437ac48d8f90003e26e8ce5b
parentf366d9f8793fc297e581321fbbd6242089f07440
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17156 from mlugg/destructure

compiler: implement destructuring syntax

19 files changed, 1208 insertions(+), 233 deletions(-)

build.zig+3-1
......@@ -204,7 +204,9 @@ pub fn build(b: *std.Build) !void {
204204 "Request creation of '.note.gnu.build-id' section",
205205 );
206206
207 if (!no_bin) {
207 if (no_bin) {
208 b.getInstallStep().dependOn(&exe.step);
209 } else {
208210 const install_exe = b.addInstallArtifact(exe, .{
209211 .dest_dir = if (flat) .{ .override = .prefix } else .default,
210212 });
doc/langref.html.in+35-15
......@@ -12382,21 +12382,22 @@ ComptimeDecl <- KEYWORD_comptime Block
1238212382
1238312383Decl
1238412384 <- (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
1238612386 / KEYWORD_usingnamespace Expr SEMICOLON
1238712387
1238812388FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
1238912389
12390VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON
12390VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection?
12391
12392GlobalVarDecl <- VarDeclProto (EQUAL Expr)? SEMICOLON
1239112393
1239212394ContainerField
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)?
1239512397
1239612398# *** Block Level ***
1239712399Statement
12398 <- KEYWORD_comptime? VarDecl
12399 / KEYWORD_comptime BlockExprStatement
12400 <- KEYWORD_comptime ComptimeStatement
1240012401 / KEYWORD_nosuspend BlockExprStatement
1240112402 / KEYWORD_suspend BlockExprStatement
1240212403 / KEYWORD_defer BlockExprStatement
......@@ -12404,7 +12405,11 @@ Statement
1240412405 / IfStatement
1240512406 / LabeledStatement
1240612407 / SwitchExpr
12407 / AssignExpr SEMICOLON
12408 / VarDeclExprStatement
12409
12410ComptimeStatement
12411 <- BlockExpr
12412 / VarDeclExprStatement
1240812413
1240912414IfStatement
1241012415 <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
......@@ -12428,8 +12433,17 @@ BlockExprStatement
1242812433
1242912434BlockExpr <- BlockLabel? Block
1243012435
12436# An expression, assignment, or any destructure, as a statement.
12437VarDeclExprStatement
12438 <- VarDeclProto (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON
12439 / Expr (AssignOp Expr / (COMMA (VarDeclProto / Expr))+ EQUAL Expr)? SEMICOLON
12440
1243112441# *** Expression Level ***
12432AssignExpr <- Expr (AssignOp Expr)?
12442
12443# An assignment or a destructure whose LHS are all lvalue expressions.
12444AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr)?
12445
12446SingleAssignExpr <- Expr (AssignOp Expr)?
1243312447
1243412448Expr <- BoolOrExpr
1243512449
......@@ -12570,7 +12584,7 @@ IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
1257012584
1257112585WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
1257212586
12573ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
12587ForPrefix <- KEYWORD_for LPAREN ForArgumentsList RPAREN PtrListPayload
1257412588
1257512589# Payloads
1257612590Payload <- PIPE IDENTIFIER PIPE
......@@ -12579,9 +12593,10 @@ PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
1257912593
1258012594PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
1258112595
12596PtrListPayload <- PIPE ASTERISK? IDENTIFIER (COMMA ASTERISK? IDENTIFIER)* COMMA? PIPE
1258212597
1258312598# Switch specific
12584SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
12599SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? SingleAssignExpr
1258512600
1258612601SwitchCase
1258712602 <- SwitchItem (COMMA SwitchItem)* COMMA?
......@@ -12589,6 +12604,11 @@ SwitchCase
1258912604
1259012605SwitchItem <- Expr (DOT3 Expr)?
1259112606
12607# For specific
12608ForArgumentsList <- ForItem (COMMA ForItem)* COMMA?
12609
12610ForItem <- Expr (DOT2 Expr?)?
12611
1259212612# Operators
1259312613AssignOp
1259412614 <- ASTERISKEQUAL
......@@ -12799,7 +12819,7 @@ STRINGLITERAL
1279912819 / (line_string skip)+
1280012820IDENTIFIER
1280112821 <- !keyword [A-Za-z_] [A-Za-z0-9_]* skip
12802 / "@\"" string_char* "\"" skip
12822 / "@" STRINGLITERALSINGLE
1280312823BUILTINIDENTIFIER <- "@"[A-Za-z_][A-Za-z0-9_]* skip
1280412824
1280512825
......@@ -12895,7 +12915,6 @@ KEYWORD_fn <- 'fn' end_of_word
1289512915KEYWORD_for <- 'for' end_of_word
1289612916KEYWORD_if <- 'if' end_of_word
1289712917KEYWORD_inline <- 'inline' end_of_word
12898KEYWORD_linksection <- 'linksection' end_of_word
1289912918KEYWORD_noalias <- 'noalias' end_of_word
1290012919KEYWORD_nosuspend <- 'nosuspend' end_of_word
1290112920KEYWORD_noinline <- 'noinline' end_of_word
......@@ -12906,6 +12925,7 @@ KEYWORD_packed <- 'packed' end_of_word
1290612925KEYWORD_pub <- 'pub' end_of_word
1290712926KEYWORD_resume <- 'resume' end_of_word
1290812927KEYWORD_return <- 'return' end_of_word
12928KEYWORD_linksection <- 'linksection' end_of_word
1290912929KEYWORD_struct <- 'struct' end_of_word
1291012930KEYWORD_suspend <- 'suspend' end_of_word
1291112931KEYWORD_switch <- 'switch' end_of_word
......@@ -12925,9 +12945,9 @@ keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and
1292512945 / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer
1292612946 / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export
1292712947 / 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
1293112951 / KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test
1293212952 / KEYWORD_threadlocal / KEYWORD_try / KEYWORD_union / KEYWORD_unreachable
1293312953 / 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 {
241241 token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(),
242242 });
243243 },
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 },
244249 .expected_fn => {
245250 return stream.print("expected function, found '{s}'", .{
246251 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 {
584589 .error_union,
585590 => n = datas[n].lhs,
586591
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
587599 .fn_decl,
588600 .fn_proto_simple,
589601 .fn_proto_multi,
......@@ -816,6 +828,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
816828 .assign_add_sat,
817829 .assign_sub_sat,
818830 .assign,
831 .assign_destructure,
819832 .merge_error_sets,
820833 .mul,
821834 .div,
......@@ -2846,6 +2859,7 @@ pub const Error = struct {
28462859 expected_container_members,
28472860 expected_expr,
28482861 expected_expr_or_assignment,
2862 expected_expr_or_var_decl,
28492863 expected_fn,
28502864 expected_inlinable,
28512865 expected_labelable,
......@@ -3006,6 +3020,20 @@ pub const Node = struct {
30063020 assign_sub_sat,
30073021 /// `lhs = rhs`. main_token is op.
30083022 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,
30093037 /// `lhs || rhs`. main_token is the `||`.
30103038 merge_error_sets,
30113039 /// `lhs * rhs`. main_token is the `*`.
lib/std/zig/Parse.zig+278-72
......@@ -658,9 +658,8 @@ fn expectTopLevelDecl(p: *Parse) !Node.Index {
658658 }
659659
660660 const thread_local_token = p.eatToken(.keyword_threadlocal);
661 const var_decl = try p.parseVarDecl();
661 const var_decl = try p.parseGlobalVarDecl();
662662 if (var_decl != 0) {
663 try p.expectSemicolon(.expected_semi_after_decl, false);
664663 return var_decl;
665664 }
666665 if (thread_local_token != null) {
......@@ -792,8 +791,9 @@ fn parseFnProto(p: *Parse) !Node.Index {
792791 }
793792}
794793
795/// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON
796fn 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.
796fn parseVarDeclProto(p: *Parse) !Node.Index {
797797 const mut_token = p.eatToken(.keyword_const) orelse
798798 p.eatToken(.keyword_var) orelse
799799 return null_node;
......@@ -803,18 +803,7 @@ fn parseVarDecl(p: *Parse) !Node.Index {
803803 const align_node = try p.parseByteAlign();
804804 const addrspace_node = try p.parseAddrSpace();
805805 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
818807 if (section_node == 0 and addrspace_node == 0) {
819808 if (align_node == 0) {
820809 return p.addNode(.{
......@@ -822,31 +811,33 @@ fn parseVarDecl(p: *Parse) !Node.Index {
822811 .main_token = mut_token,
823812 .data = .{
824813 .lhs = type_node,
825 .rhs = init_node,
814 .rhs = 0,
826815 },
827816 });
828 } else if (type_node == 0) {
817 }
818
819 if (type_node == 0) {
829820 return p.addNode(.{
830821 .tag = .aligned_var_decl,
831822 .main_token = mut_token,
832823 .data = .{
833824 .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,
847826 },
848827 });
849828 }
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 });
850841 } else {
851842 return p.addNode(.{
852843 .tag = .global_var_decl,
......@@ -858,12 +849,38 @@ fn parseVarDecl(p: *Parse) !Node.Index {
858849 .addrspace_node = addrspace_node,
859850 .section_node = section_node,
860851 }),
861 .rhs = init_node,
852 .rhs = 0,
862853 },
863854 });
864855 }
865856}
866857
858/// GlobalVarDecl <- VarDeclProto (EQUAL Expr?) SEMICOLON
859fn 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
867884/// ContainerField
868885/// <- doc_comment? KEYWORD_comptime? IDENTIFIER (COLON TypeExpr)? ByteAlign? (EQUAL Expr)?
869886/// / doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? !KEYWORD_fn TypeExpr ByteAlign? (EQUAL Expr)?
......@@ -918,8 +935,7 @@ fn expectContainerField(p: *Parse) !Node.Index {
918935}
919936
920937/// Statement
921/// <- KEYWORD_comptime? VarDecl
922/// / KEYWORD_comptime BlockExprStatement
938/// <- KEYWORD_comptime ComptimeStatement
923939/// / KEYWORD_nosuspend BlockExprStatement
924940/// / KEYWORD_suspend BlockExprStatement
925941/// / KEYWORD_defer BlockExprStatement
......@@ -927,27 +943,28 @@ fn expectContainerField(p: *Parse) !Node.Index {
927943/// / IfStatement
928944/// / LabeledStatement
929945/// / SwitchExpr
930/// / AssignExpr SEMICOLON
931fn 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
947fn 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 });
939959 }
940 }
941960
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 }
951968 }
952969
953970 switch (p.token_tags[p.tok_i]) {
......@@ -1011,21 +1028,145 @@ fn parseStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
10111028 const labeled_statement = try p.parseLabeledStatement();
10121029 if (labeled_statement != 0) return labeled_statement;
10131030
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();
10161035 try p.expectSemicolon(.expected_semi_after_stmt, true);
1017 return assign_expr;
1036 return assign;
10181037 }
1038}
10191039
1020 return null_node;
1040/// ComptimeStatement
1041/// <- BlockExpr
1042/// / VarDeclExprStatement
1043fn 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);
10211053}
10221054
1023fn 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
1058fn 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 }
10271153 }
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 });
10291170}
10301171
10311172/// 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 {
13451486 }
13461487}
13471488
1348/// AssignExpr <- Expr (AssignOp Expr)?
1489/// AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr)?
13491490///
13501491/// AssignOp
13511492/// <- ASTERISKEQUAL
......@@ -1369,8 +1510,40 @@ fn parseBlockExpr(p: *Parse) Error!Node.Index {
13691510fn parseAssignExpr(p: *Parse) !Node.Index {
13701511 const expr = try p.parseExpr();
13711512 if (expr == 0) return null_node;
1513 return p.finishAssignExpr(expr);
1514}
13721515
1373 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1516/// SingleAssignExpr <- Expr (AssignOp Expr)?
1517fn 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
1531fn 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
1545fn assignOpNode(tok: Token.Tag) ?Node.Tag {
1546 return switch (tok) {
13741547 .asterisk_equal => .assign_mul,
13751548 .slash_equal => .assign_div,
13761549 .percent_equal => .assign_mod,
......@@ -1389,18 +1562,51 @@ fn parseAssignExpr(p: *Parse) !Node.Index {
13891562 .plus_pipe_equal => .assign_add_sat,
13901563 .minus_pipe_equal => .assign_sub_sat,
13911564 .equal => .assign,
1392 else => return expr,
1565 else => null,
13931566 };
1567}
1568
1569fn 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
13941592 return p.addNode(.{
1395 .tag = tag,
1396 .main_token = p.nextToken(),
1593 .tag = .assign_destructure,
1594 .main_token = equal_token,
13971595 .data = .{
1398 .lhs = expr,
1399 .rhs = try p.expectExpr(),
1596 .lhs = @intCast(extra_start),
1597 .rhs = rhs,
14001598 },
14011599 });
14021600}
14031601
1602fn 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
14041610fn expectAssignExpr(p: *Parse) !Node.Index {
14051611 const expr = try p.parseAssignExpr();
14061612 if (expr == 0) {
......@@ -3260,7 +3466,7 @@ fn parseSwitchProng(p: *Parse) !Node.Index {
32603466 .main_token = arrow_token,
32613467 .data = .{
32623468 .lhs = 0,
3263 .rhs = try p.expectAssignExpr(),
3469 .rhs = try p.expectSingleAssignExpr(),
32643470 },
32653471 }),
32663472 1 => return p.addNode(.{
......@@ -3268,7 +3474,7 @@ fn parseSwitchProng(p: *Parse) !Node.Index {
32683474 .main_token = arrow_token,
32693475 .data = .{
32703476 .lhs = items[0],
3271 .rhs = try p.expectAssignExpr(),
3477 .rhs = try p.expectSingleAssignExpr(),
32723478 },
32733479 }),
32743480 else => return p.addNode(.{
......@@ -3276,7 +3482,7 @@ fn parseSwitchProng(p: *Parse) !Node.Index {
32763482 .main_token = arrow_token,
32773483 .data = .{
32783484 .lhs = try p.addExtra(try p.listToSpan(items)),
3279 .rhs = try p.expectAssignExpr(),
3485 .rhs = try p.expectSingleAssignExpr(),
32803486 },
32813487 }),
32823488 }
lib/std/zig/parser_test.zig+7-7
......@@ -4348,12 +4348,12 @@ test "zig fmt: invalid else branch statement" {
43484348 \\ for ("") |_| {} else defer {}
43494349 \\}
43504350 , &[_]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,
43574357 });
43584358}
43594359
......@@ -6078,7 +6078,7 @@ test "recovery: missing for payload" {
60786078 try testError(
60796079 \\comptime {
60806080 \\ const a = for(a) {};
6081 \\ const a: for(a) blk: {};
6081 \\ const a: for(a) blk: {} = {};
60826082 \\ for(a) {}
60836083 \\}
60846084 , &[_]Error{
lib/std/zig/render.zig+82-32
......@@ -164,7 +164,7 @@ fn renderMember(
164164 .local_var_decl,
165165 .simple_var_decl,
166166 .aligned_var_decl,
167 => return renderVarDecl(gpa, ais, tree, tree.fullVarDecl(decl).?),
167 => return renderVarDecl(gpa, ais, tree, tree.fullVarDecl(decl).?, false, .semicolon),
168168
169169 .test_decl => {
170170 const test_token = main_tokens[decl];
......@@ -427,6 +427,42 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
427427 return renderExpression(gpa, ais, tree, infix.rhs, space);
428428 },
429429
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
430466 .bit_not,
431467 .bool_not,
432468 .negation,
......@@ -943,7 +979,16 @@ fn renderAsmInput(
943979 return renderToken(ais, tree, datas[asm_input].rhs, space); // rparen
944980}
945981
946fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDecl) Error!void {
982fn 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 {
947992 if (var_decl.visib_token) |visib_token| {
948993 try renderToken(ais, tree, visib_token, Space.space); // pub
949994 }
......@@ -960,21 +1005,31 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec
9601005 try renderToken(ais, tree, thread_local_token, Space.space); // threadlocal
9611006 }
9621007
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 }
9651012 }
9661013
9671014 try renderToken(ais, tree, var_decl.ast.mut_token, .space); // var
9681015
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 }
9781033
9791034 if (var_decl.ast.type_node != 0) {
9801035 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
9831038 {
9841039 try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);
9851040 } 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);
9891042 }
9901043 }
9911044
......@@ -1001,8 +1054,7 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec
10011054 {
10021055 try renderToken(ais, tree, rparen, .space); // )
10031056 } else {
1004 try renderToken(ais, tree, rparen, .none); // )
1005 return renderToken(ais, tree, rparen + 1, Space.newline); // ;
1057 return renderToken(ais, tree, rparen, space); // )
10061058 }
10071059 }
10081060
......@@ -1031,23 +1083,21 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec
10311083 if (var_decl.ast.init_node != 0) {
10321084 try renderToken(ais, tree, rparen, .space); // )
10331085 } else {
1034 try renderToken(ais, tree, rparen, .none); // )
1035 return renderToken(ais, tree, rparen + 1, Space.newline); // ;
1086 return renderToken(ais, tree, rparen, space); // )
10361087 }
10371088 }
10381089
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();
10491098 }
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); // ;
10511101}
10521102
10531103fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: Space) Error!void {
......@@ -1825,7 +1875,7 @@ fn renderBlock(
18251875 .local_var_decl,
18261876 .simple_var_decl,
18271877 .aligned_var_decl,
1828 => try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(stmt).?),
1878 => try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(stmt).?, false, .semicolon),
18291879 else => try renderExpression(gpa, ais, tree, stmt, .semicolon),
18301880 }
18311881 }
src/AstGen.zig+432-44
......@@ -280,6 +280,20 @@ const ResultInfo = struct {
280280 /// The result instruction from the expression must be ignored.
281281 /// Always an instruction with tag `alloc_inferred`.
282282 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 };
283297
284298 const PtrResultLoc = struct {
285299 inst: Zir.Inst.Ref,
......@@ -298,6 +312,12 @@ const ResultInfo = struct {
298312 const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node);
299313 return gz.addUnNode(.elem_type, ptr_ty, node);
300314 },
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 },
301321 }
302322
303323 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
399419 .asm_input => unreachable,
400420
401421 .assign,
422 .assign_destructure,
402423 .assign_bit_and,
403424 .assign_bit_or,
404425 .assign_shl,
......@@ -621,6 +642,13 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
621642 return rvalue(gz, ri, .void_value, node);
622643 },
623644
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
624652 .assign_shl => {
625653 try assignShift(gz, scope, node, .shl);
626654 return rvalue(gz, ri, .void_value, node);
......@@ -1364,14 +1392,8 @@ fn arrayInitExpr(
13641392
13651393 assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
13661394
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 };
13751397
13761398 infer: {
13771399 const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer;
......@@ -1386,10 +1408,7 @@ fn arrayInitExpr(
13861408 .lhs = len_inst,
13871409 .rhs = elem_type,
13881410 });
1389 break :inst .{
1390 .array = array_type_inst,
1391 .elem = elem_type,
1392 };
1411 break :inst .{ array_type_inst, elem_type };
13931412 } else {
13941413 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
13951414 const array_type_inst = try gz.addPlNode(
......@@ -1401,10 +1420,7 @@ fn arrayInitExpr(
14011420 .sentinel = sentinel,
14021421 },
14031422 );
1404 break :inst .{
1405 .array = array_type_inst,
1406 .elem = elem_type,
1407 };
1423 break :inst .{ array_type_inst, elem_type };
14081424 }
14091425 }
14101426 }
......@@ -1413,29 +1429,26 @@ fn arrayInitExpr(
14131429 .ty = array_type_inst,
14141430 .init_count = @intCast(array_init.ast.elements.len),
14151431 });
1416 break :inst .{
1417 .array = array_type_inst,
1418 .elem = .none,
1419 };
1432 break :inst .{ array_type_inst, .none };
14201433 };
14211434
14221435 switch (ri.rl) {
14231436 .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 } };
14261439 for (array_init.ast.elements) |elem_init| {
14271440 _ = try expr(gz, scope, elem_ri, elem_init);
14281441 }
1429 } else if (types.array != .none) {
1442 } else if (array_ty != .none) {
14301443 for (array_init.ast.elements, 0..) |elem_init, i| {
1431 const elem_ty = try gz.add(.{
1444 const this_elem_ty = try gz.add(.{
14321445 .tag = .elem_type_index,
14331446 .data = .{ .bin = .{
1434 .lhs = types.array,
1447 .lhs = array_ty,
14351448 .rhs = @enumFromInt(i),
14361449 } },
14371450 });
1438 _ = try expr(gz, scope, .{ .rl = .{ .ty = elem_ty } }, elem_init);
1451 _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init);
14391452 }
14401453 } else {
14411454 for (array_init.ast.elements) |elem_init| {
......@@ -1445,15 +1458,15 @@ fn arrayInitExpr(
14451458 return Zir.Inst.Ref.void_value;
14461459 },
14471460 .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);
14501463 },
14511464 .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);
14541467 },
14551468 .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: {
14571470 const arr_ty = try gz.addUnNode(.opt_eu_base_ty, ty_inst, node);
14581471 _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
14591472 .ty = arr_ty,
......@@ -1461,22 +1474,49 @@ fn arrayInitExpr(
14611474 });
14621475 break :blk arr_ty;
14631476 };
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);
14651478 return rvalue(gz, ri, result, node);
14661479 },
14671480 .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);
14691482 },
14701483 .inferred_ptr => |ptr_inst| {
1471 if (types.array == .none) {
1484 if (array_ty == .none) {
14721485 // We treat this case differently so that we don't get a crash when
14731486 // analyzing array_base_ptr against an alloc_inferred_mut.
14741487 // See corresponding logic in structInitExpr.
14751488 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
14761489 return rvalue(gz, ri, result, node);
14771490 } 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);
14791501 }
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;
14801520 },
14811521 }
14821522}
......@@ -1707,6 +1747,23 @@ fn structInitExpr(
17071747 return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst);
17081748 }
17091749 },
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 },
17101767 }
17111768}
17121769
......@@ -1968,6 +2025,7 @@ fn restoreErrRetIndex(
19682025 // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it.
19692026 break :blk .none;
19702027 },
2028 .destructure => return, // value must be a tuple or array, so never restore/pop
19712029 else => result,
19722030 },
19732031 else => .none, // always restore/pop
......@@ -2340,6 +2398,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
23402398 .simple_var_decl,
23412399 .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?),
23422400
2401 .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator),
2402
23432403 .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
23442404 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
23452405
......@@ -2481,6 +2541,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
24812541 .elem_ptr_node,
24822542 .elem_ptr_imm,
24832543 .elem_val_node,
2544 .elem_val_imm,
24842545 .field_ptr,
24852546 .field_ptr_init,
24862547 .field_val,
......@@ -2686,6 +2747,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
26862747 .validate_array_init_ty,
26872748 .validate_struct_init_ty,
26882749 .validate_deref,
2750 .validate_destructure,
26892751 .save_err_ret_index,
26902752 .restore_err_ret_index,
26912753 => break :b true,
......@@ -3100,10 +3162,7 @@ fn varDecl(
31003162 .keyword_var => {
31013163 const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
31023164 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: {
31073166 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
31083167 const alloc = alloc: {
31093168 if (align_inst == .none) {
......@@ -3122,7 +3181,7 @@ fn varDecl(
31223181 });
31233182 }
31243183 };
3125 break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
3184 break :a .{ alloc, .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
31263185 } else a: {
31273186 const alloc = alloc: {
31283187 if (align_inst == .none) {
......@@ -3142,24 +3201,24 @@ fn varDecl(
31423201 }
31433202 };
31443203 resolve_inferred_alloc = alloc;
3145 break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .inferred_ptr = alloc } } };
3204 break :a .{ alloc, .{ .rl = .{ .inferred_ptr = alloc } } };
31463205 };
31473206 const prev_anon_name_strategy = gz.anon_name_strategy;
31483207 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);
31503209 gz.anon_name_strategy = prev_anon_name_strategy;
31513210 if (resolve_inferred_alloc != .none) {
31523211 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
31533212 }
31543213
3155 try gz.addDbgVar(.dbg_var_ptr, ident_name, var_data.alloc);
3214 try gz.addDbgVar(.dbg_var_ptr, ident_name, alloc);
31563215
31573216 const sub_scope = try block_arena.create(Scope.LocalPtr);
31583217 sub_scope.* = .{
31593218 .parent = scope,
31603219 .gen_zir = gz,
31613220 .name = ident_name,
3162 .ptr = var_data.alloc,
3221 .ptr = alloc,
31633222 .token_src = name_token,
31643223 .maybe_comptime = is_comptime,
31653224 .id_cat = .@"local variable",
......@@ -3227,6 +3286,301 @@ fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!voi
32273286 } } }, rhs);
32283287}
32293288
3289/// Handles destructure assignments where no LHS is a `const` or `var` decl.
3290fn 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.
3352fn 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
32303584fn assignOp(
32313585 gz: *GenZir,
32323586 scope: *Scope,
......@@ -9059,6 +9413,7 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
90599413 .array_cat,
90609414 .array_mult,
90619415 .assign,
9416 .assign_destructure,
90629417 .assign_bit_and,
90639418 .assign_bit_or,
90649419 .assign_shl,
......@@ -9237,6 +9592,7 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
92379592 .array_cat,
92389593 .array_mult,
92399594 .assign,
9595 .assign_destructure,
92409596 .assign_bit_and,
92419597 .assign_bit_or,
92429598 .assign_shl,
......@@ -9483,6 +9839,7 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
94839839 .array_cat,
94849840 .array_mult,
94859841 .assign,
9842 .assign_destructure,
94869843 .assign_bit_and,
94879844 .assign_bit_or,
94889845 .assign_shl,
......@@ -9830,6 +10187,37 @@ fn rvalue(
983010187 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
983110188 return .void_value;
983210189 },
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 },
983310221 }
983410222}
983510223
src/AstRlAnnotate.zig+10
......@@ -203,6 +203,16 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
203203 else => unreachable,
204204 }
205205 },
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 },
206216 .assign => {
207217 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
208218 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.typed_ptr);
src/Sema.zig+115-62
......@@ -1018,6 +1018,7 @@ fn analyzeBodyInner(
10181018 .elem_ptr_imm => try sema.zirElemPtrImm(block, inst),
10191019 .elem_val => try sema.zirElemVal(block, inst),
10201020 .elem_val_node => try sema.zirElemValNode(block, inst),
1021 .elem_val_imm => try sema.zirElemValImm(block, inst),
10211022 .elem_type_index => try sema.zirElemTypeIndex(block, inst),
10221023 .elem_type => try sema.zirElemType(block, inst),
10231024 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
......@@ -1379,6 +1380,11 @@ fn analyzeBodyInner(
13791380 i += 1;
13801381 continue;
13811382 },
1383 .validate_destructure => {
1384 try sema.zirValidateDestructure(block, inst);
1385 i += 1;
1386 continue;
1387 },
13821388 .@"export" => {
13831389 try sema.zirExport(block, inst);
13841390 i += 1;
......@@ -3780,6 +3786,21 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
37803786 return sema.analyzeDeclRef(try anon_decl.finish(elem_ty, store_val, ptr_info.flags.alignment));
37813787 }
37823788
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
37833804 return sema.makePtrConst(block, alloc);
37843805}
37853806
......@@ -5163,6 +5184,43 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
51635184 }
51645185}
51655186
5187fn 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
51665224fn failWithBadMemberAccess(
51675225 sema: *Sema,
51685226 block: *Block,
......@@ -10289,6 +10347,17 @@ fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1028910347 return sema.elemVal(block, src, array, elem_index, elem_index_src, true);
1029010348}
1029110349
10350fn 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
1029210361fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1029310362 const tracy = trace(@src());
1029410363 defer tracy.end();
......@@ -11023,17 +11092,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1102311092 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
1102411093 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
1102511094
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: {
1102711096 const maybe_ptr = try sema.resolveInst(extra.data.operand);
1102811097 if (operand_is_ref) {
1102911098 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 };
1103111100 } else {
11032 break :blk .{ .val = maybe_ptr, .ptr = undefined };
11101 break :blk .{ maybe_ptr, undefined };
1103311102 }
1103411103 };
1103511104
11036 const operand = try sema.switchCond(block, operand_src, raw_operand.val);
11105 const operand = try sema.switchCond(block, operand_src, raw_operand_val);
1103711106
1103811107 // AstGen guarantees that the instruction immediately preceding
1103911108 // switch_block(_ref) is a dbg_stmt
......@@ -11091,7 +11160,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1109111160 },
1109211161 };
1109311162
11094 const maybe_union_ty = sema.typeOf(raw_operand.val);
11163 const maybe_union_ty = sema.typeOf(raw_operand_val);
1109511164 const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union;
1109611165
1109711166 // 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
1164211711 const spa: SwitchProngAnalysis = .{
1164311712 .sema = sema,
1164411713 .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,
1164711716 .cond = operand,
1164811717 .else_error_ty = else_error_ty,
1164911718 .switch_block_inst = inst,
......@@ -15431,11 +15500,7 @@ fn analyzeArithmetic(
1543115500
1543215501 const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(casted_lhs);
1543315502 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: {
1543915504 switch (zir_tag) {
1544015505 .add, .add_unsafe => {
1544115506 // For integers:intAddSat
......@@ -15482,8 +15547,8 @@ fn analyzeArithmetic(
1548215547 } else {
1548315548 return Air.internedToRef((try Value.floatAdd(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern());
1548415549 }
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 };
1548715552 },
1548815553 .addwrap => {
1548915554 // Integers only; floats are checked above.
......@@ -15503,8 +15568,8 @@ fn analyzeArithmetic(
1550315568 }
1550415569 if (maybe_lhs_val) |lhs_val| {
1550515570 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 };
1550815573 },
1550915574 .add_sat => {
1551015575 // Integers only; floats are checked above.
......@@ -15530,14 +15595,14 @@ fn analyzeArithmetic(
1553015595
1553115596 return Air.internedToRef(val.toIntern());
1553215597 } 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,
1553615601 };
1553715602 } 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,
1554115606 };
1554215607 },
1554315608 .sub => {
......@@ -15580,8 +15645,8 @@ fn analyzeArithmetic(
1558015645 } else {
1558115646 return Air.internedToRef((try Value.floatSub(lhs_val, rhs_val, resolved_type, sema.arena, mod)).toIntern());
1558215647 }
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 };
1558515650 },
1558615651 .subwrap => {
1558715652 // Integers only; floats are checked above.
......@@ -15601,8 +15666,8 @@ fn analyzeArithmetic(
1560115666 }
1560215667 if (maybe_rhs_val) |rhs_val| {
1560315668 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 };
1560615671 },
1560715672 .sub_sat => {
1560815673 // Integers only; floats are checked above.
......@@ -15627,8 +15692,8 @@ fn analyzeArithmetic(
1562715692 try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, mod);
1562815693
1562915694 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 };
1563215697 },
1563315698 .mul => {
1563415699 // For integers:
......@@ -15720,8 +15785,8 @@ fn analyzeArithmetic(
1572015785 } else {
1572115786 return Air.internedToRef((try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, mod)).toIntern());
1572215787 }
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 };
1572515790 },
1572615791 .mulwrap => {
1572715792 // Integers only; floats are handled above.
......@@ -15765,8 +15830,8 @@ fn analyzeArithmetic(
1576515830 return mod.undefRef(resolved_type);
1576615831 }
1576715832 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 };
1577015835 },
1577115836 .mul_sat => {
1577215837 // Integers only; floats are checked above.
......@@ -15816,20 +15881,20 @@ fn analyzeArithmetic(
1581615881 try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, mod);
1581715882
1581815883 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 };
1582115886 },
1582215887 else => unreachable,
1582315888 }
1582415889 };
1582515890
15826 try sema.requireRuntimeBlock(block, src, rs.src);
15891 try sema.requireRuntimeBlock(block, src, runtime_src);
1582715892 if (block.wantSafety() and want_safety and scalar_tag == .Int) {
1582815893 if (mod.backendSupportsFeature(.safety_checked_instructions)) {
1582915894 _ = 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);
1583115896 } else {
15832 const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) {
15897 const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) {
1583315898 .add => .add_with_overflow,
1583415899 .sub => .sub_with_overflow,
1583515900 .mul => .mul_with_overflow,
......@@ -15866,7 +15931,7 @@ fn analyzeArithmetic(
1586615931 }
1586715932 }
1586815933 }
15869 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
15934 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
1587015935}
1587115936
1587215937fn analyzePtrArithmetic(
......@@ -32276,16 +32341,10 @@ fn compareIntsOnlyPossibleResult(
3227632341
3227732342 // For any other comparison, we need to know if the LHS value is
3227832343 // 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 };
3228432346
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 };
3228932348
3229032349 const ty = try mod.intType(
3229132350 if (is_negative) .signed else .unsigned,
......@@ -32294,24 +32353,18 @@ fn compareIntsOnlyPossibleResult(
3229432353 const pop_count = lhs_val.popCount(ty, mod);
3229532354
3229632355 if (is_negative) {
32297 break :edge .{
32298 .min = pop_count == 1,
32299 .max = false,
32300 };
32356 break :edge .{ pop_count == 1, false };
3230132357 } 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 };
3230632359 }
3230732360 };
3230832361
3230932362 assert(fits);
3231032363 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,
3231532368 .eq, .neq => unreachable,
3231632369 };
3231732370}
......@@ -32548,7 +32601,7 @@ const PeerResolveStrategy = enum {
3254832601 either,
3254932602 };
3255032603
32551 const res: struct { ReasonMethod, PeerResolveStrategy } = switch (s0) {
32604 const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) {
3255232605 .unknown => .{ .all_s1, s1 },
3255332606 .error_set => switch (s1) {
3255432607 .error_set => .{ .either, .error_set },
......@@ -32616,7 +32669,7 @@ const PeerResolveStrategy = enum {
3261632669 .exact => .{ .all_s0, .exact },
3261732670 };
3261832671
32619 switch (res[0]) {
32672 switch (reason_method) {
3262032673 .all_s0 => {
3262132674 if (!s0_is_a) {
3262232675 reason_peer.* = b_peer_idx;
......@@ -32633,7 +32686,7 @@ const PeerResolveStrategy = enum {
3263332686 },
3263432687 }
3263532688
32636 return res[1];
32689 return strat;
3263732690 }
3263832691
3263932692 fn select(ty: Type, mod: *Module) PeerResolveStrategy {
src/Zir.zig+29
......@@ -434,6 +434,10 @@ pub const Inst = struct {
434434 /// Payload is `Bin`.
435435 /// No OOB safety check is emitted.
436436 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,
437441 /// Emits a compile error if the operand is not `void`.
438442 /// Uses the `un_node` field.
439443 ensure_result_used,
......@@ -725,6 +729,9 @@ pub const Inst = struct {
725729 /// Check that operand type supports the dereference operand (.*).
726730 /// Uses the `un_node` field.
727731 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,
728735 /// A struct literal with a specified type, with no fields.
729736 /// Uses the `un_node` field.
730737 struct_init_empty,
......@@ -1069,6 +1076,7 @@ pub const Inst = struct {
10691076 .elem_ptr_node,
10701077 .elem_ptr_imm,
10711078 .elem_val_node,
1079 .elem_val_imm,
10721080 .ensure_result_used,
10731081 .ensure_result_non_error,
10741082 .ensure_err_union_payload_void,
......@@ -1145,6 +1153,7 @@ pub const Inst = struct {
11451153 .validate_struct_init,
11461154 .validate_array_init,
11471155 .validate_deref,
1156 .validate_destructure,
11481157 .struct_init_empty,
11491158 .struct_init,
11501159 .struct_init_ref,
......@@ -1295,6 +1304,7 @@ pub const Inst = struct {
12951304 .validate_struct_init,
12961305 .validate_array_init,
12971306 .validate_deref,
1307 .validate_destructure,
12981308 .@"export",
12991309 .export_value,
13001310 .set_runtime_safety,
......@@ -1369,6 +1379,7 @@ pub const Inst = struct {
13691379 .elem_ptr_node,
13701380 .elem_ptr_imm,
13711381 .elem_val_node,
1382 .elem_val_imm,
13721383 .field_ptr,
13731384 .field_ptr_init,
13741385 .field_val,
......@@ -1615,6 +1626,7 @@ pub const Inst = struct {
16151626 .elem_ptr_imm = .pl_node,
16161627 .elem_val = .pl_node,
16171628 .elem_val_node = .pl_node,
1629 .elem_val_imm = .elem_val_imm,
16181630 .ensure_result_used = .un_node,
16191631 .ensure_result_non_error = .un_node,
16201632 .ensure_err_union_payload_void = .un_node,
......@@ -1689,6 +1701,7 @@ pub const Inst = struct {
16891701 .validate_struct_init = .pl_node,
16901702 .validate_array_init = .pl_node,
16911703 .validate_deref = .un_node,
1704 .validate_destructure = .pl_node,
16921705 .struct_init_empty = .un_node,
16931706 .field_type = .pl_node,
16941707 .field_type_ref = .pl_node,
......@@ -2295,6 +2308,12 @@ pub const Inst = struct {
22952308 block: Ref, // If restored, the index is from this block's entrypoint
22962309 operand: Ref, // If non-error (or .none), then restore the index
22972310 },
2311 elem_val_imm: struct {
2312 /// The indexable value being accessed.
2313 operand: Ref,
2314 /// The index being accessed.
2315 idx: u32,
2316 },
22982317
22992318 // Make sure we don't accidentally add a field to make this union
23002319 // bigger than expected. Note that in Debug builds, Zig is allowed
......@@ -2334,6 +2353,7 @@ pub const Inst = struct {
23342353 defer_err_code,
23352354 save_err_ret_index,
23362355 restore_err_ret_index,
2356 elem_val_imm,
23372357 };
23382358 };
23392359
......@@ -3233,6 +3253,15 @@ pub const Inst = struct {
32333253 index: u32,
32343254 len: u32,
32353255 };
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 };
32363265};
32373266
32383267pub const SpecialProng = enum { none, @"else", under };
src/print_zir.zig+23
......@@ -242,6 +242,7 @@ const Writer = struct {
242242 .bool_br_or,
243243 => try self.writeBoolBr(stream, inst),
244244
245 .validate_destructure => try self.writeValidateDestructure(stream, inst),
245246 .validate_array_init_ty => try self.writeValidateArrayInitTy(stream, inst),
246247 .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
247248 .ptr_type => try self.writePtrType(stream, inst),
......@@ -357,6 +358,8 @@ const Writer = struct {
357358
358359 .for_len => try self.writePlNodeMultiOp(stream, inst),
359360
361 .elem_val_imm => try self.writeElemValImm(stream, inst),
362
360363 .elem_ptr_imm => try self.writeElemPtrImm(stream, inst),
361364
362365 .@"export" => try self.writePlNodeExport(stream, inst),
......@@ -585,6 +588,20 @@ const Writer = struct {
585588 try self.writeSrc(stream, inst_data.src());
586589 }
587590
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
588605 fn writeValidateArrayInitTy(
589606 self: *Writer,
590607 stream: anytype,
......@@ -892,6 +909,12 @@ const Writer = struct {
892909 try self.writeSrc(stream, inst_data.src());
893910 }
894911
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
895918 fn writeElemPtrImm(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
896919 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
897920 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 {
157157 _ = @import("behavior/decltest.zig");
158158 _ = @import("behavior/duplicated_test_names.zig");
159159 _ = @import("behavior/defer.zig");
160 _ = @import("behavior/destructure.zig");
160161 _ = @import("behavior/empty_tuple_fields.zig");
161162 _ = @import("behavior/empty_union.zig");
162163 _ = @import("behavior/enum.zig");
test/behavior/destructure.zig created+100
......@@ -0,0 +1,100 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const expect = std.testing.expect;
4
5test "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
23test "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
41test "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
59test "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
72test "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
85test "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 {
1313 var x: f32 = 0;
1414 _ = x + @floatFromInt(123);
1515}
16export fn e() void {
17 const x: u32, const y: u64 = @intCast(123);
18 _ = x + y;
19}
1620
1721// error
1822// backend=stage2
......@@ -26,3 +30,6 @@ export fn d() void {
2630// :9:10: note: use @as to provide explicit result type
2731// :14:13: error: @floatFromInt must have a known result type
2832// :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 @@
1export 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 @@
1export fn foo() void {
2 const x, const y = .{ 1, 2, 3 };
3 _ = .{ x, y };
4}
5
6export 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 @@
1export fn foo() void {
2 const x, const y = 123;
3 _ = .{ x, y };
4}
5
6export 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 @@
11pub fn main() void {
22 const x = 1;
3 const y, var z = .{ 2, 3 };
34}
45
56// error
67//
8// :3:18: error: unused local variable
9// :3:11: error: unused local constant
710// :2:11: error: unused local constant