| ... | ... | @@ -308,6 +308,9 @@ const ResultInfo = struct { |
| 308 | 308 | /// The expression must generate a pointer rather than a value, and the pointer will be coerced |
| 309 | 309 | /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type. |
| 310 | 310 | ref_coerced_ty: Zir.Inst.Ref, |
| 311 | /// Like `ref`, but the pointer will never be stored to, so local variables should not be |
| 312 | /// marked as possibly being mutated. |
| 313 | ref_const, |
| 311 | 314 | /// The expression must store its result into this typed pointer. The result instruction |
| 312 | 315 | /// from the expression must be ignored. |
| 313 | 316 | ptr: PtrResultLoc, |
| ... | ... | @@ -339,7 +342,7 @@ const ResultInfo = struct { |
| 339 | 342 | /// If the location does not have a known result type, returns `null`. |
| 340 | 343 | fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref { |
| 341 | 344 | return switch (rl) { |
| 342 | | .discard, .none, .ref, .inferred_ptr, .destructure => null, |
| 345 | .discard, .none, .ref, .ref_const, .inferred_ptr, .destructure => null, |
| 343 | 346 | .ty, .coerced_ty => |ty_ref| ty_ref, |
| 344 | 347 | .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node), |
| 345 | 348 | .ptr => |ptr| { |
| ... | ... | @@ -937,7 +940,11 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 937 | 940 | const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node); |
| 938 | 941 | _ = try gz.addUnNode(.validate_deref, lhs, node); |
| 939 | 942 | switch (ri.rl) { |
| 940 | | .ref, .ref_coerced_ty => return lhs, |
| 943 | .ref, |
| 944 | .ref_coerced_ty, |
| 945 | .ref_const, |
| 946 | => return lhs, |
| 947 | |
| 941 | 948 | else => { |
| 942 | 949 | const result = try gz.addUnNode(.load, lhs, node); |
| 943 | 950 | return rvalue(gz, ri, result, node); |
| ... | ... | @@ -973,6 +980,14 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 973 | 980 | |
| 974 | 981 | return gz.addUnNode(.optional_payload_safe_ptr, lhs, node); |
| 975 | 982 | }, |
| 983 | .ref_const => { |
| 984 | const lhs = try expr(gz, scope, .{ .rl = .ref_const }, tree.nodeData(node).node_and_token[0]); |
| 985 | |
| 986 | const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); |
| 987 | try emitDbgStmt(gz, cursor); |
| 988 | |
| 989 | return gz.addUnNode(.optional_payload_safe_ptr, lhs, node); |
| 990 | }, |
| 976 | 991 | else => { |
| 977 | 992 | const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node_and_token[0]); |
| 978 | 993 | |
| ... | ... | @@ -998,7 +1013,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 998 | 1013 | .field_name_start = str_index, |
| 999 | 1014 | }); |
| 1000 | 1015 | switch (ri.rl) { |
| 1001 | | .discard, .none, .ref, .inferred_ptr, .destructure => unreachable, // no result type |
| 1016 | .discard, .none, .ref, .ref_const, .inferred_ptr, .destructure => unreachable, // no result type |
| 1002 | 1017 | .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us |
| 1003 | 1018 | .ref_coerced_ty, .ptr => return rvalue(gz, ri, res, node), |
| 1004 | 1019 | } |
| ... | ... | @@ -1030,7 +1045,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 1030 | 1045 | return switchExpr(gz, scope, ri.br(), node, switch_full, .@"catch"); |
| 1031 | 1046 | } |
| 1032 | 1047 | switch (ri.rl) { |
| 1033 | | .ref, .ref_coerced_ty => return orelseCatchExpr( |
| 1048 | .ref, .ref_const, .ref_coerced_ty => return orelseCatchExpr( |
| 1034 | 1049 | gz, |
| 1035 | 1050 | scope, |
| 1036 | 1051 | ri, |
| ... | ... | @@ -1053,7 +1068,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 1053 | 1068 | } |
| 1054 | 1069 | }, |
| 1055 | 1070 | .@"orelse" => switch (ri.rl) { |
| 1056 | | .ref, .ref_coerced_ty => return orelseCatchExpr( |
| 1071 | .ref, .ref_const, .ref_coerced_ty => return orelseCatchExpr( |
| 1057 | 1072 | gz, |
| 1058 | 1073 | scope, |
| 1059 | 1074 | ri, |
| ... | ... | @@ -1511,7 +1526,7 @@ fn arrayInitExpr( |
| 1511 | 1526 | } |
| 1512 | 1527 | return .void_value; |
| 1513 | 1528 | }, |
| 1514 | | .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true), |
| 1529 | .ref, .ref_const => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true), |
| 1515 | 1530 | else => { |
| 1516 | 1531 | const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false); |
| 1517 | 1532 | return rvalue(gz, ri, array_inst, node); |
| ... | ... | @@ -1527,7 +1542,7 @@ fn arrayInitExpr( |
| 1527 | 1542 | } |
| 1528 | 1543 | return Zir.Inst.Ref.void_value; |
| 1529 | 1544 | }, |
| 1530 | | .ref => { |
| 1545 | .ref, .ref_const => { |
| 1531 | 1546 | const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements); |
| 1532 | 1547 | return gz.addUnTok(.ref, result, tree.firstToken(node)); |
| 1533 | 1548 | }, |
| ... | ... | @@ -1701,7 +1716,7 @@ fn structInitExpr( |
| 1701 | 1716 | const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node); |
| 1702 | 1717 | return rvalue(gz, ri, val, node); |
| 1703 | 1718 | }, |
| 1704 | | .none, .ref, .inferred_ptr => { |
| 1719 | .none, .ref, .ref_const, .inferred_ptr => { |
| 1705 | 1720 | return rvalue(gz, ri, .empty_tuple, node); |
| 1706 | 1721 | }, |
| 1707 | 1722 | .destructure => |destructure| { |
| ... | ... | @@ -1817,7 +1832,7 @@ fn structInitExpr( |
| 1817 | 1832 | const ty_inst = try typeExpr(gz, scope, type_expr); |
| 1818 | 1833 | _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); |
| 1819 | 1834 | switch (ri.rl) { |
| 1820 | | .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true), |
| 1835 | .ref, .ref_const => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true), |
| 1821 | 1836 | else => { |
| 1822 | 1837 | const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false); |
| 1823 | 1838 | return rvalue(gz, ri, struct_inst, node); |
| ... | ... | @@ -1834,7 +1849,7 @@ fn structInitExpr( |
| 1834 | 1849 | } |
| 1835 | 1850 | return .void_value; |
| 1836 | 1851 | }, |
| 1837 | | .ref => { |
| 1852 | .ref, .ref_const => { |
| 1838 | 1853 | const result = try structInitExprAnon(gz, scope, node, struct_init); |
| 1839 | 1854 | return gz.addUnTok(.ref, result, tree.firstToken(node)); |
| 1840 | 1855 | }, |
| ... | ... | @@ -5832,6 +5847,7 @@ fn tryExpr( |
| 5832 | 5847 | |
| 5833 | 5848 | const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) { |
| 5834 | 5849 | .ref, .ref_coerced_ty => .{ .ref, .try_ptr }, |
| 5850 | .ref_const => .{ .ref_const, .try_ptr }, |
| 5835 | 5851 | else => .{ .none, .@"try" }, |
| 5836 | 5852 | }; |
| 5837 | 5853 | const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr }; |
| ... | ... | @@ -5856,7 +5872,7 @@ fn tryExpr( |
| 5856 | 5872 | defer else_scope.unstack(); |
| 5857 | 5873 | |
| 5858 | 5874 | const err_tag = switch (ri.rl) { |
| 5859 | | .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr, |
| 5875 | .ref, .ref_const, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr, |
| 5860 | 5876 | else => Zir.Inst.Tag.err_union_code, |
| 5861 | 5877 | }; |
| 5862 | 5878 | const err_code = try else_scope.addUnNode(err_tag, operand, node); |
| ... | ... | @@ -5867,7 +5883,7 @@ fn tryExpr( |
| 5867 | 5883 | try else_scope.setTryBody(try_inst, operand); |
| 5868 | 5884 | const result = try_inst.toRef(); |
| 5869 | 5885 | switch (ri.rl) { |
| 5870 | | .ref, .ref_coerced_ty => return result, |
| 5886 | .ref, .ref_const, .ref_coerced_ty => return result, |
| 5871 | 5887 | else => return rvalue(parent_gz, ri, result, node), |
| 5872 | 5888 | } |
| 5873 | 5889 | } |
| ... | ... | @@ -5909,6 +5925,7 @@ fn orelseCatchExpr( |
| 5909 | 5925 | |
| 5910 | 5926 | const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) { |
| 5911 | 5927 | .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none }, |
| 5928 | .ref_const => .{ .rl = .ref_const, .ctx = if (do_err_trace) .error_handling_expr else .none }, |
| 5912 | 5929 | else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none }, |
| 5913 | 5930 | }; |
| 5914 | 5931 | // This could be a pointer or value depending on the `operand_ri` parameter. |
| ... | ... | @@ -5930,7 +5947,7 @@ fn orelseCatchExpr( |
| 5930 | 5947 | // This could be a pointer or value depending on `unwrap_op`. |
| 5931 | 5948 | const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node); |
| 5932 | 5949 | const then_result = switch (ri.rl) { |
| 5933 | | .ref, .ref_coerced_ty => unwrapped_payload, |
| 5950 | .ref, .ref_const, .ref_coerced_ty => unwrapped_payload, |
| 5934 | 5951 | else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node), |
| 5935 | 5952 | }; |
| 5936 | 5953 | _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node); |
| ... | ... | @@ -6026,8 +6043,9 @@ fn fieldAccess( |
| 6026 | 6043 | ) InnerError!Zir.Inst.Ref { |
| 6027 | 6044 | switch (ri.rl) { |
| 6028 | 6045 | .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node), |
| 6046 | .ref_const => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref_const }, node), |
| 6029 | 6047 | else => { |
| 6030 | | const access = try addFieldAccess(.field_ptr_load, gz, scope, .{ .rl = .ref }, node); |
| 6048 | const access = try addFieldAccess(.field_ptr_load, gz, scope, .{ .rl = .ref_const }, node); |
| 6031 | 6049 | return rvalue(gz, ri, access, node); |
| 6032 | 6050 | }, |
| 6033 | 6051 | } |
| ... | ... | @@ -6075,9 +6093,20 @@ fn arrayAccess( |
| 6075 | 6093 | |
| 6076 | 6094 | return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); |
| 6077 | 6095 | }, |
| 6096 | .ref_const => { |
| 6097 | const lhs_node, const rhs_node = tree.nodeData(node).node_and_node; |
| 6098 | const lhs = try expr(gz, scope, .{ .rl = .ref_const }, lhs_node); |
| 6099 | |
| 6100 | const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); |
| 6101 | |
| 6102 | const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, rhs_node); |
| 6103 | try emitDbgStmt(gz, cursor); |
| 6104 | |
| 6105 | return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }); |
| 6106 | }, |
| 6078 | 6107 | else => { |
| 6079 | 6108 | const lhs_node, const rhs_node = tree.nodeData(node).node_and_node; |
| 6080 | | const lhs = try expr(gz, scope, .{ .rl = .ref }, lhs_node); |
| 6109 | const lhs = try expr(gz, scope, .{ .rl = .ref_const }, lhs_node); |
| 6081 | 6110 | |
| 6082 | 6111 | const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); |
| 6083 | 6112 | |
| ... | ... | @@ -7092,11 +7121,15 @@ fn switchExpr( |
| 7092 | 7121 | |
| 7093 | 7122 | const catch_or_if_node = if (needs_non_err_handling) node else undefined; |
| 7094 | 7123 | const do_err_trace = needs_non_err_handling and astgen.fn_block != null; |
| 7095 | | const non_err_is_ref: bool = switch (non_err) { |
| 7124 | const non_err_is_ref: enum { no, yes, yes_const } = switch (non_err) { |
| 7096 | 7125 | .none, .peer_break_target => undefined, |
| 7097 | | .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty, |
| 7098 | | .@"if" => |if_full| if_full.payload_token != null and |
| 7099 | | tree.tokenTag(if_full.payload_token.?) == .asterisk, |
| 7126 | .@"catch" => switch (ri.rl) { |
| 7127 | .ref, .ref_coerced_ty => .yes, |
| 7128 | .ref_const => .yes_const, |
| 7129 | else => .no, |
| 7130 | }, |
| 7131 | .@"if" => |if_full| if (if_full.payload_token != null and |
| 7132 | tree.tokenTag(if_full.payload_token.?) == .asterisk) .yes else .no, |
| 7100 | 7133 | }; |
| 7101 | 7134 | |
| 7102 | 7135 | if (switch_full.label_token) |label_token| { |
| ... | ... | @@ -7285,8 +7318,12 @@ fn switchExpr( |
| 7285 | 7318 | block_scope.instructions_top = GenZir.unstacked_top; |
| 7286 | 7319 | |
| 7287 | 7320 | const operand_ri: ResultInfo = .{ |
| 7288 | | .rl = if (any_payload_is_ref or |
| 7289 | | (needs_non_err_handling and non_err_is_ref)) .ref else .none, |
| 7321 | .rl = loc: { |
| 7322 | if (any_payload_is_ref) break :loc .ref; |
| 7323 | if (needs_non_err_handling and non_err_is_ref == .yes) break :loc .ref; |
| 7324 | if (needs_non_err_handling and non_err_is_ref == .yes_const) break :loc .ref_const; |
| 7325 | break :loc .none; |
| 7326 | }, |
| 7290 | 7327 | .ctx = if (do_err_trace) .error_handling_expr else .none, |
| 7291 | 7328 | }; |
| 7292 | 7329 | |
| ... | ... | @@ -7454,10 +7491,10 @@ fn switchExpr( |
| 7454 | 7491 | .@"catch" => { |
| 7455 | 7492 | // We always effectively capture the error union payload; we use |
| 7456 | 7493 | // it to `break` from the entire `switch_block_err_union`. |
| 7457 | | non_err_capture = if (non_err_is_ref) .by_ref else .by_val; |
| 7494 | non_err_capture = if (non_err_is_ref != .no) .by_ref else .by_val; |
| 7458 | 7495 | |
| 7459 | 7496 | const then_result = switch (ri.rl) { |
| 7460 | | .ref, .ref_coerced_ty => non_err_payload_inst.toRef(), |
| 7497 | .ref, .ref_const, .ref_coerced_ty => non_err_payload_inst.toRef(), |
| 7461 | 7498 | else => try rvalue( |
| 7462 | 7499 | &scratch_scope, |
| 7463 | 7500 | block_scope.break_result_info, |
| ... | ... | @@ -7478,13 +7515,13 @@ fn switchExpr( |
| 7478 | 7515 | const then_node = if_full.ast.then_expr; |
| 7479 | 7516 | const then_sub_scope: *Scope = scope: { |
| 7480 | 7517 | if (if_full.payload_token) |payload_token| { |
| 7481 | | const ident_token = payload_token + @intFromBool(non_err_is_ref); |
| 7518 | const ident_token = payload_token + @intFromBool(non_err_is_ref != .no); |
| 7482 | 7519 | const ident_name = try astgen.identAsString(ident_token); |
| 7483 | 7520 | const ident_name_str = tree.tokenSlice(ident_token); |
| 7484 | 7521 | if (mem.eql(u8, "_", ident_name_str)) { |
| 7485 | 7522 | break :scope &scratch_scope.base; |
| 7486 | 7523 | } |
| 7487 | | non_err_capture = if (non_err_is_ref) .by_ref else .by_val; |
| 7524 | non_err_capture = if (non_err_is_ref != .no) .by_ref else .by_val; |
| 7488 | 7525 | try astgen.detectLocalShadowing(&scratch_scope.base, ident_name, ident_token, ident_name_str, .capture); |
| 7489 | 7526 | payload_val_scope = .{ |
| 7490 | 7527 | .parent = &scratch_scope.base, |
| ... | ... | @@ -7522,7 +7559,7 @@ fn switchExpr( |
| 7522 | 7559 | non_err_info = .{ |
| 7523 | 7560 | .body_len = @intCast(body_len), |
| 7524 | 7561 | .capture = non_err_capture, |
| 7525 | | .operand_is_ref = non_err_is_ref, |
| 7562 | .operand_is_ref = non_err_is_ref != .no, |
| 7526 | 7563 | }; |
| 7527 | 7564 | } |
| 7528 | 7565 | |
| ... | ... | @@ -8245,7 +8282,7 @@ fn localVarRef( |
| 8245 | 8282 | } |
| 8246 | 8283 | |
| 8247 | 8284 | switch (ri.rl) { |
| 8248 | | .ref, .ref_coerced_ty => { |
| 8285 | .ref, .ref_const, .ref_coerced_ty => { |
| 8249 | 8286 | const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure( |
| 8250 | 8287 | gz, |
| 8251 | 8288 | ident, |
| ... | ... | @@ -8254,7 +8291,7 @@ fn localVarRef( |
| 8254 | 8291 | .{ .token = local_ptr.token_src }, |
| 8255 | 8292 | name_str_index, |
| 8256 | 8293 | ) else local_ptr.ptr; |
| 8257 | | local_ptr.used_as_lvalue = true; |
| 8294 | if (ri.rl != .ref_const) local_ptr.used_as_lvalue = true; |
| 8258 | 8295 | return ptr_inst; |
| 8259 | 8296 | }, |
| 8260 | 8297 | else => { |
| ... | ... | @@ -8303,7 +8340,7 @@ fn localVarRef( |
| 8303 | 8340 | |
| 8304 | 8341 | if (found_namespaces_out > 0 and found_needs_tunnel) { |
| 8305 | 8342 | switch (ri.rl) { |
| 8306 | | .ref, .ref_coerced_ty => return tunnelThroughClosure( |
| 8343 | .ref, .ref_const, .ref_coerced_ty => return tunnelThroughClosure( |
| 8307 | 8344 | gz, |
| 8308 | 8345 | ident, |
| 8309 | 8346 | found_namespaces_out, |
| ... | ... | @@ -8326,7 +8363,7 @@ fn localVarRef( |
| 8326 | 8363 | } |
| 8327 | 8364 | |
| 8328 | 8365 | switch (ri.rl) { |
| 8329 | | .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token), |
| 8366 | .ref, .ref_const, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token), |
| 8330 | 8367 | else => { |
| 8331 | 8368 | const result = try gz.addStrTok(.decl_val, name_str_index, ident_token); |
| 8332 | 8369 | return rvalueNoCoercePreRef(gz, ri, result, ident); |
| ... | ... | @@ -9108,9 +9145,15 @@ fn builtinCall( |
| 9108 | 9145 | .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name), |
| 9109 | 9146 | }); |
| 9110 | 9147 | }, |
| 9148 | .ref_const => { |
| 9149 | return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ |
| 9150 | .lhs = try expr(gz, scope, .{ .rl = .ref_const }, params[0]), |
| 9151 | .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name), |
| 9152 | }); |
| 9153 | }, |
| 9111 | 9154 | else => { |
| 9112 | 9155 | const result = try gz.addPlNode(.field_ptr_named_load, node, Zir.Inst.FieldNamed{ |
| 9113 | | .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]), |
| 9156 | .lhs = try expr(gz, scope, .{ .rl = .ref_const }, params[0]), |
| 9114 | 9157 | .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name), |
| 9115 | 9158 | }); |
| 9116 | 9159 | return rvalue(gz, ri, result, node); |
| ... | ... | @@ -10530,7 +10573,7 @@ fn rvalueInner( |
| 10530 | 10573 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); |
| 10531 | 10574 | return .void_value; |
| 10532 | 10575 | }, |
| 10533 | | .ref, .ref_coerced_ty => { |
| 10576 | .ref, .ref_const, .ref_coerced_ty => { |
| 10534 | 10577 | const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: { |
| 10535 | 10578 | const ptr_ty = ri.rl.ref_coerced_ty; |
| 10536 | 10579 | break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{ |