| author | |
| committer | |
| log | 78d4fb20c44488117cc450177d92c44a19d97c91 |
| tree | 3a39a09a30d7667de2e4fc62ab1c14fdaddb00b2 |
| parent | 425c0ffa014fb950a4f9f90aa9a200fbc4d8e091 |
This replaces the current generic syntax for functions and replaces
it with the concept of inline parameters.
This paves the way for the "all structs anonymous" proposal.
Closes #151.19 files changed, 565 insertions(+), 293 deletions(-)
doc/langref.md+2-2| ... | ... | @@ -25,7 +25,7 @@ UseDecl = "use" Expression ";" |
| 25 | 25 | |
| 26 | 26 | ExternDecl = "extern" (FnProto | VariableDeclaration) ";" |
| 27 | 27 | |
| 28 | FnProto = "fn" option("Symbol") option(ParamDeclList) ParamDeclList option("->" TypeExpr) | |
| 28 | FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr) | |
| 29 | 29 | |
| 30 | 30 | Directive = "#" "Symbol" "(" Expression ")" |
| 31 | 31 | |
| ... | ... | @@ -35,7 +35,7 @@ FnDef = option("inline" | "extern") FnProto Block |
| 35 | 35 | |
| 36 | 36 | ParamDeclList = "(" list(ParamDecl, ",") ")" |
| 37 | 37 | |
| 38 | ParamDecl = option("noalias") option("Symbol" ":") TypeExpr | "..." | |
| 38 | ParamDecl = option("noalias" | "inline") option("Symbol" ":") TypeExpr | "..." | |
| 39 | 39 | |
| 40 | 40 | Block = "{" list(option(Statement), ";") "}" |
| 41 | 41 |
example/guess_number/main.zig+1-1| ... | ... | @@ -23,7 +23,7 @@ pub fn main(args: [][]u8) -> %void { |
| 23 | 23 | return err; |
| 24 | 24 | }; |
| 25 | 25 | |
| 26 | const guess = io.parse_unsigned(u8)(line_buf[0...line_len - 1], 10) %% { | |
| 26 | const guess = io.parse_unsigned(u8, line_buf[0...line_len - 1], 10) %% { | |
| 27 | 27 | %%io.stdout.printf("Invalid number.\n"); |
| 28 | 28 | continue; |
| 29 | 29 | }; |
src/all_types.hpp+9-6| ... | ... | @@ -195,10 +195,8 @@ struct AstNodeRoot { |
| 195 | 195 | struct AstNodeFnProto { |
| 196 | 196 | TopLevelDecl top_level_decl; |
| 197 | 197 | Buf name; |
| 198 | ZigList<AstNode *> generic_params; | |
| 199 | 198 | ZigList<AstNode *> params; |
| 200 | 199 | AstNode *return_type; |
| 201 | bool generic_params_is_var_args; | |
| 202 | 200 | bool is_var_args; |
| 203 | 201 | bool is_extern; |
| 204 | 202 | bool is_inline; |
| ... | ... | @@ -210,7 +208,10 @@ struct AstNodeFnProto { |
| 210 | 208 | FnTableEntry *fn_table_entry; |
| 211 | 209 | bool skip; |
| 212 | 210 | Expr resolved_expr; |
| 213 | TypeTableEntry *generic_fn_type; | |
| 211 | // computed from params field | |
| 212 | int inline_arg_count; | |
| 213 | // if this is a generic function implementation, this points to the generic node | |
| 214 | AstNode *generic_proto_node; | |
| 214 | 215 | }; |
| 215 | 216 | |
| 216 | 217 | struct AstNodeFnDef { |
| ... | ... | @@ -219,6 +220,7 @@ struct AstNodeFnDef { |
| 219 | 220 | |
| 220 | 221 | // populated by semantic analyzer |
| 221 | 222 | TypeTableEntry *implicit_return_type; |
| 223 | // the first child block context | |
| 222 | 224 | BlockContext *block_context; |
| 223 | 225 | }; |
| 224 | 226 | |
| ... | ... | @@ -230,6 +232,7 @@ struct AstNodeParamDecl { |
| 230 | 232 | Buf name; |
| 231 | 233 | AstNode *type; |
| 232 | 234 | bool is_noalias; |
| 235 | bool is_inline; | |
| 233 | 236 | |
| 234 | 237 | // populated by semantic analyzer |
| 235 | 238 | VariableTableEntry *variable; |
| ... | ... | @@ -841,6 +844,7 @@ struct FnTypeId { |
| 841 | 844 | bool is_naked; |
| 842 | 845 | bool is_cold; |
| 843 | 846 | bool is_extern; |
| 847 | bool is_inline; | |
| 844 | 848 | FnTypeParamInfo prealloc_param_info[fn_type_id_prealloc_param_info_count]; |
| 845 | 849 | }; |
| 846 | 850 | |
| ... | ... | @@ -1063,7 +1067,6 @@ struct FnTableEntry { |
| 1063 | 1067 | ZigList<LabelTableEntry *> all_labels; |
| 1064 | 1068 | Buf symbol_name; |
| 1065 | 1069 | TypeTableEntry *type_entry; // function type |
| 1066 | bool is_inline; | |
| 1067 | 1070 | bool internal_linkage; |
| 1068 | 1071 | bool is_extern; |
| 1069 | 1072 | bool is_test; |
| ... | ... | @@ -1172,8 +1175,8 @@ struct CodeGen { |
| 1172 | 1175 | |
| 1173 | 1176 | ZigList<ImportTableEntry *> import_queue; |
| 1174 | 1177 | int import_queue_index; |
| 1175 | ZigList<AstNode *> export_queue; | |
| 1176 | int export_queue_index; | |
| 1178 | ZigList<AstNode *> resolve_queue; | |
| 1179 | int resolve_queue_index; | |
| 1177 | 1180 | ZigList<AstNode *> use_queue; |
| 1178 | 1181 | int use_queue_index; |
| 1179 | 1182 |
src/analyze.cpp+318-152| ... | ... | @@ -32,6 +32,8 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, |
| 32 | 32 | static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node); |
| 33 | 33 | static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, |
| 34 | 34 | bool depends_on_compile_var); |
| 35 | static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode *node, | |
| 36 | TypeTableEntry *type_entry, bool depends_on_compile_var); | |
| 35 | 37 | static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type, |
| 36 | 38 | bool depends_on_compile_var); |
| 37 | 39 | static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node, |
| ... | ... | @@ -874,7 +876,8 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor |
| 874 | 876 | fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport); |
| 875 | 877 | fn_type_id.is_naked = is_naked; |
| 876 | 878 | fn_type_id.is_cold = is_cold; |
| 877 | fn_type_id.param_count = node->data.fn_proto.params.length; | |
| 879 | fn_type_id.is_inline = fn_proto->is_inline; | |
| 880 | fn_type_id.param_count = fn_proto->params.length; | |
| 878 | 881 | |
| 879 | 882 | if (fn_type_id.param_count > fn_type_id_prealloc_param_info_count) { |
| 880 | 883 | fn_type_id.param_info = allocate_nonzero<FnTypeParamInfo>(fn_type_id.param_count); |
| ... | ... | @@ -883,15 +886,52 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor |
| 883 | 886 | } |
| 884 | 887 | |
| 885 | 888 | fn_type_id.is_var_args = fn_proto->is_var_args; |
| 886 | fn_type_id.return_type = analyze_type_expr(g, import, context, node->data.fn_proto.return_type); | |
| 889 | fn_type_id.return_type = analyze_type_expr(g, import, context, fn_proto->return_type); | |
| 887 | 890 | |
| 888 | if (fn_type_id.return_type->id == TypeTableEntryIdInvalid) { | |
| 889 | fn_proto->skip = true; | |
| 891 | switch (fn_type_id.return_type->id) { | |
| 892 | case TypeTableEntryIdInvalid: | |
| 893 | fn_proto->skip = true; | |
| 894 | break; | |
| 895 | case TypeTableEntryIdNumLitFloat: | |
| 896 | case TypeTableEntryIdNumLitInt: | |
| 897 | case TypeTableEntryIdUndefLit: | |
| 898 | case TypeTableEntryIdNamespace: | |
| 899 | case TypeTableEntryIdGenericFn: | |
| 900 | fn_proto->skip = true; | |
| 901 | add_node_error(g, fn_proto->return_type, | |
| 902 | buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name))); | |
| 903 | break; | |
| 904 | case TypeTableEntryIdMetaType: | |
| 905 | if (!fn_proto->is_inline) { | |
| 906 | fn_proto->skip = true; | |
| 907 | add_node_error(g, fn_proto->return_type, | |
| 908 | buf_sprintf("function with return type '%s' must be declared inline", | |
| 909 | buf_ptr(&fn_type_id.return_type->name))); | |
| 910 | return g->builtin_types.entry_invalid; | |
| 911 | } | |
| 912 | break; | |
| 913 | case TypeTableEntryIdUnreachable: | |
| 914 | case TypeTableEntryIdVoid: | |
| 915 | case TypeTableEntryIdBool: | |
| 916 | case TypeTableEntryIdInt: | |
| 917 | case TypeTableEntryIdFloat: | |
| 918 | case TypeTableEntryIdPointer: | |
| 919 | case TypeTableEntryIdArray: | |
| 920 | case TypeTableEntryIdStruct: | |
| 921 | case TypeTableEntryIdMaybe: | |
| 922 | case TypeTableEntryIdErrorUnion: | |
| 923 | case TypeTableEntryIdPureError: | |
| 924 | case TypeTableEntryIdEnum: | |
| 925 | case TypeTableEntryIdUnion: | |
| 926 | case TypeTableEntryIdFn: | |
| 927 | case TypeTableEntryIdTypeDecl: | |
| 928 | break; | |
| 890 | 929 | } |
| 891 | 930 | |
| 892 | 931 | for (int i = 0; i < fn_type_id.param_count; i += 1) { |
| 893 | AstNode *child = node->data.fn_proto.params.at(i); | |
| 932 | AstNode *child = fn_proto->params.at(i); | |
| 894 | 933 | assert(child->type == NodeTypeParamDecl); |
| 934 | ||
| 895 | 935 | TypeTableEntry *type_entry = analyze_type_expr(g, import, context, |
| 896 | 936 | child->data.param_decl.type); |
| 897 | 937 | switch (type_entry->id) { |
| ... | ... | @@ -901,13 +941,20 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor |
| 901 | 941 | case TypeTableEntryIdNumLitFloat: |
| 902 | 942 | case TypeTableEntryIdNumLitInt: |
| 903 | 943 | case TypeTableEntryIdUndefLit: |
| 904 | case TypeTableEntryIdMetaType: | |
| 905 | 944 | case TypeTableEntryIdUnreachable: |
| 906 | 945 | case TypeTableEntryIdNamespace: |
| 907 | 946 | case TypeTableEntryIdGenericFn: |
| 908 | 947 | fn_proto->skip = true; |
| 909 | 948 | add_node_error(g, child->data.param_decl.type, |
| 910 | buf_sprintf("parameter of type '%s' not allowed'", buf_ptr(&type_entry->name))); | |
| 949 | buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name))); | |
| 950 | break; | |
| 951 | case TypeTableEntryIdMetaType: | |
| 952 | if (!child->data.param_decl.is_inline) { | |
| 953 | fn_proto->skip = true; | |
| 954 | add_node_error(g, child->data.param_decl.type, | |
| 955 | buf_sprintf("parameter of type '%s' must be declared inline", | |
| 956 | buf_ptr(&type_entry->name))); | |
| 957 | } | |
| 911 | 958 | break; |
| 912 | 959 | case TypeTableEntryIdVoid: |
| 913 | 960 | case TypeTableEntryIdBool: |
| ... | ... | @@ -998,8 +1045,6 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 998 | 1045 | return; |
| 999 | 1046 | } |
| 1000 | 1047 | |
| 1001 | fn_table_entry->is_inline = fn_proto->is_inline; | |
| 1002 | ||
| 1003 | 1048 | bool is_cold = false; |
| 1004 | 1049 | bool is_naked = false; |
| 1005 | 1050 | bool is_test = false; |
| ... | ... | @@ -1095,7 +1140,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 1095 | 1140 | return; |
| 1096 | 1141 | } |
| 1097 | 1142 | |
| 1098 | if (fn_table_entry->is_inline && fn_table_entry->is_noinline) { | |
| 1143 | if (fn_proto->is_inline && fn_table_entry->is_noinline) { | |
| 1099 | 1144 | add_node_error(g, node, buf_sprintf("function is both inline and noinline")); |
| 1100 | 1145 | fn_proto->skip = true; |
| 1101 | 1146 | return; |
| ... | ... | @@ -1109,10 +1154,14 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 1109 | 1154 | symbol_name = buf_sprintf("_%s", buf_ptr(&fn_table_entry->symbol_name)); |
| 1110 | 1155 | } |
| 1111 | 1156 | |
| 1112 | fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), | |
| 1113 | fn_type->data.fn.raw_type_ref); | |
| 1157 | if (fn_table_entry->fn_def_node) { | |
| 1158 | BlockContext *context = new_block_context(fn_table_entry->fn_def_node, containing_context); | |
| 1159 | fn_table_entry->fn_def_node->data.fn_def.block_context = context; | |
| 1160 | } | |
| 1161 | ||
| 1162 | fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_type->data.fn.raw_type_ref); | |
| 1114 | 1163 | |
| 1115 | if (fn_table_entry->is_inline) { | |
| 1164 | if (fn_proto->is_inline) { | |
| 1116 | 1165 | LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute); |
| 1117 | 1166 | } |
| 1118 | 1167 | if (fn_table_entry->is_noinline) { |
| ... | ... | @@ -1150,9 +1199,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 1150 | 1199 | fn_type->di_type, fn_table_entry->internal_linkage, |
| 1151 | 1200 | is_definition, scope_line, flags, is_optimized, nullptr); |
| 1152 | 1201 | |
| 1153 | BlockContext *context = new_block_context(fn_table_entry->fn_def_node, containing_context); | |
| 1154 | fn_table_entry->fn_def_node->data.fn_def.block_context = context; | |
| 1155 | context->di_scope = LLVMZigSubprogramToScope(subprogram); | |
| 1202 | fn_table_entry->fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram); | |
| 1156 | 1203 | ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram); |
| 1157 | 1204 | } |
| 1158 | 1205 | } |
| ... | ... | @@ -1176,6 +1223,7 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt |
| 1176 | 1223 | return; |
| 1177 | 1224 | } |
| 1178 | 1225 | |
| 1226 | assert(decl_node->type == NodeTypeContainerDecl); | |
| 1179 | 1227 | assert(enum_type->di_type); |
| 1180 | 1228 | |
| 1181 | 1229 | enum_type->deep_const = true; |
| ... | ... | @@ -1370,7 +1418,7 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE |
| 1370 | 1418 | return; |
| 1371 | 1419 | } |
| 1372 | 1420 | |
| 1373 | ||
| 1421 | assert(decl_node->type == NodeTypeContainerDecl); | |
| 1374 | 1422 | assert(struct_type->di_type); |
| 1375 | 1423 | |
| 1376 | 1424 | struct_type->deep_const = true; |
| ... | ... | @@ -1496,38 +1544,30 @@ static void get_fully_qualified_decl_name(Buf *buf, AstNode *decl_node, uint8_t |
| 1496 | 1544 | } |
| 1497 | 1545 | |
| 1498 | 1546 | static void preview_generic_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 1499 | if (node->type == NodeTypeFnProto) { | |
| 1500 | if (node->data.fn_proto.generic_params_is_var_args) { | |
| 1501 | add_node_error(g, node, buf_sprintf("generic parameters cannot be var args")); | |
| 1502 | node->data.fn_proto.skip = true; | |
| 1503 | node->data.fn_proto.generic_fn_type = g->builtin_types.entry_invalid; | |
| 1504 | return; | |
| 1505 | } | |
| 1506 | ||
| 1507 | node->data.fn_proto.generic_fn_type = get_generic_fn_type(g, node); | |
| 1508 | } else if (node->type == NodeTypeContainerDecl) { | |
| 1509 | if (node->data.struct_decl.generic_params_is_var_args) { | |
| 1510 | add_node_error(g, node, buf_sprintf("generic parameters cannot be var args")); | |
| 1511 | node->data.struct_decl.skip = true; | |
| 1512 | node->data.struct_decl.generic_fn_type = g->builtin_types.entry_invalid; | |
| 1513 | return; | |
| 1514 | } | |
| 1547 | assert(node->type == NodeTypeContainerDecl); | |
| 1515 | 1548 | |
| 1516 | node->data.struct_decl.generic_fn_type = get_generic_fn_type(g, node); | |
| 1517 | } else { | |
| 1518 | zig_unreachable(); | |
| 1549 | if (node->data.struct_decl.generic_params_is_var_args) { | |
| 1550 | add_node_error(g, node, buf_sprintf("generic parameters cannot be var args")); | |
| 1551 | node->data.struct_decl.skip = true; | |
| 1552 | node->data.struct_decl.generic_fn_type = g->builtin_types.entry_invalid; | |
| 1553 | return; | |
| 1519 | 1554 | } |
| 1520 | 1555 | |
| 1556 | node->data.struct_decl.generic_fn_type = get_generic_fn_type(g, node); | |
| 1521 | 1557 | } |
| 1522 | 1558 | |
| 1523 | 1559 | static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstNode *proto_node, |
| 1524 | 1560 | BlockContext *containing_context) |
| 1525 | 1561 | { |
| 1562 | assert(proto_node->type == NodeTypeFnProto); | |
| 1563 | ||
| 1526 | 1564 | if (proto_node->data.fn_proto.skip) { |
| 1527 | 1565 | return; |
| 1528 | 1566 | } |
| 1529 | 1567 | |
| 1530 | bool is_generic_instance = (proto_node->data.fn_proto.generic_params.length > 0); | |
| 1568 | bool is_generic_instance = proto_node->data.fn_proto.generic_proto_node; | |
| 1569 | bool is_generic_fn = proto_node->data.fn_proto.inline_arg_count > 0; | |
| 1570 | assert(!is_generic_instance || !is_generic_fn); | |
| 1531 | 1571 | |
| 1532 | 1572 | AstNode *parent_decl = proto_node->data.fn_proto.top_level_decl.parent_decl; |
| 1533 | 1573 | Buf *proto_name = &proto_node->data.fn_proto.name; |
| ... | ... | @@ -1551,41 +1591,50 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN |
| 1551 | 1591 | |
| 1552 | 1592 | get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_'); |
| 1553 | 1593 | |
| 1554 | g->fn_protos.append(fn_table_entry); | |
| 1555 | ||
| 1556 | if (fn_def_node) { | |
| 1557 | g->fn_defs.append(fn_table_entry); | |
| 1558 | } | |
| 1594 | proto_node->data.fn_proto.fn_table_entry = fn_table_entry; | |
| 1559 | 1595 | |
| 1560 | bool is_main_fn = !is_generic_instance && | |
| 1561 | !parent_decl && (import == g->root_import) && | |
| 1562 | buf_eql_str(proto_name, "main"); | |
| 1563 | if (is_main_fn) { | |
| 1564 | g->main_fn = fn_table_entry; | |
| 1565 | } | |
| 1596 | if (is_generic_fn) { | |
| 1597 | fn_table_entry->type_entry = get_generic_fn_type(g, proto_node); | |
| 1566 | 1598 | |
| 1567 | proto_node->data.fn_proto.fn_table_entry = fn_table_entry; | |
| 1568 | resolve_function_proto(g, proto_node, fn_table_entry, import, containing_context); | |
| 1569 | ||
| 1570 | if (is_main_fn && !g->link_libc) { | |
| 1571 | TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void); | |
| 1572 | TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; | |
| 1573 | if (actual_return_type != err_void) { | |
| 1574 | AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type; | |
| 1575 | add_node_error(g, return_type_node, | |
| 1576 | buf_sprintf("expected return type of main to be '%%void', instead is '%s'", | |
| 1577 | buf_ptr(&actual_return_type->name))); | |
| 1599 | if (is_extern || proto_node->data.fn_proto.top_level_decl.visib_mod == VisibModExport) { | |
| 1600 | for (int i = 0; i < proto_node->data.fn_proto.params.length; i += 1) { | |
| 1601 | AstNode *param_decl_node = proto_node->data.fn_proto.params.at(i); | |
| 1602 | if (param_decl_node->data.param_decl.is_inline) { | |
| 1603 | proto_node->data.fn_proto.skip = true; | |
| 1604 | add_node_error(g, param_decl_node, | |
| 1605 | buf_sprintf("inline parameter not allowed in extern function")); | |
| 1606 | } | |
| 1607 | } | |
| 1578 | 1608 | } |
| 1579 | } | |
| 1580 | } | |
| 1581 | 1609 | |
| 1582 | static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *proto_node) { | |
| 1583 | if (proto_node->data.fn_proto.generic_params.length > 0) { | |
| 1584 | return preview_generic_fn_proto(g, import, proto_node); | |
| 1610 | ||
| 1585 | 1611 | } else { |
| 1586 | return preview_fn_proto_instance(g, import, proto_node, proto_node->block_context); | |
| 1587 | } | |
| 1612 | g->fn_protos.append(fn_table_entry); | |
| 1613 | ||
| 1614 | if (fn_def_node) { | |
| 1615 | g->fn_defs.append(fn_table_entry); | |
| 1616 | } | |
| 1617 | ||
| 1618 | bool is_main_fn = !is_generic_instance && | |
| 1619 | !parent_decl && (import == g->root_import) && | |
| 1620 | buf_eql_str(proto_name, "main"); | |
| 1621 | if (is_main_fn) { | |
| 1622 | g->main_fn = fn_table_entry; | |
| 1623 | } | |
| 1588 | 1624 | |
| 1625 | resolve_function_proto(g, proto_node, fn_table_entry, import, containing_context); | |
| 1626 | ||
| 1627 | if (is_main_fn && !g->link_libc) { | |
| 1628 | TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void); | |
| 1629 | TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; | |
| 1630 | if (actual_return_type != err_void) { | |
| 1631 | AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type; | |
| 1632 | add_node_error(g, return_type_node, | |
| 1633 | buf_sprintf("expected return type of main to be '%%void', instead is '%s'", | |
| 1634 | buf_ptr(&actual_return_type->name))); | |
| 1635 | } | |
| 1636 | } | |
| 1637 | } | |
| 1589 | 1638 | } |
| 1590 | 1639 | |
| 1591 | 1640 | static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { |
| ... | ... | @@ -1683,7 +1732,7 @@ static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) |
| 1683 | 1732 | |
| 1684 | 1733 | switch (node->type) { |
| 1685 | 1734 | case NodeTypeFnProto: |
| 1686 | preview_fn_proto(g, import, node); | |
| 1735 | preview_fn_proto_instance(g, import, node, node->block_context); | |
| 1687 | 1736 | break; |
| 1688 | 1737 | case NodeTypeContainerDecl: |
| 1689 | 1738 | resolve_struct_decl(g, import, node); |
| ... | ... | @@ -2600,7 +2649,11 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i |
| 2600 | 2649 | |
| 2601 | 2650 | node->data.field_access_expr.is_member_fn = true; |
| 2602 | 2651 | FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry; |
| 2603 | return resolve_expr_const_val_as_fn(g, node, fn_entry, false); | |
| 2652 | if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) { | |
| 2653 | return resolve_expr_const_val_as_generic_fn(g, node, fn_entry->type_entry, false); | |
| 2654 | } else { | |
| 2655 | return resolve_expr_const_val_as_fn(g, node, fn_entry, false); | |
| 2656 | } | |
| 2604 | 2657 | } else { |
| 2605 | 2658 | add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'", |
| 2606 | 2659 | buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); |
| ... | ... | @@ -3004,13 +3057,11 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod |
| 3004 | 3057 | VariableTableEntry *var = decl_node->data.variable_declaration.variable; |
| 3005 | 3058 | return analyze_var_ref(g, source_node, var, block_context, depends_on_compile_var); |
| 3006 | 3059 | } else if (decl_node->type == NodeTypeFnProto) { |
| 3007 | if (decl_node->data.fn_proto.generic_params.length > 0) { | |
| 3008 | TypeTableEntry *type_entry = decl_node->data.fn_proto.generic_fn_type; | |
| 3009 | assert(type_entry); | |
| 3010 | return resolve_expr_const_val_as_generic_fn(g, source_node, type_entry, depends_on_compile_var); | |
| 3060 | FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry; | |
| 3061 | assert(fn_entry->type_entry); | |
| 3062 | if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) { | |
| 3063 | return resolve_expr_const_val_as_generic_fn(g, source_node, fn_entry->type_entry, depends_on_compile_var); | |
| 3011 | 3064 | } else { |
| 3012 | FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry; | |
| 3013 | assert(fn_entry->type_entry); | |
| 3014 | 3065 | return resolve_expr_const_val_as_fn(g, source_node, fn_entry, depends_on_compile_var); |
| 3015 | 3066 | } |
| 3016 | 3067 | } else if (decl_node->type == NodeTypeContainerDecl) { |
| ... | ... | @@ -5238,6 +5289,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 5238 | 5289 | zig_unreachable(); |
| 5239 | 5290 | } |
| 5240 | 5291 | |
| 5292 | // Before calling this function, set node->data.fn_call_expr.fn_table_entry if the function is known | |
| 5293 | // at compile time. Otherwise this is a function pointer call. | |
| 5241 | 5294 | static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 5242 | 5295 | TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type, |
| 5243 | 5296 | AstNode *struct_node) |
| ... | ... | @@ -5248,26 +5301,30 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 5248 | 5301 | return fn_type; |
| 5249 | 5302 | } |
| 5250 | 5303 | |
| 5251 | // count parameters | |
| 5252 | int src_param_count = fn_type->data.fn.fn_type_id.param_count; | |
| 5253 | int actual_param_count = node->data.fn_call_expr.params.length; | |
| 5304 | // The function call might include inline parameters which we need to ignore according to the | |
| 5305 | // fn_type. | |
| 5306 | FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; | |
| 5307 | AstNode *generic_proto_node = fn_table_entry ? | |
| 5308 | fn_table_entry->proto_node->data.fn_proto.generic_proto_node : nullptr; | |
| 5254 | 5309 | |
| 5255 | if (struct_node) { | |
| 5256 | actual_param_count += 1; | |
| 5257 | } | |
| 5310 | // count parameters | |
| 5311 | int struct_node_1_or_0 = struct_node ? 1 : 0; | |
| 5312 | int src_param_count = fn_type->data.fn.fn_type_id.param_count + | |
| 5313 | (generic_proto_node ? generic_proto_node->data.fn_proto.inline_arg_count : 0); | |
| 5314 | int call_param_count = node->data.fn_call_expr.params.length; | |
| 5258 | 5315 | |
| 5259 | 5316 | bool ok_invocation = true; |
| 5260 | 5317 | |
| 5261 | 5318 | if (fn_type->data.fn.fn_type_id.is_var_args) { |
| 5262 | if (actual_param_count < src_param_count) { | |
| 5319 | if (call_param_count < src_param_count - struct_node_1_or_0) { | |
| 5263 | 5320 | ok_invocation = false; |
| 5264 | 5321 | add_node_error(g, node, |
| 5265 | buf_sprintf("expected at least %d arguments, got %d", src_param_count, actual_param_count)); | |
| 5322 | buf_sprintf("expected at least %d arguments, got %d", src_param_count, call_param_count)); | |
| 5266 | 5323 | } |
| 5267 | } else if (src_param_count != actual_param_count) { | |
| 5324 | } else if (src_param_count - struct_node_1_or_0 != call_param_count) { | |
| 5268 | 5325 | ok_invocation = false; |
| 5269 | 5326 | add_node_error(g, node, |
| 5270 | buf_sprintf("expected %d arguments, got %d", src_param_count, actual_param_count)); | |
| 5327 | buf_sprintf("expected %d arguments, got %d", src_param_count, call_param_count)); | |
| 5271 | 5328 | } |
| 5272 | 5329 | |
| 5273 | 5330 | bool all_args_const_expr = true; |
| ... | ... | @@ -5281,17 +5338,30 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 5281 | 5338 | |
| 5282 | 5339 | // analyze each parameter. in the case of a method, we already analyzed the |
| 5283 | 5340 | // first parameter in order to figure out which struct we were calling a method on. |
| 5284 | for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { | |
| 5285 | AstNode **child = &node->data.fn_call_expr.params.at(i); | |
| 5341 | int next_type_i = struct_node_1_or_0; | |
| 5342 | for (int call_i = 0; call_i < call_param_count; call_i += 1) { | |
| 5343 | int proto_i = call_i + struct_node_1_or_0; | |
| 5344 | AstNode **param_node = &node->data.fn_call_expr.params.at(call_i); | |
| 5286 | 5345 | // determine the expected type for each parameter |
| 5287 | 5346 | TypeTableEntry *expected_param_type = nullptr; |
| 5288 | int fn_proto_i = i + (struct_node ? 1 : 0); | |
| 5289 | if (fn_proto_i < src_param_count) { | |
| 5290 | expected_param_type = fn_type->data.fn.fn_type_id.param_info[fn_proto_i].type; | |
| 5347 | if (proto_i < src_param_count) { | |
| 5348 | if (generic_proto_node && | |
| 5349 | generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline) | |
| 5350 | { | |
| 5351 | continue; | |
| 5352 | } | |
| 5353 | ||
| 5354 | FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[next_type_i]; | |
| 5355 | next_type_i += 1; | |
| 5356 | ||
| 5357 | expected_param_type = param_info->type; | |
| 5358 | } | |
| 5359 | TypeTableEntry *param_type = analyze_expression(g, import, context, expected_param_type, *param_node); | |
| 5360 | if (param_type->id == TypeTableEntryIdInvalid) { | |
| 5361 | return param_type; | |
| 5291 | 5362 | } |
| 5292 | analyze_expression(g, import, context, expected_param_type, *child); | |
| 5293 | 5363 | |
| 5294 | ConstExprValue *const_arg_val = &get_resolved_expr(*child)->const_val; | |
| 5364 | ConstExprValue *const_arg_val = &get_resolved_expr(*param_node)->const_val; | |
| 5295 | 5365 | if (!const_arg_val->ok) { |
| 5296 | 5366 | all_args_const_expr = false; |
| 5297 | 5367 | } |
| ... | ... | @@ -5303,7 +5373,6 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 5303 | 5373 | return return_type; |
| 5304 | 5374 | } |
| 5305 | 5375 | |
| 5306 | FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; | |
| 5307 | 5376 | ConstExprValue *result_val = &get_resolved_expr(node)->const_val; |
| 5308 | 5377 | if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) { |
| 5309 | 5378 | if (fn_table_entry->anal_state == FnAnalStateReady) { |
| ... | ... | @@ -5335,14 +5404,103 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 5335 | 5404 | return return_type; |
| 5336 | 5405 | } |
| 5337 | 5406 | |
| 5338 | static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 5339 | TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, AstNode *struct_node) | |
| 5407 | static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableEntry *import, | |
| 5408 | BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *call_node, | |
| 5409 | FnTableEntry *fn_table_entry, AstNode *struct_node) | |
| 5340 | 5410 | { |
| 5341 | assert(node->type == NodeTypeFnCallExpr); | |
| 5411 | assert(call_node->type == NodeTypeFnCallExpr); | |
| 5412 | assert(fn_table_entry); | |
| 5413 | ||
| 5414 | AstNode *decl_node = fn_table_entry->proto_node; | |
| 5415 | ||
| 5416 | // count parameters | |
| 5417 | int struct_node_1_or_0 = (struct_node ? 1 : 0); | |
| 5418 | int src_param_count = decl_node->data.fn_proto.params.length; | |
| 5419 | int call_param_count = call_node->data.fn_call_expr.params.length; | |
| 5420 | ||
| 5421 | if (src_param_count != call_param_count + struct_node_1_or_0) { | |
| 5422 | add_node_error(g, call_node, | |
| 5423 | buf_sprintf("expected %d arguments, got %d", src_param_count, call_param_count)); | |
| 5424 | return g->builtin_types.entry_invalid; | |
| 5425 | } | |
| 5426 | ||
| 5427 | int inline_arg_count = decl_node->data.fn_proto.inline_arg_count; | |
| 5428 | assert(inline_arg_count > 0); | |
| 5429 | ||
| 5430 | BlockContext *child_context = decl_node->owner->block_context; | |
| 5431 | int next_generic_param_index = 0; | |
| 5432 | ||
| 5433 | GenericFnTypeId *generic_fn_type_id = allocate<GenericFnTypeId>(1); | |
| 5434 | generic_fn_type_id->decl_node = decl_node; | |
| 5435 | generic_fn_type_id->generic_param_count = inline_arg_count; | |
| 5436 | generic_fn_type_id->generic_params = allocate<GenericParamValue>(inline_arg_count); | |
| 5437 | ||
| 5438 | for (int call_i = 0; call_i < call_param_count; call_i += 1) { | |
| 5439 | int proto_i = call_i + struct_node_1_or_0; | |
| 5440 | AstNode *generic_param_decl_node = decl_node->data.fn_proto.params.at(proto_i); | |
| 5441 | assert(generic_param_decl_node->type == NodeTypeParamDecl); | |
| 5442 | bool is_inline = generic_param_decl_node->data.param_decl.is_inline; | |
| 5443 | if (!is_inline) continue; | |
| 5444 | ||
| 5445 | AstNode **generic_param_type_node = &generic_param_decl_node->data.param_decl.type; | |
| 5446 | TypeTableEntry *expected_param_type = analyze_type_expr(g, decl_node->owner, child_context, | |
| 5447 | *generic_param_type_node); | |
| 5448 | if (expected_param_type->id == TypeTableEntryIdInvalid) { | |
| 5449 | return expected_param_type; | |
| 5450 | } | |
| 5451 | ||
| 5452 | AstNode **param_node = &call_node->data.fn_call_expr.params.at(call_i); | |
| 5453 | TypeTableEntry *param_type = analyze_expression(g, import, parent_context, | |
| 5454 | expected_param_type, *param_node); | |
| 5455 | if (param_type->id == TypeTableEntryIdInvalid) { | |
| 5456 | return param_type; | |
| 5457 | } | |
| 5458 | ||
| 5459 | // set child_context so that the previous param is in scope | |
| 5460 | child_context = new_block_context(generic_param_decl_node, child_context); | |
| 5461 | ||
| 5462 | ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val; | |
| 5463 | if (const_val->ok) { | |
| 5464 | add_local_var(g, generic_param_decl_node, decl_node->owner, child_context, | |
| 5465 | &generic_param_decl_node->data.param_decl.name, param_type, true, *param_node); | |
| 5466 | } else { | |
| 5467 | add_node_error(g, *param_node, | |
| 5468 | buf_sprintf("unable to evaluate constant expression for inline parameter")); | |
| 5469 | ||
| 5470 | return g->builtin_types.entry_invalid; | |
| 5471 | } | |
| 5472 | ||
| 5473 | GenericParamValue *generic_param_value = | |
| 5474 | &generic_fn_type_id->generic_params[next_generic_param_index]; | |
| 5475 | generic_param_value->type = param_type; | |
| 5476 | generic_param_value->node = *param_node; | |
| 5477 | next_generic_param_index += 1; | |
| 5478 | } | |
| 5479 | ||
| 5480 | assert(next_generic_param_index == inline_arg_count); | |
| 5481 | ||
| 5482 | auto entry = g->generic_table.maybe_get(generic_fn_type_id); | |
| 5483 | FnTableEntry *impl_fn; | |
| 5484 | if (entry) { | |
| 5485 | AstNode *impl_decl_node = entry->value; | |
| 5486 | assert(impl_decl_node->type == NodeTypeFnProto); | |
| 5487 | impl_fn = impl_decl_node->data.fn_proto.fn_table_entry; | |
| 5488 | } else { | |
| 5489 | AstNode *decl_node = generic_fn_type_id->decl_node; | |
| 5490 | AstNode *impl_fn_def_node = ast_clone_subtree_special(decl_node->data.fn_proto.fn_def_node, | |
| 5491 | &g->next_node_index, AstCloneSpecialOmitInlineParams); | |
| 5492 | AstNode *impl_decl_node = impl_fn_def_node->data.fn_def.fn_proto; | |
| 5493 | impl_decl_node->data.fn_proto.inline_arg_count = 0; | |
| 5494 | impl_decl_node->data.fn_proto.generic_proto_node = decl_node; | |
| 5342 | 5495 | |
| 5343 | node->data.fn_call_expr.fn_entry = fn_table_entry; | |
| 5496 | preview_fn_proto_instance(g, import, impl_decl_node, child_context); | |
| 5497 | g->generic_table.put(generic_fn_type_id, impl_decl_node); | |
| 5498 | impl_fn = impl_decl_node->data.fn_proto.fn_table_entry; | |
| 5499 | } | |
| 5344 | 5500 | |
| 5345 | return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_node); | |
| 5501 | call_node->data.fn_call_expr.fn_entry = impl_fn; | |
| 5502 | return analyze_fn_call_ptr(g, import, parent_context, expected_type, call_node, | |
| 5503 | impl_fn->type_entry, struct_node); | |
| 5346 | 5504 | } |
| 5347 | 5505 | |
| 5348 | 5506 | static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, |
| ... | ... | @@ -5352,14 +5510,8 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp |
| 5352 | 5510 | assert(generic_fn_type->id == TypeTableEntryIdGenericFn); |
| 5353 | 5511 | |
| 5354 | 5512 | AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node; |
| 5355 | ZigList<AstNode *> *generic_params; | |
| 5356 | if (decl_node->type == NodeTypeFnProto) { | |
| 5357 | generic_params = &decl_node->data.fn_proto.generic_params; | |
| 5358 | } else if (decl_node->type == NodeTypeContainerDecl) { | |
| 5359 | generic_params = &decl_node->data.struct_decl.generic_params; | |
| 5360 | } else { | |
| 5361 | zig_unreachable(); | |
| 5362 | } | |
| 5513 | assert(decl_node->type == NodeTypeContainerDecl); | |
| 5514 | ZigList<AstNode *> *generic_params = &decl_node->data.struct_decl.generic_params; | |
| 5363 | 5515 | |
| 5364 | 5516 | int expected_param_count = generic_params->length; |
| 5365 | 5517 | int actual_param_count = node->data.fn_call_expr.params.length; |
| ... | ... | @@ -5405,10 +5557,6 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp |
| 5405 | 5557 | } else { |
| 5406 | 5558 | add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression")); |
| 5407 | 5559 | |
| 5408 | add_local_var(g, generic_param_decl_node, decl_node->owner, child_context, | |
| 5409 | &generic_param_decl_node->data.param_decl.name, g->builtin_types.entry_invalid, | |
| 5410 | true, nullptr); | |
| 5411 | ||
| 5412 | 5560 | return g->builtin_types.entry_invalid; |
| 5413 | 5561 | } |
| 5414 | 5562 | |
| ... | ... | @@ -5420,36 +5568,19 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp |
| 5420 | 5568 | auto entry = g->generic_table.maybe_get(generic_fn_type_id); |
| 5421 | 5569 | if (entry) { |
| 5422 | 5570 | AstNode *impl_decl_node = entry->value; |
| 5423 | if (impl_decl_node->type == NodeTypeFnProto) { | |
| 5424 | FnTableEntry *fn_table_entry = impl_decl_node->data.fn_proto.fn_table_entry; | |
| 5425 | return resolve_expr_const_val_as_fn(g, node, fn_table_entry, false); | |
| 5426 | } else if (impl_decl_node->type == NodeTypeContainerDecl) { | |
| 5427 | TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry; | |
| 5428 | return resolve_expr_const_val_as_type(g, node, type_entry, false); | |
| 5429 | } else { | |
| 5430 | zig_unreachable(); | |
| 5431 | } | |
| 5432 | } | |
| 5433 | ||
| 5434 | // make a type from the generic parameters supplied | |
| 5435 | if (decl_node->type == NodeTypeFnProto) { | |
| 5436 | AstNode *impl_fn_def_node = ast_clone_subtree(decl_node->data.fn_proto.fn_def_node, &g->next_node_index); | |
| 5437 | AstNode *impl_decl_node = impl_fn_def_node->data.fn_def.fn_proto; | |
| 5438 | ||
| 5439 | preview_fn_proto_instance(g, import, impl_decl_node, child_context); | |
| 5440 | g->generic_table.put(generic_fn_type_id, impl_decl_node); | |
| 5441 | FnTableEntry *fn_table_entry = impl_decl_node->data.fn_proto.fn_table_entry; | |
| 5442 | return resolve_expr_const_val_as_fn(g, node, fn_table_entry, false); | |
| 5443 | } else if (decl_node->type == NodeTypeContainerDecl) { | |
| 5444 | AstNode *impl_decl_node = ast_clone_subtree(decl_node, &g->next_node_index); | |
| 5445 | g->generic_table.put(generic_fn_type_id, impl_decl_node); | |
| 5446 | scan_struct_decl(g, import, child_context, impl_decl_node); | |
| 5571 | assert(impl_decl_node->type == NodeTypeContainerDecl); | |
| 5447 | 5572 | TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry; |
| 5448 | resolve_struct_type(g, import, type_entry); | |
| 5449 | 5573 | return resolve_expr_const_val_as_type(g, node, type_entry, false); |
| 5450 | } else { | |
| 5451 | zig_unreachable(); | |
| 5452 | 5574 | } |
| 5575 | ||
| 5576 | // make a type from the generic parameters supplied | |
| 5577 | assert(decl_node->type == NodeTypeContainerDecl); | |
| 5578 | AstNode *impl_decl_node = ast_clone_subtree(decl_node, &g->next_node_index); | |
| 5579 | g->generic_table.put(generic_fn_type_id, impl_decl_node); | |
| 5580 | scan_struct_decl(g, import, child_context, impl_decl_node); | |
| 5581 | TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry; | |
| 5582 | resolve_struct_type(g, import, type_entry); | |
| 5583 | return resolve_expr_const_val_as_type(g, node, type_entry, false); | |
| 5453 | 5584 | } |
| 5454 | 5585 | |
| 5455 | 5586 | static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| ... | ... | @@ -5487,10 +5618,32 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import |
| 5487 | 5618 | struct_node = nullptr; |
| 5488 | 5619 | } |
| 5489 | 5620 | |
| 5490 | return analyze_fn_call_raw(g, import, context, expected_type, node, | |
| 5491 | const_val->data.x_fn, struct_node); | |
| 5621 | FnTableEntry *fn_table_entry = const_val->data.x_fn; | |
| 5622 | node->data.fn_call_expr.fn_entry = fn_table_entry; | |
| 5623 | return analyze_fn_call_ptr(g, import, context, expected_type, node, | |
| 5624 | fn_table_entry->type_entry, struct_node); | |
| 5492 | 5625 | } else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) { |
| 5493 | return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type); | |
| 5626 | TypeTableEntry *generic_fn_type = const_val->data.x_type; | |
| 5627 | AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node; | |
| 5628 | if (decl_node->type == NodeTypeFnProto) { | |
| 5629 | AstNode *struct_node; | |
| 5630 | if (fn_ref_expr->type == NodeTypeFieldAccessExpr && | |
| 5631 | fn_ref_expr->data.field_access_expr.is_member_fn) | |
| 5632 | { | |
| 5633 | struct_node = fn_ref_expr->data.field_access_expr.struct_expr; | |
| 5634 | } else { | |
| 5635 | struct_node = nullptr; | |
| 5636 | } | |
| 5637 | ||
| 5638 | FnTableEntry *fn_table_entry = decl_node->data.fn_proto.fn_table_entry; | |
| 5639 | if (fn_table_entry->proto_node->data.fn_proto.skip) { | |
| 5640 | return g->builtin_types.entry_invalid; | |
| 5641 | } | |
| 5642 | return analyze_fn_call_with_inline_args(g, import, context, expected_type, node, | |
| 5643 | fn_table_entry, struct_node); | |
| 5644 | } else { | |
| 5645 | return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type); | |
| 5646 | } | |
| 5494 | 5647 | } else { |
| 5495 | 5648 | add_node_error(g, fn_ref_expr, |
| 5496 | 5649 | buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name))); |
| ... | ... | @@ -6367,7 +6520,9 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 6367 | 6520 | var->src_arg_index = i; |
| 6368 | 6521 | param_decl_node->data.param_decl.variable = var; |
| 6369 | 6522 | |
| 6370 | var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; | |
| 6523 | if (fn_type->data.fn.gen_param_info) { | |
| 6524 | var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; | |
| 6525 | } | |
| 6371 | 6526 | |
| 6372 | 6527 | if (!type->deep_const) { |
| 6373 | 6528 | fn_table_entry->is_pure = false; |
| ... | ... | @@ -6406,11 +6561,11 @@ static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContex |
| 6406 | 6561 | tld->import = import; |
| 6407 | 6562 | tld->name = name; |
| 6408 | 6563 | |
| 6409 | bool want_as_export = (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport); | |
| 6410 | bool is_generic = (node->type == NodeTypeFnProto && node->data.fn_proto.generic_params.length > 0) || | |
| 6411 | (node->type == NodeTypeContainerDecl && node->data.struct_decl.generic_params.length > 0); | |
| 6412 | if (!is_generic && want_as_export) { | |
| 6413 | g->export_queue.append(node); | |
| 6564 | bool want_to_resolve = (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport); | |
| 6565 | bool is_generic_container = (node->type == NodeTypeContainerDecl && | |
| 6566 | node->data.struct_decl.generic_params.length > 0); | |
| 6567 | if (want_to_resolve && !is_generic_container) { | |
| 6568 | g->resolve_queue.append(node); | |
| 6414 | 6569 | } |
| 6415 | 6570 | |
| 6416 | 6571 | node->block_context = block_context; |
| ... | ... | @@ -6425,6 +6580,18 @@ static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContex |
| 6425 | 6580 | } |
| 6426 | 6581 | } |
| 6427 | 6582 | |
| 6583 | static int fn_proto_inline_arg_count(AstNode *proto_node) { | |
| 6584 | assert(proto_node->type == NodeTypeFnProto); | |
| 6585 | int result = 0; | |
| 6586 | for (int i = 0; i < proto_node->data.fn_proto.params.length; i += 1) { | |
| 6587 | AstNode *param_node = proto_node->data.fn_proto.params.at(i); | |
| 6588 | assert(param_node->type == NodeTypeParamDecl); | |
| 6589 | result += param_node->data.param_decl.is_inline ? 1 : 0; | |
| 6590 | } | |
| 6591 | return result; | |
| 6592 | } | |
| 6593 | ||
| 6594 | ||
| 6428 | 6595 | static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { |
| 6429 | 6596 | switch (node->type) { |
| 6430 | 6597 | case NodeTypeRoot: |
| ... | ... | @@ -6467,6 +6634,7 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte |
| 6467 | 6634 | add_node_error(g, node, buf_sprintf("missing function name")); |
| 6468 | 6635 | break; |
| 6469 | 6636 | } |
| 6637 | node->data.fn_proto.inline_arg_count = fn_proto_inline_arg_count(node); | |
| 6470 | 6638 | |
| 6471 | 6639 | add_top_level_decl(g, import, context, node, fn_name); |
| 6472 | 6640 | break; |
| ... | ... | @@ -6692,8 +6860,8 @@ void semantic_analyze(CodeGen *g) { |
| 6692 | 6860 | resolve_use_decl(g, use_decl_node); |
| 6693 | 6861 | } |
| 6694 | 6862 | |
| 6695 | for (; g->export_queue_index < g->export_queue.length; g->export_queue_index += 1) { | |
| 6696 | AstNode *decl_node = g->export_queue.at(g->export_queue_index); | |
| 6863 | for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) { | |
| 6864 | AstNode *decl_node = g->resolve_queue.at(g->resolve_queue_index); | |
| 6697 | 6865 | bool pointer_only = false; |
| 6698 | 6866 | resolve_top_level_decl(g, decl_node, pointer_only); |
| 6699 | 6867 | } |
| ... | ... | @@ -6983,11 +7151,9 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { |
| 6983 | 7151 | FnTypeParamInfo *a_param_info = &a->param_info[i]; |
| 6984 | 7152 | FnTypeParamInfo *b_param_info = &b->param_info[i]; |
| 6985 | 7153 | |
| 6986 | if (a_param_info->type != b_param_info->type) { | |
| 6987 | return false; | |
| 6988 | } | |
| 6989 | ||
| 6990 | if (a_param_info->is_noalias != b_param_info->is_noalias) { | |
| 7154 | if (a_param_info->type != b_param_info->type || | |
| 7155 | a_param_info->is_noalias != b_param_info->is_noalias) | |
| 7156 | { | |
| 6991 | 7157 | return false; |
| 6992 | 7158 | } |
| 6993 | 7159 | } |
src/ast_render.cpp+2-1| ... | ... | @@ -353,7 +353,8 @@ static void render_node(AstRender *ar, AstNode *node) { |
| 353 | 353 | assert(param_decl->type == NodeTypeParamDecl); |
| 354 | 354 | if (buf_len(&param_decl->data.param_decl.name) > 0) { |
| 355 | 355 | const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : ""; |
| 356 | fprintf(ar->f, "%s", noalias_str); | |
| 356 | const char *inline_str = param_decl->data.param_decl.is_inline ? "inline " : ""; | |
| 357 | fprintf(ar->f, "%s%s", noalias_str, inline_str); | |
| 357 | 358 | print_symbol(ar, &param_decl->data.param_decl.name); |
| 358 | 359 | fprintf(ar->f, ": "); |
| 359 | 360 | } |
src/codegen.cpp+14-5| ... | ... | @@ -1062,12 +1062,15 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 1062 | 1062 | |
| 1063 | 1063 | TypeTableEntry *fn_type; |
| 1064 | 1064 | LLVMValueRef fn_val; |
| 1065 | AstNode *generic_proto_node; | |
| 1065 | 1066 | if (fn_table_entry) { |
| 1066 | 1067 | fn_val = fn_table_entry->fn_value; |
| 1067 | 1068 | fn_type = fn_table_entry->type_entry; |
| 1069 | generic_proto_node = fn_table_entry->proto_node->data.fn_proto.generic_proto_node; | |
| 1068 | 1070 | } else { |
| 1069 | 1071 | fn_val = gen_expr(g, fn_ref_expr); |
| 1070 | 1072 | fn_type = get_expr_type(fn_ref_expr); |
| 1073 | generic_proto_node = nullptr; | |
| 1071 | 1074 | } |
| 1072 | 1075 | |
| 1073 | 1076 | TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type; |
| ... | ... | @@ -1093,8 +1096,14 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 1093 | 1096 | gen_param_index += 1; |
| 1094 | 1097 | } |
| 1095 | 1098 | |
| 1096 | for (int i = 0; i < fn_call_param_count; i += 1) { | |
| 1097 | AstNode *expr_node = node->data.fn_call_expr.params.at(i); | |
| 1099 | for (int call_i = 0; call_i < fn_call_param_count; call_i += 1) { | |
| 1100 | int proto_i = call_i + (struct_type ? 1 : 0); | |
| 1101 | if (generic_proto_node && | |
| 1102 | generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline) | |
| 1103 | { | |
| 1104 | continue; | |
| 1105 | } | |
| 1106 | AstNode *expr_node = node->data.fn_call_expr.params.at(call_i); | |
| 1098 | 1107 | LLVMValueRef param_value = gen_expr(g, expr_node); |
| 1099 | 1108 | assert(param_value); |
| 1100 | 1109 | TypeTableEntry *param_type = get_expr_type(expr_node); |
| ... | ... | @@ -3734,7 +3743,7 @@ static void delete_unused_builtin_fns(CodeGen *g) { |
| 3734 | 3743 | } |
| 3735 | 3744 | } |
| 3736 | 3745 | |
| 3737 | static bool skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) { | |
| 3746 | static bool should_skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) { | |
| 3738 | 3747 | if (g->is_test_build) { |
| 3739 | 3748 | if (fn_entry->is_test) { |
| 3740 | 3749 | return false; |
| ... | ... | @@ -3889,7 +3898,7 @@ static void do_code_gen(CodeGen *g) { |
| 3889 | 3898 | // Generate function prototypes |
| 3890 | 3899 | for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) { |
| 3891 | 3900 | FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i); |
| 3892 | if (skip_fn_codegen(g, fn_table_entry)) { | |
| 3901 | if (should_skip_fn_codegen(g, fn_table_entry)) { | |
| 3893 | 3902 | // huge time saver |
| 3894 | 3903 | LLVMDeleteFunction(fn_table_entry->fn_value); |
| 3895 | 3904 | fn_table_entry->fn_value = nullptr; |
| ... | ... | @@ -3995,7 +4004,7 @@ static void do_code_gen(CodeGen *g) { |
| 3995 | 4004 | // Generate function definitions. |
| 3996 | 4005 | for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { |
| 3997 | 4006 | FnTableEntry *fn_table_entry = g->fn_defs.at(fn_i); |
| 3998 | if (skip_fn_codegen(g, fn_table_entry)) { | |
| 4007 | if (should_skip_fn_codegen(g, fn_table_entry)) { | |
| 3999 | 4008 | // huge time saver |
| 4000 | 4009 | continue; |
| 4001 | 4010 | } |
src/eval.cpp+23-10| ... | ... | @@ -884,9 +884,9 @@ static bool eval_fn_call_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val |
| 884 | 884 | |
| 885 | 885 | int param_count = node->data.fn_call_expr.params.length; |
| 886 | 886 | ConstExprValue *args = allocate<ConstExprValue>(param_count); |
| 887 | for (int i = 0; i < param_count; i += 1) { | |
| 888 | AstNode *param_expr_node = node->data.fn_call_expr.params.at(i); | |
| 889 | ConstExprValue *param_val = &args[i]; | |
| 887 | for (int call_i = 0; call_i < param_count; call_i += 1) { | |
| 888 | AstNode *param_expr_node = node->data.fn_call_expr.params.at(call_i); | |
| 889 | ConstExprValue *param_val = &args[call_i]; | |
| 890 | 890 | if (eval_expr(ef, param_expr_node, param_val)) return true; |
| 891 | 891 | } |
| 892 | 892 | |
| ... | ... | @@ -1291,6 +1291,13 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { |
| 1291 | 1291 | } |
| 1292 | 1292 | |
| 1293 | 1293 | static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val) { |
| 1294 | AstNode *acting_proto_node; | |
| 1295 | if (fn->proto_node->data.fn_proto.generic_proto_node) { | |
| 1296 | acting_proto_node = fn->proto_node->data.fn_proto.generic_proto_node; | |
| 1297 | } else { | |
| 1298 | acting_proto_node = fn->proto_node; | |
| 1299 | } | |
| 1300 | ||
| 1294 | 1301 | EvalFn ef = {0}; |
| 1295 | 1302 | ef.root = efr; |
| 1296 | 1303 | ef.fn = fn; |
| ... | ... | @@ -1300,12 +1307,12 @@ static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args |
| 1300 | 1307 | root_scope->block_context = fn->fn_def_node->data.fn_def.body->block_context; |
| 1301 | 1308 | ef.scope_stack.append(root_scope); |
| 1302 | 1309 | |
| 1303 | int param_count = fn->type_entry->data.fn.fn_type_id.param_count; | |
| 1304 | for (int i = 0; i < param_count; i += 1) { | |
| 1305 | AstNode *decl_param_node = fn->proto_node->data.fn_proto.params.at(i); | |
| 1310 | int param_count = acting_proto_node->data.fn_proto.params.length; | |
| 1311 | for (int proto_i = 0; proto_i < param_count; proto_i += 1) { | |
| 1312 | AstNode *decl_param_node = acting_proto_node->data.fn_proto.params.at(proto_i); | |
| 1306 | 1313 | assert(decl_param_node->type == NodeTypeParamDecl); |
| 1307 | 1314 | |
| 1308 | ConstExprValue *src_const_val = &args[i]; | |
| 1315 | ConstExprValue *src_const_val = &args[proto_i]; | |
| 1309 | 1316 | assert(src_const_val->ok); |
| 1310 | 1317 | |
| 1311 | 1318 | root_scope->vars.add_one(); |
| ... | ... | @@ -1315,7 +1322,6 @@ static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args |
| 1315 | 1322 | } |
| 1316 | 1323 | |
| 1317 | 1324 | return eval_expr(&ef, fn->fn_def_node->data.fn_def.body, out_val); |
| 1318 | ||
| 1319 | 1325 | } |
| 1320 | 1326 | |
| 1321 | 1327 | bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_val, |
| ... | ... | @@ -1329,9 +1335,16 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va |
| 1329 | 1335 | efr.call_node = node; |
| 1330 | 1336 | efr.branch_quota = branch_quota; |
| 1331 | 1337 | |
| 1338 | AstNode *acting_proto_node; | |
| 1339 | if (fn->proto_node->data.fn_proto.generic_proto_node) { | |
| 1340 | acting_proto_node = fn->proto_node->data.fn_proto.generic_proto_node; | |
| 1341 | } else { | |
| 1342 | acting_proto_node = fn->proto_node; | |
| 1343 | } | |
| 1344 | ||
| 1332 | 1345 | int call_param_count = node->data.fn_call_expr.params.length; |
| 1333 | int type_param_count = fn->type_entry->data.fn.fn_type_id.param_count; | |
| 1334 | ConstExprValue *args = allocate<ConstExprValue>(type_param_count); | |
| 1346 | int proto_param_count = acting_proto_node->data.fn_proto.params.length; | |
| 1347 | ConstExprValue *args = allocate<ConstExprValue>(proto_param_count); | |
| 1335 | 1348 | int next_arg_index = 0; |
| 1336 | 1349 | if (struct_node) { |
| 1337 | 1350 | ConstExprValue *struct_val = &get_resolved_expr(struct_node)->const_val; |
src/parser.cpp+45-22| ... | ... | @@ -747,7 +747,7 @@ static void ast_parse_directives(ParseContext *pc, int *token_index, |
| 747 | 747 | } |
| 748 | 748 | |
| 749 | 749 | /* |
| 750 | ParamDecl = option("noalias") option("Symbol" ":") PrefixOpExpression | "..." | |
| 750 | ParamDecl = option("noalias" | "inline") option("Symbol" ":") TypeExpr | "..." | |
| 751 | 751 | */ |
| 752 | 752 | static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { |
| 753 | 753 | Token *token = &pc->tokens->at(*token_index); |
| ... | ... | @@ -763,6 +763,10 @@ static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { |
| 763 | 763 | node->data.param_decl.is_noalias = true; |
| 764 | 764 | *token_index += 1; |
| 765 | 765 | token = &pc->tokens->at(*token_index); |
| 766 | } else if (token->id == TokenIdKeywordInline) { | |
| 767 | node->data.param_decl.is_inline = true; | |
| 768 | *token_index += 1; | |
| 769 | token = &pc->tokens->at(*token_index); | |
| 766 | 770 | } |
| 767 | 771 | |
| 768 | 772 | buf_resize(&node->data.param_decl.name, 0); |
| ... | ... | @@ -2472,7 +2476,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato |
| 2472 | 2476 | } |
| 2473 | 2477 | |
| 2474 | 2478 | /* |
| 2475 | FnProto = "fn" option("Symbol") option(ParamDeclList) ParamDeclList option("->" TypeExpr) | |
| 2479 | FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr) | |
| 2476 | 2480 | */ |
| 2477 | 2481 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory, |
| 2478 | 2482 | ZigList<AstNode*> *directives, VisibMod visib_mod) |
| ... | ... | @@ -2502,17 +2506,6 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand |
| 2502 | 2506 | |
| 2503 | 2507 | ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args); |
| 2504 | 2508 | |
| 2505 | Token *maybe_lparen = &pc->tokens->at(*token_index); | |
| 2506 | if (maybe_lparen->id == TokenIdLParen) { | |
| 2507 | for (int i = 0; i < node->data.fn_proto.params.length; i += 1) { | |
| 2508 | node->data.fn_proto.generic_params.append(node->data.fn_proto.params.at(i)); | |
| 2509 | } | |
| 2510 | node->data.fn_proto.generic_params_is_var_args = node->data.fn_proto.is_var_args; | |
| 2511 | ||
| 2512 | node->data.fn_proto.params.resize(0); | |
| 2513 | ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args); | |
| 2514 | } | |
| 2515 | ||
| 2516 | 2509 | Token *next_token = &pc->tokens->at(*token_index); |
| 2517 | 2510 | if (next_token->id == TokenIdArrow) { |
| 2518 | 2511 | *token_index += 1; |
| ... | ... | @@ -2931,7 +2924,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont |
| 2931 | 2924 | case NodeTypeFnProto: |
| 2932 | 2925 | visit_field(&node->data.fn_proto.return_type, visit, context); |
| 2933 | 2926 | visit_node_list(node->data.fn_proto.top_level_decl.directives, visit, context); |
| 2934 | visit_node_list(&node->data.fn_proto.generic_params, visit, context); | |
| 2935 | 2927 | visit_node_list(&node->data.fn_proto.params, visit, context); |
| 2936 | 2928 | break; |
| 2937 | 2929 | case NodeTypeFnDef: |
| ... | ... | @@ -3123,6 +3115,22 @@ static void clone_subtree_list(ZigList<AstNode *> *dest, ZigList<AstNode *> *src |
| 3123 | 3115 | } |
| 3124 | 3116 | } |
| 3125 | 3117 | |
| 3118 | static void clone_subtree_list_omit_inline_params(ZigList<AstNode *> *dest, ZigList<AstNode *> *src, | |
| 3119 | uint32_t *next_node_index) | |
| 3120 | { | |
| 3121 | memset(dest, 0, sizeof(ZigList<AstNode *>)); | |
| 3122 | dest->ensure_capacity(src->length); | |
| 3123 | for (int i = 0; i < src->length; i += 1) { | |
| 3124 | AstNode *src_node = src->at(i); | |
| 3125 | assert(src_node->type == NodeTypeParamDecl); | |
| 3126 | if (src_node->data.param_decl.is_inline) { | |
| 3127 | continue; | |
| 3128 | } | |
| 3129 | dest->append(ast_clone_subtree(src_node, next_node_index)); | |
| 3130 | dest->last()->parent_field = &dest->last(); | |
| 3131 | } | |
| 3132 | } | |
| 3133 | ||
| 3126 | 3134 | static void clone_subtree_list_ptr(ZigList<AstNode *> **dest_ptr, ZigList<AstNode *> *src, |
| 3127 | 3135 | uint32_t *next_node_index) |
| 3128 | 3136 | { |
| ... | ... | @@ -3133,20 +3141,26 @@ static void clone_subtree_list_ptr(ZigList<AstNode *> **dest_ptr, ZigList<AstNod |
| 3133 | 3141 | } |
| 3134 | 3142 | } |
| 3135 | 3143 | |
| 3136 | static void clone_subtree_field(AstNode **dest, AstNode *src, uint32_t *next_node_index) { | |
| 3144 | static void clone_subtree_field_special(AstNode **dest, AstNode *src, uint32_t *next_node_index, | |
| 3145 | enum AstCloneSpecial special) | |
| 3146 | { | |
| 3137 | 3147 | if (src) { |
| 3138 | *dest = ast_clone_subtree(src, next_node_index); | |
| 3148 | *dest = ast_clone_subtree_special(src, next_node_index, special); | |
| 3139 | 3149 | (*dest)->parent_field = dest; |
| 3140 | 3150 | } else { |
| 3141 | 3151 | *dest = nullptr; |
| 3142 | 3152 | } |
| 3143 | 3153 | } |
| 3144 | 3154 | |
| 3155 | static void clone_subtree_field(AstNode **dest, AstNode *src, uint32_t *next_node_index) { | |
| 3156 | return clone_subtree_field_special(dest, src, next_node_index, AstCloneSpecialNone); | |
| 3157 | } | |
| 3158 | ||
| 3145 | 3159 | static void clone_subtree_tld(TopLevelDecl *dest, TopLevelDecl *src, uint32_t *next_node_index) { |
| 3146 | 3160 | clone_subtree_list_ptr(&dest->directives, src->directives, next_node_index); |
| 3147 | 3161 | } |
| 3148 | 3162 | |
| 3149 | AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { | |
| 3163 | AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, enum AstCloneSpecial special) { | |
| 3150 | 3164 | AstNode *new_node = allocate_nonzero<AstNode>(1); |
| 3151 | 3165 | memcpy(new_node, old_node, sizeof(AstNode)); |
| 3152 | 3166 | new_node->create_index = *next_node_index; |
| ... | ... | @@ -3163,14 +3177,19 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { |
| 3163 | 3177 | next_node_index); |
| 3164 | 3178 | clone_subtree_field(&new_node->data.fn_proto.return_type, old_node->data.fn_proto.return_type, |
| 3165 | 3179 | next_node_index); |
| 3166 | clone_subtree_list(&new_node->data.fn_proto.generic_params, | |
| 3167 | &old_node->data.fn_proto.generic_params, next_node_index); | |
| 3168 | clone_subtree_list(&new_node->data.fn_proto.params, &old_node->data.fn_proto.params, | |
| 3169 | next_node_index); | |
| 3180 | ||
| 3181 | if (special == AstCloneSpecialOmitInlineParams) { | |
| 3182 | clone_subtree_list_omit_inline_params(&new_node->data.fn_proto.params, &old_node->data.fn_proto.params, | |
| 3183 | next_node_index); | |
| 3184 | } else { | |
| 3185 | clone_subtree_list(&new_node->data.fn_proto.params, &old_node->data.fn_proto.params, | |
| 3186 | next_node_index); | |
| 3187 | } | |
| 3170 | 3188 | |
| 3171 | 3189 | break; |
| 3172 | 3190 | case NodeTypeFnDef: |
| 3173 | clone_subtree_field(&new_node->data.fn_def.fn_proto, old_node->data.fn_def.fn_proto, next_node_index); | |
| 3191 | clone_subtree_field_special(&new_node->data.fn_def.fn_proto, old_node->data.fn_def.fn_proto, | |
| 3192 | next_node_index, special); | |
| 3174 | 3193 | new_node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = new_node; |
| 3175 | 3194 | clone_subtree_field(&new_node->data.fn_def.body, old_node->data.fn_def.body, next_node_index); |
| 3176 | 3195 | break; |
| ... | ... | @@ -3354,3 +3373,7 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { |
| 3354 | 3373 | |
| 3355 | 3374 | return new_node; |
| 3356 | 3375 | } |
| 3376 | ||
| 3377 | AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { | |
| 3378 | return ast_clone_subtree_special(old_node, next_node_index, AstCloneSpecialNone); | |
| 3379 | } |
src/parser.hpp+7| ... | ... | @@ -25,6 +25,13 @@ void ast_print(AstNode *node, int indent); |
| 25 | 25 | void normalize_parent_ptrs(AstNode *node); |
| 26 | 26 | |
| 27 | 27 | AstNode *ast_clone_subtree(AstNode *node, uint32_t *next_node_index); |
| 28 | ||
| 29 | enum AstCloneSpecial { | |
| 30 | AstCloneSpecialNone, | |
| 31 | AstCloneSpecialOmitInlineParams, | |
| 32 | }; | |
| 33 | AstNode *ast_clone_subtree_special(AstNode *node, uint32_t *next_node_index, enum AstCloneSpecial special); | |
| 34 | ||
| 28 | 35 | void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context); |
| 29 | 36 | |
| 30 | 37 | #endif |
std/hash_map.zig+10-9| ... | ... | @@ -7,7 +7,7 @@ const want_modification_safety = !@compile_var("is_release"); |
| 7 | 7 | const debug_u32 = if (want_modification_safety) u32 else void; |
| 8 | 8 | |
| 9 | 9 | /* |
| 10 | pub fn HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool) { | |
| 10 | pub inline fn HashMap(inline K: type, inline V: type, inline hash: fn(key: K)->u32, inline eql: fn(a: K, b: K)->bool) { | |
| 11 | 11 | SmallHashMap(K, V, hash, eql, 8); |
| 12 | 12 | } |
| 13 | 13 | */ |
| ... | ... | @@ -70,7 +70,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 70 | 70 | |
| 71 | 71 | pub fn deinit(hm: &Self) { |
| 72 | 72 | if (hm.entries.ptr != &hm.prealloc_entries[0]) { |
| 73 | hm.allocator.free(hm.allocator, ([]u8)(hm.entries)); | |
| 73 | hm.allocator.free(Entry, hm.entries); | |
| 74 | 74 | } |
| 75 | 75 | } |
| 76 | 76 | |
| ... | ... | @@ -103,7 +103,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 103 | 103 | } |
| 104 | 104 | } |
| 105 | 105 | if (old_entries.ptr != &hm.prealloc_entries[0]) { |
| 106 | hm.allocator.free(hm.allocator, ([]u8)(old_entries)); | |
| 106 | hm.allocator.free(Entry, old_entries); | |
| 107 | 107 | } |
| 108 | 108 | } |
| 109 | 109 | |
| ... | ... | @@ -152,7 +152,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 152 | 152 | } |
| 153 | 153 | |
| 154 | 154 | fn init_capacity(hm: &Self, capacity: isize) -> %void { |
| 155 | hm.entries = ([]Entry)(%return hm.allocator.alloc(hm.allocator, capacity * @sizeof(Entry))); | |
| 155 | hm.entries = %return hm.allocator.alloc(Entry, capacity); | |
| 156 | 156 | hm.size = 0; |
| 157 | 157 | hm.max_distance_from_start_index = 0; |
| 158 | 158 | for (hm.entries) |*entry| { |
| ... | ... | @@ -180,7 +180,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 180 | 180 | if (entry.distance_from_start_index < distance_from_start_index) { |
| 181 | 181 | // robin hood to the rescue |
| 182 | 182 | const tmp = *entry; |
| 183 | hm.max_distance_from_start_index = math.max(isize)( | |
| 183 | hm.max_distance_from_start_index = math.max(isize, | |
| 184 | 184 | hm.max_distance_from_start_index, distance_from_start_index); |
| 185 | 185 | *entry = Entry { |
| 186 | 186 | .used = true, |
| ... | ... | @@ -201,7 +201,8 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 201 | 201 | hm.size += 1; |
| 202 | 202 | } |
| 203 | 203 | |
| 204 | hm.max_distance_from_start_index = math.max(isize)(distance_from_start_index, hm.max_distance_from_start_index); | |
| 204 | hm.max_distance_from_start_index = math.max(isize, distance_from_start_index, | |
| 205 | hm.max_distance_from_start_index); | |
| 205 | 206 | *entry = Entry { |
| 206 | 207 | .used = true, |
| 207 | 208 | .distance_from_start_index = distance_from_start_index, |
| ... | ... | @@ -231,9 +232,9 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 231 | 232 | } |
| 232 | 233 | |
| 233 | 234 | var global_allocator = Allocator { |
| 234 | .alloc = global_alloc, | |
| 235 | .realloc = global_realloc, | |
| 236 | .free = global_free, | |
| 235 | .alloc_fn = global_alloc, | |
| 236 | .realloc_fn = global_realloc, | |
| 237 | .free_fn = global_free, | |
| 237 | 238 | .context = null, |
| 238 | 239 | }; |
| 239 | 240 |
std/io.zig+22-32| ... | ... | @@ -69,7 +69,7 @@ pub struct OutStream { |
| 69 | 69 | const dest_space_left = os.buffer.len - os.index; |
| 70 | 70 | |
| 71 | 71 | while (src_bytes_left > 0) { |
| 72 | const copy_amt = math.min(isize)(dest_space_left, src_bytes_left); | |
| 72 | const copy_amt = math.min(isize, dest_space_left, src_bytes_left); | |
| 73 | 73 | @memcpy(&os.buffer[os.index], &bytes[src_index], copy_amt); |
| 74 | 74 | os.index += copy_amt; |
| 75 | 75 | if (os.index == os.buffer.len) { |
| ... | ... | @@ -208,59 +208,47 @@ pub struct InStream { |
| 208 | 208 | } |
| 209 | 209 | } |
| 210 | 210 | |
| 211 | pub error InvalidChar; | |
| 212 | pub error Overflow; | |
| 213 | ||
| 214 | pub fn parse_unsigned(T: type)(buf: []u8, radix: u8) -> %T { | |
| 211 | pub fn parse_unsigned(inline T: type, buf: []u8, radix: u8) -> %T { | |
| 215 | 212 | var x: T = 0; |
| 216 | 213 | |
| 217 | 214 | for (buf) |c| { |
| 218 | const digit = char_to_digit(c); | |
| 219 | ||
| 220 | if (digit >= radix) { | |
| 221 | return error.InvalidChar; | |
| 222 | } | |
| 223 | ||
| 224 | // x *= radix | |
| 225 | if (@mul_with_overflow(T, x, radix, &x)) { | |
| 226 | return error.Overflow; | |
| 227 | } | |
| 228 | ||
| 229 | // x += digit | |
| 230 | if (@add_with_overflow(T, x, digit, &x)) { | |
| 231 | return error.Overflow; | |
| 232 | } | |
| 215 | const digit = %return char_to_digit(c, radix); | |
| 216 | x = %return math.mul_overflow(T, x, radix); | |
| 217 | x = %return math.add_overflow(T, x, digit); | |
| 233 | 218 | } |
| 234 | 219 | |
| 235 | 220 | return x; |
| 236 | 221 | } |
| 237 | 222 | |
| 238 | fn char_to_digit(c: u8) -> u8 { | |
| 239 | // TODO use switch with range | |
| 240 | if ('0' <= c && c <= '9') { | |
| 223 | pub error InvalidChar; | |
| 224 | fn char_to_digit(c: u8, radix: u8) -> %u8 { | |
| 225 | const value = if ('0' <= c && c <= '9') { | |
| 241 | 226 | c - '0' |
| 242 | 227 | } else if ('A' <= c && c <= 'Z') { |
| 243 | 228 | c - 'A' + 10 |
| 244 | 229 | } else if ('a' <= c && c <= 'z') { |
| 245 | 230 | c - 'a' + 10 |
| 246 | 231 | } else { |
| 247 | @max_value(u8) | |
| 248 | } | |
| 232 | return error.InvalidChar; | |
| 233 | }; | |
| 234 | return if (value >= radix) error.InvalidChar else value; | |
| 249 | 235 | } |
| 250 | 236 | |
| 251 | pub fn buf_print_signed(T: type)(out_buf: []u8, x: T) -> isize { | |
| 237 | pub fn buf_print_signed(inline T: type, out_buf: []u8, x: T) -> isize { | |
| 252 | 238 | const uint = @int_type(false, T.bit_count, false); |
| 253 | 239 | if (x < 0) { |
| 254 | 240 | out_buf[0] = '-'; |
| 255 | return 1 + buf_print_unsigned(uint)(out_buf[1...], uint(-(x + 1)) + 1); | |
| 241 | return 1 + buf_print_unsigned(uint, out_buf[1...], uint(-(x + 1)) + 1); | |
| 256 | 242 | } else { |
| 257 | return buf_print_unsigned(uint)(out_buf, uint(x)); | |
| 243 | return buf_print_unsigned(uint, out_buf, uint(x)); | |
| 258 | 244 | } |
| 259 | 245 | } |
| 260 | 246 | |
| 261 | pub const buf_print_i64 = buf_print_signed(i64); | |
| 247 | pub fn buf_print_i64(out_buf: []u8, x: i64) -> isize { | |
| 248 | buf_print_signed(i64, out_buf, x) | |
| 249 | } | |
| 262 | 250 | |
| 263 | pub fn buf_print_unsigned(T: type)(out_buf: []u8, x: T) -> isize { | |
| 251 | pub fn buf_print_unsigned(inline T: type, out_buf: []u8, x: T) -> isize { | |
| 264 | 252 | var buf: [max_u64_base10_digits]u8 = undefined; |
| 265 | 253 | var a = x; |
| 266 | 254 | var index: isize = buf.len; |
| ... | ... | @@ -281,7 +269,9 @@ pub fn buf_print_unsigned(T: type)(out_buf: []u8, x: T) -> isize { |
| 281 | 269 | return len; |
| 282 | 270 | } |
| 283 | 271 | |
| 284 | pub const buf_print_u64 = buf_print_unsigned(u64); | |
| 272 | pub fn buf_print_u64(out_buf: []u8, x: u64) -> isize { | |
| 273 | buf_print_unsigned(u64, out_buf, x) | |
| 274 | } | |
| 285 | 275 | |
| 286 | 276 | pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize { |
| 287 | 277 | const numExpBits = 11; |
| ... | ... | @@ -409,7 +399,7 @@ pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize { |
| 409 | 399 | |
| 410 | 400 | #attribute("test") |
| 411 | 401 | fn parse_u64_digit_too_big() { |
| 412 | parse_unsigned(u64)("123a", 10) %% |err| { | |
| 402 | parse_unsigned(u64, "123a", 10) %% |err| { | |
| 413 | 403 | if (err == error.InvalidChar) return; |
| 414 | 404 | unreachable{}; |
| 415 | 405 | }; |
std/list.zig+14-15| ... | ... | @@ -2,59 +2,58 @@ const assert = @import("debug.zig").assert; |
| 2 | 2 | const mem = @import("mem.zig"); |
| 3 | 3 | const Allocator = mem.Allocator; |
| 4 | 4 | |
| 5 | /* | |
| 6 | pub fn List(T: type) -> type { | |
| 5 | pub inline fn List(inline T: type) -> type { | |
| 7 | 6 | SmallList(T, 8) |
| 8 | 7 | } |
| 9 | */ | |
| 10 | 8 | |
| 11 | 9 | pub struct SmallList(T: type, STATIC_SIZE: isize) { |
| 10 | const Self = SmallList(T, STATIC_SIZE); | |
| 11 | ||
| 12 | 12 | items: []T, |
| 13 | 13 | length: isize, |
| 14 | 14 | prealloc_items: [STATIC_SIZE]T, |
| 15 | 15 | allocator: &Allocator, |
| 16 | 16 | |
| 17 | pub fn init(l: &SmallList(T, STATIC_SIZE), allocator: &Allocator) { | |
| 17 | pub fn init(l: &Self, allocator: &Allocator) { | |
| 18 | 18 | l.items = l.prealloc_items[0...]; |
| 19 | 19 | l.length = 0; |
| 20 | 20 | l.allocator = allocator; |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | pub fn deinit(l: &SmallList(T, STATIC_SIZE)) { | |
| 23 | pub fn deinit(l: &Self) { | |
| 24 | 24 | if (l.items.ptr != &l.prealloc_items[0]) { |
| 25 | l.allocator.free(l.allocator, ([]u8)(l.items)); | |
| 25 | l.allocator.free(T, l.items); | |
| 26 | 26 | } |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | pub fn append(l: &SmallList(T, STATIC_SIZE), item: T) -> %void { | |
| 29 | pub fn append(l: &Self, item: T) -> %void { | |
| 30 | 30 | const new_length = l.length + 1; |
| 31 | 31 | %return l.ensure_capacity(new_length); |
| 32 | 32 | l.items[l.length] = item; |
| 33 | 33 | l.length = new_length; |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | pub fn ensure_capacity(l: &SmallList(T, STATIC_SIZE), new_capacity: isize) -> %void { | |
| 36 | pub fn ensure_capacity(l: &Self, new_capacity: isize) -> %void { | |
| 37 | 37 | const old_capacity = l.items.len; |
| 38 | 38 | var better_capacity = old_capacity; |
| 39 | 39 | while (better_capacity < new_capacity) { |
| 40 | 40 | better_capacity *= 2; |
| 41 | 41 | } |
| 42 | 42 | if (better_capacity != old_capacity) { |
| 43 | const alloc_bytes = better_capacity * @sizeof(T); | |
| 44 | 43 | if (l.items.ptr == &l.prealloc_items[0]) { |
| 45 | l.items = ([]T)(%return l.allocator.alloc(l.allocator, alloc_bytes)); | |
| 46 | @memcpy(l.items.ptr, &l.prealloc_items[0], old_capacity * @sizeof(T)); | |
| 44 | l.items = %return l.allocator.alloc(T, better_capacity); | |
| 45 | mem.copy(T, l.items, l.prealloc_items[0...old_capacity]); | |
| 47 | 46 | } else { |
| 48 | l.items = ([]T)(%return l.allocator.realloc(l.allocator, ([]u8)(l.items), alloc_bytes)); | |
| 47 | l.items = %return l.allocator.realloc(T, l.items, better_capacity); | |
| 49 | 48 | } |
| 50 | 49 | } |
| 51 | 50 | } |
| 52 | 51 | } |
| 53 | 52 | |
| 54 | 53 | var global_allocator = Allocator { |
| 55 | .alloc = global_alloc, | |
| 56 | .realloc = global_realloc, | |
| 57 | .free = global_free, | |
| 54 | .alloc_fn = global_alloc, | |
| 55 | .realloc_fn = global_realloc, | |
| 56 | .free_fn = global_free, | |
| 58 | 57 | .context = null, |
| 59 | 58 | }; |
| 60 | 59 |
std/math.zig+16-2| ... | ... | @@ -26,10 +26,24 @@ pub fn f64_is_inf(f: f64) -> bool { |
| 26 | 26 | f == f64_get_neg_inf() || f == f64_get_pos_inf() |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | pub fn min(T: type)(x: T, y: T) -> T { | |
| 29 | pub fn min(inline T: type, x: T, y: T) -> T { | |
| 30 | 30 | if (x < y) x else y |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | pub fn max(T: type)(x: T, y: T) -> T { | |
| 33 | pub fn max(inline T: type, x: T, y: T) -> T { | |
| 34 | 34 | if (x > y) x else y |
| 35 | 35 | } |
| 36 | ||
| 37 | pub error Overflow; | |
| 38 | pub fn mul_overflow(inline T: type, a: T, b: T) -> %T { | |
| 39 | var answer: T = undefined; | |
| 40 | if (@mul_with_overflow(T, a, b, &answer)) error.Overflow else answer | |
| 41 | } | |
| 42 | pub fn add_overflow(inline T: type, a: T, b: T) -> %T { | |
| 43 | var answer: T = undefined; | |
| 44 | if (@add_with_overflow(T, a, b, &answer)) error.Overflow else answer | |
| 45 | } | |
| 46 | pub fn sub_overflow(inline T: type, a: T, b: T) -> %T { | |
| 47 | var answer: T = undefined; | |
| 48 | if (@sub_with_overflow(T, a, b, &answer)) error.Overflow else answer | |
| 49 | } |
std/mem.zig+32-4| ... | ... | @@ -1,18 +1,46 @@ |
| 1 | 1 | const assert = @import("debug.zig").assert; |
| 2 | const math = @import("math.zig"); | |
| 3 | const os = @import("os.zig"); | |
| 4 | const io = @import("io.zig"); | |
| 2 | 5 | |
| 3 | 6 | pub error NoMem; |
| 4 | 7 | |
| 5 | 8 | pub type Context = u8; |
| 6 | 9 | pub struct Allocator { |
| 7 | alloc: fn (self: &Allocator, n: isize) -> %[]u8, | |
| 8 | realloc: fn (self: &Allocator, old_mem: []u8, new_size: isize) -> %[]u8, | |
| 9 | free: fn (self: &Allocator, mem: []u8), | |
| 10 | alloc_fn: fn (self: &Allocator, n: isize) -> %[]u8, | |
| 11 | realloc_fn: fn (self: &Allocator, old_mem: []u8, new_size: isize) -> %[]u8, | |
| 12 | free_fn: fn (self: &Allocator, mem: []u8), | |
| 10 | 13 | context: ?&Context, |
| 14 | ||
| 15 | /// Aborts the program if an allocation fails. | |
| 16 | fn checked_alloc(self: &Allocator, inline T: type, n: isize) -> []T { | |
| 17 | alloc(self, T, n) %% |err| { | |
| 18 | // TODO var args printf | |
| 19 | %%io.stderr.write("allocation failure: "); | |
| 20 | %%io.stderr.write(@err_name(err)); | |
| 21 | %%io.stderr.printf("\n"); | |
| 22 | os.abort() | |
| 23 | } | |
| 24 | } | |
| 25 | ||
| 26 | fn alloc(self: &Allocator, inline T: type, n: isize) -> %[]T { | |
| 27 | const byte_count = %return math.mul_overflow(isize, @sizeof(T), n); | |
| 28 | ([]T)(%return self.alloc_fn(self, byte_count)) | |
| 29 | } | |
| 30 | ||
| 31 | fn realloc(self: &Allocator, inline T: type, old_mem: []T, n: isize) -> %[]T { | |
| 32 | const byte_count = %return math.mul_overflow(isize, @sizeof(T), n); | |
| 33 | ([]T)(%return self.realloc_fn(self, ([]u8)(old_mem), byte_count)) | |
| 34 | } | |
| 35 | ||
| 36 | fn free(self: &Allocator, inline T: type, mem: []T) { | |
| 37 | self.free_fn(self, ([]u8)(mem)); | |
| 38 | } | |
| 11 | 39 | } |
| 12 | 40 | |
| 13 | 41 | /// Copy all of source into dest at position 0. |
| 14 | 42 | /// dest.len must be >= source.len. |
| 15 | pub fn copy(T)(dest: []T, source: []T) { | |
| 43 | pub fn copy(inline T: type, dest: []T, source: []T) { | |
| 16 | 44 | assert(dest.len >= source.len); |
| 17 | 45 | @memcpy(dest.ptr, source.ptr, @sizeof(T) * source.len); |
| 18 | 46 | } |
std/net.zig+6-7| ... | ... | @@ -99,14 +99,14 @@ pub fn connect_addr(addr: &Address, port: u16) -> %Connection { |
| 99 | 99 | const connect_ret = if (addr.family == linux.AF_INET) { |
| 100 | 100 | var os_addr: linux.sockaddr_in = undefined; |
| 101 | 101 | os_addr.family = addr.family; |
| 102 | os_addr.port = host_to_be(u16)(port); | |
| 102 | os_addr.port = swap_if_little_endian(u16, port); | |
| 103 | 103 | @memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4); |
| 104 | 104 | @memset(&os_addr.zero, 0, @sizeof(@typeof(os_addr.zero))); |
| 105 | 105 | linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeof(linux.sockaddr_in)) |
| 106 | 106 | } else if (addr.family == linux.AF_INET6) { |
| 107 | 107 | var os_addr: linux.sockaddr_in6 = undefined; |
| 108 | 108 | os_addr.family = addr.family; |
| 109 | os_addr.port = host_to_be(u16)(port); | |
| 109 | os_addr.port = swap_if_little_endian(u16, port); | |
| 110 | 110 | os_addr.flowinfo = 0; |
| 111 | 111 | os_addr.scope_id = addr.scope_id; |
| 112 | 112 | @memcpy(&os_addr.addr[0], &addr.addr[0], 16); |
| ... | ... | @@ -319,7 +319,7 @@ fn parse_ip4(buf: []const u8) -> %u32 { |
| 319 | 319 | |
| 320 | 320 | #attribute("test") |
| 321 | 321 | fn test_parse_ip4() { |
| 322 | assert(%%parse_ip4("127.0.0.1") == be_to_host(u32)(0x7f000001)); | |
| 322 | assert(%%parse_ip4("127.0.0.1") == swap_if_little_endian(u32, 0x7f000001)); | |
| 323 | 323 | switch (parse_ip4("256.0.0.1")) { Overflow => {}, else => unreachable {}, } |
| 324 | 324 | switch (parse_ip4("x.0.0.1")) { InvalidChar => {}, else => unreachable {}, } |
| 325 | 325 | switch (parse_ip4("127.0.0.1.1")) { JunkAtEnd => {}, else => unreachable {}, } |
| ... | ... | @@ -352,12 +352,11 @@ fn test_lookup_simple_ip() { |
| 352 | 352 | } |
| 353 | 353 | } |
| 354 | 354 | |
| 355 | const be_to_host = host_to_be; | |
| 356 | fn host_to_be(T: type)(x: T) -> T { | |
| 357 | if (@compile_var("is_big_endian")) x else endian_swap(T)(x) | |
| 355 | fn swap_if_little_endian(inline T: type, x: T) -> T { | |
| 356 | if (@compile_var("is_big_endian")) x else endian_swap(T, x) | |
| 358 | 357 | } |
| 359 | 358 | |
| 360 | fn endian_swap(T: type)(x: T) -> T { | |
| 359 | fn endian_swap(inline T: type, x: T) -> T { | |
| 361 | 360 | const x_slice = ([]u8)((&const x)[0...1]); |
| 362 | 361 | var result: T = undefined; |
| 363 | 362 | const result_slice = ([]u8)((&result)[0...1]); |
std/str.zig+4-2| ... | ... | @@ -1,8 +1,10 @@ |
| 1 | 1 | const assert = @import("debug.zig").assert; |
| 2 | 2 | |
| 3 | pub const eql = slice_eql(u8); | |
| 3 | pub fn eql(a: []const u8, b: []const u8) -> bool { | |
| 4 | slice_eql(u8, a, b) | |
| 5 | } | |
| 4 | 6 | |
| 5 | pub fn slice_eql(T: type)(a: []const T, b: []const T) -> bool { | |
| 7 | pub fn slice_eql(inline T: type, a: []const T, b: []const T) -> bool { | |
| 6 | 8 | if (a.len != b.len) return false; |
| 7 | 9 | for (a) |item, index| { |
| 8 | 10 | if (b[index] != item) return false; |
std/test_runner.zig+1| ... | ... | @@ -9,6 +9,7 @@ extern var zig_test_fn_list: []TestFn; |
| 9 | 9 | |
| 10 | 10 | pub fn run_tests() -> %void { |
| 11 | 11 | for (zig_test_fn_list) |test_fn, i| { |
| 12 | // TODO: print var args | |
| 12 | 13 | %%io.stderr.write("Test "); |
| 13 | 14 | %%io.stderr.print_i64(i + 1); |
| 14 | 15 | %%io.stderr.write("/"); |
test/run_tests.cpp+24-3| ... | ... | @@ -1181,11 +1181,11 @@ const invalid = foo > foo; |
| 1181 | 1181 | )SOURCE", 1, ".tmp_source.zig:3:21: error: operator not allowed for type 'fn()'"); |
| 1182 | 1182 | |
| 1183 | 1183 | add_compile_fail_case("generic function instance with non-constant expression", R"SOURCE( |
| 1184 | fn foo(x: i32)(y: i32) -> i32 { return x + y; } | |
| 1184 | fn foo(inline x: i32, y: i32) -> i32 { return x + y; } | |
| 1185 | 1185 | fn test1(a: i32, b: i32) -> i32 { |
| 1186 | return foo(a)(b); | |
| 1186 | return foo(a, b); | |
| 1187 | 1187 | } |
| 1188 | )SOURCE", 1, ".tmp_source.zig:4:16: error: unable to evaluate constant expression"); | |
| 1188 | )SOURCE", 1, ".tmp_source.zig:4:16: error: unable to evaluate constant expression for inline parameter"); | |
| 1189 | 1189 | |
| 1190 | 1190 | add_compile_fail_case("goto jumping into block", R"SOURCE( |
| 1191 | 1191 | fn f() { |
| ... | ... | @@ -1406,6 +1406,27 @@ fn f() { |
| 1406 | 1406 | } |
| 1407 | 1407 | )SOURCE", 1, ".tmp_source.zig:3:13: error: unable to evaluate constant expression"); |
| 1408 | 1408 | |
| 1409 | add_compile_fail_case("export function with inline parameter", R"SOURCE( | |
| 1410 | export fn foo(inline x: i32, y: i32) -> i32{ | |
| 1411 | x + y | |
| 1412 | } | |
| 1413 | )SOURCE", 1, ".tmp_source.zig:2:15: error: inline parameter not allowed in extern function"); | |
| 1414 | ||
| 1415 | add_compile_fail_case("extern function with inline parameter", R"SOURCE( | |
| 1416 | extern fn foo(inline x: i32, y: i32) -> i32; | |
| 1417 | fn f() -> i32 { | |
| 1418 | foo(1, 2) | |
| 1419 | } | |
| 1420 | )SOURCE", 1, ".tmp_source.zig:2:15: error: inline parameter not allowed in extern function"); | |
| 1421 | ||
| 1422 | /* TODO | |
| 1423 | add_compile_fail_case("inline export function", R"SOURCE( | |
| 1424 | export inline fn foo(x: i32, y: i32) -> i32{ | |
| 1425 | x + y | |
| 1426 | } | |
| 1427 | )SOURCE", 1, ".tmp_source.zig:2:1: error: extern functions cannot be inline"); | |
| 1428 | */ | |
| 1429 | ||
| 1409 | 1430 | } |
| 1410 | 1431 | |
| 1411 | 1432 | ////////////////////////////////////////////////////////////////////////////// |
test/self_hosted.zig+15-20| ... | ... | @@ -712,17 +712,17 @@ three)"; |
| 712 | 712 | |
| 713 | 713 | #attribute("test") |
| 714 | 714 | fn simple_generic_fn() { |
| 715 | assert(max(i32)(3, -1) == 3); | |
| 716 | assert(max(f32)(0.123, 0.456) == 0.456); | |
| 717 | assert(add(2)(3) == 5); | |
| 715 | assert(max(i32, 3, -1) == 3); | |
| 716 | assert(max(f32, 0.123, 0.456) == 0.456); | |
| 717 | assert(add(2, 3) == 5); | |
| 718 | 718 | } |
| 719 | 719 | |
| 720 | fn max(T: type)(a: T, b: T) -> T { | |
| 720 | fn max(inline T: type, a: T, b: T) -> T { | |
| 721 | 721 | return if (a > b) a else b; |
| 722 | 722 | } |
| 723 | 723 | |
| 724 | fn add(a: i32)(b: i32) -> i32 { | |
| 725 | return a + b; | |
| 724 | fn add(inline a: i32, b: i32) -> i32 { | |
| 725 | return @const_eval(a) + b; | |
| 726 | 726 | } |
| 727 | 727 | |
| 728 | 728 | |
| ... | ... | @@ -734,23 +734,18 @@ fn constant_equal_function_pointers() { |
| 734 | 734 | |
| 735 | 735 | fn empty_fn() {} |
| 736 | 736 | |
| 737 | #attribute("test") | |
| 738 | fn generic_function_equality() { | |
| 739 | assert(max(i32) == max(i32)); | |
| 740 | } | |
| 741 | ||
| 742 | 737 | |
| 743 | 738 | #attribute("test") |
| 744 | 739 | fn generic_malloc_free() { |
| 745 | const a = %%mem_alloc(u8)(10); | |
| 746 | mem_free(u8)(a); | |
| 740 | const a = %%mem_alloc(u8, 10); | |
| 741 | mem_free(u8, a); | |
| 747 | 742 | } |
| 748 | 743 | const some_mem : [100]u8 = undefined; |
| 749 | 744 | #static_eval_enable(false) |
| 750 | fn mem_alloc(T: type)(n: isize) -> %[]T { | |
| 745 | fn mem_alloc(inline T: type, n: isize) -> %[]T { | |
| 751 | 746 | return (&T)(&some_mem[0])[0...n]; |
| 752 | 747 | } |
| 753 | fn mem_free(T: type)(mem: []T) { } | |
| 748 | fn mem_free(inline T: type, mem: []T) { } | |
| 754 | 749 | |
| 755 | 750 | |
| 756 | 751 | #attribute("test") |
| ... | ... | @@ -982,11 +977,11 @@ pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 { |
| 982 | 977 | |
| 983 | 978 | #attribute("test") |
| 984 | 979 | fn generic_fn_with_implicit_cast() { |
| 985 | assert(get_first_byte(u8)([]u8 {13}) == 13); | |
| 986 | assert(get_first_byte(u16)([]u16 {0, 13}) == 0); | |
| 980 | assert(get_first_byte(u8, []u8 {13}) == 13); | |
| 981 | assert(get_first_byte(u16, []u16 {0, 13}) == 0); | |
| 987 | 982 | } |
| 988 | 983 | fn get_byte(ptr: ?&u8) -> u8 {*??ptr} |
| 989 | fn get_first_byte(T: type)(mem: []T) -> u8 { | |
| 984 | fn get_first_byte(inline T: type, mem: []T) -> u8 { | |
| 990 | 985 | get_byte((&u8)(&mem[0])) |
| 991 | 986 | } |
| 992 | 987 | |
| ... | ... | @@ -1651,9 +1646,9 @@ struct GenericDataThing(count: isize) { |
| 1651 | 1646 | |
| 1652 | 1647 | #attribute("test") |
| 1653 | 1648 | fn use_generic_param_in_generic_param() { |
| 1654 | assert(a_generic_fn(i32, 3)(4) == 7); | |
| 1649 | assert(a_generic_fn(i32, 3, 4) == 7); | |
| 1655 | 1650 | } |
| 1656 | fn a_generic_fn(T: type, a: T)(b: T) -> T { | |
| 1651 | fn a_generic_fn(inline T: type, inline a: T, b: T) -> T { | |
| 1657 | 1652 | return a + b; |
| 1658 | 1653 | } |
| 1659 | 1654 |