| author | |
| committer | |
| log | ea7bdeb67d474526732b117992971603e4065f98 |
| tree | 5dcd7d8c1cdd311cb40505d986c8fbceab52dfc9 |
| parent | 9fd3aeb8088cd9a3b0744d5f508ca256a2bbf19f |
| parent | 7e9b23e6dce4d87615acd635f3731731a8601d39 |
| signature |
stage2 generic functions18 files changed, 1823 insertions(+), 926 deletions(-)
lib/std/hash_map.zig+1-1| ... | ... | @@ -563,7 +563,7 @@ pub fn HashMap( |
| 563 | 563 | return self.unmanaged.getPtrContext(key, self.ctx); |
| 564 | 564 | } |
| 565 | 565 | pub fn getPtrAdapted(self: Self, key: anytype, ctx: anytype) ?*V { |
| 566 | return self.unmanaged.getPtrAdapted(key, self.ctx); | |
| 566 | return self.unmanaged.getPtrAdapted(key, ctx); | |
| 567 | 567 | } |
| 568 | 568 | |
| 569 | 569 | /// Finds the key and value associated with a key in the map |
lib/std/zig/ast.zig+3| ... | ... | @@ -2198,6 +2198,9 @@ pub const full = struct { |
| 2198 | 2198 | .type_expr = param_type, |
| 2199 | 2199 | }; |
| 2200 | 2200 | } |
| 2201 | if (token_tags[it.tok_i] == .comma) { | |
| 2202 | it.tok_i += 1; | |
| 2203 | } | |
| 2201 | 2204 | if (token_tags[it.tok_i] == .r_paren) { |
| 2202 | 2205 | return null; |
| 2203 | 2206 | } |
src/AstGen.zig+262-288| ... | ... | @@ -42,7 +42,7 @@ const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 42 | 42 | |
| 43 | 43 | fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 { |
| 44 | 44 | const fields = std.meta.fields(@TypeOf(extra)); |
| 45 | try astgen.extra.ensureCapacity(astgen.gpa, astgen.extra.items.len + fields.len); | |
| 45 | try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len); | |
| 46 | 46 | return addExtraAssumeCapacity(astgen, extra); |
| 47 | 47 | } |
| 48 | 48 | |
| ... | ... | @@ -195,6 +195,9 @@ pub const ResultLoc = union(enum) { |
| 195 | 195 | none_or_ref, |
| 196 | 196 | /// The expression will be coerced into this type, but it will be evaluated as an rvalue. |
| 197 | 197 | ty: Zir.Inst.Ref, |
| 198 | /// Same as `ty` but it is guaranteed that Sema will additionall perform the coercion, | |
| 199 | /// so no `as` instruction needs to be emitted. | |
| 200 | coerced_ty: Zir.Inst.Ref, | |
| 198 | 201 | /// The expression must store its result into this typed pointer. The result instruction |
| 199 | 202 | /// from the expression must be ignored. |
| 200 | 203 | ptr: Zir.Inst.Ref, |
| ... | ... | @@ -225,7 +228,7 @@ pub const ResultLoc = union(enum) { |
| 225 | 228 | fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy { |
| 226 | 229 | switch (rl) { |
| 227 | 230 | // In this branch there will not be any store_to_block_ptr instructions. |
| 228 | .discard, .none, .none_or_ref, .ty, .ref => return .{ | |
| 231 | .discard, .none, .none_or_ref, .ty, .coerced_ty, .ref => return .{ | |
| 229 | 232 | .tag = .break_operand, |
| 230 | 233 | .elide_store_to_block_ptr_instructions = false, |
| 231 | 234 | }, |
| ... | ... | @@ -259,13 +262,15 @@ pub const ResultLoc = union(enum) { |
| 259 | 262 | |
| 260 | 263 | pub const align_rl: ResultLoc = .{ .ty = .u16_type }; |
| 261 | 264 | pub const bool_rl: ResultLoc = .{ .ty = .bool_type }; |
| 265 | pub const type_rl: ResultLoc = .{ .ty = .type_type }; | |
| 266 | pub const coerced_type_rl: ResultLoc = .{ .coerced_ty = .type_type }; | |
| 262 | 267 | |
| 263 | 268 | fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!Zir.Inst.Ref { |
| 264 | 269 | const prev_force_comptime = gz.force_comptime; |
| 265 | 270 | gz.force_comptime = true; |
| 266 | 271 | defer gz.force_comptime = prev_force_comptime; |
| 267 | 272 | |
| 268 | return expr(gz, scope, .{ .ty = .type_type }, type_node); | |
| 273 | return expr(gz, scope, coerced_type_rl, type_node); | |
| 269 | 274 | } |
| 270 | 275 | |
| 271 | 276 | /// Same as `expr` but fails with a compile error if the result type is `noreturn`. |
| ... | ... | @@ -1046,71 +1051,55 @@ fn fnProtoExpr( |
| 1046 | 1051 | }; |
| 1047 | 1052 | assert(!is_extern); |
| 1048 | 1053 | |
| 1049 | // The AST params array does not contain anytype and ... parameters. | |
| 1050 | // We must iterate to count how many param types to allocate. | |
| 1051 | const param_count = blk: { | |
| 1052 | var count: usize = 0; | |
| 1053 | var it = fn_proto.iterate(tree.*); | |
| 1054 | while (it.next()) |param| { | |
| 1055 | if (param.anytype_ellipsis3) |token| switch (token_tags[token]) { | |
| 1056 | .ellipsis3 => break, | |
| 1057 | .keyword_anytype => {}, | |
| 1058 | else => unreachable, | |
| 1059 | }; | |
| 1060 | count += 1; | |
| 1061 | } | |
| 1062 | break :blk count; | |
| 1063 | }; | |
| 1064 | const param_types = try gpa.alloc(Zir.Inst.Ref, param_count); | |
| 1065 | defer gpa.free(param_types); | |
| 1066 | ||
| 1067 | const bits_per_param = 1; | |
| 1068 | const params_per_u32 = 32 / bits_per_param; | |
| 1069 | // We only need this if there are greater than params_per_u32 fields. | |
| 1070 | var bit_bag = ArrayListUnmanaged(u32){}; | |
| 1071 | defer bit_bag.deinit(gpa); | |
| 1072 | var cur_bit_bag: u32 = 0; | |
| 1073 | var is_var_args = false; | |
| 1074 | { | |
| 1054 | const is_var_args = is_var_args: { | |
| 1075 | 1055 | var param_type_i: usize = 0; |
| 1076 | 1056 | var it = fn_proto.iterate(tree.*); |
| 1077 | 1057 | while (it.next()) |param| : (param_type_i += 1) { |
| 1078 | if (param_type_i % params_per_u32 == 0 and param_type_i != 0) { | |
| 1079 | try bit_bag.append(gpa, cur_bit_bag); | |
| 1080 | cur_bit_bag = 0; | |
| 1081 | } | |
| 1082 | 1058 | const is_comptime = if (param.comptime_noalias) |token| |
| 1083 | 1059 | token_tags[token] == .keyword_comptime |
| 1084 | 1060 | else |
| 1085 | 1061 | false; |
| 1086 | cur_bit_bag = (cur_bit_bag >> bits_per_param) | | |
| 1087 | (@as(u32, @boolToInt(is_comptime)) << 31); | |
| 1088 | 1062 | |
| 1089 | if (param.anytype_ellipsis3) |token| { | |
| 1063 | const is_anytype = if (param.anytype_ellipsis3) |token| blk: { | |
| 1090 | 1064 | switch (token_tags[token]) { |
| 1091 | .keyword_anytype => { | |
| 1092 | param_types[param_type_i] = .none; | |
| 1093 | continue; | |
| 1094 | }, | |
| 1095 | .ellipsis3 => { | |
| 1096 | is_var_args = true; | |
| 1097 | break; | |
| 1098 | }, | |
| 1065 | .keyword_anytype => break :blk true, | |
| 1066 | .ellipsis3 => break :is_var_args true, | |
| 1099 | 1067 | else => unreachable, |
| 1100 | 1068 | } |
| 1101 | } | |
| 1102 | const param_type_node = param.type_expr; | |
| 1103 | assert(param_type_node != 0); | |
| 1104 | param_types[param_type_i] = | |
| 1105 | try expr(gz, scope, .{ .ty = .type_type }, param_type_node); | |
| 1106 | } | |
| 1107 | assert(param_type_i == param_count); | |
| 1069 | } else false; | |
| 1070 | ||
| 1071 | const param_name: u32 = if (param.name_token) |name_token| blk: { | |
| 1072 | if (mem.eql(u8, "_", tree.tokenSlice(name_token))) | |
| 1073 | break :blk 0; | |
| 1108 | 1074 | |
| 1109 | const empty_slot_count = params_per_u32 - (param_type_i % params_per_u32); | |
| 1110 | if (empty_slot_count < params_per_u32) { | |
| 1111 | cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_param); | |
| 1075 | break :blk try astgen.identAsString(name_token); | |
| 1076 | } else 0; | |
| 1077 | ||
| 1078 | if (is_anytype) { | |
| 1079 | const name_token = param.name_token orelse param.anytype_ellipsis3.?; | |
| 1080 | ||
| 1081 | const tag: Zir.Inst.Tag = if (is_comptime) | |
| 1082 | .param_anytype_comptime | |
| 1083 | else | |
| 1084 | .param_anytype; | |
| 1085 | _ = try gz.addStrTok(tag, param_name, name_token); | |
| 1086 | } else { | |
| 1087 | const param_type_node = param.type_expr; | |
| 1088 | assert(param_type_node != 0); | |
| 1089 | var param_gz = gz.makeSubBlock(scope); | |
| 1090 | defer param_gz.instructions.deinit(gpa); | |
| 1091 | const param_type = try expr(&param_gz, scope, coerced_type_rl, param_type_node); | |
| 1092 | const param_inst_expected = @intCast(u32, astgen.instructions.len + 1); | |
| 1093 | _ = try param_gz.addBreak(.break_inline, param_inst_expected, param_type); | |
| 1094 | const main_tokens = tree.nodes.items(.main_token); | |
| 1095 | const name_token = param.name_token orelse main_tokens[param_type_node]; | |
| 1096 | const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; | |
| 1097 | const param_inst = try gz.addParam(tag, name_token, param_name, param_gz.instructions.items); | |
| 1098 | assert(param_inst_expected == param_inst); | |
| 1099 | } | |
| 1112 | 1100 | } |
| 1113 | } | |
| 1101 | break :is_var_args false; | |
| 1102 | }; | |
| 1114 | 1103 | |
| 1115 | 1104 | const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { |
| 1116 | 1105 | break :inst try expr(gz, scope, align_rl, fn_proto.ast.align_expr); |
| ... | ... | @@ -1124,15 +1113,13 @@ fn fnProtoExpr( |
| 1124 | 1113 | if (is_inferred_error) { |
| 1125 | 1114 | return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); |
| 1126 | 1115 | } |
| 1127 | const return_type_inst = try AstGen.expr( | |
| 1128 | gz, | |
| 1129 | scope, | |
| 1130 | .{ .ty = .type_type }, | |
| 1131 | fn_proto.ast.return_type, | |
| 1132 | ); | |
| 1116 | var ret_gz = gz.makeSubBlock(scope); | |
| 1117 | defer ret_gz.instructions.deinit(gpa); | |
| 1118 | const ret_ty = try expr(&ret_gz, scope, coerced_type_rl, fn_proto.ast.return_type); | |
| 1119 | const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); | |
| 1133 | 1120 | |
| 1134 | 1121 | const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0) |
| 1135 | try AstGen.expr( | |
| 1122 | try expr( | |
| 1136 | 1123 | gz, |
| 1137 | 1124 | scope, |
| 1138 | 1125 | .{ .ty = .calling_convention_type }, |
| ... | ... | @@ -1143,8 +1130,9 @@ fn fnProtoExpr( |
| 1143 | 1130 | |
| 1144 | 1131 | const result = try gz.addFunc(.{ |
| 1145 | 1132 | .src_node = fn_proto.ast.proto_node, |
| 1146 | .ret_ty = return_type_inst, | |
| 1147 | .param_types = param_types, | |
| 1133 | .param_block = 0, | |
| 1134 | .ret_ty = ret_gz.instructions.items, | |
| 1135 | .ret_br = ret_br, | |
| 1148 | 1136 | .body = &[0]Zir.Inst.Index{}, |
| 1149 | 1137 | .cc = cc, |
| 1150 | 1138 | .align_inst = align_inst, |
| ... | ... | @@ -1153,8 +1141,6 @@ fn fnProtoExpr( |
| 1153 | 1141 | .is_inferred_error = false, |
| 1154 | 1142 | .is_test = false, |
| 1155 | 1143 | .is_extern = false, |
| 1156 | .cur_bit_bag = cur_bit_bag, | |
| 1157 | .bit_bag = bit_bag.items, | |
| 1158 | 1144 | }); |
| 1159 | 1145 | return rvalue(gz, rl, result, fn_proto.ast.proto_node); |
| 1160 | 1146 | } |
| ... | ... | @@ -1239,7 +1225,7 @@ fn arrayInitExpr( |
| 1239 | 1225 | return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); |
| 1240 | 1226 | } |
| 1241 | 1227 | }, |
| 1242 | .ty => |ty_inst| { | |
| 1228 | .ty, .coerced_ty => |ty_inst| { | |
| 1243 | 1229 | if (types.array != .none) { |
| 1244 | 1230 | const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, .array_init); |
| 1245 | 1231 | return rvalue(gz, rl, result, node); |
| ... | ... | @@ -1408,7 +1394,7 @@ fn structInitExpr( |
| 1408 | 1394 | return structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon); |
| 1409 | 1395 | } |
| 1410 | 1396 | }, |
| 1411 | .ty => |ty_inst| { | |
| 1397 | .ty, .coerced_ty => |ty_inst| { | |
| 1412 | 1398 | if (struct_init.ast.type_expr == 0) { |
| 1413 | 1399 | return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); |
| 1414 | 1400 | } |
| ... | ... | @@ -1447,8 +1433,8 @@ fn structInitExprRlNone( |
| 1447 | 1433 | const init_inst = try gz.addPlNode(tag, node, Zir.Inst.StructInitAnon{ |
| 1448 | 1434 | .fields_len = @intCast(u32, fields_list.len), |
| 1449 | 1435 | }); |
| 1450 | try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + | |
| 1451 | fields_list.len * @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len); | |
| 1436 | try astgen.extra.ensureUnusedCapacity(gpa, fields_list.len * | |
| 1437 | @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len); | |
| 1452 | 1438 | for (fields_list) |field| { |
| 1453 | 1439 | _ = gz.astgen.addExtraAssumeCapacity(field); |
| 1454 | 1440 | } |
| ... | ... | @@ -1520,8 +1506,8 @@ fn structInitExprRlTy( |
| 1520 | 1506 | const init_inst = try gz.addPlNode(tag, node, Zir.Inst.StructInit{ |
| 1521 | 1507 | .fields_len = @intCast(u32, fields_list.len), |
| 1522 | 1508 | }); |
| 1523 | try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + | |
| 1524 | fields_list.len * @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len); | |
| 1509 | try astgen.extra.ensureUnusedCapacity(gpa, fields_list.len * | |
| 1510 | @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len); | |
| 1525 | 1511 | for (fields_list) |field| { |
| 1526 | 1512 | _ = gz.astgen.addExtraAssumeCapacity(field); |
| 1527 | 1513 | } |
| ... | ... | @@ -1918,7 +1904,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 1918 | 1904 | // ZIR instructions that might be a type other than `noreturn` or `void`. |
| 1919 | 1905 | .add, |
| 1920 | 1906 | .addwrap, |
| 1921 | .arg, | |
| 1907 | .param, | |
| 1908 | .param_comptime, | |
| 1909 | .param_anytype, | |
| 1910 | .param_anytype_comptime, | |
| 1922 | 1911 | .alloc, |
| 1923 | 1912 | .alloc_mut, |
| 1924 | 1913 | .alloc_comptime, |
| ... | ... | @@ -2488,7 +2477,7 @@ fn varDecl( |
| 2488 | 2477 | // Move the init_scope instructions into the parent scope, swapping |
| 2489 | 2478 | // store_to_block_ptr for store_to_inferred_ptr. |
| 2490 | 2479 | const expected_len = parent_zir.items.len + init_scope.instructions.items.len; |
| 2491 | try parent_zir.ensureCapacity(gpa, expected_len); | |
| 2480 | try parent_zir.ensureTotalCapacity(gpa, expected_len); | |
| 2492 | 2481 | for (init_scope.instructions.items) |src_inst| { |
| 2493 | 2482 | if (zir_tags[src_inst] == .store_to_block_ptr) { |
| 2494 | 2483 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) { |
| ... | ... | @@ -2634,7 +2623,7 @@ fn assignOp( |
| 2634 | 2623 | const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); |
| 2635 | 2624 | const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); |
| 2636 | 2625 | const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node); |
| 2637 | const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs); | |
| 2626 | const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs); | |
| 2638 | 2627 | |
| 2639 | 2628 | const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{ |
| 2640 | 2629 | .lhs = lhs, |
| ... | ... | @@ -2750,10 +2739,10 @@ fn ptrType( |
| 2750 | 2739 | } |
| 2751 | 2740 | |
| 2752 | 2741 | const gpa = gz.astgen.gpa; |
| 2753 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | |
| 2754 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | |
| 2755 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | |
| 2756 | @typeInfo(Zir.Inst.PtrType).Struct.fields.len + trailing_count); | |
| 2742 | try gz.instructions.ensureUnusedCapacity(gpa, 1); | |
| 2743 | try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); | |
| 2744 | try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).Struct.fields.len + | |
| 2745 | trailing_count); | |
| 2757 | 2746 | |
| 2758 | 2747 | const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{ .elem_type = elem_type }); |
| 2759 | 2748 | if (sentinel_ref != .none) { |
| ... | ... | @@ -2899,6 +2888,16 @@ fn fnDecl( |
| 2899 | 2888 | }; |
| 2900 | 2889 | defer decl_gz.instructions.deinit(gpa); |
| 2901 | 2890 | |
| 2891 | var fn_gz: GenZir = .{ | |
| 2892 | .force_comptime = false, | |
| 2893 | .in_defer = false, | |
| 2894 | .decl_node_index = fn_proto.ast.proto_node, | |
| 2895 | .decl_line = decl_gz.decl_line, | |
| 2896 | .parent = &decl_gz.base, | |
| 2897 | .astgen = astgen, | |
| 2898 | }; | |
| 2899 | defer fn_gz.instructions.deinit(gpa); | |
| 2900 | ||
| 2902 | 2901 | // TODO: support noinline |
| 2903 | 2902 | const is_pub = fn_proto.visib_token != null; |
| 2904 | 2903 | const is_export = blk: { |
| ... | ... | @@ -2913,80 +2912,82 @@ fn fnDecl( |
| 2913 | 2912 | const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false; |
| 2914 | 2913 | break :blk token_tags[maybe_inline_token] == .keyword_inline; |
| 2915 | 2914 | }; |
| 2916 | const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { | |
| 2917 | break :inst try expr(&decl_gz, &decl_gz.base, align_rl, fn_proto.ast.align_expr); | |
| 2918 | }; | |
| 2919 | const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: { | |
| 2920 | break :inst try comptimeExpr(&decl_gz, &decl_gz.base, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr); | |
| 2921 | }; | |
| 2922 | ||
| 2923 | try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, section_inst != .none); | |
| 2924 | ||
| 2925 | // The AST params array does not contain anytype and ... parameters. | |
| 2926 | // We must iterate to count how many param types to allocate. | |
| 2927 | const param_count = blk: { | |
| 2928 | var count: usize = 0; | |
| 2929 | var it = fn_proto.iterate(tree.*); | |
| 2930 | while (it.next()) |param| { | |
| 2931 | if (param.anytype_ellipsis3) |token| switch (token_tags[token]) { | |
| 2932 | .ellipsis3 => break, | |
| 2933 | .keyword_anytype => {}, | |
| 2934 | else => unreachable, | |
| 2935 | }; | |
| 2936 | count += 1; | |
| 2937 | } | |
| 2938 | break :blk count; | |
| 2939 | }; | |
| 2940 | const param_types = try gpa.alloc(Zir.Inst.Ref, param_count); | |
| 2941 | defer gpa.free(param_types); | |
| 2915 | try wip_decls.next(gpa, is_pub, is_export, fn_proto.ast.align_expr != 0, fn_proto.ast.section_expr != 0); | |
| 2942 | 2916 | |
| 2943 | const bits_per_param = 1; | |
| 2944 | const params_per_u32 = 32 / bits_per_param; | |
| 2945 | // We only need this if there are greater than params_per_u32 fields. | |
| 2946 | var bit_bag = ArrayListUnmanaged(u32){}; | |
| 2947 | defer bit_bag.deinit(gpa); | |
| 2948 | var cur_bit_bag: u32 = 0; | |
| 2949 | var is_var_args = false; | |
| 2950 | { | |
| 2917 | var params_scope = &fn_gz.base; | |
| 2918 | const is_var_args = is_var_args: { | |
| 2951 | 2919 | var param_type_i: usize = 0; |
| 2952 | 2920 | var it = fn_proto.iterate(tree.*); |
| 2953 | 2921 | while (it.next()) |param| : (param_type_i += 1) { |
| 2954 | if (param_type_i % params_per_u32 == 0 and param_type_i != 0) { | |
| 2955 | try bit_bag.append(gpa, cur_bit_bag); | |
| 2956 | cur_bit_bag = 0; | |
| 2957 | } | |
| 2958 | 2922 | const is_comptime = if (param.comptime_noalias) |token| |
| 2959 | 2923 | token_tags[token] == .keyword_comptime |
| 2960 | 2924 | else |
| 2961 | 2925 | false; |
| 2962 | cur_bit_bag = (cur_bit_bag >> bits_per_param) | | |
| 2963 | (@as(u32, @boolToInt(is_comptime)) << 31); | |
| 2964 | 2926 | |
| 2965 | if (param.anytype_ellipsis3) |token| { | |
| 2927 | const is_anytype = if (param.anytype_ellipsis3) |token| blk: { | |
| 2966 | 2928 | switch (token_tags[token]) { |
| 2967 | .keyword_anytype => { | |
| 2968 | param_types[param_type_i] = .none; | |
| 2969 | continue; | |
| 2970 | }, | |
| 2971 | .ellipsis3 => { | |
| 2972 | is_var_args = true; | |
| 2973 | break; | |
| 2974 | }, | |
| 2929 | .keyword_anytype => break :blk true, | |
| 2930 | .ellipsis3 => break :is_var_args true, | |
| 2975 | 2931 | else => unreachable, |
| 2976 | 2932 | } |
| 2977 | } | |
| 2978 | const param_type_node = param.type_expr; | |
| 2979 | assert(param_type_node != 0); | |
| 2980 | param_types[param_type_i] = | |
| 2981 | try expr(&decl_gz, &decl_gz.base, .{ .ty = .type_type }, param_type_node); | |
| 2982 | } | |
| 2983 | assert(param_type_i == param_count); | |
| 2933 | } else false; | |
| 2934 | ||
| 2935 | const param_name: u32 = if (param.name_token) |name_token| blk: { | |
| 2936 | if (mem.eql(u8, "_", tree.tokenSlice(name_token))) | |
| 2937 | break :blk 0; | |
| 2938 | ||
| 2939 | const param_name = try astgen.identAsString(name_token); | |
| 2940 | if (!is_extern) { | |
| 2941 | try astgen.detectLocalShadowing(params_scope, param_name, name_token); | |
| 2942 | } | |
| 2943 | break :blk param_name; | |
| 2944 | } else if (!is_extern) { | |
| 2945 | if (param.anytype_ellipsis3) |tok| { | |
| 2946 | return astgen.failTok(tok, "missing parameter name", .{}); | |
| 2947 | } else { | |
| 2948 | return astgen.failNode(param.type_expr, "missing parameter name", .{}); | |
| 2949 | } | |
| 2950 | } else 0; | |
| 2984 | 2951 | |
| 2985 | const empty_slot_count = params_per_u32 - (param_type_i % params_per_u32); | |
| 2986 | if (empty_slot_count < params_per_u32) { | |
| 2987 | cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_param); | |
| 2952 | const param_inst = if (is_anytype) param: { | |
| 2953 | const name_token = param.name_token orelse param.anytype_ellipsis3.?; | |
| 2954 | const tag: Zir.Inst.Tag = if (is_comptime) | |
| 2955 | .param_anytype_comptime | |
| 2956 | else | |
| 2957 | .param_anytype; | |
| 2958 | break :param try decl_gz.addStrTok(tag, param_name, name_token); | |
| 2959 | } else param: { | |
| 2960 | const param_type_node = param.type_expr; | |
| 2961 | assert(param_type_node != 0); | |
| 2962 | var param_gz = decl_gz.makeSubBlock(scope); | |
| 2963 | defer param_gz.instructions.deinit(gpa); | |
| 2964 | const param_type = try expr(&param_gz, params_scope, coerced_type_rl, param_type_node); | |
| 2965 | const param_inst_expected = @intCast(u32, astgen.instructions.len + 1); | |
| 2966 | _ = try param_gz.addBreak(.break_inline, param_inst_expected, param_type); | |
| 2967 | ||
| 2968 | const main_tokens = tree.nodes.items(.main_token); | |
| 2969 | const name_token = param.name_token orelse main_tokens[param_type_node]; | |
| 2970 | const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param; | |
| 2971 | const param_inst = try decl_gz.addParam(tag, name_token, param_name, param_gz.instructions.items); | |
| 2972 | assert(param_inst_expected == param_inst); | |
| 2973 | break :param indexToRef(param_inst); | |
| 2974 | }; | |
| 2975 | ||
| 2976 | if (param_name == 0) continue; | |
| 2977 | ||
| 2978 | const sub_scope = try astgen.arena.create(Scope.LocalVal); | |
| 2979 | sub_scope.* = .{ | |
| 2980 | .parent = params_scope, | |
| 2981 | .gen_zir = &decl_gz, | |
| 2982 | .name = param_name, | |
| 2983 | .inst = param_inst, | |
| 2984 | .token_src = param.name_token.?, | |
| 2985 | .id_cat = .@"function parameter", | |
| 2986 | }; | |
| 2987 | params_scope = &sub_scope.base; | |
| 2988 | 2988 | } |
| 2989 | } | |
| 2989 | break :is_var_args false; | |
| 2990 | }; | |
| 2990 | 2991 | |
| 2991 | 2992 | const lib_name: u32 = if (fn_proto.lib_name) |lib_name_token| blk: { |
| 2992 | 2993 | const lib_name_str = try astgen.strLitAsString(lib_name_token); |
| ... | ... | @@ -2996,12 +2997,17 @@ fn fnDecl( |
| 2996 | 2997 | const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; |
| 2997 | 2998 | const is_inferred_error = token_tags[maybe_bang] == .bang; |
| 2998 | 2999 | |
| 2999 | const return_type_inst = try AstGen.expr( | |
| 3000 | &decl_gz, | |
| 3001 | &decl_gz.base, | |
| 3002 | .{ .ty = .type_type }, | |
| 3003 | fn_proto.ast.return_type, | |
| 3004 | ); | |
| 3000 | const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { | |
| 3001 | break :inst try expr(&decl_gz, params_scope, align_rl, fn_proto.ast.align_expr); | |
| 3002 | }; | |
| 3003 | const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: { | |
| 3004 | break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr); | |
| 3005 | }; | |
| 3006 | ||
| 3007 | var ret_gz = decl_gz.makeSubBlock(params_scope); | |
| 3008 | defer ret_gz.instructions.deinit(gpa); | |
| 3009 | const ret_ty = try expr(&ret_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type); | |
| 3010 | const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); | |
| 3005 | 3011 | |
| 3006 | 3012 | const cc: Zir.Inst.Ref = blk: { |
| 3007 | 3013 | if (fn_proto.ast.callconv_expr != 0) { |
| ... | ... | @@ -3012,9 +3018,9 @@ fn fnDecl( |
| 3012 | 3018 | .{}, |
| 3013 | 3019 | ); |
| 3014 | 3020 | } |
| 3015 | break :blk try AstGen.expr( | |
| 3021 | break :blk try expr( | |
| 3016 | 3022 | &decl_gz, |
| 3017 | &decl_gz.base, | |
| 3023 | params_scope, | |
| 3018 | 3024 | .{ .ty = .calling_convention_type }, |
| 3019 | 3025 | fn_proto.ast.callconv_expr, |
| 3020 | 3026 | ); |
| ... | ... | @@ -3037,8 +3043,9 @@ fn fnDecl( |
| 3037 | 3043 | } |
| 3038 | 3044 | break :func try decl_gz.addFunc(.{ |
| 3039 | 3045 | .src_node = decl_node, |
| 3040 | .ret_ty = return_type_inst, | |
| 3041 | .param_types = param_types, | |
| 3046 | .ret_ty = ret_gz.instructions.items, | |
| 3047 | .ret_br = ret_br, | |
| 3048 | .param_block = block_inst, | |
| 3042 | 3049 | .body = &[0]Zir.Inst.Index{}, |
| 3043 | 3050 | .cc = cc, |
| 3044 | 3051 | .align_inst = .none, // passed in the per-decl data |
| ... | ... | @@ -3047,75 +3054,18 @@ fn fnDecl( |
| 3047 | 3054 | .is_inferred_error = false, |
| 3048 | 3055 | .is_test = false, |
| 3049 | 3056 | .is_extern = true, |
| 3050 | .cur_bit_bag = cur_bit_bag, | |
| 3051 | .bit_bag = bit_bag.items, | |
| 3052 | 3057 | }); |
| 3053 | 3058 | } else func: { |
| 3054 | 3059 | if (is_var_args) { |
| 3055 | 3060 | return astgen.failTok(fn_proto.ast.fn_token, "non-extern function is variadic", .{}); |
| 3056 | 3061 | } |
| 3057 | 3062 | |
| 3058 | var fn_gz: GenZir = .{ | |
| 3059 | .force_comptime = false, | |
| 3060 | .in_defer = false, | |
| 3061 | .decl_node_index = fn_proto.ast.proto_node, | |
| 3062 | .decl_line = decl_gz.decl_line, | |
| 3063 | .parent = &decl_gz.base, | |
| 3064 | .astgen = astgen, | |
| 3065 | }; | |
| 3066 | defer fn_gz.instructions.deinit(gpa); | |
| 3067 | ||
| 3068 | 3063 | const prev_fn_block = astgen.fn_block; |
| 3069 | 3064 | astgen.fn_block = &fn_gz; |
| 3070 | 3065 | defer astgen.fn_block = prev_fn_block; |
| 3071 | 3066 | |
| 3072 | // Iterate over the parameters. We put the param names as the first N | |
| 3073 | // items inside `extra` so that debug info later can refer to the parameter names | |
| 3074 | // even while the respective source code is unloaded. | |
| 3075 | try astgen.extra.ensureUnusedCapacity(gpa, param_count); | |
| 3076 | ||
| 3077 | { | |
| 3078 | var params_scope = &fn_gz.base; | |
| 3079 | var i: usize = 0; | |
| 3080 | var it = fn_proto.iterate(tree.*); | |
| 3081 | while (it.next()) |param| : (i += 1) { | |
| 3082 | const name_token = param.name_token orelse { | |
| 3083 | if (param.anytype_ellipsis3) |tok| { | |
| 3084 | return astgen.failTok(tok, "missing parameter name", .{}); | |
| 3085 | } else { | |
| 3086 | return astgen.failNode(param.type_expr, "missing parameter name", .{}); | |
| 3087 | } | |
| 3088 | }; | |
| 3089 | if (param.type_expr != 0) | |
| 3090 | _ = try typeExpr(&fn_gz, params_scope, param.type_expr); | |
| 3091 | if (mem.eql(u8, "_", tree.tokenSlice(name_token))) | |
| 3092 | continue; | |
| 3093 | const param_name = try astgen.identAsString(name_token); | |
| 3094 | // Create an arg instruction. This is needed to emit a semantic analysis | |
| 3095 | // error for shadowing decls. | |
| 3096 | try astgen.detectLocalShadowing(params_scope, param_name, name_token); | |
| 3097 | const arg_inst = try fn_gz.addStrTok(.arg, param_name, name_token); | |
| 3098 | const sub_scope = try astgen.arena.create(Scope.LocalVal); | |
| 3099 | sub_scope.* = .{ | |
| 3100 | .parent = params_scope, | |
| 3101 | .gen_zir = &fn_gz, | |
| 3102 | .name = param_name, | |
| 3103 | .inst = arg_inst, | |
| 3104 | .token_src = name_token, | |
| 3105 | .id_cat = .@"function parameter", | |
| 3106 | }; | |
| 3107 | params_scope = &sub_scope.base; | |
| 3108 | ||
| 3109 | // Additionally put the param name into `string_bytes` and reference it with | |
| 3110 | // `extra` so that we have access to the data in codegen, for debug info. | |
| 3111 | const str_index = try astgen.identAsString(name_token); | |
| 3112 | try astgen.extra.append(astgen.gpa, str_index); | |
| 3113 | } | |
| 3114 | _ = try typeExpr(&fn_gz, params_scope, fn_proto.ast.return_type); | |
| 3115 | ||
| 3116 | _ = try expr(&fn_gz, params_scope, .none, body_node); | |
| 3117 | try checkUsed(gz, &fn_gz.base, params_scope); | |
| 3118 | } | |
| 3067 | _ = try expr(&fn_gz, params_scope, .none, body_node); | |
| 3068 | try checkUsed(gz, &fn_gz.base, params_scope); | |
| 3119 | 3069 | |
| 3120 | 3070 | const need_implicit_ret = blk: { |
| 3121 | 3071 | if (fn_gz.instructions.items.len == 0) |
| ... | ... | @@ -3132,8 +3082,9 @@ fn fnDecl( |
| 3132 | 3082 | |
| 3133 | 3083 | break :func try decl_gz.addFunc(.{ |
| 3134 | 3084 | .src_node = decl_node, |
| 3135 | .ret_ty = return_type_inst, | |
| 3136 | .param_types = param_types, | |
| 3085 | .param_block = block_inst, | |
| 3086 | .ret_ty = ret_gz.instructions.items, | |
| 3087 | .ret_br = ret_br, | |
| 3137 | 3088 | .body = fn_gz.instructions.items, |
| 3138 | 3089 | .cc = cc, |
| 3139 | 3090 | .align_inst = .none, // passed in the per-decl data |
| ... | ... | @@ -3142,8 +3093,6 @@ fn fnDecl( |
| 3142 | 3093 | .is_inferred_error = is_inferred_error, |
| 3143 | 3094 | .is_test = false, |
| 3144 | 3095 | .is_extern = false, |
| 3145 | .cur_bit_bag = cur_bit_bag, | |
| 3146 | .bit_bag = bit_bag.items, | |
| 3147 | 3096 | }); |
| 3148 | 3097 | }; |
| 3149 | 3098 | |
| ... | ... | @@ -3479,8 +3428,9 @@ fn testDecl( |
| 3479 | 3428 | |
| 3480 | 3429 | const func_inst = try decl_block.addFunc(.{ |
| 3481 | 3430 | .src_node = node, |
| 3482 | .ret_ty = .void_type, | |
| 3483 | .param_types = &[0]Zir.Inst.Ref{}, | |
| 3431 | .param_block = block_inst, | |
| 3432 | .ret_ty = &.{}, | |
| 3433 | .ret_br = 0, | |
| 3484 | 3434 | .body = fn_block.instructions.items, |
| 3485 | 3435 | .cc = .none, |
| 3486 | 3436 | .align_inst = .none, |
| ... | ... | @@ -3489,8 +3439,6 @@ fn testDecl( |
| 3489 | 3439 | .is_inferred_error = true, |
| 3490 | 3440 | .is_test = true, |
| 3491 | 3441 | .is_extern = false, |
| 3492 | .cur_bit_bag = 0, | |
| 3493 | .bit_bag = &.{}, | |
| 3494 | 3442 | }); |
| 3495 | 3443 | |
| 3496 | 3444 | _ = try decl_block.addBreak(.break_inline, block_inst, func_inst); |
| ... | ... | @@ -4238,7 +4186,7 @@ fn containerDecl( |
| 4238 | 4186 | var fields_data = ArrayListUnmanaged(u32){}; |
| 4239 | 4187 | defer fields_data.deinit(gpa); |
| 4240 | 4188 | |
| 4241 | try fields_data.ensureCapacity(gpa, counts.total_fields + counts.values); | |
| 4189 | try fields_data.ensureTotalCapacity(gpa, counts.total_fields + counts.values); | |
| 4242 | 4190 | |
| 4243 | 4191 | // We only need this if there are greater than 32 fields. |
| 4244 | 4192 | var bit_bag = ArrayListUnmanaged(u32){}; |
| ... | ... | @@ -5184,8 +5132,7 @@ fn setCondBrPayload( |
| 5184 | 5132 | ) !void { |
| 5185 | 5133 | const astgen = then_scope.astgen; |
| 5186 | 5134 | |
| 5187 | try astgen.extra.ensureCapacity(astgen.gpa, astgen.extra.items.len + | |
| 5188 | @typeInfo(Zir.Inst.CondBr).Struct.fields.len + | |
| 5135 | try astgen.extra.ensureUnusedCapacity(astgen.gpa, @typeInfo(Zir.Inst.CondBr).Struct.fields.len + | |
| 5189 | 5136 | then_scope.instructions.items.len + else_scope.instructions.items.len); |
| 5190 | 5137 | |
| 5191 | 5138 | const zir_datas = astgen.instructions.items(.data); |
| ... | ... | @@ -5476,7 +5423,7 @@ fn forExpr( |
| 5476 | 5423 | const tree = astgen.tree; |
| 5477 | 5424 | const token_tags = tree.tokens.items(.tag); |
| 5478 | 5425 | |
| 5479 | const array_ptr = try expr(parent_gz, scope, .ref, for_full.ast.cond_expr); | |
| 5426 | const array_ptr = try expr(parent_gz, scope, .none_or_ref, for_full.ast.cond_expr); | |
| 5480 | 5427 | const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr); |
| 5481 | 5428 | |
| 5482 | 5429 | const index_ptr = blk: { |
| ... | ... | @@ -5839,10 +5786,9 @@ fn switchExpr( |
| 5839 | 5786 | _ = try case_scope.addBreak(.@"break", switch_block, case_result); |
| 5840 | 5787 | } |
| 5841 | 5788 | // Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`. |
| 5842 | try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + | |
| 5789 | try scalar_cases_payload.ensureUnusedCapacity(gpa, case_scope.instructions.items.len + | |
| 5843 | 5790 | 3 + // operand, scalar_cases_len, else body len |
| 5844 | @boolToInt(multi_cases_len != 0) + | |
| 5845 | case_scope.instructions.items.len); | |
| 5791 | @boolToInt(multi_cases_len != 0)); | |
| 5846 | 5792 | scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand)); |
| 5847 | 5793 | scalar_cases_payload.appendAssumeCapacity(scalar_cases_len); |
| 5848 | 5794 | if (multi_cases_len != 0) { |
| ... | ... | @@ -5852,9 +5798,11 @@ fn switchExpr( |
| 5852 | 5798 | scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items); |
| 5853 | 5799 | } else { |
| 5854 | 5800 | // Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`. |
| 5855 | try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + | |
| 5856 | 2 + // operand, scalar_cases_len | |
| 5857 | @boolToInt(multi_cases_len != 0)); | |
| 5801 | try scalar_cases_payload.ensureUnusedCapacity( | |
| 5802 | gpa, | |
| 5803 | @as(usize, 2) + // operand, scalar_cases_len | |
| 5804 | @boolToInt(multi_cases_len != 0), | |
| 5805 | ); | |
| 5858 | 5806 | scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand)); |
| 5859 | 5807 | scalar_cases_payload.appendAssumeCapacity(scalar_cases_len); |
| 5860 | 5808 | if (multi_cases_len != 0) { |
| ... | ... | @@ -5975,8 +5923,8 @@ fn switchExpr( |
| 5975 | 5923 | block_scope.break_count += 1; |
| 5976 | 5924 | _ = try case_scope.addBreak(.@"break", switch_block, case_result); |
| 5977 | 5925 | } |
| 5978 | try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len + | |
| 5979 | 2 + case_scope.instructions.items.len); | |
| 5926 | try scalar_cases_payload.ensureUnusedCapacity(gpa, 2 + | |
| 5927 | case_scope.instructions.items.len); | |
| 5980 | 5928 | scalar_cases_payload.appendAssumeCapacity(@enumToInt(item_inst)); |
| 5981 | 5929 | scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len)); |
| 5982 | 5930 | scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items); |
| ... | ... | @@ -6012,8 +5960,8 @@ fn switchExpr( |
| 6012 | 5960 | const payload_index = astgen.extra.items.len; |
| 6013 | 5961 | const zir_datas = astgen.instructions.items(.data); |
| 6014 | 5962 | zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index); |
| 6015 | try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + | |
| 6016 | scalar_cases_payload.items.len + multi_cases_payload.items.len); | |
| 5963 | try astgen.extra.ensureUnusedCapacity(gpa, scalar_cases_payload.items.len + | |
| 5964 | multi_cases_payload.items.len); | |
| 6017 | 5965 | const strat = rl.strategy(&block_scope); |
| 6018 | 5966 | switch (strat.tag) { |
| 6019 | 5967 | .break_operand => { |
| ... | ... | @@ -6821,7 +6769,7 @@ fn as( |
| 6821 | 6769 | ) InnerError!Zir.Inst.Ref { |
| 6822 | 6770 | const dest_type = try typeExpr(gz, scope, lhs); |
| 6823 | 6771 | switch (rl) { |
| 6824 | .none, .none_or_ref, .discard, .ref, .ty => { | |
| 6772 | .none, .none_or_ref, .discard, .ref, .ty, .coerced_ty => { | |
| 6825 | 6773 | const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node); |
| 6826 | 6774 | return rvalue(gz, rl, result, node); |
| 6827 | 6775 | }, |
| ... | ... | @@ -6844,7 +6792,7 @@ fn unionInit( |
| 6844 | 6792 | const union_type = try typeExpr(gz, scope, params[0]); |
| 6845 | 6793 | const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); |
| 6846 | 6794 | switch (rl) { |
| 6847 | .none, .none_or_ref, .discard, .ref, .ty, .inferred_ptr => { | |
| 6795 | .none, .none_or_ref, .discard, .ref, .ty, .coerced_ty, .inferred_ptr => { | |
| 6848 | 6796 | _ = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{ |
| 6849 | 6797 | .container_type = union_type, |
| 6850 | 6798 | .field_name = field_name, |
| ... | ... | @@ -6930,7 +6878,7 @@ fn bitCast( |
| 6930 | 6878 | const astgen = gz.astgen; |
| 6931 | 6879 | const dest_type = try typeExpr(gz, scope, lhs); |
| 6932 | 6880 | switch (rl) { |
| 6933 | .none, .none_or_ref, .discard, .ty => { | |
| 6881 | .none, .none_or_ref, .discard, .ty, .coerced_ty => { | |
| 6934 | 6882 | const operand = try expr(gz, scope, .none, rhs); |
| 6935 | 6883 | const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{ |
| 6936 | 6884 | .lhs = dest_type, |
| ... | ... | @@ -7740,7 +7688,7 @@ fn callExpr( |
| 7740 | 7688 | .param_index = @intCast(u32, i), |
| 7741 | 7689 | } }, |
| 7742 | 7690 | }); |
| 7743 | args[i] = try expr(gz, scope, .{ .ty = param_type }, param_node); | |
| 7691 | args[i] = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node); | |
| 7744 | 7692 | } |
| 7745 | 7693 | |
| 7746 | 7694 | const modifier: std.builtin.CallOptions.Modifier = blk: { |
| ... | ... | @@ -8433,7 +8381,7 @@ fn rvalue( |
| 8433 | 8381 | src_node: ast.Node.Index, |
| 8434 | 8382 | ) InnerError!Zir.Inst.Ref { |
| 8435 | 8383 | switch (rl) { |
| 8436 | .none, .none_or_ref => return result, | |
| 8384 | .none, .none_or_ref, .coerced_ty => return result, | |
| 8437 | 8385 | .discard => { |
| 8438 | 8386 | // Emit a compile error for discarding error values. |
| 8439 | 8387 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); |
| ... | ... | @@ -8659,7 +8607,7 @@ fn failNodeNotes( |
| 8659 | 8607 | } |
| 8660 | 8608 | const notes_index: u32 = if (notes.len != 0) blk: { |
| 8661 | 8609 | const notes_start = astgen.extra.items.len; |
| 8662 | try astgen.extra.ensureCapacity(astgen.gpa, notes_start + 1 + notes.len); | |
| 8610 | try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len); | |
| 8663 | 8611 | astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); |
| 8664 | 8612 | astgen.extra.appendSliceAssumeCapacity(notes); |
| 8665 | 8613 | break :blk @intCast(u32, notes_start); |
| ... | ... | @@ -8700,7 +8648,7 @@ fn failTokNotes( |
| 8700 | 8648 | } |
| 8701 | 8649 | const notes_index: u32 = if (notes.len != 0) blk: { |
| 8702 | 8650 | const notes_start = astgen.extra.items.len; |
| 8703 | try astgen.extra.ensureCapacity(astgen.gpa, notes_start + 1 + notes.len); | |
| 8651 | try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len); | |
| 8704 | 8652 | astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len)); |
| 8705 | 8653 | astgen.extra.appendSliceAssumeCapacity(notes); |
| 8706 | 8654 | break :blk @intCast(u32, notes_start); |
| ... | ... | @@ -8864,7 +8812,7 @@ fn strLitNodeAsString(astgen: *AstGen, node: ast.Node.Index) !IndexSlice { |
| 8864 | 8812 | while (tok_i <= end) : (tok_i += 1) { |
| 8865 | 8813 | const slice = tree.tokenSlice(tok_i); |
| 8866 | 8814 | const line_bytes = slice[2 .. slice.len - 1]; |
| 8867 | try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1); | |
| 8815 | try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1); | |
| 8868 | 8816 | string_bytes.appendAssumeCapacity('\n'); |
| 8869 | 8817 | string_bytes.appendSliceAssumeCapacity(line_bytes); |
| 8870 | 8818 | } |
| ... | ... | @@ -9105,7 +9053,7 @@ const GenZir = struct { |
| 9105 | 9053 | // we emit ZIR for the block break instructions to have the result values, |
| 9106 | 9054 | // and then rvalue() on that to pass the value to the result location. |
| 9107 | 9055 | switch (parent_rl) { |
| 9108 | .ty => |ty_inst| { | |
| 9056 | .ty, .coerced_ty => |ty_inst| { | |
| 9109 | 9057 | gz.rl_ty_inst = ty_inst; |
| 9110 | 9058 | gz.break_result_loc = parent_rl; |
| 9111 | 9059 | }, |
| ... | ... | @@ -9131,8 +9079,8 @@ const GenZir = struct { |
| 9131 | 9079 | |
| 9132 | 9080 | fn setBoolBrBody(gz: GenZir, inst: Zir.Inst.Index) !void { |
| 9133 | 9081 | const gpa = gz.astgen.gpa; |
| 9134 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | |
| 9135 | @typeInfo(Zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); | |
| 9082 | try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Block).Struct.fields.len + | |
| 9083 | gz.instructions.items.len); | |
| 9136 | 9084 | const zir_datas = gz.astgen.instructions.items(.data); |
| 9137 | 9085 | zir_datas[inst].bool_br.payload_index = gz.astgen.addExtraAssumeCapacity( |
| 9138 | 9086 | Zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, |
| ... | ... | @@ -9142,8 +9090,8 @@ const GenZir = struct { |
| 9142 | 9090 | |
| 9143 | 9091 | fn setBlockBody(gz: GenZir, inst: Zir.Inst.Index) !void { |
| 9144 | 9092 | const gpa = gz.astgen.gpa; |
| 9145 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | |
| 9146 | @typeInfo(Zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); | |
| 9093 | try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Block).Struct.fields.len + | |
| 9094 | gz.instructions.items.len); | |
| 9147 | 9095 | const zir_datas = gz.astgen.instructions.items(.data); |
| 9148 | 9096 | zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity( |
| 9149 | 9097 | Zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) }, |
| ... | ... | @@ -9155,8 +9103,8 @@ const GenZir = struct { |
| 9155 | 9103 | /// `store_to_block_ptr` instructions with lhs set to .none. |
| 9156 | 9104 | fn setBlockBodyEliding(gz: GenZir, inst: Zir.Inst.Index) !void { |
| 9157 | 9105 | const gpa = gz.astgen.gpa; |
| 9158 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | |
| 9159 | @typeInfo(Zir.Inst.Block).Struct.fields.len + gz.instructions.items.len); | |
| 9106 | try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Block).Struct.fields.len + | |
| 9107 | gz.instructions.items.len); | |
| 9160 | 9108 | const zir_datas = gz.astgen.instructions.items(.data); |
| 9161 | 9109 | const zir_tags = gz.astgen.instructions.items(.tag); |
| 9162 | 9110 | const block_pl_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Block{ |
| ... | ... | @@ -9177,9 +9125,10 @@ const GenZir = struct { |
| 9177 | 9125 | |
| 9178 | 9126 | fn addFunc(gz: *GenZir, args: struct { |
| 9179 | 9127 | src_node: ast.Node.Index, |
| 9180 | param_types: []const Zir.Inst.Ref, | |
| 9181 | 9128 | body: []const Zir.Inst.Index, |
| 9182 | ret_ty: Zir.Inst.Ref, | |
| 9129 | param_block: Zir.Inst.Index, | |
| 9130 | ret_ty: []const Zir.Inst.Index, | |
| 9131 | ret_br: Zir.Inst.Index, | |
| 9183 | 9132 | cc: Zir.Inst.Ref, |
| 9184 | 9133 | align_inst: Zir.Inst.Ref, |
| 9185 | 9134 | lib_name: u32, |
| ... | ... | @@ -9187,11 +9136,8 @@ const GenZir = struct { |
| 9187 | 9136 | is_inferred_error: bool, |
| 9188 | 9137 | is_test: bool, |
| 9189 | 9138 | is_extern: bool, |
| 9190 | cur_bit_bag: u32, | |
| 9191 | bit_bag: []const u32, | |
| 9192 | 9139 | }) !Zir.Inst.Ref { |
| 9193 | 9140 | assert(args.src_node != 0); |
| 9194 | assert(args.ret_ty != .none); | |
| 9195 | 9141 | const astgen = gz.astgen; |
| 9196 | 9142 | const gpa = astgen.gpa; |
| 9197 | 9143 | |
| ... | ... | @@ -9226,27 +9172,22 @@ const GenZir = struct { |
| 9226 | 9172 | src_locs = &src_locs_buffer; |
| 9227 | 9173 | } |
| 9228 | 9174 | |
| 9229 | const any_are_comptime = args.cur_bit_bag != 0 or for (args.bit_bag) |x| { | |
| 9230 | if (x != 0) break true; | |
| 9231 | } else false; | |
| 9232 | ||
| 9233 | 9175 | if (args.cc != .none or args.lib_name != 0 or |
| 9234 | 9176 | args.is_var_args or args.is_test or args.align_inst != .none or |
| 9235 | args.is_extern or any_are_comptime) | |
| 9177 | args.is_extern) | |
| 9236 | 9178 | { |
| 9237 | 9179 | try astgen.extra.ensureUnusedCapacity( |
| 9238 | 9180 | gpa, |
| 9239 | 9181 | @typeInfo(Zir.Inst.ExtendedFunc).Struct.fields.len + |
| 9240 | @boolToInt(any_are_comptime) + args.bit_bag.len + | |
| 9241 | args.param_types.len + args.body.len + src_locs.len + | |
| 9182 | args.ret_ty.len + args.body.len + src_locs.len + | |
| 9242 | 9183 | @boolToInt(args.lib_name != 0) + |
| 9243 | 9184 | @boolToInt(args.align_inst != .none) + |
| 9244 | 9185 | @boolToInt(args.cc != .none), |
| 9245 | 9186 | ); |
| 9246 | 9187 | const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedFunc{ |
| 9247 | 9188 | .src_node = gz.nodeIndexToRelative(args.src_node), |
| 9248 | .return_type = args.ret_ty, | |
| 9249 | .param_types_len = @intCast(u32, args.param_types.len), | |
| 9189 | .param_block = args.param_block, | |
| 9190 | .ret_body_len = @intCast(u32, args.ret_ty.len), | |
| 9250 | 9191 | .body_len = @intCast(u32, args.body.len), |
| 9251 | 9192 | }); |
| 9252 | 9193 | if (args.lib_name != 0) { |
| ... | ... | @@ -9258,15 +9199,14 @@ const GenZir = struct { |
| 9258 | 9199 | if (args.align_inst != .none) { |
| 9259 | 9200 | astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst)); |
| 9260 | 9201 | } |
| 9261 | if (any_are_comptime) { | |
| 9262 | astgen.extra.appendSliceAssumeCapacity(args.bit_bag); // Likely empty. | |
| 9263 | astgen.extra.appendAssumeCapacity(args.cur_bit_bag); | |
| 9264 | } | |
| 9265 | astgen.appendRefsAssumeCapacity(args.param_types); | |
| 9202 | astgen.extra.appendSliceAssumeCapacity(args.ret_ty); | |
| 9266 | 9203 | astgen.extra.appendSliceAssumeCapacity(args.body); |
| 9267 | 9204 | astgen.extra.appendSliceAssumeCapacity(src_locs); |
| 9268 | 9205 | |
| 9269 | 9206 | const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); |
| 9207 | if (args.ret_br != 0) { | |
| 9208 | astgen.instructions.items(.data)[args.ret_br].@"break".block_inst = new_index; | |
| 9209 | } | |
| 9270 | 9210 | astgen.instructions.appendAssumeCapacity(.{ |
| 9271 | 9211 | .tag = .extended, |
| 9272 | 9212 | .data = .{ .extended = .{ |
| ... | ... | @@ -9279,7 +9219,6 @@ const GenZir = struct { |
| 9279 | 9219 | .has_align = args.align_inst != .none, |
| 9280 | 9220 | .is_test = args.is_test, |
| 9281 | 9221 | .is_extern = args.is_extern, |
| 9282 | .has_comptime_bits = any_are_comptime, | |
| 9283 | 9222 | }), |
| 9284 | 9223 | .operand = payload_index, |
| 9285 | 9224 | } }, |
| ... | ... | @@ -9287,24 +9226,27 @@ const GenZir = struct { |
| 9287 | 9226 | gz.instructions.appendAssumeCapacity(new_index); |
| 9288 | 9227 | return indexToRef(new_index); |
| 9289 | 9228 | } else { |
| 9290 | try gz.astgen.extra.ensureUnusedCapacity( | |
| 9229 | try astgen.extra.ensureUnusedCapacity( | |
| 9291 | 9230 | gpa, |
| 9292 | 9231 | @typeInfo(Zir.Inst.Func).Struct.fields.len + |
| 9293 | args.param_types.len + args.body.len + src_locs.len, | |
| 9232 | args.ret_ty.len + args.body.len + src_locs.len, | |
| 9294 | 9233 | ); |
| 9295 | 9234 | |
| 9296 | const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Func{ | |
| 9297 | .return_type = args.ret_ty, | |
| 9298 | .param_types_len = @intCast(u32, args.param_types.len), | |
| 9235 | const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{ | |
| 9236 | .param_block = args.param_block, | |
| 9237 | .ret_body_len = @intCast(u32, args.ret_ty.len), | |
| 9299 | 9238 | .body_len = @intCast(u32, args.body.len), |
| 9300 | 9239 | }); |
| 9301 | gz.astgen.appendRefsAssumeCapacity(args.param_types); | |
| 9302 | gz.astgen.extra.appendSliceAssumeCapacity(args.body); | |
| 9303 | gz.astgen.extra.appendSliceAssumeCapacity(src_locs); | |
| 9240 | astgen.extra.appendSliceAssumeCapacity(args.ret_ty); | |
| 9241 | astgen.extra.appendSliceAssumeCapacity(args.body); | |
| 9242 | astgen.extra.appendSliceAssumeCapacity(src_locs); | |
| 9304 | 9243 | |
| 9305 | 9244 | const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func; |
| 9306 | const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); | |
| 9307 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 9245 | const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); | |
| 9246 | if (args.ret_br != 0) { | |
| 9247 | astgen.instructions.items(.data)[args.ret_br].@"break".block_inst = new_index; | |
| 9248 | } | |
| 9249 | astgen.instructions.appendAssumeCapacity(.{ | |
| 9308 | 9250 | .tag = tag, |
| 9309 | 9251 | .data = .{ .pl_node = .{ |
| 9310 | 9252 | .src_node = gz.nodeIndexToRelative(args.src_node), |
| ... | ... | @@ -9380,10 +9322,10 @@ const GenZir = struct { |
| 9380 | 9322 | assert(callee != .none); |
| 9381 | 9323 | assert(src_node != 0); |
| 9382 | 9324 | const gpa = gz.astgen.gpa; |
| 9383 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | |
| 9384 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | |
| 9385 | try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len + | |
| 9386 | @typeInfo(Zir.Inst.Call).Struct.fields.len + args.len); | |
| 9325 | try gz.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9326 | try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9327 | try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Call).Struct.fields.len + | |
| 9328 | args.len); | |
| 9387 | 9329 | |
| 9388 | 9330 | const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Call{ |
| 9389 | 9331 | .callee = callee, |
| ... | ... | @@ -9412,8 +9354,8 @@ const GenZir = struct { |
| 9412 | 9354 | ) !Zir.Inst.Index { |
| 9413 | 9355 | assert(lhs != .none); |
| 9414 | 9356 | const gpa = gz.astgen.gpa; |
| 9415 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | |
| 9416 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | |
| 9357 | try gz.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9358 | try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9417 | 9359 | |
| 9418 | 9360 | const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); |
| 9419 | 9361 | gz.astgen.instructions.appendAssumeCapacity(.{ |
| ... | ... | @@ -9486,8 +9428,8 @@ const GenZir = struct { |
| 9486 | 9428 | extra: anytype, |
| 9487 | 9429 | ) !Zir.Inst.Ref { |
| 9488 | 9430 | const gpa = gz.astgen.gpa; |
| 9489 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | |
| 9490 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | |
| 9431 | try gz.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9432 | try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9491 | 9433 | |
| 9492 | 9434 | const payload_index = try gz.astgen.addExtra(extra); |
| 9493 | 9435 | const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); |
| ... | ... | @@ -9502,6 +9444,38 @@ const GenZir = struct { |
| 9502 | 9444 | return indexToRef(new_index); |
| 9503 | 9445 | } |
| 9504 | 9446 | |
| 9447 | fn addParam( | |
| 9448 | gz: *GenZir, | |
| 9449 | tag: Zir.Inst.Tag, | |
| 9450 | /// Absolute token index. This function does the conversion to Decl offset. | |
| 9451 | abs_tok_index: ast.TokenIndex, | |
| 9452 | name: u32, | |
| 9453 | body: []const u32, | |
| 9454 | ) !Zir.Inst.Index { | |
| 9455 | const gpa = gz.astgen.gpa; | |
| 9456 | try gz.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9457 | try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9458 | try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).Struct.fields.len + | |
| 9459 | body.len); | |
| 9460 | ||
| 9461 | const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{ | |
| 9462 | .name = name, | |
| 9463 | .body_len = @intCast(u32, body.len), | |
| 9464 | }); | |
| 9465 | gz.astgen.extra.appendSliceAssumeCapacity(body); | |
| 9466 | ||
| 9467 | const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); | |
| 9468 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 9469 | .tag = tag, | |
| 9470 | .data = .{ .pl_tok = .{ | |
| 9471 | .src_tok = gz.tokenIndexToRelative(abs_tok_index), | |
| 9472 | .payload_index = payload_index, | |
| 9473 | } }, | |
| 9474 | }); | |
| 9475 | gz.instructions.appendAssumeCapacity(new_index); | |
| 9476 | return new_index; | |
| 9477 | } | |
| 9478 | ||
| 9505 | 9479 | fn addExtendedPayload( |
| 9506 | 9480 | gz: *GenZir, |
| 9507 | 9481 | opcode: Zir.Inst.Extended, |
| ... | ... | @@ -9509,8 +9483,8 @@ const GenZir = struct { |
| 9509 | 9483 | ) !Zir.Inst.Ref { |
| 9510 | 9484 | const gpa = gz.astgen.gpa; |
| 9511 | 9485 | |
| 9512 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | |
| 9513 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | |
| 9486 | try gz.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9487 | try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9514 | 9488 | |
| 9515 | 9489 | const payload_index = try gz.astgen.addExtra(extra); |
| 9516 | 9490 | const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); |
| ... | ... | @@ -9566,8 +9540,8 @@ const GenZir = struct { |
| 9566 | 9540 | elem_type: Zir.Inst.Ref, |
| 9567 | 9541 | ) !Zir.Inst.Ref { |
| 9568 | 9542 | const gpa = gz.astgen.gpa; |
| 9569 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | |
| 9570 | try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); | |
| 9543 | try gz.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9544 | try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9571 | 9545 | |
| 9572 | 9546 | const payload_index = try gz.astgen.addExtra(Zir.Inst.ArrayTypeSentinel{ |
| 9573 | 9547 | .sentinel = sentinel, |
| ... | ... | @@ -9822,7 +9796,7 @@ const GenZir = struct { |
| 9822 | 9796 | /// Leaves the `payload_index` field undefined. |
| 9823 | 9797 | fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: ast.Node.Index) !Zir.Inst.Index { |
| 9824 | 9798 | const gpa = gz.astgen.gpa; |
| 9825 | try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); | |
| 9799 | try gz.instructions.ensureUnusedCapacity(gpa, 1); | |
| 9826 | 9800 | const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); |
| 9827 | 9801 | try gz.astgen.instructions.append(gpa, .{ |
| 9828 | 9802 | .tag = tag, |
src/Compilation.zig+1-1| ... | ... | @@ -2116,7 +2116,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 2116 | 2116 | if (builtin.mode == .Debug and self.verbose_air) { |
| 2117 | 2117 | std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); |
| 2118 | 2118 | @import("print_air.zig").dump(gpa, air, decl.namespace.file_scope.zir, liveness); |
| 2119 | std.debug.print("# End Function AIR: {s}:\n", .{decl.name}); | |
| 2119 | std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); | |
| 2120 | 2120 | } |
| 2121 | 2121 | |
| 2122 | 2122 | self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) { |
src/Module.zig+141-26| ... | ... | @@ -61,6 +61,11 @@ export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, |
| 61 | 61 | /// Keys are fully resolved file paths. This table owns the keys and values. |
| 62 | 62 | import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, |
| 63 | 63 | |
| 64 | /// The set of all the generic function instantiations. This is used so that when a generic | |
| 65 | /// function is called twice with the same comptime parameter arguments, both calls dispatch | |
| 66 | /// to the same function. | |
| 67 | monomorphed_funcs: MonomorphedFuncsSet = .{}, | |
| 68 | ||
| 64 | 69 | /// We optimize memory usage for a compilation with no compile errors by storing the |
| 65 | 70 | /// error messages and mapping outside of `Decl`. |
| 66 | 71 | /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. |
| ... | ... | @@ -114,6 +119,44 @@ emit_h: ?*GlobalEmitH, |
| 114 | 119 | |
| 115 | 120 | test_functions: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, |
| 116 | 121 | |
| 122 | const MonomorphedFuncsSet = std.HashMapUnmanaged( | |
| 123 | *Fn, | |
| 124 | void, | |
| 125 | MonomorphedFuncsContext, | |
| 126 | std.hash_map.default_max_load_percentage, | |
| 127 | ); | |
| 128 | ||
| 129 | const MonomorphedFuncsContext = struct { | |
| 130 | pub fn eql(ctx: @This(), a: *Fn, b: *Fn) bool { | |
| 131 | _ = ctx; | |
| 132 | return a == b; | |
| 133 | } | |
| 134 | ||
| 135 | /// Must match `Sema.GenericCallAdapter.hash`. | |
| 136 | pub fn hash(ctx: @This(), key: *Fn) u64 { | |
| 137 | _ = ctx; | |
| 138 | var hasher = std.hash.Wyhash.init(0); | |
| 139 | ||
| 140 | // The generic function Decl is guaranteed to be the first dependency | |
| 141 | // of each of its instantiations. | |
| 142 | const generic_owner_decl = key.owner_decl.dependencies.keys()[0]; | |
| 143 | const generic_func = generic_owner_decl.val.castTag(.function).?.data; | |
| 144 | std.hash.autoHash(&hasher, @ptrToInt(generic_func)); | |
| 145 | ||
| 146 | // This logic must be kept in sync with the logic in `analyzeCall` that | |
| 147 | // computes the hash. | |
| 148 | const comptime_args = key.comptime_args.?; | |
| 149 | const generic_ty_info = generic_owner_decl.ty.fnInfo(); | |
| 150 | for (generic_ty_info.param_types) |param_ty, i| { | |
| 151 | if (generic_ty_info.paramIsComptime(i) and param_ty.tag() != .generic_poison) { | |
| 152 | comptime_args[i].val.hash(param_ty, &hasher); | |
| 153 | } | |
| 154 | } | |
| 155 | ||
| 156 | return hasher.final(); | |
| 157 | } | |
| 158 | }; | |
| 159 | ||
| 117 | 160 | /// A `Module` has zero or one of these depending on whether `-femit-h` is enabled. |
| 118 | 161 | pub const GlobalEmitH = struct { |
| 119 | 162 | /// Where to put the output. |
| ... | ... | @@ -757,6 +800,10 @@ pub const Union = struct { |
| 757 | 800 | pub const Fn = struct { |
| 758 | 801 | /// The Decl that corresponds to the function itself. |
| 759 | 802 | owner_decl: *Decl, |
| 803 | /// If this is not null, this function is a generic function instantiation, and | |
| 804 | /// there is a `Value` here for each parameter of the function. Non-comptime | |
| 805 | /// parameters are marked with an `unreachable_value`. | |
| 806 | comptime_args: ?[*]TypedValue = null, | |
| 760 | 807 | /// The ZIR instruction that is a function instruction. Use this to find |
| 761 | 808 | /// the body. We store this rather than the body directly so that when ZIR |
| 762 | 809 | /// is regenerated on update(), we can map this to the new corresponding |
| ... | ... | @@ -795,6 +842,9 @@ pub const Fn = struct { |
| 795 | 842 | |
| 796 | 843 | pub fn getInferredErrorSet(func: *Fn) ?*std.StringHashMapUnmanaged(void) { |
| 797 | 844 | const ret_ty = func.owner_decl.ty.fnReturnType(); |
| 845 | if (ret_ty.tag() == .generic_poison) { | |
| 846 | return null; | |
| 847 | } | |
| 798 | 848 | if (ret_ty.zigTypeTag() == .ErrorUnion) { |
| 799 | 849 | if (ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| { |
| 800 | 850 | return &payload.data.map; |
| ... | ... | @@ -1169,6 +1219,8 @@ pub const Scope = struct { |
| 1169 | 1219 | /// for the one that will be the same for all Block instances. |
| 1170 | 1220 | src_decl: *Decl, |
| 1171 | 1221 | instructions: ArrayListUnmanaged(Air.Inst.Index), |
| 1222 | // `param` instructions are collected here to be used by the `func` instruction. | |
| 1223 | params: std.ArrayListUnmanaged(Param) = .{}, | |
| 1172 | 1224 | label: ?*Label = null, |
| 1173 | 1225 | inlining: ?*Inlining, |
| 1174 | 1226 | /// If runtime_index is not 0 then one of these is guaranteed to be non null. |
| ... | ... | @@ -1183,6 +1235,12 @@ pub const Scope = struct { |
| 1183 | 1235 | /// when null, it is determined by build mode, changed by @setRuntimeSafety |
| 1184 | 1236 | want_safety: ?bool = null, |
| 1185 | 1237 | |
| 1238 | const Param = struct { | |
| 1239 | /// `noreturn` means `anytype`. | |
| 1240 | ty: Type, | |
| 1241 | is_comptime: bool, | |
| 1242 | }; | |
| 1243 | ||
| 1186 | 1244 | /// This `Block` maps a block ZIR instruction to the corresponding |
| 1187 | 1245 | /// AIR instruction for break instruction analysis. |
| 1188 | 1246 | pub const Label = struct { |
| ... | ... | @@ -1630,8 +1688,11 @@ pub const SrcLoc = struct { |
| 1630 | 1688 | .@"asm" => tree.asmFull(node), |
| 1631 | 1689 | else => unreachable, |
| 1632 | 1690 | }; |
| 1691 | const asm_output = full.outputs[0]; | |
| 1692 | const node_datas = tree.nodes.items(.data); | |
| 1693 | const ret_ty_node = node_datas[asm_output].lhs; | |
| 1633 | 1694 | const main_tokens = tree.nodes.items(.main_token); |
| 1634 | const tok_index = main_tokens[full.outputs[0]]; | |
| 1695 | const tok_index = main_tokens[ret_ty_node]; | |
| 1635 | 1696 | const token_starts = tree.tokens.items(.start); |
| 1636 | 1697 | return token_starts[tok_index]; |
| 1637 | 1698 | }, |
| ... | ... | @@ -2095,7 +2156,20 @@ pub const LazySrcLoc = union(enum) { |
| 2095 | 2156 | }; |
| 2096 | 2157 | |
| 2097 | 2158 | pub const SemaError = error{ OutOfMemory, AnalysisFail }; |
| 2098 | pub const CompileError = error{ OutOfMemory, AnalysisFail, NeededSourceLocation }; | |
| 2159 | pub const CompileError = error{ | |
| 2160 | OutOfMemory, | |
| 2161 | /// When this is returned, the compile error for the failure has already been recorded. | |
| 2162 | AnalysisFail, | |
| 2163 | /// Returned when a compile error needed to be reported but a provided LazySrcLoc was set | |
| 2164 | /// to the `unneeded` tag. The source location was, in fact, needed. It is expected that | |
| 2165 | /// somewhere up the call stack, the operation will be retried after doing expensive work | |
| 2166 | /// to compute a source location. | |
| 2167 | NeededSourceLocation, | |
| 2168 | /// A Type or Value was needed to be used during semantic analysis, but it was not available | |
| 2169 | /// because the function is generic. This is only seen when analyzing the body of a param | |
| 2170 | /// instruction. | |
| 2171 | GenericPoison, | |
| 2172 | }; | |
| 2099 | 2173 | |
| 2100 | 2174 | pub fn deinit(mod: *Module) void { |
| 2101 | 2175 | const gpa = mod.gpa; |
| ... | ... | @@ -2177,6 +2251,7 @@ pub fn deinit(mod: *Module) void { |
| 2177 | 2251 | |
| 2178 | 2252 | mod.error_name_list.deinit(gpa); |
| 2179 | 2253 | mod.test_functions.deinit(gpa); |
| 2254 | mod.monomorphed_funcs.deinit(gpa); | |
| 2180 | 2255 | } |
| 2181 | 2256 | |
| 2182 | 2257 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { |
| ... | ... | @@ -2792,14 +2867,16 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { |
| 2792 | 2867 | } |
| 2793 | 2868 | return error.AnalysisFail; |
| 2794 | 2869 | }, |
| 2795 | else => { | |
| 2870 | error.NeededSourceLocation => unreachable, | |
| 2871 | error.GenericPoison => unreachable, | |
| 2872 | else => |e| { | |
| 2796 | 2873 | decl.analysis = .sema_failure_retryable; |
| 2797 | 2874 | try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); |
| 2798 | 2875 | mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 2799 | 2876 | mod.gpa, |
| 2800 | 2877 | decl.srcLoc(), |
| 2801 | 2878 | "unable to analyze: {s}", |
| 2802 | .{@errorName(err)}, | |
| 2879 | .{@errorName(e)}, | |
| 2803 | 2880 | )); |
| 2804 | 2881 | return error.AnalysisFail; |
| 2805 | 2882 | }, |
| ... | ... | @@ -2899,7 +2976,6 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void { |
| 2899 | 2976 | .namespace = &struct_obj.namespace, |
| 2900 | 2977 | .func = null, |
| 2901 | 2978 | .owner_func = null, |
| 2902 | .param_inst_list = &.{}, | |
| 2903 | 2979 | }; |
| 2904 | 2980 | defer sema.deinit(); |
| 2905 | 2981 | var block_scope: Scope.Block = .{ |
| ... | ... | @@ -2954,7 +3030,6 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2954 | 3030 | .namespace = decl.namespace, |
| 2955 | 3031 | .func = null, |
| 2956 | 3032 | .owner_func = null, |
| 2957 | .param_inst_list = &.{}, | |
| 2958 | 3033 | }; |
| 2959 | 3034 | defer sema.deinit(); |
| 2960 | 3035 | |
| ... | ... | @@ -2980,7 +3055,10 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2980 | 3055 | .inlining = null, |
| 2981 | 3056 | .is_comptime = true, |
| 2982 | 3057 | }; |
| 2983 | defer block_scope.instructions.deinit(gpa); | |
| 3058 | defer { | |
| 3059 | block_scope.instructions.deinit(gpa); | |
| 3060 | block_scope.params.deinit(gpa); | |
| 3061 | } | |
| 2984 | 3062 | |
| 2985 | 3063 | const zir_block_index = decl.zirBlockIndex(); |
| 2986 | 3064 | const inst_data = zir_datas[zir_block_index].pl_node; |
| ... | ... | @@ -3625,8 +3703,6 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { |
| 3625 | 3703 | defer decl.value_arena.?.* = arena.state; |
| 3626 | 3704 | |
| 3627 | 3705 | const fn_ty = decl.ty; |
| 3628 | const param_inst_list = try gpa.alloc(Air.Inst.Ref, fn_ty.fnParamLen()); | |
| 3629 | defer gpa.free(param_inst_list); | |
| 3630 | 3706 | |
| 3631 | 3707 | var sema: Sema = .{ |
| 3632 | 3708 | .mod = mod, |
| ... | ... | @@ -3637,7 +3713,6 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { |
| 3637 | 3713 | .namespace = decl.namespace, |
| 3638 | 3714 | .func = func, |
| 3639 | 3715 | .owner_func = func, |
| 3640 | .param_inst_list = param_inst_list, | |
| 3641 | 3716 | }; |
| 3642 | 3717 | defer sema.deinit(); |
| 3643 | 3718 | |
| ... | ... | @@ -3656,29 +3731,71 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { |
| 3656 | 3731 | }; |
| 3657 | 3732 | defer inner_block.instructions.deinit(gpa); |
| 3658 | 3733 | |
| 3659 | // AIR requires the arg parameters to be the first N instructions. | |
| 3660 | try inner_block.instructions.ensureTotalCapacity(gpa, param_inst_list.len); | |
| 3661 | for (param_inst_list) |*param_inst, param_index| { | |
| 3662 | const param_type = fn_ty.fnParamType(param_index); | |
| 3734 | const fn_info = sema.code.getFnInfo(func.zir_body_inst); | |
| 3735 | const zir_tags = sema.code.instructions.items(.tag); | |
| 3736 | ||
| 3737 | // Here we are performing "runtime semantic analysis" for a function body, which means | |
| 3738 | // we must map the parameter ZIR instructions to `arg` AIR instructions. | |
| 3739 | // AIR requires the `arg` parameters to be the first N instructions. | |
| 3740 | // This could be a generic function instantiation, however, in which case we need to | |
| 3741 | // map the comptime parameters to constant values and only emit arg AIR instructions | |
| 3742 | // for the runtime ones. | |
| 3743 | const runtime_params_len = @intCast(u32, fn_ty.fnParamLen()); | |
| 3744 | try inner_block.instructions.ensureTotalCapacity(gpa, runtime_params_len); | |
| 3745 | try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType` | |
| 3746 | try sema.inst_map.ensureUnusedCapacity(gpa, fn_info.total_params_len); | |
| 3747 | ||
| 3748 | var runtime_param_index: usize = 0; | |
| 3749 | var total_param_index: usize = 0; | |
| 3750 | for (fn_info.param_body) |inst| { | |
| 3751 | const name = switch (zir_tags[inst]) { | |
| 3752 | .param, .param_comptime => blk: { | |
| 3753 | const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; | |
| 3754 | const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data; | |
| 3755 | break :blk extra.name; | |
| 3756 | }, | |
| 3757 | ||
| 3758 | .param_anytype, .param_anytype_comptime => blk: { | |
| 3759 | const str_tok = sema.code.instructions.items(.data)[inst].str_tok; | |
| 3760 | break :blk str_tok.start; | |
| 3761 | }, | |
| 3762 | ||
| 3763 | else => continue, | |
| 3764 | }; | |
| 3765 | if (func.comptime_args) |comptime_args| { | |
| 3766 | const arg_tv = comptime_args[total_param_index]; | |
| 3767 | if (arg_tv.val.tag() != .unreachable_value) { | |
| 3768 | // We have a comptime value for this parameter. | |
| 3769 | const arg = try sema.addConstant(arg_tv.ty, arg_tv.val); | |
| 3770 | sema.inst_map.putAssumeCapacityNoClobber(inst, arg); | |
| 3771 | total_param_index += 1; | |
| 3772 | continue; | |
| 3773 | } | |
| 3774 | } | |
| 3775 | const param_type = fn_ty.fnParamType(runtime_param_index); | |
| 3663 | 3776 | const ty_ref = try sema.addType(param_type); |
| 3664 | 3777 | const arg_index = @intCast(u32, sema.air_instructions.len); |
| 3665 | 3778 | inner_block.instructions.appendAssumeCapacity(arg_index); |
| 3666 | param_inst.* = Air.indexToRef(arg_index); | |
| 3667 | try sema.air_instructions.append(gpa, .{ | |
| 3779 | sema.air_instructions.appendAssumeCapacity(.{ | |
| 3668 | 3780 | .tag = .arg, |
| 3669 | .data = .{ | |
| 3670 | .ty_str = .{ | |
| 3671 | .ty = ty_ref, | |
| 3672 | .str = undefined, // Set in the semantic analysis of the arg instruction. | |
| 3673 | }, | |
| 3674 | }, | |
| 3781 | .data = .{ .ty_str = .{ | |
| 3782 | .ty = ty_ref, | |
| 3783 | .str = name, | |
| 3784 | } }, | |
| 3675 | 3785 | }); |
| 3786 | sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index)); | |
| 3787 | total_param_index += 1; | |
| 3788 | runtime_param_index += 1; | |
| 3676 | 3789 | } |
| 3677 | 3790 | |
| 3678 | 3791 | func.state = .in_progress; |
| 3679 | 3792 | log.debug("set {s} to in_progress", .{decl.name}); |
| 3680 | 3793 | |
| 3681 | try sema.analyzeFnBody(&inner_block, func.zir_body_inst); | |
| 3794 | _ = sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) { | |
| 3795 | error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"), | |
| 3796 | error.GenericPoison => @panic("zig compiler bug: GenericPoison"), | |
| 3797 | else => |e| return e, | |
| 3798 | }; | |
| 3682 | 3799 | |
| 3683 | 3800 | // Copy the block into place and mark that as the main block. |
| 3684 | 3801 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + |
| ... | ... | @@ -3714,7 +3831,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { |
| 3714 | 3831 | decl.analysis = .outdated; |
| 3715 | 3832 | } |
| 3716 | 3833 | |
| 3717 | fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node.Index) !*Decl { | |
| 3834 | pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node.Index) !*Decl { | |
| 3718 | 3835 | // If we have emit-h then we must allocate a bigger structure to store the emit-h state. |
| 3719 | 3836 | const new_decl: *Decl = if (mod.emit_h != null) blk: { |
| 3720 | 3837 | const parent_struct = try mod.gpa.create(DeclPlusEmitH); |
| ... | ... | @@ -4330,7 +4447,6 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void |
| 4330 | 4447 | .namespace = &struct_obj.namespace, |
| 4331 | 4448 | .owner_func = null, |
| 4332 | 4449 | .func = null, |
| 4333 | .param_inst_list = &.{}, | |
| 4334 | 4450 | }; |
| 4335 | 4451 | defer sema.deinit(); |
| 4336 | 4452 | |
| ... | ... | @@ -4484,7 +4600,6 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void { |
| 4484 | 4600 | .namespace = &union_obj.namespace, |
| 4485 | 4601 | .owner_func = null, |
| 4486 | 4602 | .func = null, |
| 4487 | .param_inst_list = &.{}, | |
| 4488 | 4603 | }; |
| 4489 | 4604 | defer sema.deinit(); |
| 4490 | 4605 |
src/Sema.zig+625-191| ... | ... | @@ -29,13 +29,6 @@ owner_func: ?*Module.Fn, |
| 29 | 29 | /// This starts out the same as `owner_func` and then diverges in the case of |
| 30 | 30 | /// an inline or comptime function call. |
| 31 | 31 | func: ?*Module.Fn, |
| 32 | /// For now, AIR requires arg instructions to be the first N instructions in the | |
| 33 | /// AIR code. We store references here for the purpose of `resolveInst`. | |
| 34 | /// This can get reworked with AIR memory layout changes, into simply: | |
| 35 | /// > Denormalized data to make `resolveInst` faster. This is 0 if not inside a function, | |
| 36 | /// > otherwise it is the number of parameters of the function. | |
| 37 | /// > param_count: u32 | |
| 38 | param_inst_list: []const Air.Inst.Ref, | |
| 39 | 32 | branch_quota: u32 = 1000, |
| 40 | 33 | branch_count: u32 = 0, |
| 41 | 34 | /// This field is updated when a new source location becomes active, so that |
| ... | ... | @@ -43,8 +36,22 @@ branch_count: u32 = 0, |
| 43 | 36 | /// access to the source location set by the previous instruction which did |
| 44 | 37 | /// contain a mapped source location. |
| 45 | 38 | src: LazySrcLoc = .{ .token_offset = 0 }, |
| 46 | next_arg_index: usize = 0, | |
| 47 | 39 | decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{}, |
| 40 | /// When doing a generic function instantiation, this array collects a | |
| 41 | /// `Value` object for each parameter that is comptime known and thus elided | |
| 42 | /// from the generated function. This memory is allocated by a parent `Sema` and | |
| 43 | /// owned by the values arena of the Sema owner_decl. | |
| 44 | comptime_args: []TypedValue = &.{}, | |
| 45 | /// Marks the function instruction that `comptime_args` applies to so that we | |
| 46 | /// don't accidentally apply it to a function prototype which is used in the | |
| 47 | /// type expression of a generic function parameter. | |
| 48 | comptime_args_fn_inst: Zir.Inst.Index = 0, | |
| 49 | /// When `comptime_args` is provided, this field is also provided. It was used as | |
| 50 | /// the key in the `monomorphed_funcs` set. The `func` instruction is supposed | |
| 51 | /// to use this instead of allocating a fresh one. This avoids an unnecessary | |
| 52 | /// extra hash table lookup in the `monomorphed_funcs` set. | |
| 53 | /// Sema will set this to null when it takes ownership. | |
| 54 | preallocated_new_func: ?*Module.Fn = null, | |
| 48 | 55 | |
| 49 | 56 | const std = @import("std"); |
| 50 | 57 | const mem = std.mem; |
| ... | ... | @@ -80,45 +87,6 @@ pub fn deinit(sema: *Sema) void { |
| 80 | 87 | sema.* = undefined; |
| 81 | 88 | } |
| 82 | 89 | |
| 83 | pub fn analyzeFnBody( | |
| 84 | sema: *Sema, | |
| 85 | block: *Scope.Block, | |
| 86 | fn_body_inst: Zir.Inst.Index, | |
| 87 | ) SemaError!void { | |
| 88 | const tags = sema.code.instructions.items(.tag); | |
| 89 | const datas = sema.code.instructions.items(.data); | |
| 90 | const body: []const Zir.Inst.Index = switch (tags[fn_body_inst]) { | |
| 91 | .func, .func_inferred => blk: { | |
| 92 | const inst_data = datas[fn_body_inst].pl_node; | |
| 93 | const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); | |
| 94 | const param_types_len = extra.data.param_types_len; | |
| 95 | const body = sema.code.extra[extra.end + param_types_len ..][0..extra.data.body_len]; | |
| 96 | break :blk body; | |
| 97 | }, | |
| 98 | .extended => blk: { | |
| 99 | const extended = datas[fn_body_inst].extended; | |
| 100 | assert(extended.opcode == .func); | |
| 101 | const extra = sema.code.extraData(Zir.Inst.ExtendedFunc, extended.operand); | |
| 102 | const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small); | |
| 103 | var extra_index: usize = extra.end; | |
| 104 | extra_index += @boolToInt(small.has_lib_name); | |
| 105 | extra_index += @boolToInt(small.has_cc); | |
| 106 | extra_index += @boolToInt(small.has_align); | |
| 107 | if (small.has_comptime_bits) { | |
| 108 | extra_index += (extra.data.param_types_len + 31) / 32; | |
| 109 | } | |
| 110 | extra_index += extra.data.param_types_len; | |
| 111 | const body = sema.code.extra[extra_index..][0..extra.data.body_len]; | |
| 112 | break :blk body; | |
| 113 | }, | |
| 114 | else => unreachable, | |
| 115 | }; | |
| 116 | _ = sema.analyzeBody(block, body) catch |err| switch (err) { | |
| 117 | error.NeededSourceLocation => unreachable, | |
| 118 | else => |e| return e, | |
| 119 | }; | |
| 120 | } | |
| 121 | ||
| 122 | 90 | /// Returns only the result from the body that is specified. |
| 123 | 91 | /// Only appropriate to call when it is determined at comptime that this body |
| 124 | 92 | /// has no peers. |
| ... | ... | @@ -162,7 +130,6 @@ pub fn analyzeBody( |
| 162 | 130 | const inst = body[i]; |
| 163 | 131 | const air_inst: Air.Inst.Ref = switch (tags[inst]) { |
| 164 | 132 | // zig fmt: off |
| 165 | .arg => try sema.zirArg(block, inst), | |
| 166 | 133 | .alloc => try sema.zirAlloc(block, inst), |
| 167 | 134 | .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)), |
| 168 | 135 | .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)), |
| ... | ... | @@ -499,6 +466,26 @@ pub fn analyzeBody( |
| 499 | 466 | i += 1; |
| 500 | 467 | continue; |
| 501 | 468 | }, |
| 469 | .param => { | |
| 470 | try sema.zirParam(block, inst, false); | |
| 471 | i += 1; | |
| 472 | continue; | |
| 473 | }, | |
| 474 | .param_comptime => { | |
| 475 | try sema.zirParam(block, inst, true); | |
| 476 | i += 1; | |
| 477 | continue; | |
| 478 | }, | |
| 479 | .param_anytype => { | |
| 480 | try sema.zirParamAnytype(block, inst, false); | |
| 481 | i += 1; | |
| 482 | continue; | |
| 483 | }, | |
| 484 | .param_anytype_comptime => { | |
| 485 | try sema.zirParamAnytype(block, inst, true); | |
| 486 | i += 1; | |
| 487 | continue; | |
| 488 | }, | |
| 502 | 489 | |
| 503 | 490 | // Special case instructions to handle comptime control flow. |
| 504 | 491 | .repeat_inline => { |
| ... | ... | @@ -648,6 +635,7 @@ fn resolveValue( |
| 648 | 635 | air_ref: Air.Inst.Ref, |
| 649 | 636 | ) CompileError!Value { |
| 650 | 637 | if (try sema.resolveMaybeUndefValAllowVariables(block, src, air_ref)) |val| { |
| 638 | if (val.tag() == .generic_poison) return error.GenericPoison; | |
| 651 | 639 | return val; |
| 652 | 640 | } |
| 653 | 641 | return sema.failWithNeededComptime(block, src); |
| ... | ... | @@ -665,6 +653,7 @@ fn resolveConstValue( |
| 665 | 653 | switch (val.tag()) { |
| 666 | 654 | .undef => return sema.failWithUseOfUndef(block, src), |
| 667 | 655 | .variable => return sema.failWithNeededComptime(block, src), |
| 656 | .generic_poison => return error.GenericPoison, | |
| 668 | 657 | else => return val, |
| 669 | 658 | } |
| 670 | 659 | } |
| ... | ... | @@ -1044,7 +1033,6 @@ fn zirEnumDecl( |
| 1044 | 1033 | .namespace = &enum_obj.namespace, |
| 1045 | 1034 | .owner_func = null, |
| 1046 | 1035 | .func = null, |
| 1047 | .param_inst_list = &.{}, | |
| 1048 | 1036 | .branch_quota = sema.branch_quota, |
| 1049 | 1037 | .branch_count = sema.branch_count, |
| 1050 | 1038 | }; |
| ... | ... | @@ -1324,57 +1312,44 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co |
| 1324 | 1312 | |
| 1325 | 1313 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 1326 | 1314 | const src = inst_data.src(); |
| 1327 | const array_ptr = sema.resolveInst(inst_data.operand); | |
| 1328 | const array_ptr_src = src; | |
| 1315 | const array = sema.resolveInst(inst_data.operand); | |
| 1316 | const array_ty = sema.typeOf(array); | |
| 1329 | 1317 | |
| 1330 | const elem_ty = sema.typeOf(array_ptr).elemType(); | |
| 1331 | if (elem_ty.isSlice()) { | |
| 1332 | const slice_inst = try sema.analyzeLoad(block, src, array_ptr, array_ptr_src); | |
| 1333 | return sema.analyzeSliceLen(block, src, slice_inst); | |
| 1318 | if (array_ty.isSlice()) { | |
| 1319 | return sema.analyzeSliceLen(block, src, array); | |
| 1334 | 1320 | } |
| 1335 | if (!elem_ty.isIndexable()) { | |
| 1336 | const cond_src: LazySrcLoc = .{ .node_offset_for_cond = inst_data.src_node }; | |
| 1337 | const msg = msg: { | |
| 1338 | const msg = try sema.mod.errMsg( | |
| 1339 | &block.base, | |
| 1340 | cond_src, | |
| 1341 | "type '{}' does not support indexing", | |
| 1342 | .{elem_ty}, | |
| 1343 | ); | |
| 1344 | errdefer msg.destroy(sema.gpa); | |
| 1345 | try sema.mod.errNote( | |
| 1346 | &block.base, | |
| 1347 | cond_src, | |
| 1348 | msg, | |
| 1349 | "for loop operand must be an array, slice, tuple, or vector", | |
| 1350 | .{}, | |
| 1351 | ); | |
| 1352 | break :msg msg; | |
| 1353 | }; | |
| 1354 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 1355 | } | |
| 1356 | const result_ptr = try sema.fieldPtr(block, src, array_ptr, "len", src); | |
| 1357 | const result_ptr_src = array_ptr_src; | |
| 1358 | return sema.analyzeLoad(block, src, result_ptr, result_ptr_src); | |
| 1359 | } | |
| 1360 | 1321 | |
| 1361 | fn zirArg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 1362 | const inst_data = sema.code.instructions.items(.data)[inst].str_tok; | |
| 1363 | const arg_name = inst_data.get(sema.code); | |
| 1364 | const arg_index = sema.next_arg_index; | |
| 1365 | sema.next_arg_index += 1; | |
| 1366 | ||
| 1367 | // TODO check if arg_name shadows a Decl | |
| 1368 | _ = arg_name; | |
| 1369 | ||
| 1370 | if (block.inlining) |_| { | |
| 1371 | return sema.param_inst_list[arg_index]; | |
| 1322 | if (array_ty.isSinglePointer()) { | |
| 1323 | const elem_ty = array_ty.elemType(); | |
| 1324 | if (elem_ty.isSlice()) { | |
| 1325 | const slice_inst = try sema.analyzeLoad(block, src, array, src); | |
| 1326 | return sema.analyzeSliceLen(block, src, slice_inst); | |
| 1327 | } | |
| 1328 | if (!elem_ty.isIndexable()) { | |
| 1329 | const msg = msg: { | |
| 1330 | const msg = try sema.mod.errMsg( | |
| 1331 | &block.base, | |
| 1332 | src, | |
| 1333 | "type '{}' does not support indexing", | |
| 1334 | .{elem_ty}, | |
| 1335 | ); | |
| 1336 | errdefer msg.destroy(sema.gpa); | |
| 1337 | try sema.mod.errNote( | |
| 1338 | &block.base, | |
| 1339 | src, | |
| 1340 | msg, | |
| 1341 | "for loop operand must be an array, slice, tuple, or vector", | |
| 1342 | .{}, | |
| 1343 | ); | |
| 1344 | break :msg msg; | |
| 1345 | }; | |
| 1346 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 1347 | } | |
| 1348 | const result_ptr = try sema.fieldPtr(block, src, array, "len", src); | |
| 1349 | return sema.analyzeLoad(block, src, result_ptr, src); | |
| 1372 | 1350 | } |
| 1373 | 1351 | |
| 1374 | // Set the name of the Air.Arg instruction for use by codegen debug info. | |
| 1375 | const air_arg = sema.param_inst_list[arg_index]; | |
| 1376 | sema.air_instructions.items(.data)[Air.refToIndex(air_arg).?].ty_str.str = inst_data.start; | |
| 1377 | return air_arg; | |
| 1352 | return sema.mod.fail(&block.base, src, "TODO implement Sema.zirIndexablePtrLen", .{}); | |
| 1378 | 1353 | } |
| 1379 | 1354 | |
| 1380 | 1355 | fn zirAllocExtended( |
| ... | ... | @@ -2385,6 +2360,40 @@ fn zirCall( |
| 2385 | 2360 | return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args); |
| 2386 | 2361 | } |
| 2387 | 2362 | |
| 2363 | const GenericCallAdapter = struct { | |
| 2364 | generic_fn: *Module.Fn, | |
| 2365 | precomputed_hash: u64, | |
| 2366 | func_ty_info: Type.Payload.Function.Data, | |
| 2367 | comptime_vals: []const Value, | |
| 2368 | ||
| 2369 | pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool { | |
| 2370 | _ = adapted_key; | |
| 2371 | // The generic function Decl is guaranteed to be the first dependency | |
| 2372 | // of each of its instantiations. | |
| 2373 | const generic_owner_decl = other_key.owner_decl.dependencies.keys()[0]; | |
| 2374 | if (ctx.generic_fn.owner_decl != generic_owner_decl) return false; | |
| 2375 | ||
| 2376 | // This logic must be kept in sync with the logic in `analyzeCall` that | |
| 2377 | // computes the hash. | |
| 2378 | const other_comptime_args = other_key.comptime_args.?; | |
| 2379 | for (ctx.func_ty_info.param_types) |param_ty, i| { | |
| 2380 | if (ctx.func_ty_info.paramIsComptime(i) and param_ty.tag() != .generic_poison) { | |
| 2381 | if (!ctx.comptime_vals[i].eql(other_comptime_args[i].val, param_ty)) { | |
| 2382 | return false; | |
| 2383 | } | |
| 2384 | } | |
| 2385 | } | |
| 2386 | return true; | |
| 2387 | } | |
| 2388 | ||
| 2389 | /// The implementation of the hash is in semantic analysis of function calls, so | |
| 2390 | /// that any errors when computing the hash can be properly reported. | |
| 2391 | pub fn hash(ctx: @This(), adapted_key: void) u64 { | |
| 2392 | _ = adapted_key; | |
| 2393 | return ctx.precomputed_hash; | |
| 2394 | } | |
| 2395 | }; | |
| 2396 | ||
| 2388 | 2397 | fn analyzeCall( |
| 2389 | 2398 | sema: *Sema, |
| 2390 | 2399 | block: *Scope.Block, |
| ... | ... | @@ -2393,41 +2402,44 @@ fn analyzeCall( |
| 2393 | 2402 | call_src: LazySrcLoc, |
| 2394 | 2403 | modifier: std.builtin.CallOptions.Modifier, |
| 2395 | 2404 | ensure_result_used: bool, |
| 2396 | args: []const Air.Inst.Ref, | |
| 2405 | uncasted_args: []const Air.Inst.Ref, | |
| 2397 | 2406 | ) CompileError!Air.Inst.Ref { |
| 2407 | const mod = sema.mod; | |
| 2408 | ||
| 2398 | 2409 | const func_ty = sema.typeOf(func); |
| 2399 | 2410 | if (func_ty.zigTypeTag() != .Fn) |
| 2400 | return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func_ty}); | |
| 2411 | return mod.fail(&block.base, func_src, "type '{}' not a function", .{func_ty}); | |
| 2401 | 2412 | |
| 2402 | const cc = func_ty.fnCallingConvention(); | |
| 2413 | const func_ty_info = func_ty.fnInfo(); | |
| 2414 | const cc = func_ty_info.cc; | |
| 2403 | 2415 | if (cc == .Naked) { |
| 2404 | 2416 | // TODO add error note: declared here |
| 2405 | return sema.mod.fail( | |
| 2417 | return mod.fail( | |
| 2406 | 2418 | &block.base, |
| 2407 | 2419 | func_src, |
| 2408 | 2420 | "unable to call function with naked calling convention", |
| 2409 | 2421 | .{}, |
| 2410 | 2422 | ); |
| 2411 | 2423 | } |
| 2412 | const fn_params_len = func_ty.fnParamLen(); | |
| 2413 | if (func_ty.fnIsVarArgs()) { | |
| 2424 | const fn_params_len = func_ty_info.param_types.len; | |
| 2425 | if (func_ty_info.is_var_args) { | |
| 2414 | 2426 | assert(cc == .C); |
| 2415 | if (args.len < fn_params_len) { | |
| 2427 | if (uncasted_args.len < fn_params_len) { | |
| 2416 | 2428 | // TODO add error note: declared here |
| 2417 | return sema.mod.fail( | |
| 2429 | return mod.fail( | |
| 2418 | 2430 | &block.base, |
| 2419 | 2431 | func_src, |
| 2420 | 2432 | "expected at least {d} argument(s), found {d}", |
| 2421 | .{ fn_params_len, args.len }, | |
| 2433 | .{ fn_params_len, uncasted_args.len }, | |
| 2422 | 2434 | ); |
| 2423 | 2435 | } |
| 2424 | } else if (fn_params_len != args.len) { | |
| 2436 | } else if (fn_params_len != uncasted_args.len) { | |
| 2425 | 2437 | // TODO add error note: declared here |
| 2426 | return sema.mod.fail( | |
| 2438 | return mod.fail( | |
| 2427 | 2439 | &block.base, |
| 2428 | 2440 | func_src, |
| 2429 | 2441 | "expected {d} argument(s), found {d}", |
| 2430 | .{ fn_params_len, args.len }, | |
| 2442 | .{ fn_params_len, uncasted_args.len }, | |
| 2431 | 2443 | ); |
| 2432 | 2444 | } |
| 2433 | 2445 | |
| ... | ... | @@ -2442,21 +2454,30 @@ fn analyzeCall( |
| 2442 | 2454 | .never_inline, |
| 2443 | 2455 | .no_async, |
| 2444 | 2456 | .always_tail, |
| 2445 | => return sema.mod.fail(&block.base, call_src, "TODO implement call with modifier {}", .{ | |
| 2457 | => return mod.fail(&block.base, call_src, "TODO implement call with modifier {}", .{ | |
| 2446 | 2458 | modifier, |
| 2447 | 2459 | }), |
| 2448 | 2460 | } |
| 2449 | 2461 | |
| 2450 | 2462 | const gpa = sema.gpa; |
| 2451 | 2463 | |
| 2452 | const is_comptime_call = block.is_comptime or modifier == .compile_time; | |
| 2464 | const is_comptime_call = block.is_comptime or modifier == .compile_time or | |
| 2465 | func_ty_info.return_type.requiresComptime(); | |
| 2453 | 2466 | const is_inline_call = is_comptime_call or modifier == .always_inline or |
| 2454 | func_ty.fnCallingConvention() == .Inline; | |
| 2467 | func_ty_info.cc == .Inline; | |
| 2455 | 2468 | const result: Air.Inst.Ref = if (is_inline_call) res: { |
| 2469 | // TODO look into not allocating this args array | |
| 2470 | const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); | |
| 2471 | for (uncasted_args) |uncasted_arg, i| { | |
| 2472 | const param_ty = func_ty.fnParamType(i); | |
| 2473 | const arg_src = call_src; // TODO: better source location | |
| 2474 | args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src); | |
| 2475 | } | |
| 2476 | ||
| 2456 | 2477 | const func_val = try sema.resolveConstValue(block, func_src, func); |
| 2457 | 2478 | const module_fn = switch (func_val.tag()) { |
| 2458 | 2479 | .function => func_val.castTag(.function).?.data, |
| 2459 | .extern_fn => return sema.mod.fail(&block.base, call_src, "{s} call of extern function", .{ | |
| 2480 | .extern_fn => return mod.fail(&block.base, call_src, "{s} call of extern function", .{ | |
| 2460 | 2481 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), |
| 2461 | 2482 | }), |
| 2462 | 2483 | else => unreachable, |
| ... | ... | @@ -2502,14 +2523,6 @@ fn analyzeCall( |
| 2502 | 2523 | sema.func = module_fn; |
| 2503 | 2524 | defer sema.func = parent_func; |
| 2504 | 2525 | |
| 2505 | const parent_param_inst_list = sema.param_inst_list; | |
| 2506 | sema.param_inst_list = args; | |
| 2507 | defer sema.param_inst_list = parent_param_inst_list; | |
| 2508 | ||
| 2509 | const parent_next_arg_index = sema.next_arg_index; | |
| 2510 | sema.next_arg_index = 0; | |
| 2511 | defer sema.next_arg_index = parent_next_arg_index; | |
| 2512 | ||
| 2513 | 2526 | var child_block: Scope.Block = .{ |
| 2514 | 2527 | .parent = null, |
| 2515 | 2528 | .sema = sema, |
| ... | ... | @@ -2529,16 +2542,229 @@ fn analyzeCall( |
| 2529 | 2542 | try sema.emitBackwardBranch(&child_block, call_src); |
| 2530 | 2543 | |
| 2531 | 2544 | // This will have return instructions analyzed as break instructions to |
| 2532 | // the block_inst above. | |
| 2533 | try sema.analyzeFnBody(&child_block, module_fn.zir_body_inst); | |
| 2545 | // the block_inst above. Here we are performing "comptime/inline semantic analysis" | |
| 2546 | // for a function body, which means we must map the parameter ZIR instructions to | |
| 2547 | // the AIR instructions of the callsite. | |
| 2548 | const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst); | |
| 2549 | const zir_tags = sema.code.instructions.items(.tag); | |
| 2550 | var arg_i: usize = 0; | |
| 2551 | try sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, args.len)); | |
| 2552 | for (fn_info.param_body) |inst| { | |
| 2553 | switch (zir_tags[inst]) { | |
| 2554 | .param, .param_comptime, .param_anytype, .param_anytype_comptime => {}, | |
| 2555 | else => continue, | |
| 2556 | } | |
| 2557 | sema.inst_map.putAssumeCapacityNoClobber(inst, args[arg_i]); | |
| 2558 | arg_i += 1; | |
| 2559 | } | |
| 2560 | _ = try sema.analyzeBody(&child_block, fn_info.body); | |
| 2561 | break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges); | |
| 2562 | } else if (func_ty_info.is_generic) res: { | |
| 2563 | const func_val = try sema.resolveConstValue(block, func_src, func); | |
| 2564 | const module_fn = func_val.castTag(.function).?.data; | |
| 2565 | // Check the Module's generic function map with an adapted context, so that we | |
| 2566 | // can match against `uncasted_args` rather than doing the work below to create a | |
| 2567 | // generic Scope only to junk it if it matches an existing instantiation. | |
| 2568 | const namespace = module_fn.owner_decl.namespace; | |
| 2569 | const fn_zir = namespace.file_scope.zir; | |
| 2570 | const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst); | |
| 2571 | const zir_tags = fn_zir.instructions.items(.tag); | |
| 2572 | const new_module_func = new_func: { | |
| 2573 | // This hash must match `Module.MonomorphedFuncsContext.hash`. | |
| 2574 | // For parameters explicitly marked comptime and simple parameter type expressions, | |
| 2575 | // we know whether a parameter is elided from a monomorphed function, and can | |
| 2576 | // use it in the hash here. However, for parameter type expressions that are not | |
| 2577 | // explicitly marked comptime and rely on previous parameter comptime values, we | |
| 2578 | // don't find out until after generating a monomorphed function whether the parameter | |
| 2579 | // type ended up being a "must-be-comptime-known" type. | |
| 2580 | var hasher = std.hash.Wyhash.init(0); | |
| 2581 | std.hash.autoHash(&hasher, @ptrToInt(module_fn)); | |
| 2582 | ||
| 2583 | const comptime_vals = try sema.arena.alloc(Value, func_ty_info.param_types.len); | |
| 2584 | ||
| 2585 | for (func_ty_info.param_types) |param_ty, i| { | |
| 2586 | const is_comptime = func_ty_info.paramIsComptime(i); | |
| 2587 | if (is_comptime and param_ty.tag() != .generic_poison) { | |
| 2588 | const arg_src = call_src; // TODO better source location | |
| 2589 | const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src); | |
| 2590 | if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| { | |
| 2591 | arg_val.hash(param_ty, &hasher); | |
| 2592 | comptime_vals[i] = arg_val; | |
| 2593 | } else { | |
| 2594 | return sema.failWithNeededComptime(block, arg_src); | |
| 2595 | } | |
| 2596 | } | |
| 2597 | } | |
| 2598 | ||
| 2599 | const adapter: GenericCallAdapter = .{ | |
| 2600 | .generic_fn = module_fn, | |
| 2601 | .precomputed_hash = hasher.final(), | |
| 2602 | .func_ty_info = func_ty_info, | |
| 2603 | .comptime_vals = comptime_vals, | |
| 2604 | }; | |
| 2605 | const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter); | |
| 2606 | if (gop.found_existing) { | |
| 2607 | const callee_func = gop.key_ptr.*; | |
| 2608 | break :res try sema.finishGenericCall( | |
| 2609 | block, | |
| 2610 | call_src, | |
| 2611 | callee_func, | |
| 2612 | func_src, | |
| 2613 | uncasted_args, | |
| 2614 | fn_info, | |
| 2615 | zir_tags, | |
| 2616 | ); | |
| 2617 | } | |
| 2618 | gop.key_ptr.* = try gpa.create(Module.Fn); | |
| 2619 | break :new_func gop.key_ptr.*; | |
| 2620 | }; | |
| 2621 | ||
| 2622 | { | |
| 2623 | try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); | |
| 2624 | ||
| 2625 | // Create a Decl for the new function. | |
| 2626 | const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node); | |
| 2627 | // TODO better names for generic function instantiations | |
| 2628 | const name_index = mod.getNextAnonNameIndex(); | |
| 2629 | new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{ | |
| 2630 | module_fn.owner_decl.name, name_index, | |
| 2631 | }); | |
| 2632 | new_decl.src_line = module_fn.owner_decl.src_line; | |
| 2633 | new_decl.is_pub = module_fn.owner_decl.is_pub; | |
| 2634 | new_decl.is_exported = module_fn.owner_decl.is_exported; | |
| 2635 | new_decl.has_align = module_fn.owner_decl.has_align; | |
| 2636 | new_decl.has_linksection = module_fn.owner_decl.has_linksection; | |
| 2637 | new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index; | |
| 2638 | new_decl.alive = true; // This Decl is called at runtime. | |
| 2639 | new_decl.has_tv = true; | |
| 2640 | new_decl.owns_tv = true; | |
| 2641 | new_decl.analysis = .in_progress; | |
| 2642 | new_decl.generation = mod.generation; | |
| 2643 | ||
| 2644 | namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {}); | |
| 2645 | ||
| 2646 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | |
| 2647 | errdefer new_decl_arena.deinit(); | |
| 2648 | ||
| 2649 | // Re-run the block that creates the function, with the comptime parameters | |
| 2650 | // pre-populated inside `inst_map`. This causes `param_comptime` and | |
| 2651 | // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a | |
| 2652 | // new, monomorphized function, with the comptime parameters elided. | |
| 2653 | var child_sema: Sema = .{ | |
| 2654 | .mod = mod, | |
| 2655 | .gpa = gpa, | |
| 2656 | .arena = sema.arena, | |
| 2657 | .code = fn_zir, | |
| 2658 | .owner_decl = new_decl, | |
| 2659 | .namespace = namespace, | |
| 2660 | .func = null, | |
| 2661 | .owner_func = null, | |
| 2662 | .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len), | |
| 2663 | .comptime_args_fn_inst = module_fn.zir_body_inst, | |
| 2664 | .preallocated_new_func = new_module_func, | |
| 2665 | }; | |
| 2666 | defer child_sema.deinit(); | |
| 2667 | ||
| 2668 | var child_block: Scope.Block = .{ | |
| 2669 | .parent = null, | |
| 2670 | .sema = &child_sema, | |
| 2671 | .src_decl = new_decl, | |
| 2672 | .instructions = .{}, | |
| 2673 | .inlining = null, | |
| 2674 | .is_comptime = true, | |
| 2675 | }; | |
| 2676 | defer { | |
| 2677 | child_block.instructions.deinit(gpa); | |
| 2678 | child_block.params.deinit(gpa); | |
| 2679 | } | |
| 2680 | ||
| 2681 | try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len)); | |
| 2682 | var arg_i: usize = 0; | |
| 2683 | for (fn_info.param_body) |inst| { | |
| 2684 | const is_comptime = switch (zir_tags[inst]) { | |
| 2685 | .param_comptime, .param_anytype_comptime => true, | |
| 2686 | .param, .param_anytype => false, | |
| 2687 | else => continue, | |
| 2688 | } or func_ty_info.paramIsComptime(arg_i); | |
| 2689 | const arg_src = call_src; // TODO: better source location | |
| 2690 | const arg = uncasted_args[arg_i]; | |
| 2691 | if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { | |
| 2692 | const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); | |
| 2693 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); | |
| 2694 | } else if (is_comptime) { | |
| 2695 | return sema.failWithNeededComptime(block, arg_src); | |
| 2696 | } | |
| 2697 | arg_i += 1; | |
| 2698 | } | |
| 2699 | const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body); | |
| 2700 | const new_func_val = try child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst); | |
| 2701 | const new_func = new_func_val.castTag(.function).?.data; | |
| 2702 | assert(new_func == new_module_func); | |
| 2703 | ||
| 2704 | arg_i = 0; | |
| 2705 | for (fn_info.param_body) |inst| { | |
| 2706 | switch (zir_tags[inst]) { | |
| 2707 | .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, | |
| 2708 | else => continue, | |
| 2709 | } | |
| 2710 | const arg = child_sema.inst_map.get(inst).?; | |
| 2711 | const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(&child_block, .unneeded, arg) catch unreachable).?; | |
| 2712 | ||
| 2713 | if (arg_val.tag() == .generic_poison) { | |
| 2714 | child_sema.comptime_args[arg_i] = .{ | |
| 2715 | .ty = Type.initTag(.noreturn), | |
| 2716 | .val = Value.initTag(.unreachable_value), | |
| 2717 | }; | |
| 2718 | } else { | |
| 2719 | child_sema.comptime_args[arg_i] = .{ | |
| 2720 | .ty = try child_sema.typeOf(arg).copy(&new_decl_arena.allocator), | |
| 2721 | .val = try arg_val.copy(&new_decl_arena.allocator), | |
| 2722 | }; | |
| 2723 | } | |
| 2724 | ||
| 2725 | arg_i += 1; | |
| 2726 | } | |
| 2727 | ||
| 2728 | // Populate the Decl ty/val with the function and its type. | |
| 2729 | new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator); | |
| 2730 | new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func); | |
| 2731 | new_decl.analysis = .complete; | |
| 2534 | 2732 | |
| 2535 | const result = try sema.analyzeBlockBody(block, call_src, &child_block, merges); | |
| 2733 | // The generic function Decl is guaranteed to be the first dependency | |
| 2734 | // of each of its instantiations. | |
| 2735 | assert(new_decl.dependencies.keys().len == 0); | |
| 2736 | try mod.declareDeclDependency(new_decl, module_fn.owner_decl); | |
| 2536 | 2737 | |
| 2537 | break :res result; | |
| 2738 | // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field | |
| 2739 | // will be populated, ensuring it will have `analyzeBody` called with the ZIR | |
| 2740 | // parameters mapped appropriately. | |
| 2741 | try mod.comp.bin_file.allocateDeclIndexes(new_decl); | |
| 2742 | try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); | |
| 2743 | ||
| 2744 | try new_decl.finalizeNewArena(&new_decl_arena); | |
| 2745 | } | |
| 2746 | ||
| 2747 | break :res try sema.finishGenericCall( | |
| 2748 | block, | |
| 2749 | call_src, | |
| 2750 | new_module_func, | |
| 2751 | func_src, | |
| 2752 | uncasted_args, | |
| 2753 | fn_info, | |
| 2754 | zir_tags, | |
| 2755 | ); | |
| 2538 | 2756 | } else res: { |
| 2539 | if (func_ty.fnIsGeneric()) { | |
| 2540 | return sema.mod.fail(&block.base, func_src, "TODO implement generic fn call", .{}); | |
| 2757 | const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); | |
| 2758 | for (uncasted_args) |uncasted_arg, i| { | |
| 2759 | if (i < fn_params_len) { | |
| 2760 | const param_ty = func_ty.fnParamType(i); | |
| 2761 | const arg_src = call_src; // TODO: better source location | |
| 2762 | args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src); | |
| 2763 | } else { | |
| 2764 | args[i] = uncasted_arg; | |
| 2765 | } | |
| 2541 | 2766 | } |
| 2767 | ||
| 2542 | 2768 | try sema.requireRuntimeBlock(block, call_src); |
| 2543 | 2769 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + |
| 2544 | 2770 | args.len); |
| ... | ... | @@ -2561,6 +2787,75 @@ fn analyzeCall( |
| 2561 | 2787 | return result; |
| 2562 | 2788 | } |
| 2563 | 2789 | |
| 2790 | fn finishGenericCall( | |
| 2791 | sema: *Sema, | |
| 2792 | block: *Scope.Block, | |
| 2793 | call_src: LazySrcLoc, | |
| 2794 | callee: *Module.Fn, | |
| 2795 | func_src: LazySrcLoc, | |
| 2796 | uncasted_args: []const Air.Inst.Ref, | |
| 2797 | fn_info: Zir.FnInfo, | |
| 2798 | zir_tags: []const Zir.Inst.Tag, | |
| 2799 | ) CompileError!Air.Inst.Ref { | |
| 2800 | const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl); | |
| 2801 | ||
| 2802 | // Make a runtime call to the new function, making sure to omit the comptime args. | |
| 2803 | try sema.requireRuntimeBlock(block, call_src); | |
| 2804 | ||
| 2805 | const comptime_args = callee.comptime_args.?; | |
| 2806 | const runtime_args_len = count: { | |
| 2807 | var count: u32 = 0; | |
| 2808 | var arg_i: usize = 0; | |
| 2809 | for (fn_info.param_body) |inst| { | |
| 2810 | switch (zir_tags[inst]) { | |
| 2811 | .param_comptime, .param_anytype_comptime, .param, .param_anytype => { | |
| 2812 | if (comptime_args[arg_i].val.tag() == .unreachable_value) { | |
| 2813 | count += 1; | |
| 2814 | } | |
| 2815 | arg_i += 1; | |
| 2816 | }, | |
| 2817 | else => continue, | |
| 2818 | } | |
| 2819 | } | |
| 2820 | break :count count; | |
| 2821 | }; | |
| 2822 | const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len); | |
| 2823 | { | |
| 2824 | const new_fn_ty = callee.owner_decl.ty; | |
| 2825 | var runtime_i: u32 = 0; | |
| 2826 | var total_i: u32 = 0; | |
| 2827 | for (fn_info.param_body) |inst| { | |
| 2828 | switch (zir_tags[inst]) { | |
| 2829 | .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, | |
| 2830 | else => continue, | |
| 2831 | } | |
| 2832 | const is_runtime = comptime_args[total_i].val.tag() == .unreachable_value; | |
| 2833 | if (is_runtime) { | |
| 2834 | const param_ty = new_fn_ty.fnParamType(runtime_i); | |
| 2835 | const arg_src = call_src; // TODO: better source location | |
| 2836 | const uncasted_arg = uncasted_args[total_i]; | |
| 2837 | const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); | |
| 2838 | runtime_args[runtime_i] = casted_arg; | |
| 2839 | runtime_i += 1; | |
| 2840 | } | |
| 2841 | total_i += 1; | |
| 2842 | } | |
| 2843 | } | |
| 2844 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + | |
| 2845 | runtime_args_len); | |
| 2846 | const func_inst = try block.addInst(.{ | |
| 2847 | .tag = .call, | |
| 2848 | .data = .{ .pl_op = .{ | |
| 2849 | .operand = callee_inst, | |
| 2850 | .payload = sema.addExtraAssumeCapacity(Air.Call{ | |
| 2851 | .args_len = runtime_args_len, | |
| 2852 | }), | |
| 2853 | } }, | |
| 2854 | }); | |
| 2855 | sema.appendRefsAssumeCapacity(runtime_args); | |
| 2856 | return func_inst; | |
| 2857 | } | |
| 2858 | ||
| 2564 | 2859 | fn zirIntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 2565 | 2860 | _ = block; |
| 2566 | 2861 | const tracy = trace(@src()); |
| ... | ... | @@ -3186,13 +3481,15 @@ fn zirFunc( |
| 3186 | 3481 | |
| 3187 | 3482 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 3188 | 3483 | const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); |
| 3189 | const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len); | |
| 3484 | var extra_index = extra.end; | |
| 3485 | const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 3486 | extra_index += ret_ty_body.len; | |
| 3190 | 3487 | |
| 3191 | 3488 | var body_inst: Zir.Inst.Index = 0; |
| 3192 | 3489 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; |
| 3193 | 3490 | if (extra.data.body_len != 0) { |
| 3194 | 3491 | body_inst = inst; |
| 3195 | const extra_index = extra.end + extra.data.param_types_len + extra.data.body_len; | |
| 3492 | extra_index += extra.data.body_len; | |
| 3196 | 3493 | src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; |
| 3197 | 3494 | } |
| 3198 | 3495 | |
| ... | ... | @@ -3204,9 +3501,8 @@ fn zirFunc( |
| 3204 | 3501 | return sema.funcCommon( |
| 3205 | 3502 | block, |
| 3206 | 3503 | inst_data.src_node, |
| 3207 | param_types, | |
| 3208 | 3504 | body_inst, |
| 3209 | extra.data.return_type, | |
| 3505 | ret_ty_body, | |
| 3210 | 3506 | cc, |
| 3211 | 3507 | Value.initTag(.null_value), |
| 3212 | 3508 | false, |
| ... | ... | @@ -3214,7 +3510,6 @@ fn zirFunc( |
| 3214 | 3510 | false, |
| 3215 | 3511 | src_locs, |
| 3216 | 3512 | null, |
| 3217 | &.{}, | |
| 3218 | 3513 | ); |
| 3219 | 3514 | } |
| 3220 | 3515 | |
| ... | ... | @@ -3222,9 +3517,8 @@ fn funcCommon( |
| 3222 | 3517 | sema: *Sema, |
| 3223 | 3518 | block: *Scope.Block, |
| 3224 | 3519 | src_node_offset: i32, |
| 3225 | zir_param_types: []const Zir.Inst.Ref, | |
| 3226 | 3520 | body_inst: Zir.Inst.Index, |
| 3227 | zir_return_type: Zir.Inst.Ref, | |
| 3521 | ret_ty_body: []const Zir.Inst.Index, | |
| 3228 | 3522 | cc: std.builtin.CallingConvention, |
| 3229 | 3523 | align_val: Value, |
| 3230 | 3524 | var_args: bool, |
| ... | ... | @@ -3232,21 +3526,59 @@ fn funcCommon( |
| 3232 | 3526 | is_extern: bool, |
| 3233 | 3527 | src_locs: Zir.Inst.Func.SrcLocs, |
| 3234 | 3528 | opt_lib_name: ?[]const u8, |
| 3235 | comptime_bits: []const u32, | |
| 3236 | 3529 | ) CompileError!Air.Inst.Ref { |
| 3237 | 3530 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; |
| 3238 | 3531 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; |
| 3239 | const bare_return_type = try sema.resolveType(block, ret_ty_src, zir_return_type); | |
| 3532 | ||
| 3533 | // The return type body might be a type expression that depends on generic parameters. | |
| 3534 | // In such case we need to use a generic_poison value for the return type and mark | |
| 3535 | // the function as generic. | |
| 3536 | var is_generic = false; | |
| 3537 | const bare_return_type: Type = ret_ty: { | |
| 3538 | if (ret_ty_body.len == 0) break :ret_ty Type.initTag(.void); | |
| 3539 | ||
| 3540 | const err = err: { | |
| 3541 | // Make sure any nested param instructions don't clobber our work. | |
| 3542 | const prev_params = block.params; | |
| 3543 | block.params = .{}; | |
| 3544 | defer { | |
| 3545 | block.params.deinit(sema.gpa); | |
| 3546 | block.params = prev_params; | |
| 3547 | } | |
| 3548 | if (sema.resolveBody(block, ret_ty_body)) |ret_ty_inst| { | |
| 3549 | if (sema.analyzeAsType(block, ret_ty_src, ret_ty_inst)) |ret_ty| { | |
| 3550 | break :ret_ty ret_ty; | |
| 3551 | } else |err| break :err err; | |
| 3552 | } else |err| break :err err; | |
| 3553 | }; | |
| 3554 | switch (err) { | |
| 3555 | error.GenericPoison => { | |
| 3556 | // The type is not available until the generic instantiation. | |
| 3557 | is_generic = true; | |
| 3558 | break :ret_ty Type.initTag(.generic_poison); | |
| 3559 | }, | |
| 3560 | else => |e| return e, | |
| 3561 | } | |
| 3562 | }; | |
| 3240 | 3563 | |
| 3241 | 3564 | const mod = sema.mod; |
| 3242 | 3565 | |
| 3243 | const new_func = if (body_inst == 0) undefined else try sema.gpa.create(Module.Fn); | |
| 3566 | const new_func: *Module.Fn = new_func: { | |
| 3567 | if (body_inst == 0) break :new_func undefined; | |
| 3568 | if (sema.comptime_args_fn_inst == body_inst) { | |
| 3569 | const new_func = sema.preallocated_new_func.?; | |
| 3570 | sema.preallocated_new_func = null; // take ownership | |
| 3571 | break :new_func new_func; | |
| 3572 | } | |
| 3573 | break :new_func try sema.gpa.create(Module.Fn); | |
| 3574 | }; | |
| 3244 | 3575 | errdefer if (body_inst != 0) sema.gpa.destroy(new_func); |
| 3245 | 3576 | |
| 3246 | 3577 | const fn_ty: Type = fn_ty: { |
| 3247 | 3578 | // Hot path for some common function types. |
| 3248 | if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value and | |
| 3249 | !inferred_error_set) | |
| 3579 | // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated. | |
| 3580 | if (!is_generic and block.params.items.len == 0 and !var_args and | |
| 3581 | align_val.tag() == .null_value and !inferred_error_set) | |
| 3250 | 3582 | { |
| 3251 | 3583 | if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { |
| 3252 | 3584 | break :fn_ty Type.initTag(.fn_noreturn_no_args); |
| ... | ... | @@ -3265,30 +3597,24 @@ fn funcCommon( |
| 3265 | 3597 | } |
| 3266 | 3598 | } |
| 3267 | 3599 | |
| 3268 | var any_are_comptime = false; | |
| 3269 | const param_types = try sema.arena.alloc(Type, zir_param_types.len); | |
| 3270 | for (zir_param_types) |param_type, i| { | |
| 3271 | // TODO make a compile error from `resolveType` report the source location | |
| 3272 | // of the specific parameter. Will need to take a similar strategy as | |
| 3273 | // `resolveSwitchItemVal` to avoid resolving the source location unless | |
| 3274 | // we actually need to report an error. | |
| 3275 | const param_src = src; | |
| 3276 | param_types[i] = try sema.resolveType(block, param_src, param_type); | |
| 3277 | ||
| 3278 | any_are_comptime = any_are_comptime or blk: { | |
| 3279 | if (comptime_bits.len == 0) | |
| 3280 | break :blk false; | |
| 3281 | const bag = comptime_bits[i / 32]; | |
| 3282 | const is_comptime = @truncate(u1, bag >> @intCast(u5, i % 32)) != 0; | |
| 3283 | break :blk is_comptime; | |
| 3284 | }; | |
| 3600 | const param_types = try sema.arena.alloc(Type, block.params.items.len); | |
| 3601 | const comptime_params = try sema.arena.alloc(bool, block.params.items.len); | |
| 3602 | for (block.params.items) |param, i| { | |
| 3603 | param_types[i] = param.ty; | |
| 3604 | comptime_params[i] = param.is_comptime; | |
| 3605 | is_generic = is_generic or param.is_comptime or | |
| 3606 | param.ty.tag() == .generic_poison or param.ty.requiresComptime(); | |
| 3285 | 3607 | } |
| 3286 | 3608 | |
| 3287 | 3609 | if (align_val.tag() != .null_value) { |
| 3288 | 3610 | return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{}); |
| 3289 | 3611 | } |
| 3290 | 3612 | |
| 3291 | const return_type = if (!inferred_error_set) bare_return_type else blk: { | |
| 3613 | is_generic = is_generic or bare_return_type.requiresComptime(); | |
| 3614 | ||
| 3615 | const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison) | |
| 3616 | bare_return_type | |
| 3617 | else blk: { | |
| 3292 | 3618 | const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, .{ |
| 3293 | 3619 | .func = new_func, |
| 3294 | 3620 | .map = .{}, |
| ... | ... | @@ -3301,10 +3627,11 @@ fn funcCommon( |
| 3301 | 3627 | |
| 3302 | 3628 | break :fn_ty try Type.Tag.function.create(sema.arena, .{ |
| 3303 | 3629 | .param_types = param_types, |
| 3630 | .comptime_params = comptime_params.ptr, | |
| 3304 | 3631 | .return_type = return_type, |
| 3305 | 3632 | .cc = cc, |
| 3306 | 3633 | .is_var_args = var_args, |
| 3307 | .is_generic = any_are_comptime, | |
| 3634 | .is_generic = is_generic, | |
| 3308 | 3635 | }); |
| 3309 | 3636 | }; |
| 3310 | 3637 | |
| ... | ... | @@ -3363,11 +3690,16 @@ fn funcCommon( |
| 3363 | 3690 | const is_inline = fn_ty.fnCallingConvention() == .Inline; |
| 3364 | 3691 | const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued; |
| 3365 | 3692 | |
| 3693 | const comptime_args: ?[*]TypedValue = if (sema.comptime_args_fn_inst == body_inst) blk: { | |
| 3694 | break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr; | |
| 3695 | } else null; | |
| 3696 | ||
| 3366 | 3697 | const fn_payload = try sema.arena.create(Value.Payload.Function); |
| 3367 | 3698 | new_func.* = .{ |
| 3368 | 3699 | .state = anal_state, |
| 3369 | 3700 | .zir_body_inst = body_inst, |
| 3370 | 3701 | .owner_decl = sema.owner_decl, |
| 3702 | .comptime_args = comptime_args, | |
| 3371 | 3703 | .lbrace_line = src_locs.lbrace_line, |
| 3372 | 3704 | .rbrace_line = src_locs.rbrace_line, |
| 3373 | 3705 | .lbrace_column = @truncate(u16, src_locs.columns), |
| ... | ... | @@ -3380,6 +3712,113 @@ fn funcCommon( |
| 3380 | 3712 | return sema.addConstant(fn_ty, Value.initPayload(&fn_payload.base)); |
| 3381 | 3713 | } |
| 3382 | 3714 | |
| 3715 | fn zirParam( | |
| 3716 | sema: *Sema, | |
| 3717 | block: *Scope.Block, | |
| 3718 | inst: Zir.Inst.Index, | |
| 3719 | is_comptime: bool, | |
| 3720 | ) CompileError!void { | |
| 3721 | const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; | |
| 3722 | const src = inst_data.src(); | |
| 3723 | const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); | |
| 3724 | const param_name = sema.code.nullTerminatedString(extra.data.name); | |
| 3725 | const body = sema.code.extra[extra.end..][0..extra.data.body_len]; | |
| 3726 | ||
| 3727 | // TODO check if param_name shadows a Decl. This only needs to be done if | |
| 3728 | // usingnamespace is implemented. | |
| 3729 | _ = param_name; | |
| 3730 | ||
| 3731 | // We could be in a generic function instantiation, or we could be evaluating a generic | |
| 3732 | // function without any comptime args provided. | |
| 3733 | const param_ty = param_ty: { | |
| 3734 | const err = err: { | |
| 3735 | // Make sure any nested param instructions don't clobber our work. | |
| 3736 | const prev_params = block.params; | |
| 3737 | block.params = .{}; | |
| 3738 | defer { | |
| 3739 | block.params.deinit(sema.gpa); | |
| 3740 | block.params = prev_params; | |
| 3741 | } | |
| 3742 | ||
| 3743 | if (sema.resolveBody(block, body)) |param_ty_inst| { | |
| 3744 | if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| { | |
| 3745 | break :param_ty param_ty; | |
| 3746 | } else |err| break :err err; | |
| 3747 | } else |err| break :err err; | |
| 3748 | }; | |
| 3749 | switch (err) { | |
| 3750 | error.GenericPoison => { | |
| 3751 | // The type is not available until the generic instantiation. | |
| 3752 | // We result the param instruction with a poison value and | |
| 3753 | // insert an anytype parameter. | |
| 3754 | try block.params.append(sema.gpa, .{ | |
| 3755 | .ty = Type.initTag(.generic_poison), | |
| 3756 | .is_comptime = is_comptime, | |
| 3757 | }); | |
| 3758 | try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison); | |
| 3759 | return; | |
| 3760 | }, | |
| 3761 | else => |e| return e, | |
| 3762 | } | |
| 3763 | }; | |
| 3764 | if (sema.inst_map.get(inst)) |arg| { | |
| 3765 | if (is_comptime or param_ty.requiresComptime()) { | |
| 3766 | // We have a comptime value for this parameter so it should be elided from the | |
| 3767 | // function type of the function instruction in this block. | |
| 3768 | const coerced_arg = try sema.coerce(block, param_ty, arg, src); | |
| 3769 | sema.inst_map.putAssumeCapacity(inst, coerced_arg); | |
| 3770 | return; | |
| 3771 | } | |
| 3772 | // Even though a comptime argument is provided, the generic function wants to treat | |
| 3773 | // this as a runtime parameter. | |
| 3774 | assert(sema.inst_map.remove(inst)); | |
| 3775 | } | |
| 3776 | ||
| 3777 | try block.params.append(sema.gpa, .{ | |
| 3778 | .ty = param_ty, | |
| 3779 | .is_comptime = is_comptime or param_ty.requiresComptime(), | |
| 3780 | }); | |
| 3781 | const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); | |
| 3782 | try sema.inst_map.putNoClobber(sema.gpa, inst, result); | |
| 3783 | } | |
| 3784 | ||
| 3785 | fn zirParamAnytype( | |
| 3786 | sema: *Sema, | |
| 3787 | block: *Scope.Block, | |
| 3788 | inst: Zir.Inst.Index, | |
| 3789 | is_comptime: bool, | |
| 3790 | ) CompileError!void { | |
| 3791 | const inst_data = sema.code.instructions.items(.data)[inst].str_tok; | |
| 3792 | const param_name = inst_data.get(sema.code); | |
| 3793 | ||
| 3794 | // TODO check if param_name shadows a Decl. This only needs to be done if | |
| 3795 | // usingnamespace is implemented. | |
| 3796 | _ = param_name; | |
| 3797 | ||
| 3798 | if (sema.inst_map.get(inst)) |air_ref| { | |
| 3799 | const param_ty = sema.typeOf(air_ref); | |
| 3800 | if (is_comptime or param_ty.requiresComptime()) { | |
| 3801 | // We have a comptime value for this parameter so it should be elided from the | |
| 3802 | // function type of the function instruction in this block. | |
| 3803 | return; | |
| 3804 | } | |
| 3805 | // The map is already populated but we do need to add a runtime parameter. | |
| 3806 | try block.params.append(sema.gpa, .{ | |
| 3807 | .ty = param_ty, | |
| 3808 | .is_comptime = false, | |
| 3809 | }); | |
| 3810 | return; | |
| 3811 | } | |
| 3812 | ||
| 3813 | // We are evaluating a generic function without any comptime args provided. | |
| 3814 | ||
| 3815 | try block.params.append(sema.gpa, .{ | |
| 3816 | .ty = Type.initTag(.generic_poison), | |
| 3817 | .is_comptime = is_comptime, | |
| 3818 | }); | |
| 3819 | try sema.inst_map.put(sema.gpa, inst, .generic_poison); | |
| 3820 | } | |
| 3821 | ||
| 3383 | 3822 | fn zirAs(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3384 | 3823 | const tracy = trace(@src()); |
| 3385 | 3824 | defer tracy.end(); |
| ... | ... | @@ -4898,18 +5337,18 @@ fn analyzeArithmetic( |
| 4898 | 5337 | ) CompileError!Air.Inst.Ref { |
| 4899 | 5338 | const lhs_ty = sema.typeOf(lhs); |
| 4900 | 5339 | const rhs_ty = sema.typeOf(rhs); |
| 4901 | if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) { | |
| 5340 | const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); | |
| 5341 | const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); | |
| 5342 | if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) { | |
| 4902 | 5343 | if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) { |
| 4903 | 5344 | return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{ |
| 4904 | lhs_ty.arrayLen(), | |
| 4905 | rhs_ty.arrayLen(), | |
| 5345 | lhs_ty.arrayLen(), rhs_ty.arrayLen(), | |
| 4906 | 5346 | }); |
| 4907 | 5347 | } |
| 4908 | 5348 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{}); |
| 4909 | } else if (lhs_ty.zigTypeTag() == .Vector or rhs_ty.zigTypeTag() == .Vector) { | |
| 5349 | } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { | |
| 4910 | 5350 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ |
| 4911 | lhs_ty, | |
| 4912 | rhs_ty, | |
| 5351 | lhs_ty, rhs_ty, | |
| 4913 | 5352 | }); |
| 4914 | 5353 | } |
| 4915 | 5354 | |
| ... | ... | @@ -4929,7 +5368,9 @@ fn analyzeArithmetic( |
| 4929 | 5368 | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; |
| 4930 | 5369 | |
| 4931 | 5370 | if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { |
| 4932 | return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag()), @tagName(rhs_ty.zigTypeTag()) }); | |
| 5371 | return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ | |
| 5372 | @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag), | |
| 5373 | }); | |
| 4933 | 5374 | } |
| 4934 | 5375 | |
| 4935 | 5376 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { |
| ... | ... | @@ -5728,6 +6169,10 @@ fn analyzeRet( |
| 5728 | 6169 | const casted_operand = if (!need_coercion) operand else op: { |
| 5729 | 6170 | const func = sema.func.?; |
| 5730 | 6171 | const fn_ty = func.owner_decl.ty; |
| 6172 | // TODO: In the case of a comptime/inline function call of a generic function, | |
| 6173 | // this needs to be the resolved return type based on the function parameter type | |
| 6174 | // expressions being evaluated with comptime arguments passed in. Otherwise, this | |
| 6175 | // ends up being .generic_poison and failing the comptime/inline function call analysis. | |
| 5731 | 6176 | const fn_ret_ty = fn_ty.fnReturnType(); |
| 5732 | 6177 | break :op try sema.coerce(block, fn_ret_ty, operand, src); |
| 5733 | 6178 | }; |
| ... | ... | @@ -6545,15 +6990,8 @@ fn zirFuncExtended( |
| 6545 | 6990 | break :blk align_tv.val; |
| 6546 | 6991 | } else Value.initTag(.null_value); |
| 6547 | 6992 | |
| 6548 | const comptime_bits: []const u32 = if (!small.has_comptime_bits) &.{} else blk: { | |
| 6549 | const amt = (extra.data.param_types_len + 31) / 32; | |
| 6550 | const bit_bags = sema.code.extra[extra_index..][0..amt]; | |
| 6551 | extra_index += amt; | |
| 6552 | break :blk bit_bags; | |
| 6553 | }; | |
| 6554 | ||
| 6555 | const param_types = sema.code.refSlice(extra_index, extra.data.param_types_len); | |
| 6556 | extra_index += param_types.len; | |
| 6993 | const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 6994 | extra_index += ret_ty_body.len; | |
| 6557 | 6995 | |
| 6558 | 6996 | var body_inst: Zir.Inst.Index = 0; |
| 6559 | 6997 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; |
| ... | ... | @@ -6570,9 +7008,8 @@ fn zirFuncExtended( |
| 6570 | 7008 | return sema.funcCommon( |
| 6571 | 7009 | block, |
| 6572 | 7010 | extra.data.src_node, |
| 6573 | param_types, | |
| 6574 | 7011 | body_inst, |
| 6575 | extra.data.return_type, | |
| 7012 | ret_ty_body, | |
| 6576 | 7013 | cc, |
| 6577 | 7014 | align_val, |
| 6578 | 7015 | is_var_args, |
| ... | ... | @@ -6580,7 +7017,6 @@ fn zirFuncExtended( |
| 6580 | 7017 | is_extern, |
| 6581 | 7018 | src_locs, |
| 6582 | 7019 | lib_name, |
| 6583 | comptime_bits, | |
| 6584 | 7020 | ); |
| 6585 | 7021 | } |
| 6586 | 7022 | |
| ... | ... | @@ -6797,19 +7233,12 @@ fn safetyPanic( |
| 6797 | 7233 | const msg_inst = msg_inst: { |
| 6798 | 7234 | // TODO instead of making a new decl for every panic in the entire compilation, |
| 6799 | 7235 | // introduce the concept of a reference-counted decl for these |
| 6800 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | |
| 6801 | errdefer new_decl_arena.deinit(); | |
| 6802 | ||
| 6803 | const decl_ty = try Type.Tag.array_u8.create(&new_decl_arena.allocator, msg.len); | |
| 6804 | const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, msg); | |
| 6805 | ||
| 6806 | const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ | |
| 6807 | .ty = decl_ty, | |
| 6808 | .val = decl_val, | |
| 6809 | }); | |
| 6810 | errdefer sema.mod.deleteAnonDecl(&block.base, new_decl); | |
| 6811 | try new_decl.finalizeNewArena(&new_decl_arena); | |
| 6812 | break :msg_inst try sema.analyzeDeclRef(new_decl); | |
| 7236 | var anon_decl = try block.startAnonDecl(); | |
| 7237 | defer anon_decl.deinit(); | |
| 7238 | break :msg_inst try sema.analyzeDeclRef(try anon_decl.finish( | |
| 7239 | try Type.Tag.array_u8.create(anon_decl.arena(), msg.len), | |
| 7240 | try Value.Tag.bytes.create(anon_decl.arena(), msg), | |
| 7241 | )); | |
| 6813 | 7242 | }; |
| 6814 | 7243 | |
| 6815 | 7244 | const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src); |
| ... | ... | @@ -7469,8 +7898,10 @@ fn coerce( |
| 7469 | 7898 | inst: Air.Inst.Ref, |
| 7470 | 7899 | inst_src: LazySrcLoc, |
| 7471 | 7900 | ) CompileError!Air.Inst.Ref { |
| 7472 | if (dest_type_unresolved.tag() == .var_args_param) { | |
| 7473 | return sema.coerceVarArgParam(block, inst, inst_src); | |
| 7901 | switch (dest_type_unresolved.tag()) { | |
| 7902 | .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src), | |
| 7903 | .generic_poison => return inst, | |
| 7904 | else => {}, | |
| 7474 | 7905 | } |
| 7475 | 7906 | const dest_type_src = inst_src; // TODO better source location |
| 7476 | 7907 | const dest_type = try sema.resolveTypeFields(block, dest_type_src, dest_type_unresolved); |
| ... | ... | @@ -8671,6 +9102,7 @@ fn typeHasOnePossibleValue( |
| 8671 | 9102 | |
| 8672 | 9103 | .inferred_alloc_const => unreachable, |
| 8673 | 9104 | .inferred_alloc_mut => unreachable, |
| 9105 | .generic_poison => return error.GenericPoison, | |
| 8674 | 9106 | }; |
| 8675 | 9107 | } |
| 8676 | 9108 | |
| ... | ... | @@ -8793,6 +9225,8 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { |
| 8793 | 9225 | .fn_ccc_void_no_args => return .fn_ccc_void_no_args_type, |
| 8794 | 9226 | .single_const_pointer_to_comptime_int => return .single_const_pointer_to_comptime_int_type, |
| 8795 | 9227 | .const_slice_u8 => return .const_slice_u8_type, |
| 9228 | .anyerror_void_error_union => return .anyerror_void_error_union_type, | |
| 9229 | .generic_poison => return .generic_poison_type, | |
| 8796 | 9230 | else => {}, |
| 8797 | 9231 | } |
| 8798 | 9232 | try sema.air_instructions.append(sema.gpa, .{ |
| ... | ... | @@ -8810,7 +9244,7 @@ fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref { |
| 8810 | 9244 | return sema.addConstant(ty, Value.initTag(.undef)); |
| 8811 | 9245 | } |
| 8812 | 9246 | |
| 8813 | fn addConstant(sema: *Sema, ty: Type, val: Value) CompileError!Air.Inst.Ref { | |
| 9247 | pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref { | |
| 8814 | 9248 | const gpa = sema.gpa; |
| 8815 | 9249 | const ty_inst = try sema.addType(ty); |
| 8816 | 9250 | try sema.air_values.append(gpa, val); |
src/Zir.zig+196-63| ... | ... | @@ -61,7 +61,7 @@ pub const ExtraIndex = enum(u32) { |
| 61 | 61 | _, |
| 62 | 62 | }; |
| 63 | 63 | |
| 64 | pub fn getMainStruct(zir: Zir) Zir.Inst.Index { | |
| 64 | pub fn getMainStruct(zir: Zir) Inst.Index { | |
| 65 | 65 | return zir.extra[@enumToInt(ExtraIndex.main_struct)] - |
| 66 | 66 | @intCast(u32, Inst.Ref.typed_value_map.len); |
| 67 | 67 | } |
| ... | ... | @@ -173,11 +173,22 @@ pub const Inst = struct { |
| 173 | 173 | /// Twos complement wrapping integer addition. |
| 174 | 174 | /// Uses the `pl_node` union field. Payload is `Bin`. |
| 175 | 175 | addwrap, |
| 176 | /// Declares a parameter of the current function. Used for debug info and | |
| 177 | /// for checking shadowing against declarations in the current namespace. | |
| 178 | /// Uses the `str_tok` field. Token is the parameter name, string is the | |
| 179 | /// parameter name. | |
| 180 | arg, | |
| 176 | /// Declares a parameter of the current function. Used for: | |
| 177 | /// * debug info | |
| 178 | /// * checking shadowing against declarations in the current namespace | |
| 179 | /// * parameter type expressions referencing other parameters | |
| 180 | /// These occur in the block outside a function body (the same block as | |
| 181 | /// contains the func instruction). | |
| 182 | /// Uses the `pl_tok` field. Token is the parameter name, payload is a `Param`. | |
| 183 | param, | |
| 184 | /// Same as `param` except the parameter is marked comptime. | |
| 185 | param_comptime, | |
| 186 | /// Same as `param` except the parameter is marked anytype. | |
| 187 | /// Uses the `str_tok` field. Token is the parameter name. String is the parameter name. | |
| 188 | param_anytype, | |
| 189 | /// Same as `param` except the parameter is marked both comptime and anytype. | |
| 190 | /// Uses the `str_tok` field. Token is the parameter name. String is the parameter name. | |
| 191 | param_anytype_comptime, | |
| 181 | 192 | /// Array concatenation. `a ++ b` |
| 182 | 193 | /// Uses the `pl_node` union field. Payload is `Bin`. |
| 183 | 194 | array_cat, |
| ... | ... | @@ -971,7 +982,10 @@ pub const Inst = struct { |
| 971 | 982 | /// Function calls do not count. |
| 972 | 983 | pub fn isNoReturn(tag: Tag) bool { |
| 973 | 984 | return switch (tag) { |
| 974 | .arg, | |
| 985 | .param, | |
| 986 | .param_comptime, | |
| 987 | .param_anytype, | |
| 988 | .param_anytype_comptime, | |
| 975 | 989 | .add, |
| 976 | 990 | .addwrap, |
| 977 | 991 | .alloc, |
| ... | ... | @@ -1233,7 +1247,10 @@ pub const Inst = struct { |
| 1233 | 1247 | break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{ |
| 1234 | 1248 | .add = .pl_node, |
| 1235 | 1249 | .addwrap = .pl_node, |
| 1236 | .arg = .str_tok, | |
| 1250 | .param = .pl_tok, | |
| 1251 | .param_comptime = .pl_tok, | |
| 1252 | .param_anytype = .str_tok, | |
| 1253 | .param_anytype_comptime = .str_tok, | |
| 1237 | 1254 | .array_cat = .pl_node, |
| 1238 | 1255 | .array_mul = .pl_node, |
| 1239 | 1256 | .array_type = .bin, |
| ... | ... | @@ -1687,6 +1704,8 @@ pub const Inst = struct { |
| 1687 | 1704 | fn_ccc_void_no_args_type, |
| 1688 | 1705 | single_const_pointer_to_comptime_int_type, |
| 1689 | 1706 | const_slice_u8_type, |
| 1707 | anyerror_void_error_union_type, | |
| 1708 | generic_poison_type, | |
| 1690 | 1709 | |
| 1691 | 1710 | /// `undefined` (untyped) |
| 1692 | 1711 | undef, |
| ... | ... | @@ -1714,6 +1733,9 @@ pub const Inst = struct { |
| 1714 | 1733 | calling_convention_c, |
| 1715 | 1734 | /// `std.builtin.CallingConvention.Inline` |
| 1716 | 1735 | calling_convention_inline, |
| 1736 | /// Used for generic parameters where the type and value | |
| 1737 | /// is not known until generic function instantiation. | |
| 1738 | generic_poison, | |
| 1717 | 1739 | |
| 1718 | 1740 | _, |
| 1719 | 1741 | |
| ... | ... | @@ -1892,6 +1914,14 @@ pub const Inst = struct { |
| 1892 | 1914 | .ty = Type.initTag(.type), |
| 1893 | 1915 | .val = Value.initTag(.const_slice_u8_type), |
| 1894 | 1916 | }, |
| 1917 | .anyerror_void_error_union_type = .{ | |
| 1918 | .ty = Type.initTag(.type), | |
| 1919 | .val = Value.initTag(.anyerror_void_error_union_type), | |
| 1920 | }, | |
| 1921 | .generic_poison_type = .{ | |
| 1922 | .ty = Type.initTag(.type), | |
| 1923 | .val = Value.initTag(.generic_poison_type), | |
| 1924 | }, | |
| 1895 | 1925 | .enum_literal_type = .{ |
| 1896 | 1926 | .ty = Type.initTag(.type), |
| 1897 | 1927 | .val = Value.initTag(.enum_literal_type), |
| ... | ... | @@ -1989,6 +2019,10 @@ pub const Inst = struct { |
| 1989 | 2019 | .ty = Type.initTag(.calling_convention), |
| 1990 | 2020 | .val = .{ .ptr_otherwise = &calling_convention_inline_payload.base }, |
| 1991 | 2021 | }, |
| 2022 | .generic_poison = .{ | |
| 2023 | .ty = Type.initTag(.generic_poison), | |
| 2024 | .val = Value.initTag(.generic_poison), | |
| 2025 | }, | |
| 1992 | 2026 | }); |
| 1993 | 2027 | }; |
| 1994 | 2028 | |
| ... | ... | @@ -2047,6 +2081,17 @@ pub const Inst = struct { |
| 2047 | 2081 | return .{ .node_offset = self.src_node }; |
| 2048 | 2082 | } |
| 2049 | 2083 | }, |
| 2084 | pl_tok: struct { | |
| 2085 | /// Offset from Decl AST token index. | |
| 2086 | src_tok: ast.TokenIndex, | |
| 2087 | /// index into extra. | |
| 2088 | /// `Tag` determines what lives there. | |
| 2089 | payload_index: u32, | |
| 2090 | ||
| 2091 | pub fn src(self: @This()) LazySrcLoc { | |
| 2092 | return .{ .token_offset = self.src_tok }; | |
| 2093 | } | |
| 2094 | }, | |
| 2050 | 2095 | bin: Bin, |
| 2051 | 2096 | /// For strings which may contain null bytes. |
| 2052 | 2097 | str: struct { |
| ... | ... | @@ -2170,6 +2215,7 @@ pub const Inst = struct { |
| 2170 | 2215 | un_node, |
| 2171 | 2216 | un_tok, |
| 2172 | 2217 | pl_node, |
| 2218 | pl_tok, | |
| 2173 | 2219 | bin, |
| 2174 | 2220 | str, |
| 2175 | 2221 | str_tok, |
| ... | ... | @@ -2226,17 +2272,15 @@ pub const Inst = struct { |
| 2226 | 2272 | /// 0. lib_name: u32, // null terminated string index, if has_lib_name is set |
| 2227 | 2273 | /// 1. cc: Ref, // if has_cc is set |
| 2228 | 2274 | /// 2. align: Ref, // if has_align is set |
| 2229 | /// 3. comptime_bits: u32 // for every 32 parameters, if has_comptime_bits is set | |
| 2230 | /// - sets of 1 bit: | |
| 2231 | /// 0bX: whether corresponding parameter is comptime | |
| 2232 | /// 4. param_type: Ref // for each param_types_len | |
| 2233 | /// - `none` indicates that the param type is `anytype`. | |
| 2234 | /// 5. body: Index // for each body_len | |
| 2235 | /// 6. src_locs: Func.SrcLocs // if body_len != 0 | |
| 2275 | /// 3. return_type: Index // for each ret_body_len | |
| 2276 | /// 4. body: Index // for each body_len | |
| 2277 | /// 5. src_locs: Func.SrcLocs // if body_len != 0 | |
| 2236 | 2278 | pub const ExtendedFunc = struct { |
| 2237 | 2279 | src_node: i32, |
| 2238 | return_type: Ref, | |
| 2239 | param_types_len: u32, | |
| 2280 | /// If this is 0 it means a void return type. | |
| 2281 | ret_body_len: u32, | |
| 2282 | /// Points to the block that contains the param instructions for this function. | |
| 2283 | param_block: Index, | |
| 2240 | 2284 | body_len: u32, |
| 2241 | 2285 | |
| 2242 | 2286 | pub const Small = packed struct { |
| ... | ... | @@ -2247,8 +2291,7 @@ pub const Inst = struct { |
| 2247 | 2291 | has_align: bool, |
| 2248 | 2292 | is_test: bool, |
| 2249 | 2293 | is_extern: bool, |
| 2250 | has_comptime_bits: bool, | |
| 2251 | _: u8 = undefined, | |
| 2294 | _: u9 = undefined, | |
| 2252 | 2295 | }; |
| 2253 | 2296 | }; |
| 2254 | 2297 | |
| ... | ... | @@ -2271,13 +2314,14 @@ pub const Inst = struct { |
| 2271 | 2314 | }; |
| 2272 | 2315 | |
| 2273 | 2316 | /// Trailing: |
| 2274 | /// 0. param_type: Ref // for each param_types_len | |
| 2275 | /// - `none` indicates that the param type is `anytype`. | |
| 2317 | /// 0. return_type: Index // for each ret_body_len | |
| 2276 | 2318 | /// 1. body: Index // for each body_len |
| 2277 | 2319 | /// 2. src_locs: SrcLocs // if body_len != 0 |
| 2278 | 2320 | pub const Func = struct { |
| 2279 | return_type: Ref, | |
| 2280 | param_types_len: u32, | |
| 2321 | /// If this is 0 it means a void return type. | |
| 2322 | ret_body_len: u32, | |
| 2323 | /// Points to the block that contains the param instructions for this function. | |
| 2324 | param_block: Index, | |
| 2281 | 2325 | body_len: u32, |
| 2282 | 2326 | |
| 2283 | 2327 | pub const SrcLocs = struct { |
| ... | ... | @@ -2764,6 +2808,14 @@ pub const Inst = struct { |
| 2764 | 2808 | args: Ref, |
| 2765 | 2809 | }; |
| 2766 | 2810 | |
| 2811 | /// Trailing: inst: Index // for every body_len | |
| 2812 | pub const Param = struct { | |
| 2813 | /// Null-terminated string index. | |
| 2814 | name: u32, | |
| 2815 | /// The body contains the type of the parameter. | |
| 2816 | body_len: u32, | |
| 2817 | }; | |
| 2818 | ||
| 2767 | 2819 | /// Trailing: |
| 2768 | 2820 | /// 0. type_inst: Ref, // if small 0b000X is set |
| 2769 | 2821 | /// 1. align_inst: Ref, // if small 0b00X0 is set |
| ... | ... | @@ -3108,11 +3160,14 @@ const Writer = struct { |
| 3108 | 3160 | .decl_ref, |
| 3109 | 3161 | .decl_val, |
| 3110 | 3162 | .import, |
| 3111 | .arg, | |
| 3112 | 3163 | .ret_err_value, |
| 3113 | 3164 | .ret_err_value_code, |
| 3165 | .param_anytype, | |
| 3166 | .param_anytype_comptime, | |
| 3114 | 3167 | => try self.writeStrTok(stream, inst), |
| 3115 | 3168 | |
| 3169 | .param, .param_comptime => try self.writeParam(stream, inst), | |
| 3170 | ||
| 3116 | 3171 | .func => try self.writeFunc(stream, inst, false), |
| 3117 | 3172 | .func_inferred => try self.writeFunc(stream, inst, true), |
| 3118 | 3173 | |
| ... | ... | @@ -3314,6 +3369,22 @@ const Writer = struct { |
| 3314 | 3369 | try self.writeSrc(stream, inst_data.src()); |
| 3315 | 3370 | } |
| 3316 | 3371 | |
| 3372 | fn writeParam(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3373 | const inst_data = self.code.instructions.items(.data)[inst].pl_tok; | |
| 3374 | const extra = self.code.extraData(Inst.Param, inst_data.payload_index); | |
| 3375 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | |
| 3376 | try stream.print("\"{}\", ", .{ | |
| 3377 | std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)), | |
| 3378 | }); | |
| 3379 | try stream.writeAll("{\n"); | |
| 3380 | self.indent += 2; | |
| 3381 | try self.writeBody(stream, body); | |
| 3382 | self.indent -= 2; | |
| 3383 | try stream.writeByteNTimes(' ', self.indent); | |
| 3384 | try stream.writeAll(") "); | |
| 3385 | try self.writeSrc(stream, inst_data.src()); | |
| 3386 | } | |
| 3387 | ||
| 3317 | 3388 | fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 3318 | 3389 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 3319 | 3390 | const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data; |
| ... | ... | @@ -4277,17 +4348,21 @@ const Writer = struct { |
| 4277 | 4348 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 4278 | 4349 | const src = inst_data.src(); |
| 4279 | 4350 | const extra = self.code.extraData(Inst.Func, inst_data.payload_index); |
| 4280 | const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); | |
| 4281 | const body = self.code.extra[extra.end + param_types.len ..][0..extra.data.body_len]; | |
| 4351 | var extra_index = extra.end; | |
| 4352 | ||
| 4353 | const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4354 | extra_index += ret_ty_body.len; | |
| 4355 | ||
| 4356 | const body = self.code.extra[extra_index..][0..extra.data.body_len]; | |
| 4357 | extra_index += body.len; | |
| 4358 | ||
| 4282 | 4359 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; |
| 4283 | 4360 | if (body.len != 0) { |
| 4284 | const extra_index = extra.end + param_types.len + body.len; | |
| 4285 | 4361 | src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; |
| 4286 | 4362 | } |
| 4287 | 4363 | return self.writeFuncCommon( |
| 4288 | 4364 | stream, |
| 4289 | param_types, | |
| 4290 | extra.data.return_type, | |
| 4365 | ret_ty_body, | |
| 4291 | 4366 | inferred_error_set, |
| 4292 | 4367 | false, |
| 4293 | 4368 | false, |
| ... | ... | @@ -4296,7 +4371,6 @@ const Writer = struct { |
| 4296 | 4371 | body, |
| 4297 | 4372 | src, |
| 4298 | 4373 | src_locs, |
| 4299 | &.{}, | |
| 4300 | 4374 | ); |
| 4301 | 4375 | } |
| 4302 | 4376 | |
| ... | ... | @@ -4323,15 +4397,8 @@ const Writer = struct { |
| 4323 | 4397 | break :blk align_inst; |
| 4324 | 4398 | }; |
| 4325 | 4399 | |
| 4326 | const comptime_bits: []const u32 = if (!small.has_comptime_bits) &.{} else blk: { | |
| 4327 | const amt = (extra.data.param_types_len + 31) / 32; | |
| 4328 | const bit_bags = self.code.extra[extra_index..][0..amt]; | |
| 4329 | extra_index += amt; | |
| 4330 | break :blk bit_bags; | |
| 4331 | }; | |
| 4332 | ||
| 4333 | const param_types = self.code.refSlice(extra_index, extra.data.param_types_len); | |
| 4334 | extra_index += param_types.len; | |
| 4400 | const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4401 | extra_index += ret_ty_body.len; | |
| 4335 | 4402 | |
| 4336 | 4403 | const body = self.code.extra[extra_index..][0..extra.data.body_len]; |
| 4337 | 4404 | extra_index += body.len; |
| ... | ... | @@ -4342,8 +4409,7 @@ const Writer = struct { |
| 4342 | 4409 | } |
| 4343 | 4410 | return self.writeFuncCommon( |
| 4344 | 4411 | stream, |
| 4345 | param_types, | |
| 4346 | extra.data.return_type, | |
| 4412 | ret_ty_body, | |
| 4347 | 4413 | small.is_inferred_error, |
| 4348 | 4414 | small.is_var_args, |
| 4349 | 4415 | small.is_extern, |
| ... | ... | @@ -4352,7 +4418,6 @@ const Writer = struct { |
| 4352 | 4418 | body, |
| 4353 | 4419 | src, |
| 4354 | 4420 | src_locs, |
| 4355 | comptime_bits, | |
| 4356 | 4421 | ); |
| 4357 | 4422 | } |
| 4358 | 4423 | |
| ... | ... | @@ -4426,8 +4491,7 @@ const Writer = struct { |
| 4426 | 4491 | fn writeFuncCommon( |
| 4427 | 4492 | self: *Writer, |
| 4428 | 4493 | stream: anytype, |
| 4429 | param_types: []const Inst.Ref, | |
| 4430 | ret_ty: Inst.Ref, | |
| 4494 | ret_ty_body: []const Inst.Index, | |
| 4431 | 4495 | inferred_error_set: bool, |
| 4432 | 4496 | var_args: bool, |
| 4433 | 4497 | is_extern: bool, |
| ... | ... | @@ -4436,20 +4500,18 @@ const Writer = struct { |
| 4436 | 4500 | body: []const Inst.Index, |
| 4437 | 4501 | src: LazySrcLoc, |
| 4438 | 4502 | src_locs: Zir.Inst.Func.SrcLocs, |
| 4439 | comptime_bits: []const u32, | |
| 4440 | 4503 | ) !void { |
| 4441 | try stream.writeAll("["); | |
| 4442 | for (param_types) |param_type, i| { | |
| 4443 | if (i != 0) try stream.writeAll(", "); | |
| 4444 | if (comptime_bits.len != 0) { | |
| 4445 | const bag = comptime_bits[i / 32]; | |
| 4446 | const is_comptime = @truncate(u1, bag >> @intCast(u5, i % 32)) != 0; | |
| 4447 | try self.writeFlag(stream, "comptime ", is_comptime); | |
| 4448 | } | |
| 4449 | try self.writeInstRef(stream, param_type); | |
| 4504 | if (ret_ty_body.len == 0) { | |
| 4505 | try stream.writeAll("ret_ty=void"); | |
| 4506 | } else { | |
| 4507 | try stream.writeAll("ret_ty={\n"); | |
| 4508 | self.indent += 2; | |
| 4509 | try self.writeBody(stream, ret_ty_body); | |
| 4510 | self.indent -= 2; | |
| 4511 | try stream.writeByteNTimes(' ', self.indent); | |
| 4512 | try stream.writeAll("}"); | |
| 4450 | 4513 | } |
| 4451 | try stream.writeAll("], "); | |
| 4452 | try self.writeInstRef(stream, ret_ty); | |
| 4514 | ||
| 4453 | 4515 | try self.writeOptionalInstRef(stream, ", cc=", cc); |
| 4454 | 4516 | try self.writeOptionalInstRef(stream, ", align=", align_inst); |
| 4455 | 4517 | try self.writeFlag(stream, ", vargs", var_args); |
| ... | ... | @@ -4457,9 +4519,9 @@ const Writer = struct { |
| 4457 | 4519 | try self.writeFlag(stream, ", inferror", inferred_error_set); |
| 4458 | 4520 | |
| 4459 | 4521 | if (body.len == 0) { |
| 4460 | try stream.writeAll(", {}) "); | |
| 4522 | try stream.writeAll(", body={}) "); | |
| 4461 | 4523 | } else { |
| 4462 | try stream.writeAll(", {\n"); | |
| 4524 | try stream.writeAll(", body={\n"); | |
| 4463 | 4525 | self.indent += 2; |
| 4464 | 4526 | try self.writeBody(stream, body); |
| 4465 | 4527 | self.indent -= 2; |
| ... | ... | @@ -4714,8 +4776,7 @@ fn findDeclsInner( |
| 4714 | 4776 | |
| 4715 | 4777 | const inst_data = datas[inst].pl_node; |
| 4716 | 4778 | const extra = zir.extraData(Inst.Func, inst_data.payload_index); |
| 4717 | const param_types_len = extra.data.param_types_len; | |
| 4718 | const body = zir.extra[extra.end + param_types_len ..][0..extra.data.body_len]; | |
| 4779 | const body = zir.extra[extra.end..][0..extra.data.body_len]; | |
| 4719 | 4780 | return zir.findDeclsBody(list, body); |
| 4720 | 4781 | }, |
| 4721 | 4782 | .extended => { |
| ... | ... | @@ -4730,7 +4791,6 @@ fn findDeclsInner( |
| 4730 | 4791 | extra_index += @boolToInt(small.has_lib_name); |
| 4731 | 4792 | extra_index += @boolToInt(small.has_cc); |
| 4732 | 4793 | extra_index += @boolToInt(small.has_align); |
| 4733 | extra_index += extra.data.param_types_len; | |
| 4734 | 4794 | const body = zir.extra[extra_index..][0..extra.data.body_len]; |
| 4735 | 4795 | return zir.findDeclsBody(list, body); |
| 4736 | 4796 | }, |
| ... | ... | @@ -4885,10 +4945,83 @@ fn findDeclsSwitchMulti( |
| 4885 | 4945 | |
| 4886 | 4946 | fn findDeclsBody( |
| 4887 | 4947 | zir: Zir, |
| 4888 | list: *std.ArrayList(Zir.Inst.Index), | |
| 4889 | body: []const Zir.Inst.Index, | |
| 4948 | list: *std.ArrayList(Inst.Index), | |
| 4949 | body: []const Inst.Index, | |
| 4890 | 4950 | ) Allocator.Error!void { |
| 4891 | 4951 | for (body) |member| { |
| 4892 | 4952 | try zir.findDeclsInner(list, member); |
| 4893 | 4953 | } |
| 4894 | 4954 | } |
| 4955 | ||
| 4956 | pub const FnInfo = struct { | |
| 4957 | param_body: []const Inst.Index, | |
| 4958 | ret_ty_body: []const Inst.Index, | |
| 4959 | body: []const Inst.Index, | |
| 4960 | total_params_len: u32, | |
| 4961 | }; | |
| 4962 | ||
| 4963 | pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { | |
| 4964 | const tags = zir.instructions.items(.tag); | |
| 4965 | const datas = zir.instructions.items(.data); | |
| 4966 | const info: struct { | |
| 4967 | param_block: Inst.Index, | |
| 4968 | body: []const Inst.Index, | |
| 4969 | ret_ty_body: []const Inst.Index, | |
| 4970 | } = switch (tags[fn_inst]) { | |
| 4971 | .func, .func_inferred => blk: { | |
| 4972 | const inst_data = datas[fn_inst].pl_node; | |
| 4973 | const extra = zir.extraData(Inst.Func, inst_data.payload_index); | |
| 4974 | var extra_index: usize = extra.end; | |
| 4975 | ||
| 4976 | const ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4977 | extra_index += ret_ty_body.len; | |
| 4978 | ||
| 4979 | const body = zir.extra[extra_index..][0..extra.data.body_len]; | |
| 4980 | extra_index += body.len; | |
| 4981 | ||
| 4982 | break :blk .{ | |
| 4983 | .param_block = extra.data.param_block, | |
| 4984 | .ret_ty_body = ret_ty_body, | |
| 4985 | .body = body, | |
| 4986 | }; | |
| 4987 | }, | |
| 4988 | .extended => blk: { | |
| 4989 | const extended = datas[fn_inst].extended; | |
| 4990 | assert(extended.opcode == .func); | |
| 4991 | const extra = zir.extraData(Inst.ExtendedFunc, extended.operand); | |
| 4992 | const small = @bitCast(Inst.ExtendedFunc.Small, extended.small); | |
| 4993 | var extra_index: usize = extra.end; | |
| 4994 | extra_index += @boolToInt(small.has_lib_name); | |
| 4995 | extra_index += @boolToInt(small.has_cc); | |
| 4996 | extra_index += @boolToInt(small.has_align); | |
| 4997 | const ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4998 | extra_index += ret_ty_body.len; | |
| 4999 | const body = zir.extra[extra_index..][0..extra.data.body_len]; | |
| 5000 | extra_index += body.len; | |
| 5001 | break :blk .{ | |
| 5002 | .param_block = extra.data.param_block, | |
| 5003 | .ret_ty_body = ret_ty_body, | |
| 5004 | .body = body, | |
| 5005 | }; | |
| 5006 | }, | |
| 5007 | else => unreachable, | |
| 5008 | }; | |
| 5009 | assert(tags[info.param_block] == .block or tags[info.param_block] == .block_inline); | |
| 5010 | const param_block = zir.extraData(Inst.Block, datas[info.param_block].pl_node.payload_index); | |
| 5011 | const param_body = zir.extra[param_block.end..][0..param_block.data.body_len]; | |
| 5012 | var total_params_len: u32 = 0; | |
| 5013 | for (param_body) |inst| { | |
| 5014 | switch (tags[inst]) { | |
| 5015 | .param, .param_comptime, .param_anytype, .param_anytype_comptime => { | |
| 5016 | total_params_len += 1; | |
| 5017 | }, | |
| 5018 | else => continue, | |
| 5019 | } | |
| 5020 | } | |
| 5021 | return .{ | |
| 5022 | .param_body = param_body, | |
| 5023 | .ret_ty_body = info.ret_ty_body, | |
| 5024 | .body = info.body, | |
| 5025 | .total_params_len = total_params_len, | |
| 5026 | }; | |
| 5027 | } |
src/codegen/llvm.zig+44-6| ... | ... | @@ -575,6 +575,14 @@ pub const DeclGen = struct { |
| 575 | 575 | const info = t.intInfo(self.module.getTarget()); |
| 576 | 576 | return self.context.intType(info.bits); |
| 577 | 577 | }, |
| 578 | .Float => switch (t.floatBits(self.module.getTarget())) { | |
| 579 | 16 => return self.context.halfType(), | |
| 580 | 32 => return self.context.floatType(), | |
| 581 | 64 => return self.context.doubleType(), | |
| 582 | 80 => return self.context.x86FP80Type(), | |
| 583 | 128 => return self.context.fp128Type(), | |
| 584 | else => unreachable, | |
| 585 | }, | |
| 578 | 586 | .Bool => return self.context.intType(1), |
| 579 | 587 | .Pointer => { |
| 580 | 588 | if (t.isSlice()) { |
| ... | ... | @@ -661,7 +669,6 @@ pub const DeclGen = struct { |
| 661 | 669 | |
| 662 | 670 | .BoundFn => @panic("TODO remove BoundFn from the language"), |
| 663 | 671 | |
| 664 | .Float, | |
| 665 | 672 | .Enum, |
| 666 | 673 | .Union, |
| 667 | 674 | .Opaque, |
| ... | ... | @@ -699,13 +706,40 @@ pub const DeclGen = struct { |
| 699 | 706 | } |
| 700 | 707 | return llvm_int; |
| 701 | 708 | }, |
| 709 | .Float => { | |
| 710 | if (tv.ty.floatBits(self.module.getTarget()) <= 64) { | |
| 711 | const llvm_ty = try self.llvmType(tv.ty); | |
| 712 | return llvm_ty.constReal(tv.val.toFloat(f64)); | |
| 713 | } | |
| 714 | return self.todo("bitcast to f128 from an integer", .{}); | |
| 715 | }, | |
| 702 | 716 | .Pointer => switch (tv.val.tag()) { |
| 703 | 717 | .decl_ref => { |
| 704 | const decl = tv.val.castTag(.decl_ref).?.data; | |
| 705 | decl.alive = true; | |
| 706 | const val = try self.resolveGlobalDecl(decl); | |
| 707 | const llvm_type = try self.llvmType(tv.ty); | |
| 708 | return val.constBitCast(llvm_type); | |
| 718 | if (tv.ty.isSlice()) { | |
| 719 | var buf: Type.Payload.ElemType = undefined; | |
| 720 | const ptr_ty = tv.ty.slicePtrFieldType(&buf); | |
| 721 | var slice_len: Value.Payload.U64 = .{ | |
| 722 | .base = .{ .tag = .int_u64 }, | |
| 723 | .data = tv.val.sliceLen(), | |
| 724 | }; | |
| 725 | const fields: [2]*const llvm.Value = .{ | |
| 726 | try self.genTypedValue(.{ | |
| 727 | .ty = ptr_ty, | |
| 728 | .val = tv.val, | |
| 729 | }), | |
| 730 | try self.genTypedValue(.{ | |
| 731 | .ty = Type.initTag(.usize), | |
| 732 | .val = Value.initPayload(&slice_len.base), | |
| 733 | }), | |
| 734 | }; | |
| 735 | return self.context.constStruct(&fields, fields.len, .False); | |
| 736 | } else { | |
| 737 | const decl = tv.val.castTag(.decl_ref).?.data; | |
| 738 | decl.alive = true; | |
| 739 | const val = try self.resolveGlobalDecl(decl); | |
| 740 | const llvm_type = try self.llvmType(tv.ty); | |
| 741 | return val.constBitCast(llvm_type); | |
| 742 | } | |
| 709 | 743 | }, |
| 710 | 744 | .variable => { |
| 711 | 745 | const decl = tv.val.castTag(.variable).?.data.owner_decl; |
| ... | ... | @@ -839,6 +873,10 @@ pub const DeclGen = struct { |
| 839 | 873 | .False, |
| 840 | 874 | ); |
| 841 | 875 | }, |
| 876 | .ComptimeInt => unreachable, | |
| 877 | .ComptimeFloat => unreachable, | |
| 878 | .Type => unreachable, | |
| 879 | .EnumLiteral => unreachable, | |
| 842 | 880 | else => return self.todo("implement const of type '{}'", .{tv.ty}), |
| 843 | 881 | } |
| 844 | 882 | } |
src/codegen/llvm/bindings.zig+18| ... | ... | @@ -31,6 +31,21 @@ pub const Context = opaque { |
| 31 | 31 | pub const intType = LLVMIntTypeInContext; |
| 32 | 32 | extern fn LLVMIntTypeInContext(C: *const Context, NumBits: c_uint) *const Type; |
| 33 | 33 | |
| 34 | pub const halfType = LLVMHalfTypeInContext; | |
| 35 | extern fn LLVMHalfTypeInContext(C: *const Context) *const Type; | |
| 36 | ||
| 37 | pub const floatType = LLVMFloatTypeInContext; | |
| 38 | extern fn LLVMFloatTypeInContext(C: *const Context) *const Type; | |
| 39 | ||
| 40 | pub const doubleType = LLVMDoubleTypeInContext; | |
| 41 | extern fn LLVMDoubleTypeInContext(C: *const Context) *const Type; | |
| 42 | ||
| 43 | pub const x86FP80Type = LLVMX86FP80TypeInContext; | |
| 44 | extern fn LLVMX86FP80TypeInContext(C: *const Context) *const Type; | |
| 45 | ||
| 46 | pub const fp128Type = LLVMFP128TypeInContext; | |
| 47 | extern fn LLVMFP128TypeInContext(C: *const Context) *const Type; | |
| 48 | ||
| 34 | 49 | pub const voidType = LLVMVoidTypeInContext; |
| 35 | 50 | extern fn LLVMVoidTypeInContext(C: *const Context) *const Type; |
| 36 | 51 | |
| ... | ... | @@ -127,6 +142,9 @@ pub const Type = opaque { |
| 127 | 142 | pub const constInt = LLVMConstInt; |
| 128 | 143 | extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value; |
| 129 | 144 | |
| 145 | pub const constReal = LLVMConstReal; | |
| 146 | extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value; | |
| 147 | ||
| 130 | 148 | pub const constArray = LLVMConstArray; |
| 131 | 149 | extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value; |
| 132 | 150 |
src/print_air.zig+1-1| ... | ... | @@ -222,7 +222,7 @@ const Writer = struct { |
| 222 | 222 | const extra = w.air.extraData(Air.Block, ty_pl.payload); |
| 223 | 223 | const body = w.air.extra[extra.end..][0..extra.data.body_len]; |
| 224 | 224 | |
| 225 | try s.writeAll("{\n"); | |
| 225 | try s.print("{}, {{\n", .{w.air.getRefType(ty_pl.ty)}); | |
| 226 | 226 | const old_indent = w.indent; |
| 227 | 227 | w.indent += 2; |
| 228 | 228 | try w.writeBody(s, body); |
src/type.zig+193-26| ... | ... | @@ -21,8 +21,14 @@ pub const Type = extern union { |
| 21 | 21 | tag_if_small_enough: usize, |
| 22 | 22 | ptr_otherwise: *Payload, |
| 23 | 23 | |
| 24 | pub fn zigTypeTag(self: Type) std.builtin.TypeId { | |
| 25 | switch (self.tag()) { | |
| 24 | pub fn zigTypeTag(ty: Type) std.builtin.TypeId { | |
| 25 | return ty.zigTypeTagOrPoison() catch unreachable; | |
| 26 | } | |
| 27 | ||
| 28 | pub fn zigTypeTagOrPoison(ty: Type) error{GenericPoison}!std.builtin.TypeId { | |
| 29 | switch (ty.tag()) { | |
| 30 | .generic_poison => return error.GenericPoison, | |
| 31 | ||
| 26 | 32 | .u1, |
| 27 | 33 | .u8, |
| 28 | 34 | .i8, |
| ... | ... | @@ -548,8 +554,13 @@ pub const Type = extern union { |
| 548 | 554 | |
| 549 | 555 | pub fn hash(self: Type) u64 { |
| 550 | 556 | var hasher = std.hash.Wyhash.init(0); |
| 557 | self.hashWithHasher(&hasher); | |
| 558 | return hasher.final(); | |
| 559 | } | |
| 560 | ||
| 561 | pub fn hashWithHasher(self: Type, hasher: *std.hash.Wyhash) void { | |
| 551 | 562 | const zig_type_tag = self.zigTypeTag(); |
| 552 | std.hash.autoHash(&hasher, zig_type_tag); | |
| 563 | std.hash.autoHash(hasher, zig_type_tag); | |
| 553 | 564 | switch (zig_type_tag) { |
| 554 | 565 | .Type, |
| 555 | 566 | .Void, |
| ... | ... | @@ -567,34 +578,34 @@ pub const Type = extern union { |
| 567 | 578 | .Int => { |
| 568 | 579 | // Detect that e.g. u64 != usize, even if the bits match on a particular target. |
| 569 | 580 | if (self.isNamedInt()) { |
| 570 | std.hash.autoHash(&hasher, self.tag()); | |
| 581 | std.hash.autoHash(hasher, self.tag()); | |
| 571 | 582 | } else { |
| 572 | 583 | // Remaining cases are arbitrary sized integers. |
| 573 | 584 | // The target will not be branched upon, because we handled target-dependent cases above. |
| 574 | 585 | const info = self.intInfo(@as(Target, undefined)); |
| 575 | std.hash.autoHash(&hasher, info.signedness); | |
| 576 | std.hash.autoHash(&hasher, info.bits); | |
| 586 | std.hash.autoHash(hasher, info.signedness); | |
| 587 | std.hash.autoHash(hasher, info.bits); | |
| 577 | 588 | } |
| 578 | 589 | }, |
| 579 | 590 | .Array, .Vector => { |
| 580 | std.hash.autoHash(&hasher, self.arrayLen()); | |
| 581 | std.hash.autoHash(&hasher, self.elemType().hash()); | |
| 591 | std.hash.autoHash(hasher, self.arrayLen()); | |
| 592 | std.hash.autoHash(hasher, self.elemType().hash()); | |
| 582 | 593 | // TODO hash array sentinel |
| 583 | 594 | }, |
| 584 | 595 | .Fn => { |
| 585 | std.hash.autoHash(&hasher, self.fnReturnType().hash()); | |
| 586 | std.hash.autoHash(&hasher, self.fnCallingConvention()); | |
| 596 | std.hash.autoHash(hasher, self.fnReturnType().hash()); | |
| 597 | std.hash.autoHash(hasher, self.fnCallingConvention()); | |
| 587 | 598 | const params_len = self.fnParamLen(); |
| 588 | std.hash.autoHash(&hasher, params_len); | |
| 599 | std.hash.autoHash(hasher, params_len); | |
| 589 | 600 | var i: usize = 0; |
| 590 | 601 | while (i < params_len) : (i += 1) { |
| 591 | std.hash.autoHash(&hasher, self.fnParamType(i).hash()); | |
| 602 | std.hash.autoHash(hasher, self.fnParamType(i).hash()); | |
| 592 | 603 | } |
| 593 | std.hash.autoHash(&hasher, self.fnIsVarArgs()); | |
| 604 | std.hash.autoHash(hasher, self.fnIsVarArgs()); | |
| 594 | 605 | }, |
| 595 | 606 | .Optional => { |
| 596 | 607 | var buf: Payload.ElemType = undefined; |
| 597 | std.hash.autoHash(&hasher, self.optionalChild(&buf).hash()); | |
| 608 | std.hash.autoHash(hasher, self.optionalChild(&buf).hash()); | |
| 598 | 609 | }, |
| 599 | 610 | .Float, |
| 600 | 611 | .Struct, |
| ... | ... | @@ -611,7 +622,6 @@ pub const Type = extern union { |
| 611 | 622 | // TODO implement more type hashing |
| 612 | 623 | }, |
| 613 | 624 | } |
| 614 | return hasher.final(); | |
| 615 | 625 | } |
| 616 | 626 | |
| 617 | 627 | pub const HashContext64 = struct { |
| ... | ... | @@ -699,6 +709,7 @@ pub const Type = extern union { |
| 699 | 709 | .export_options, |
| 700 | 710 | .extern_options, |
| 701 | 711 | .@"anyframe", |
| 712 | .generic_poison, | |
| 702 | 713 | => unreachable, |
| 703 | 714 | |
| 704 | 715 | .array_u8, |
| ... | ... | @@ -759,12 +770,15 @@ pub const Type = extern union { |
| 759 | 770 | for (payload.param_types) |param_type, i| { |
| 760 | 771 | param_types[i] = try param_type.copy(allocator); |
| 761 | 772 | } |
| 773 | const other_comptime_params = payload.comptime_params[0..payload.param_types.len]; | |
| 774 | const comptime_params = try allocator.dupe(bool, other_comptime_params); | |
| 762 | 775 | return Tag.function.create(allocator, .{ |
| 763 | 776 | .return_type = try payload.return_type.copy(allocator), |
| 764 | 777 | .param_types = param_types, |
| 765 | 778 | .cc = payload.cc, |
| 766 | 779 | .is_var_args = payload.is_var_args, |
| 767 | 780 | .is_generic = payload.is_generic, |
| 781 | .comptime_params = comptime_params.ptr, | |
| 768 | 782 | }); |
| 769 | 783 | }, |
| 770 | 784 | .pointer => { |
| ... | ... | @@ -1080,11 +1094,118 @@ pub const Type = extern union { |
| 1080 | 1094 | }, |
| 1081 | 1095 | .inferred_alloc_const => return writer.writeAll("(inferred_alloc_const)"), |
| 1082 | 1096 | .inferred_alloc_mut => return writer.writeAll("(inferred_alloc_mut)"), |
| 1097 | .generic_poison => return writer.writeAll("(generic poison)"), | |
| 1083 | 1098 | } |
| 1084 | 1099 | unreachable; |
| 1085 | 1100 | } |
| 1086 | 1101 | } |
| 1087 | 1102 | |
| 1103 | /// Anything that reports hasCodeGenBits() false returns false here as well. | |
| 1104 | /// `generic_poison` will return false. | |
| 1105 | pub fn requiresComptime(ty: Type) bool { | |
| 1106 | return switch (ty.tag()) { | |
| 1107 | .u1, | |
| 1108 | .u8, | |
| 1109 | .i8, | |
| 1110 | .u16, | |
| 1111 | .i16, | |
| 1112 | .u32, | |
| 1113 | .i32, | |
| 1114 | .u64, | |
| 1115 | .i64, | |
| 1116 | .u128, | |
| 1117 | .i128, | |
| 1118 | .usize, | |
| 1119 | .isize, | |
| 1120 | .c_short, | |
| 1121 | .c_ushort, | |
| 1122 | .c_int, | |
| 1123 | .c_uint, | |
| 1124 | .c_long, | |
| 1125 | .c_ulong, | |
| 1126 | .c_longlong, | |
| 1127 | .c_ulonglong, | |
| 1128 | .c_longdouble, | |
| 1129 | .f16, | |
| 1130 | .f32, | |
| 1131 | .f64, | |
| 1132 | .f128, | |
| 1133 | .c_void, | |
| 1134 | .bool, | |
| 1135 | .void, | |
| 1136 | .anyerror, | |
| 1137 | .noreturn, | |
| 1138 | .@"anyframe", | |
| 1139 | .@"null", | |
| 1140 | .@"undefined", | |
| 1141 | .atomic_ordering, | |
| 1142 | .atomic_rmw_op, | |
| 1143 | .calling_convention, | |
| 1144 | .float_mode, | |
| 1145 | .reduce_op, | |
| 1146 | .call_options, | |
| 1147 | .export_options, | |
| 1148 | .extern_options, | |
| 1149 | .manyptr_u8, | |
| 1150 | .manyptr_const_u8, | |
| 1151 | .fn_noreturn_no_args, | |
| 1152 | .fn_void_no_args, | |
| 1153 | .fn_naked_noreturn_no_args, | |
| 1154 | .fn_ccc_void_no_args, | |
| 1155 | .single_const_pointer_to_comptime_int, | |
| 1156 | .const_slice_u8, | |
| 1157 | .anyerror_void_error_union, | |
| 1158 | .empty_struct_literal, | |
| 1159 | .function, | |
| 1160 | .empty_struct, | |
| 1161 | .error_set, | |
| 1162 | .error_set_single, | |
| 1163 | .error_set_inferred, | |
| 1164 | .@"opaque", | |
| 1165 | .generic_poison, | |
| 1166 | => false, | |
| 1167 | ||
| 1168 | .type, | |
| 1169 | .comptime_int, | |
| 1170 | .comptime_float, | |
| 1171 | .enum_literal, | |
| 1172 | => true, | |
| 1173 | ||
| 1174 | .var_args_param => unreachable, | |
| 1175 | .inferred_alloc_mut => unreachable, | |
| 1176 | .inferred_alloc_const => unreachable, | |
| 1177 | ||
| 1178 | .array_u8, | |
| 1179 | .array_u8_sentinel_0, | |
| 1180 | .array, | |
| 1181 | .array_sentinel, | |
| 1182 | .vector, | |
| 1183 | .pointer, | |
| 1184 | .single_const_pointer, | |
| 1185 | .single_mut_pointer, | |
| 1186 | .many_const_pointer, | |
| 1187 | .many_mut_pointer, | |
| 1188 | .c_const_pointer, | |
| 1189 | .c_mut_pointer, | |
| 1190 | .const_slice, | |
| 1191 | .mut_slice, | |
| 1192 | .int_signed, | |
| 1193 | .int_unsigned, | |
| 1194 | .optional, | |
| 1195 | .optional_single_mut_pointer, | |
| 1196 | .optional_single_const_pointer, | |
| 1197 | .error_union, | |
| 1198 | .anyframe_T, | |
| 1199 | .@"struct", | |
| 1200 | .@"union", | |
| 1201 | .union_tagged, | |
| 1202 | .enum_simple, | |
| 1203 | .enum_full, | |
| 1204 | .enum_nonexhaustive, | |
| 1205 | => false, // TODO some of these should be `true` depending on their child types | |
| 1206 | }; | |
| 1207 | } | |
| 1208 | ||
| 1088 | 1209 | pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value { |
| 1089 | 1210 | switch (self.tag()) { |
| 1090 | 1211 | .u1 => return Value.initTag(.u1_type), |
| ... | ... | @@ -1179,7 +1300,6 @@ pub const Type = extern union { |
| 1179 | 1300 | .fn_void_no_args, |
| 1180 | 1301 | .fn_naked_noreturn_no_args, |
| 1181 | 1302 | .fn_ccc_void_no_args, |
| 1182 | .function, | |
| 1183 | 1303 | .single_const_pointer_to_comptime_int, |
| 1184 | 1304 | .const_slice_u8, |
| 1185 | 1305 | .array_u8_sentinel_0, |
| ... | ... | @@ -1204,6 +1324,8 @@ pub const Type = extern union { |
| 1204 | 1324 | .anyframe_T, |
| 1205 | 1325 | => true, |
| 1206 | 1326 | |
| 1327 | .function => !self.castTag(.function).?.data.is_generic, | |
| 1328 | ||
| 1207 | 1329 | .@"struct" => { |
| 1208 | 1330 | // TODO introduce lazy value mechanism |
| 1209 | 1331 | const struct_obj = self.castTag(.@"struct").?.data; |
| ... | ... | @@ -1283,6 +1405,7 @@ pub const Type = extern union { |
| 1283 | 1405 | .inferred_alloc_const => unreachable, |
| 1284 | 1406 | .inferred_alloc_mut => unreachable, |
| 1285 | 1407 | .var_args_param => unreachable, |
| 1408 | .generic_poison => unreachable, | |
| 1286 | 1409 | }; |
| 1287 | 1410 | } |
| 1288 | 1411 | |
| ... | ... | @@ -1505,6 +1628,8 @@ pub const Type = extern union { |
| 1505 | 1628 | .@"opaque", |
| 1506 | 1629 | .var_args_param, |
| 1507 | 1630 | => unreachable, |
| 1631 | ||
| 1632 | .generic_poison => unreachable, | |
| 1508 | 1633 | }; |
| 1509 | 1634 | } |
| 1510 | 1635 | |
| ... | ... | @@ -1532,6 +1657,7 @@ pub const Type = extern union { |
| 1532 | 1657 | .inferred_alloc_mut => unreachable, |
| 1533 | 1658 | .@"opaque" => unreachable, |
| 1534 | 1659 | .var_args_param => unreachable, |
| 1660 | .generic_poison => unreachable, | |
| 1535 | 1661 | |
| 1536 | 1662 | .@"struct" => { |
| 1537 | 1663 | const s = self.castTag(.@"struct").?.data; |
| ... | ... | @@ -1698,6 +1824,7 @@ pub const Type = extern union { |
| 1698 | 1824 | .inferred_alloc_mut => unreachable, |
| 1699 | 1825 | .@"opaque" => unreachable, |
| 1700 | 1826 | .var_args_param => unreachable, |
| 1827 | .generic_poison => unreachable, | |
| 1701 | 1828 | |
| 1702 | 1829 | .@"struct" => { |
| 1703 | 1830 | @panic("TODO bitSize struct"); |
| ... | ... | @@ -2408,14 +2535,41 @@ pub const Type = extern union { |
| 2408 | 2535 | }; |
| 2409 | 2536 | } |
| 2410 | 2537 | |
| 2411 | /// Asserts the type is a function. | |
| 2412 | pub fn fnIsGeneric(self: Type) bool { | |
| 2413 | return switch (self.tag()) { | |
| 2414 | .fn_noreturn_no_args => false, | |
| 2415 | .fn_void_no_args => false, | |
| 2416 | .fn_naked_noreturn_no_args => false, | |
| 2417 | .fn_ccc_void_no_args => false, | |
| 2418 | .function => self.castTag(.function).?.data.is_generic, | |
| 2538 | pub fn fnInfo(ty: Type) Payload.Function.Data { | |
| 2539 | return switch (ty.tag()) { | |
| 2540 | .fn_noreturn_no_args => .{ | |
| 2541 | .param_types = &.{}, | |
| 2542 | .comptime_params = undefined, | |
| 2543 | .return_type = initTag(.noreturn), | |
| 2544 | .cc = .Unspecified, | |
| 2545 | .is_var_args = false, | |
| 2546 | .is_generic = false, | |
| 2547 | }, | |
| 2548 | .fn_void_no_args => .{ | |
| 2549 | .param_types = &.{}, | |
| 2550 | .comptime_params = undefined, | |
| 2551 | .return_type = initTag(.void), | |
| 2552 | .cc = .Unspecified, | |
| 2553 | .is_var_args = false, | |
| 2554 | .is_generic = false, | |
| 2555 | }, | |
| 2556 | .fn_naked_noreturn_no_args => .{ | |
| 2557 | .param_types = &.{}, | |
| 2558 | .comptime_params = undefined, | |
| 2559 | .return_type = initTag(.noreturn), | |
| 2560 | .cc = .Naked, | |
| 2561 | .is_var_args = false, | |
| 2562 | .is_generic = false, | |
| 2563 | }, | |
| 2564 | .fn_ccc_void_no_args => .{ | |
| 2565 | .param_types = &.{}, | |
| 2566 | .comptime_params = undefined, | |
| 2567 | .return_type = initTag(.void), | |
| 2568 | .cc = .C, | |
| 2569 | .is_var_args = false, | |
| 2570 | .is_generic = false, | |
| 2571 | }, | |
| 2572 | .function => ty.castTag(.function).?.data, | |
| 2419 | 2573 | |
| 2420 | 2574 | else => unreachable, |
| 2421 | 2575 | }; |
| ... | ... | @@ -2595,6 +2749,7 @@ pub const Type = extern union { |
| 2595 | 2749 | |
| 2596 | 2750 | .inferred_alloc_const => unreachable, |
| 2597 | 2751 | .inferred_alloc_mut => unreachable, |
| 2752 | .generic_poison => unreachable, | |
| 2598 | 2753 | }; |
| 2599 | 2754 | } |
| 2600 | 2755 | |
| ... | ... | @@ -3008,6 +3163,7 @@ pub const Type = extern union { |
| 3008 | 3163 | single_const_pointer_to_comptime_int, |
| 3009 | 3164 | const_slice_u8, |
| 3010 | 3165 | anyerror_void_error_union, |
| 3166 | generic_poison, | |
| 3011 | 3167 | /// This is a special type for variadic parameters of a function call. |
| 3012 | 3168 | /// Casts to it will validate that the type can be passed to a c calling convetion function. |
| 3013 | 3169 | var_args_param, |
| ... | ... | @@ -3105,6 +3261,7 @@ pub const Type = extern union { |
| 3105 | 3261 | .single_const_pointer_to_comptime_int, |
| 3106 | 3262 | .anyerror_void_error_union, |
| 3107 | 3263 | .const_slice_u8, |
| 3264 | .generic_poison, | |
| 3108 | 3265 | .inferred_alloc_const, |
| 3109 | 3266 | .inferred_alloc_mut, |
| 3110 | 3267 | .var_args_param, |
| ... | ... | @@ -3223,13 +3380,23 @@ pub const Type = extern union { |
| 3223 | 3380 | pub const base_tag = Tag.function; |
| 3224 | 3381 | |
| 3225 | 3382 | base: Payload = Payload{ .tag = base_tag }, |
| 3226 | data: struct { | |
| 3383 | data: Data, | |
| 3384 | ||
| 3385 | // TODO look into optimizing this memory to take fewer bytes | |
| 3386 | pub const Data = struct { | |
| 3227 | 3387 | param_types: []Type, |
| 3388 | comptime_params: [*]bool, | |
| 3228 | 3389 | return_type: Type, |
| 3229 | 3390 | cc: std.builtin.CallingConvention, |
| 3230 | 3391 | is_var_args: bool, |
| 3231 | 3392 | is_generic: bool, |
| 3232 | }, | |
| 3393 | ||
| 3394 | pub fn paramIsComptime(self: @This(), i: usize) bool { | |
| 3395 | if (!self.is_generic) return false; | |
| 3396 | assert(i < self.param_types.len); | |
| 3397 | return self.comptime_params[i]; | |
| 3398 | } | |
| 3399 | }; | |
| 3233 | 3400 | }; |
| 3234 | 3401 | |
| 3235 | 3402 | pub const ErrorSet = struct { |
src/value.zig+89-111| ... | ... | @@ -76,6 +76,8 @@ pub const Value = extern union { |
| 76 | 76 | fn_ccc_void_no_args_type, |
| 77 | 77 | single_const_pointer_to_comptime_int_type, |
| 78 | 78 | const_slice_u8_type, |
| 79 | anyerror_void_error_union_type, | |
| 80 | generic_poison_type, | |
| 79 | 81 | |
| 80 | 82 | undef, |
| 81 | 83 | zero, |
| ... | ... | @@ -85,6 +87,7 @@ pub const Value = extern union { |
| 85 | 87 | null_value, |
| 86 | 88 | bool_true, |
| 87 | 89 | bool_false, |
| 90 | generic_poison, | |
| 88 | 91 | |
| 89 | 92 | abi_align_default, |
| 90 | 93 | empty_struct_value, |
| ... | ... | @@ -188,6 +191,8 @@ pub const Value = extern union { |
| 188 | 191 | .single_const_pointer_to_comptime_int_type, |
| 189 | 192 | .anyframe_type, |
| 190 | 193 | .const_slice_u8_type, |
| 194 | .anyerror_void_error_union_type, | |
| 195 | .generic_poison_type, | |
| 191 | 196 | .enum_literal_type, |
| 192 | 197 | .undef, |
| 193 | 198 | .zero, |
| ... | ... | @@ -210,6 +215,7 @@ pub const Value = extern union { |
| 210 | 215 | .call_options_type, |
| 211 | 216 | .export_options_type, |
| 212 | 217 | .extern_options_type, |
| 218 | .generic_poison, | |
| 213 | 219 | => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"), |
| 214 | 220 | |
| 215 | 221 | .int_big_positive, |
| ... | ... | @@ -366,6 +372,8 @@ pub const Value = extern union { |
| 366 | 372 | .single_const_pointer_to_comptime_int_type, |
| 367 | 373 | .anyframe_type, |
| 368 | 374 | .const_slice_u8_type, |
| 375 | .anyerror_void_error_union_type, | |
| 376 | .generic_poison_type, | |
| 369 | 377 | .enum_literal_type, |
| 370 | 378 | .undef, |
| 371 | 379 | .zero, |
| ... | ... | @@ -388,6 +396,7 @@ pub const Value = extern union { |
| 388 | 396 | .call_options_type, |
| 389 | 397 | .export_options_type, |
| 390 | 398 | .extern_options_type, |
| 399 | .generic_poison, | |
| 391 | 400 | => unreachable, |
| 392 | 401 | |
| 393 | 402 | .ty => { |
| ... | ... | @@ -556,6 +565,9 @@ pub const Value = extern union { |
| 556 | 565 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| 557 | 566 | .anyframe_type => return out_stream.writeAll("anyframe"), |
| 558 | 567 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), |
| 568 | .anyerror_void_error_union_type => return out_stream.writeAll("anyerror!void"), | |
| 569 | .generic_poison_type => return out_stream.writeAll("(generic poison type)"), | |
| 570 | .generic_poison => return out_stream.writeAll("(generic poison)"), | |
| 559 | 571 | .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"), |
| 560 | 572 | .manyptr_u8_type => return out_stream.writeAll("[*]u8"), |
| 561 | 573 | .manyptr_const_u8_type => return out_stream.writeAll("[*]const u8"), |
| ... | ... | @@ -709,6 +721,8 @@ pub const Value = extern union { |
| 709 | 721 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| 710 | 722 | .anyframe_type => Type.initTag(.@"anyframe"), |
| 711 | 723 | .const_slice_u8_type => Type.initTag(.const_slice_u8), |
| 724 | .anyerror_void_error_union_type => Type.initTag(.anyerror_void_error_union), | |
| 725 | .generic_poison_type => Type.initTag(.generic_poison), | |
| 712 | 726 | .enum_literal_type => Type.initTag(.enum_literal), |
| 713 | 727 | .manyptr_u8_type => Type.initTag(.manyptr_u8), |
| 714 | 728 | .manyptr_const_u8_type => Type.initTag(.manyptr_const_u8), |
| ... | ... | @@ -732,46 +746,7 @@ pub const Value = extern union { |
| 732 | 746 | return Type.initPayload(&buffer.base); |
| 733 | 747 | }, |
| 734 | 748 | |
| 735 | .undef, | |
| 736 | .zero, | |
| 737 | .one, | |
| 738 | .void_value, | |
| 739 | .unreachable_value, | |
| 740 | .empty_array, | |
| 741 | .bool_true, | |
| 742 | .bool_false, | |
| 743 | .null_value, | |
| 744 | .int_u64, | |
| 745 | .int_i64, | |
| 746 | .int_big_positive, | |
| 747 | .int_big_negative, | |
| 748 | .function, | |
| 749 | .extern_fn, | |
| 750 | .variable, | |
| 751 | .decl_ref, | |
| 752 | .decl_ref_mut, | |
| 753 | .elem_ptr, | |
| 754 | .field_ptr, | |
| 755 | .bytes, | |
| 756 | .repeated, | |
| 757 | .array, | |
| 758 | .slice, | |
| 759 | .float_16, | |
| 760 | .float_32, | |
| 761 | .float_64, | |
| 762 | .float_128, | |
| 763 | .enum_literal, | |
| 764 | .enum_field_index, | |
| 765 | .@"error", | |
| 766 | .error_union, | |
| 767 | .empty_struct_value, | |
| 768 | .@"struct", | |
| 769 | .@"union", | |
| 770 | .inferred_alloc, | |
| 771 | .inferred_alloc_comptime, | |
| 772 | .abi_align_default, | |
| 773 | .eu_payload_ptr, | |
| 774 | => unreachable, | |
| 749 | else => unreachable, | |
| 775 | 750 | }; |
| 776 | 751 | } |
| 777 | 752 | |
| ... | ... | @@ -1142,12 +1117,82 @@ pub const Value = extern union { |
| 1142 | 1117 | return order(a, b).compare(.eq); |
| 1143 | 1118 | } |
| 1144 | 1119 | |
| 1120 | pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void { | |
| 1121 | switch (ty.zigTypeTag()) { | |
| 1122 | .BoundFn => unreachable, // TODO remove this from the language | |
| 1123 | ||
| 1124 | .Void, | |
| 1125 | .NoReturn, | |
| 1126 | .Undefined, | |
| 1127 | .Null, | |
| 1128 | => {}, | |
| 1129 | ||
| 1130 | .Type => { | |
| 1131 | var buf: ToTypeBuffer = undefined; | |
| 1132 | return val.toType(&buf).hashWithHasher(hasher); | |
| 1133 | }, | |
| 1134 | .Bool => { | |
| 1135 | std.hash.autoHash(hasher, val.toBool()); | |
| 1136 | }, | |
| 1137 | .Int, .ComptimeInt => { | |
| 1138 | var space: BigIntSpace = undefined; | |
| 1139 | const big = val.toBigInt(&space); | |
| 1140 | std.hash.autoHash(hasher, big.positive); | |
| 1141 | for (big.limbs) |limb| { | |
| 1142 | std.hash.autoHash(hasher, limb); | |
| 1143 | } | |
| 1144 | }, | |
| 1145 | .Float, .ComptimeFloat => { | |
| 1146 | @panic("TODO implement hashing float values"); | |
| 1147 | }, | |
| 1148 | .Pointer => { | |
| 1149 | @panic("TODO implement hashing pointer values"); | |
| 1150 | }, | |
| 1151 | .Array, .Vector => { | |
| 1152 | @panic("TODO implement hashing array/vector values"); | |
| 1153 | }, | |
| 1154 | .Struct => { | |
| 1155 | @panic("TODO implement hashing struct values"); | |
| 1156 | }, | |
| 1157 | .Optional => { | |
| 1158 | @panic("TODO implement hashing optional values"); | |
| 1159 | }, | |
| 1160 | .ErrorUnion => { | |
| 1161 | @panic("TODO implement hashing error union values"); | |
| 1162 | }, | |
| 1163 | .ErrorSet => { | |
| 1164 | @panic("TODO implement hashing error set values"); | |
| 1165 | }, | |
| 1166 | .Enum => { | |
| 1167 | @panic("TODO implement hashing enum values"); | |
| 1168 | }, | |
| 1169 | .Union => { | |
| 1170 | @panic("TODO implement hashing union values"); | |
| 1171 | }, | |
| 1172 | .Fn => { | |
| 1173 | @panic("TODO implement hashing function values"); | |
| 1174 | }, | |
| 1175 | .Opaque => { | |
| 1176 | @panic("TODO implement hashing opaque values"); | |
| 1177 | }, | |
| 1178 | .Frame => { | |
| 1179 | @panic("TODO implement hashing frame values"); | |
| 1180 | }, | |
| 1181 | .AnyFrame => { | |
| 1182 | @panic("TODO implement hashing anyframe values"); | |
| 1183 | }, | |
| 1184 | .EnumLiteral => { | |
| 1185 | @panic("TODO implement hashing enum literal values"); | |
| 1186 | }, | |
| 1187 | } | |
| 1188 | } | |
| 1189 | ||
| 1145 | 1190 | pub const ArrayHashContext = struct { |
| 1146 | 1191 | ty: Type, |
| 1147 | 1192 | |
| 1148 | pub fn hash(self: @This(), v: Value) u32 { | |
| 1193 | pub fn hash(self: @This(), val: Value) u32 { | |
| 1149 | 1194 | const other_context: HashContext = .{ .ty = self.ty }; |
| 1150 | return @truncate(u32, other_context.hash(v)); | |
| 1195 | return @truncate(u32, other_context.hash(val)); | |
| 1151 | 1196 | } |
| 1152 | 1197 | pub fn eql(self: @This(), a: Value, b: Value) bool { |
| 1153 | 1198 | return a.eql(b, self.ty); |
| ... | ... | @@ -1157,76 +1202,9 @@ pub const Value = extern union { |
| 1157 | 1202 | pub const HashContext = struct { |
| 1158 | 1203 | ty: Type, |
| 1159 | 1204 | |
| 1160 | pub fn hash(self: @This(), v: Value) u64 { | |
| 1205 | pub fn hash(self: @This(), val: Value) u64 { | |
| 1161 | 1206 | var hasher = std.hash.Wyhash.init(0); |
| 1162 | ||
| 1163 | switch (self.ty.zigTypeTag()) { | |
| 1164 | .BoundFn => unreachable, // TODO remove this from the language | |
| 1165 | ||
| 1166 | .Void, | |
| 1167 | .NoReturn, | |
| 1168 | .Undefined, | |
| 1169 | .Null, | |
| 1170 | => {}, | |
| 1171 | ||
| 1172 | .Type => { | |
| 1173 | var buf: ToTypeBuffer = undefined; | |
| 1174 | return v.toType(&buf).hash(); | |
| 1175 | }, | |
| 1176 | .Bool => { | |
| 1177 | std.hash.autoHash(&hasher, v.toBool()); | |
| 1178 | }, | |
| 1179 | .Int, .ComptimeInt => { | |
| 1180 | var space: BigIntSpace = undefined; | |
| 1181 | const big = v.toBigInt(&space); | |
| 1182 | std.hash.autoHash(&hasher, big.positive); | |
| 1183 | for (big.limbs) |limb| { | |
| 1184 | std.hash.autoHash(&hasher, limb); | |
| 1185 | } | |
| 1186 | }, | |
| 1187 | .Float, .ComptimeFloat => { | |
| 1188 | @panic("TODO implement hashing float values"); | |
| 1189 | }, | |
| 1190 | .Pointer => { | |
| 1191 | @panic("TODO implement hashing pointer values"); | |
| 1192 | }, | |
| 1193 | .Array, .Vector => { | |
| 1194 | @panic("TODO implement hashing array/vector values"); | |
| 1195 | }, | |
| 1196 | .Struct => { | |
| 1197 | @panic("TODO implement hashing struct values"); | |
| 1198 | }, | |
| 1199 | .Optional => { | |
| 1200 | @panic("TODO implement hashing optional values"); | |
| 1201 | }, | |
| 1202 | .ErrorUnion => { | |
| 1203 | @panic("TODO implement hashing error union values"); | |
| 1204 | }, | |
| 1205 | .ErrorSet => { | |
| 1206 | @panic("TODO implement hashing error set values"); | |
| 1207 | }, | |
| 1208 | .Enum => { | |
| 1209 | @panic("TODO implement hashing enum values"); | |
| 1210 | }, | |
| 1211 | .Union => { | |
| 1212 | @panic("TODO implement hashing union values"); | |
| 1213 | }, | |
| 1214 | .Fn => { | |
| 1215 | @panic("TODO implement hashing function values"); | |
| 1216 | }, | |
| 1217 | .Opaque => { | |
| 1218 | @panic("TODO implement hashing opaque values"); | |
| 1219 | }, | |
| 1220 | .Frame => { | |
| 1221 | @panic("TODO implement hashing frame values"); | |
| 1222 | }, | |
| 1223 | .AnyFrame => { | |
| 1224 | @panic("TODO implement hashing anyframe values"); | |
| 1225 | }, | |
| 1226 | .EnumLiteral => { | |
| 1227 | @panic("TODO implement hashing enum literal values"); | |
| 1228 | }, | |
| 1229 | } | |
| 1207 | val.hash(self.ty, &hasher); | |
| 1230 | 1208 | return hasher.final(); |
| 1231 | 1209 | } |
| 1232 | 1210 |
test/behavior.zig+2-1| ... | ... | @@ -4,6 +4,7 @@ test { |
| 4 | 4 | // Tests that pass for both. |
| 5 | 5 | _ = @import("behavior/bool.zig"); |
| 6 | 6 | _ = @import("behavior/basic.zig"); |
| 7 | _ = @import("behavior/generics.zig"); | |
| 7 | 8 | |
| 8 | 9 | if (!builtin.zig_is_stage2) { |
| 9 | 10 | // Tests that only pass for stage1. |
| ... | ... | @@ -94,7 +95,7 @@ test { |
| 94 | 95 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); |
| 95 | 96 | _ = @import("behavior/fn_delegation.zig"); |
| 96 | 97 | _ = @import("behavior/for.zig"); |
| 97 | _ = @import("behavior/generics.zig"); | |
| 98 | _ = @import("behavior/generics_stage1.zig"); | |
| 98 | 99 | _ = @import("behavior/hasdecl.zig"); |
| 99 | 100 | _ = @import("behavior/hasfield.zig"); |
| 100 | 101 | _ = @import("behavior/if.zig"); |
test/behavior/basic.zig+79| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | |
| 2 | 3 | const expect = std.testing.expect; |
| 3 | 4 | |
| 4 | 5 | // normal comment |
| ... | ... | @@ -83,3 +84,81 @@ test "unicode escape in character literal" { |
| 83 | 84 | test "unicode character in character literal" { |
| 84 | 85 | try expect('💩' == 128169); |
| 85 | 86 | } |
| 87 | ||
| 88 | fn first4KeysOfHomeRow() []const u8 { | |
| 89 | return "aoeu"; | |
| 90 | } | |
| 91 | ||
| 92 | test "return string from function" { | |
| 93 | try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); | |
| 94 | } | |
| 95 | ||
| 96 | test "hex escape" { | |
| 97 | try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); | |
| 98 | } | |
| 99 | ||
| 100 | test "multiline string" { | |
| 101 | const s1 = | |
| 102 | \\one | |
| 103 | \\two) | |
| 104 | \\three | |
| 105 | ; | |
| 106 | const s2 = "one\ntwo)\nthree"; | |
| 107 | try expect(mem.eql(u8, s1, s2)); | |
| 108 | } | |
| 109 | ||
| 110 | test "multiline string comments at start" { | |
| 111 | const s1 = | |
| 112 | //\\one | |
| 113 | \\two) | |
| 114 | \\three | |
| 115 | ; | |
| 116 | const s2 = "two)\nthree"; | |
| 117 | try expect(mem.eql(u8, s1, s2)); | |
| 118 | } | |
| 119 | ||
| 120 | test "multiline string comments at end" { | |
| 121 | const s1 = | |
| 122 | \\one | |
| 123 | \\two) | |
| 124 | //\\three | |
| 125 | ; | |
| 126 | const s2 = "one\ntwo)"; | |
| 127 | try expect(mem.eql(u8, s1, s2)); | |
| 128 | } | |
| 129 | ||
| 130 | test "multiline string comments in middle" { | |
| 131 | const s1 = | |
| 132 | \\one | |
| 133 | //\\two) | |
| 134 | \\three | |
| 135 | ; | |
| 136 | const s2 = "one\nthree"; | |
| 137 | try expect(mem.eql(u8, s1, s2)); | |
| 138 | } | |
| 139 | ||
| 140 | test "multiline string comments at multiple places" { | |
| 141 | const s1 = | |
| 142 | \\one | |
| 143 | //\\two | |
| 144 | \\three | |
| 145 | //\\four | |
| 146 | \\five | |
| 147 | ; | |
| 148 | const s2 = "one\nthree\nfive"; | |
| 149 | try expect(mem.eql(u8, s1, s2)); | |
| 150 | } | |
| 151 | ||
| 152 | test "call result of if else expression" { | |
| 153 | try expect(mem.eql(u8, f2(true), "a")); | |
| 154 | try expect(mem.eql(u8, f2(false), "b")); | |
| 155 | } | |
| 156 | fn f2(x: bool) []const u8 { | |
| 157 | return (if (x) fA else fB)(); | |
| 158 | } | |
| 159 | fn fA() []const u8 { | |
| 160 | return "a"; | |
| 161 | } | |
| 162 | fn fB() []const u8 { | |
| 163 | return "b"; | |
| 164 | } |
test/behavior/generics.zig+34-131| ... | ... | @@ -1,16 +1,43 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | const testing = std.testing; |
| 3 | 4 | const expect = testing.expect; |
| 4 | 5 | const expectEqual = testing.expectEqual; |
| 5 | 6 | |
| 7 | test "one param, explicit comptime" { | |
| 8 | var x: usize = 0; | |
| 9 | x += checkSize(i32); | |
| 10 | x += checkSize(bool); | |
| 11 | x += checkSize(bool); | |
| 12 | try expect(x == 6); | |
| 13 | } | |
| 14 | ||
| 15 | fn checkSize(comptime T: type) usize { | |
| 16 | return @sizeOf(T); | |
| 17 | } | |
| 18 | ||
| 6 | 19 | test "simple generic fn" { |
| 7 | 20 | try expect(max(i32, 3, -1) == 3); |
| 8 | try expect(max(f32, 0.123, 0.456) == 0.456); | |
| 21 | try expect(max(u8, 1, 100) == 100); | |
| 22 | if (!builtin.zig_is_stage2) { | |
| 23 | // TODO: stage2 is incorrectly emitting the following: | |
| 24 | // error: cast of value 1.23e-01 to type 'f32' loses information | |
| 25 | try expect(max(f32, 0.123, 0.456) == 0.456); | |
| 26 | } | |
| 9 | 27 | try expect(add(2, 3) == 5); |
| 10 | 28 | } |
| 11 | 29 | |
| 12 | 30 | fn max(comptime T: type, a: T, b: T) T { |
| 13 | return if (a > b) a else b; | |
| 31 | if (!builtin.zig_is_stage2) { | |
| 32 | // TODO: stage2 is incorrectly emitting AIR that allocates a result | |
| 33 | // value, stores to it, but then returns void instead of the result. | |
| 34 | return if (a > b) a else b; | |
| 35 | } | |
| 36 | if (a > b) { | |
| 37 | return a; | |
| 38 | } else { | |
| 39 | return b; | |
| 40 | } | |
| 14 | 41 | } |
| 15 | 42 | |
| 16 | 43 | fn add(comptime a: i32, b: i32) i32 { |
| ... | ... | @@ -37,133 +64,9 @@ fn sameButWithFloats(a: f64, b: f64) f64 { |
| 37 | 64 | test "fn with comptime args" { |
| 38 | 65 | try expect(gimmeTheBigOne(1234, 5678) == 5678); |
| 39 | 66 | try expect(shouldCallSameInstance(34, 12) == 34); |
| 40 | try expect(sameButWithFloats(0.43, 0.49) == 0.49); | |
| 41 | } | |
| 42 | ||
| 43 | test "var params" { | |
| 44 | try expect(max_i32(12, 34) == 34); | |
| 45 | try expect(max_f64(1.2, 3.4) == 3.4); | |
| 46 | } | |
| 47 | ||
| 48 | test { | |
| 49 | comptime try expect(max_i32(12, 34) == 34); | |
| 50 | comptime try expect(max_f64(1.2, 3.4) == 3.4); | |
| 51 | } | |
| 52 | ||
| 53 | fn max_var(a: anytype, b: anytype) @TypeOf(a + b) { | |
| 54 | return if (a > b) a else b; | |
| 55 | } | |
| 56 | ||
| 57 | fn max_i32(a: i32, b: i32) i32 { | |
| 58 | return max_var(a, b); | |
| 59 | } | |
| 60 | ||
| 61 | fn max_f64(a: f64, b: f64) f64 { | |
| 62 | return max_var(a, b); | |
| 63 | } | |
| 64 | ||
| 65 | pub fn List(comptime T: type) type { | |
| 66 | return SmallList(T, 8); | |
| 67 | } | |
| 68 | ||
| 69 | pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type { | |
| 70 | return struct { | |
| 71 | items: []T, | |
| 72 | length: usize, | |
| 73 | prealloc_items: [STATIC_SIZE]T, | |
| 74 | }; | |
| 75 | } | |
| 76 | ||
| 77 | test "function with return type type" { | |
| 78 | var list: List(i32) = undefined; | |
| 79 | var list2: List(i32) = undefined; | |
| 80 | list.length = 10; | |
| 81 | list2.length = 10; | |
| 82 | try expect(list.prealloc_items.len == 8); | |
| 83 | try expect(list2.prealloc_items.len == 8); | |
| 84 | } | |
| 85 | ||
| 86 | test "generic struct" { | |
| 87 | var a1 = GenNode(i32){ | |
| 88 | .value = 13, | |
| 89 | .next = null, | |
| 90 | }; | |
| 91 | var b1 = GenNode(bool){ | |
| 92 | .value = true, | |
| 93 | .next = null, | |
| 94 | }; | |
| 95 | try expect(a1.value == 13); | |
| 96 | try expect(a1.value == a1.getVal()); | |
| 97 | try expect(b1.getVal()); | |
| 98 | } | |
| 99 | fn GenNode(comptime T: type) type { | |
| 100 | return struct { | |
| 101 | value: T, | |
| 102 | next: ?*GenNode(T), | |
| 103 | fn getVal(n: *const GenNode(T)) T { | |
| 104 | return n.value; | |
| 105 | } | |
| 106 | }; | |
| 107 | } | |
| 108 | ||
| 109 | test "const decls in struct" { | |
| 110 | try expect(GenericDataThing(3).count_plus_one == 4); | |
| 111 | } | |
| 112 | fn GenericDataThing(comptime count: isize) type { | |
| 113 | return struct { | |
| 114 | const count_plus_one = count + 1; | |
| 115 | }; | |
| 116 | } | |
| 117 | ||
| 118 | test "use generic param in generic param" { | |
| 119 | try expect(aGenericFn(i32, 3, 4) == 7); | |
| 120 | } | |
| 121 | fn aGenericFn(comptime T: type, comptime a: T, b: T) T { | |
| 122 | return a + b; | |
| 123 | } | |
| 124 | ||
| 125 | test "generic fn with implicit cast" { | |
| 126 | try expect(getFirstByte(u8, &[_]u8{13}) == 13); | |
| 127 | try expect(getFirstByte(u16, &[_]u16{ | |
| 128 | 0, | |
| 129 | 13, | |
| 130 | }) == 0); | |
| 131 | } | |
| 132 | fn getByte(ptr: ?*const u8) u8 { | |
| 133 | return ptr.?.*; | |
| 134 | } | |
| 135 | fn getFirstByte(comptime T: type, mem: []const T) u8 { | |
| 136 | return getByte(@ptrCast(*const u8, &mem[0])); | |
| 137 | } | |
| 138 | ||
| 139 | const foos = [_]fn (anytype) bool{ | |
| 140 | foo1, | |
| 141 | foo2, | |
| 142 | }; | |
| 143 | ||
| 144 | fn foo1(arg: anytype) bool { | |
| 145 | return arg; | |
| 146 | } | |
| 147 | fn foo2(arg: anytype) bool { | |
| 148 | return !arg; | |
| 149 | } | |
| 150 | ||
| 151 | test "array of generic fns" { | |
| 152 | try expect(foos[0](true)); | |
| 153 | try expect(!foos[1](true)); | |
| 154 | } | |
| 155 | ||
| 156 | test "generic fn keeps non-generic parameter types" { | |
| 157 | const A = 128; | |
| 158 | ||
| 159 | const S = struct { | |
| 160 | fn f(comptime T: type, s: []T) !void { | |
| 161 | try expect(A != @typeInfo(@TypeOf(s)).Pointer.alignment); | |
| 162 | } | |
| 163 | }; | |
| 164 | ||
| 165 | // The compiler monomorphizes `S.f` for `T=u8` on its first use, check that | |
| 166 | // `x` type not affect `s` parameter type. | |
| 167 | var x: [16]u8 align(A) = undefined; | |
| 168 | try S.f(u8, &x); | |
| 67 | if (!builtin.zig_is_stage2) { | |
| 68 | // TODO: stage2 llvm backend needs to use fcmp instead of icmp | |
| 69 | // probably AIR should just have different instructions for floats. | |
| 70 | try expect(sameButWithFloats(0.43, 0.49) == 0.49); | |
| 71 | } | |
| 169 | 72 | } |
test/behavior/generics_stage1.zig created+132| ... | ... | @@ -0,0 +1,132 @@ |
| 1 | const std = @import("std"); | |
| 2 | const testing = std.testing; | |
| 3 | const expect = testing.expect; | |
| 4 | const expectEqual = testing.expectEqual; | |
| 5 | ||
| 6 | test "anytype params" { | |
| 7 | try expect(max_i32(12, 34) == 34); | |
| 8 | try expect(max_f64(1.2, 3.4) == 3.4); | |
| 9 | } | |
| 10 | ||
| 11 | test { | |
| 12 | comptime try expect(max_i32(12, 34) == 34); | |
| 13 | comptime try expect(max_f64(1.2, 3.4) == 3.4); | |
| 14 | } | |
| 15 | ||
| 16 | fn max_anytype(a: anytype, b: anytype) @TypeOf(a + b) { | |
| 17 | return if (a > b) a else b; | |
| 18 | } | |
| 19 | ||
| 20 | fn max_i32(a: i32, b: i32) i32 { | |
| 21 | return max_anytype(a, b); | |
| 22 | } | |
| 23 | ||
| 24 | fn max_f64(a: f64, b: f64) f64 { | |
| 25 | return max_anytype(a, b); | |
| 26 | } | |
| 27 | ||
| 28 | pub fn List(comptime T: type) type { | |
| 29 | return SmallList(T, 8); | |
| 30 | } | |
| 31 | ||
| 32 | pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type { | |
| 33 | return struct { | |
| 34 | items: []T, | |
| 35 | length: usize, | |
| 36 | prealloc_items: [STATIC_SIZE]T, | |
| 37 | }; | |
| 38 | } | |
| 39 | ||
| 40 | test "function with return type type" { | |
| 41 | var list: List(i32) = undefined; | |
| 42 | var list2: List(i32) = undefined; | |
| 43 | list.length = 10; | |
| 44 | list2.length = 10; | |
| 45 | try expect(list.prealloc_items.len == 8); | |
| 46 | try expect(list2.prealloc_items.len == 8); | |
| 47 | } | |
| 48 | ||
| 49 | test "generic struct" { | |
| 50 | var a1 = GenNode(i32){ | |
| 51 | .value = 13, | |
| 52 | .next = null, | |
| 53 | }; | |
| 54 | var b1 = GenNode(bool){ | |
| 55 | .value = true, | |
| 56 | .next = null, | |
| 57 | }; | |
| 58 | try expect(a1.value == 13); | |
| 59 | try expect(a1.value == a1.getVal()); | |
| 60 | try expect(b1.getVal()); | |
| 61 | } | |
| 62 | fn GenNode(comptime T: type) type { | |
| 63 | return struct { | |
| 64 | value: T, | |
| 65 | next: ?*GenNode(T), | |
| 66 | fn getVal(n: *const GenNode(T)) T { | |
| 67 | return n.value; | |
| 68 | } | |
| 69 | }; | |
| 70 | } | |
| 71 | ||
| 72 | test "const decls in struct" { | |
| 73 | try expect(GenericDataThing(3).count_plus_one == 4); | |
| 74 | } | |
| 75 | fn GenericDataThing(comptime count: isize) type { | |
| 76 | return struct { | |
| 77 | const count_plus_one = count + 1; | |
| 78 | }; | |
| 79 | } | |
| 80 | ||
| 81 | test "use generic param in generic param" { | |
| 82 | try expect(aGenericFn(i32, 3, 4) == 7); | |
| 83 | } | |
| 84 | fn aGenericFn(comptime T: type, comptime a: T, b: T) T { | |
| 85 | return a + b; | |
| 86 | } | |
| 87 | ||
| 88 | test "generic fn with implicit cast" { | |
| 89 | try expect(getFirstByte(u8, &[_]u8{13}) == 13); | |
| 90 | try expect(getFirstByte(u16, &[_]u16{ | |
| 91 | 0, | |
| 92 | 13, | |
| 93 | }) == 0); | |
| 94 | } | |
| 95 | fn getByte(ptr: ?*const u8) u8 { | |
| 96 | return ptr.?.*; | |
| 97 | } | |
| 98 | fn getFirstByte(comptime T: type, mem: []const T) u8 { | |
| 99 | return getByte(@ptrCast(*const u8, &mem[0])); | |
| 100 | } | |
| 101 | ||
| 102 | const foos = [_]fn (anytype) bool{ | |
| 103 | foo1, | |
| 104 | foo2, | |
| 105 | }; | |
| 106 | ||
| 107 | fn foo1(arg: anytype) bool { | |
| 108 | return arg; | |
| 109 | } | |
| 110 | fn foo2(arg: anytype) bool { | |
| 111 | return !arg; | |
| 112 | } | |
| 113 | ||
| 114 | test "array of generic fns" { | |
| 115 | try expect(foos[0](true)); | |
| 116 | try expect(!foos[1](true)); | |
| 117 | } | |
| 118 | ||
| 119 | test "generic fn keeps non-generic parameter types" { | |
| 120 | const A = 128; | |
| 121 | ||
| 122 | const S = struct { | |
| 123 | fn f(comptime T: type, s: []T) !void { | |
| 124 | try expect(A != @typeInfo(@TypeOf(s)).Pointer.alignment); | |
| 125 | } | |
| 126 | }; | |
| 127 | ||
| 128 | // The compiler monomorphizes `S.f` for `T=u8` on its first use, check that | |
| 129 | // `x` type not affect `s` parameter type. | |
| 130 | var x: [16]u8 align(A) = undefined; | |
| 131 | try S.f(u8, &x); | |
| 132 | } |
test/behavior/misc.zig+1-79| ... | ... | @@ -5,14 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | 7 | |
| 8 | fn first4KeysOfHomeRow() []const u8 { | |
| 9 | return "aoeu"; | |
| 10 | } | |
| 11 | ||
| 12 | test "return string from function" { | |
| 13 | try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); | |
| 14 | } | |
| 15 | ||
| 16 | 8 | test "memcpy and memset intrinsics" { |
| 17 | 9 | var foo: [20]u8 = undefined; |
| 18 | 10 | var bar: [20]u8 = undefined; |
| ... | ... | @@ -48,10 +40,6 @@ test "constant equal function pointers" { |
| 48 | 40 | |
| 49 | 41 | fn emptyFn() void {} |
| 50 | 42 | |
| 51 | test "hex escape" { | |
| 52 | try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); | |
| 53 | } | |
| 54 | ||
| 55 | 43 | test "string concatenation" { |
| 56 | 44 | try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); |
| 57 | 45 | } |
| ... | ... | @@ -70,59 +58,7 @@ test "string escapes" { |
| 70 | 58 | try expectEqualStrings("\u{1234}\u{069}\u{1}", "\xe1\x88\xb4\x69\x01"); |
| 71 | 59 | } |
| 72 | 60 | |
| 73 | test "multiline string" { | |
| 74 | const s1 = | |
| 75 | \\one | |
| 76 | \\two) | |
| 77 | \\three | |
| 78 | ; | |
| 79 | const s2 = "one\ntwo)\nthree"; | |
| 80 | try expect(mem.eql(u8, s1, s2)); | |
| 81 | } | |
| 82 | ||
| 83 | test "multiline string comments at start" { | |
| 84 | const s1 = | |
| 85 | //\\one | |
| 86 | \\two) | |
| 87 | \\three | |
| 88 | ; | |
| 89 | const s2 = "two)\nthree"; | |
| 90 | try expect(mem.eql(u8, s1, s2)); | |
| 91 | } | |
| 92 | ||
| 93 | test "multiline string comments at end" { | |
| 94 | const s1 = | |
| 95 | \\one | |
| 96 | \\two) | |
| 97 | //\\three | |
| 98 | ; | |
| 99 | const s2 = "one\ntwo)"; | |
| 100 | try expect(mem.eql(u8, s1, s2)); | |
| 101 | } | |
| 102 | ||
| 103 | test "multiline string comments in middle" { | |
| 104 | const s1 = | |
| 105 | \\one | |
| 106 | //\\two) | |
| 107 | \\three | |
| 108 | ; | |
| 109 | const s2 = "one\nthree"; | |
| 110 | try expect(mem.eql(u8, s1, s2)); | |
| 111 | } | |
| 112 | ||
| 113 | test "multiline string comments at multiple places" { | |
| 114 | const s1 = | |
| 115 | \\one | |
| 116 | //\\two | |
| 117 | \\three | |
| 118 | //\\four | |
| 119 | \\five | |
| 120 | ; | |
| 121 | const s2 = "one\nthree\nfive"; | |
| 122 | try expect(mem.eql(u8, s1, s2)); | |
| 123 | } | |
| 124 | ||
| 125 | test "multiline C string" { | |
| 61 | test "multiline string literal is null terminated" { | |
| 126 | 62 | const s1 = |
| 127 | 63 | \\one |
| 128 | 64 | \\two) |
| ... | ... | @@ -177,20 +113,6 @@ fn outer() i64 { |
| 177 | 113 | return inner(); |
| 178 | 114 | } |
| 179 | 115 | |
| 180 | test "call result of if else expression" { | |
| 181 | try expect(mem.eql(u8, f2(true), "a")); | |
| 182 | try expect(mem.eql(u8, f2(false), "b")); | |
| 183 | } | |
| 184 | fn f2(x: bool) []const u8 { | |
| 185 | return (if (x) fA else fB)(); | |
| 186 | } | |
| 187 | fn fA() []const u8 { | |
| 188 | return "a"; | |
| 189 | } | |
| 190 | fn fB() []const u8 { | |
| 191 | return "b"; | |
| 192 | } | |
| 193 | ||
| 194 | 116 | test "constant enum initialization with differing sizes" { |
| 195 | 117 | try test3_1(test3_foo); |
| 196 | 118 | try test3_2(test3_bar); |
test/cases.zig+1-1| ... | ... | @@ -1572,7 +1572,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1572 | 1572 | \\ const x = asm volatile ("syscall" |
| 1573 | 1573 | \\ : [o] "{rax}" (-> number) |
| 1574 | 1574 | \\ : [number] "{rax}" (231), |
| 1575 | \\ [arg1] "{rdi}" (code) | |
| 1575 | \\ [arg1] "{rdi}" (60) | |
| 1576 | 1576 | \\ : "rcx", "r11", "memory" |
| 1577 | 1577 | \\ ); |
| 1578 | 1578 | \\ _ = x; |