| 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,7 +25,7 @@ UseDecl = "use" Expression ";" |
| 25 | 25 | ||
| 26 | ExternDecl = "extern" (FnProto | VariableDeclaration) ";" | 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 | Directive = "#" "Symbol" "(" Expression ")" | 30 | Directive = "#" "Symbol" "(" Expression ")" |
| 31 | 31 | ||
| ... | @@ -35,7 +35,7 @@ FnDef = option("inline" | "extern") FnProto Block | ... | @@ -35,7 +35,7 @@ FnDef = option("inline" | "extern") FnProto Block |
| 35 | 35 | ||
| 36 | ParamDeclList = "(" list(ParamDecl, ",") ")" | 36 | ParamDeclList = "(" list(ParamDecl, ",") ")" |
| 37 | 37 | ||
| 38 | ParamDecl = option("noalias") option("Symbol" ":") TypeExpr | "..." | 38 | ParamDecl = option("noalias" | "inline") option("Symbol" ":") TypeExpr | "..." |
| 39 | 39 | ||
| 40 | Block = "{" list(option(Statement), ";") "}" | 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,7 +23,7 @@ pub fn main(args: [][]u8) -> %void { |
| 23 | return err; | 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 | %%io.stdout.printf("Invalid number.\n"); | 27 | %%io.stdout.printf("Invalid number.\n"); |
| 28 | continue; | 28 | continue; |
| 29 | }; | 29 | }; |
src/all_types.hpp+9-6| ... | @@ -195,10 +195,8 @@ struct AstNodeRoot { | ... | @@ -195,10 +195,8 @@ struct AstNodeRoot { |
| 195 | struct AstNodeFnProto { | 195 | struct AstNodeFnProto { |
| 196 | TopLevelDecl top_level_decl; | 196 | TopLevelDecl top_level_decl; |
| 197 | Buf name; | 197 | Buf name; |
| 198 | ZigList<AstNode *> generic_params; | ||
| 199 | ZigList<AstNode *> params; | 198 | ZigList<AstNode *> params; |
| 200 | AstNode *return_type; | 199 | AstNode *return_type; |
| 201 | bool generic_params_is_var_args; | ||
| 202 | bool is_var_args; | 200 | bool is_var_args; |
| 203 | bool is_extern; | 201 | bool is_extern; |
| 204 | bool is_inline; | 202 | bool is_inline; |
| ... | @@ -210,7 +208,10 @@ struct AstNodeFnProto { | ... | @@ -210,7 +208,10 @@ struct AstNodeFnProto { |
| 210 | FnTableEntry *fn_table_entry; | 208 | FnTableEntry *fn_table_entry; |
| 211 | bool skip; | 209 | bool skip; |
| 212 | Expr resolved_expr; | 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 | struct AstNodeFnDef { | 217 | struct AstNodeFnDef { |
| ... | @@ -219,6 +220,7 @@ struct AstNodeFnDef { | ... | @@ -219,6 +220,7 @@ struct AstNodeFnDef { |
| 219 | 220 | ||
| 220 | // populated by semantic analyzer | 221 | // populated by semantic analyzer |
| 221 | TypeTableEntry *implicit_return_type; | 222 | TypeTableEntry *implicit_return_type; |
| 223 | // the first child block context | ||
| 222 | BlockContext *block_context; | 224 | BlockContext *block_context; |
| 223 | }; | 225 | }; |
| 224 | 226 | ||
| ... | @@ -230,6 +232,7 @@ struct AstNodeParamDecl { | ... | @@ -230,6 +232,7 @@ struct AstNodeParamDecl { |
| 230 | Buf name; | 232 | Buf name; |
| 231 | AstNode *type; | 233 | AstNode *type; |
| 232 | bool is_noalias; | 234 | bool is_noalias; |
| 235 | bool is_inline; | ||
| 233 | 236 | ||
| 234 | // populated by semantic analyzer | 237 | // populated by semantic analyzer |
| 235 | VariableTableEntry *variable; | 238 | VariableTableEntry *variable; |
| ... | @@ -841,6 +844,7 @@ struct FnTypeId { | ... | @@ -841,6 +844,7 @@ struct FnTypeId { |
| 841 | bool is_naked; | 844 | bool is_naked; |
| 842 | bool is_cold; | 845 | bool is_cold; |
| 843 | bool is_extern; | 846 | bool is_extern; |
| 847 | bool is_inline; | ||
| 844 | FnTypeParamInfo prealloc_param_info[fn_type_id_prealloc_param_info_count]; | 848 | FnTypeParamInfo prealloc_param_info[fn_type_id_prealloc_param_info_count]; |
| 845 | }; | 849 | }; |
| 846 | 850 | ||
| ... | @@ -1063,7 +1067,6 @@ struct FnTableEntry { | ... | @@ -1063,7 +1067,6 @@ struct FnTableEntry { |
| 1063 | ZigList<LabelTableEntry *> all_labels; | 1067 | ZigList<LabelTableEntry *> all_labels; |
| 1064 | Buf symbol_name; | 1068 | Buf symbol_name; |
| 1065 | TypeTableEntry *type_entry; // function type | 1069 | TypeTableEntry *type_entry; // function type |
| 1066 | bool is_inline; | ||
| 1067 | bool internal_linkage; | 1070 | bool internal_linkage; |
| 1068 | bool is_extern; | 1071 | bool is_extern; |
| 1069 | bool is_test; | 1072 | bool is_test; |
| ... | @@ -1172,8 +1175,8 @@ struct CodeGen { | ... | @@ -1172,8 +1175,8 @@ struct CodeGen { |
| 1172 | 1175 | ||
| 1173 | ZigList<ImportTableEntry *> import_queue; | 1176 | ZigList<ImportTableEntry *> import_queue; |
| 1174 | int import_queue_index; | 1177 | int import_queue_index; |
| 1175 | ZigList<AstNode *> export_queue; | 1178 | ZigList<AstNode *> resolve_queue; |
| 1176 | int export_queue_index; | 1179 | int resolve_queue_index; |
| 1177 | ZigList<AstNode *> use_queue; | 1180 | ZigList<AstNode *> use_queue; |
| 1178 | int use_queue_index; | 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,6 +32,8 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, |
| 32 | static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node); | 32 | static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node); |
| 33 | static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, | 33 | static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, |
| 34 | bool depends_on_compile_var); | 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 | static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type, | 37 | static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type, |
| 36 | bool depends_on_compile_var); | 38 | bool depends_on_compile_var); |
| 37 | static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node, | 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,7 +876,8 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor |
| 874 | fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport); | 876 | fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport); |
| 875 | fn_type_id.is_naked = is_naked; | 877 | fn_type_id.is_naked = is_naked; |
| 876 | fn_type_id.is_cold = is_cold; | 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 | if (fn_type_id.param_count > fn_type_id_prealloc_param_info_count) { | 882 | if (fn_type_id.param_count > fn_type_id_prealloc_param_info_count) { |
| 880 | fn_type_id.param_info = allocate_nonzero<FnTypeParamInfo>(fn_type_id.param_count); | 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,15 +886,52 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor |
| 883 | } | 886 | } |
| 884 | 887 | ||
| 885 | fn_type_id.is_var_args = fn_proto->is_var_args; | 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) { | 891 | switch (fn_type_id.return_type->id) { |
| 889 | fn_proto->skip = true; | 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 | for (int i = 0; i < fn_type_id.param_count; i += 1) { | 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 | assert(child->type == NodeTypeParamDecl); | 933 | assert(child->type == NodeTypeParamDecl); |
| 934 | |||
| 895 | TypeTableEntry *type_entry = analyze_type_expr(g, import, context, | 935 | TypeTableEntry *type_entry = analyze_type_expr(g, import, context, |
| 896 | child->data.param_decl.type); | 936 | child->data.param_decl.type); |
| 897 | switch (type_entry->id) { | 937 | switch (type_entry->id) { |
| ... | @@ -901,13 +941,20 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor | ... | @@ -901,13 +941,20 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor |
| 901 | case TypeTableEntryIdNumLitFloat: | 941 | case TypeTableEntryIdNumLitFloat: |
| 902 | case TypeTableEntryIdNumLitInt: | 942 | case TypeTableEntryIdNumLitInt: |
| 903 | case TypeTableEntryIdUndefLit: | 943 | case TypeTableEntryIdUndefLit: |
| 904 | case TypeTableEntryIdMetaType: | ||
| 905 | case TypeTableEntryIdUnreachable: | 944 | case TypeTableEntryIdUnreachable: |
| 906 | case TypeTableEntryIdNamespace: | 945 | case TypeTableEntryIdNamespace: |
| 907 | case TypeTableEntryIdGenericFn: | 946 | case TypeTableEntryIdGenericFn: |
| 908 | fn_proto->skip = true; | 947 | fn_proto->skip = true; |
| 909 | add_node_error(g, child->data.param_decl.type, | 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 | break; | 958 | break; |
| 912 | case TypeTableEntryIdVoid: | 959 | case TypeTableEntryIdVoid: |
| 913 | case TypeTableEntryIdBool: | 960 | case TypeTableEntryIdBool: |
| ... | @@ -998,8 +1045,6 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t | ... | @@ -998,8 +1045,6 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 998 | return; | 1045 | return; |
| 999 | } | 1046 | } |
| 1000 | 1047 | ||
| 1001 | fn_table_entry->is_inline = fn_proto->is_inline; | ||
| 1002 | |||
| 1003 | bool is_cold = false; | 1048 | bool is_cold = false; |
| 1004 | bool is_naked = false; | 1049 | bool is_naked = false; |
| 1005 | bool is_test = false; | 1050 | bool is_test = false; |
| ... | @@ -1095,7 +1140,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t | ... | @@ -1095,7 +1140,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 1095 | return; | 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 | add_node_error(g, node, buf_sprintf("function is both inline and noinline")); | 1144 | add_node_error(g, node, buf_sprintf("function is both inline and noinline")); |
| 1100 | fn_proto->skip = true; | 1145 | fn_proto->skip = true; |
| 1101 | return; | 1146 | return; |
| ... | @@ -1109,10 +1154,14 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t | ... | @@ -1109,10 +1154,14 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 1109 | symbol_name = buf_sprintf("_%s", buf_ptr(&fn_table_entry->symbol_name)); | 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), | 1157 | if (fn_table_entry->fn_def_node) { |
| 1113 | fn_type->data.fn.raw_type_ref); | 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 | LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute); | 1165 | LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute); |
| 1117 | } | 1166 | } |
| 1118 | if (fn_table_entry->is_noinline) { | 1167 | if (fn_table_entry->is_noinline) { |
| ... | @@ -1150,9 +1199,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t | ... | @@ -1150,9 +1199,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 1150 | fn_type->di_type, fn_table_entry->internal_linkage, | 1199 | fn_type->di_type, fn_table_entry->internal_linkage, |
| 1151 | is_definition, scope_line, flags, is_optimized, nullptr); | 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); | 1202 | fn_table_entry->fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram); |
| 1154 | fn_table_entry->fn_def_node->data.fn_def.block_context = context; | ||
| 1155 | context->di_scope = LLVMZigSubprogramToScope(subprogram); | ||
| 1156 | ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram); | 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,6 +1223,7 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt |
| 1176 | return; | 1223 | return; |
| 1177 | } | 1224 | } |
| 1178 | 1225 | ||
| 1226 | assert(decl_node->type == NodeTypeContainerDecl); | ||
| 1179 | assert(enum_type->di_type); | 1227 | assert(enum_type->di_type); |
| 1180 | 1228 | ||
| 1181 | enum_type->deep_const = true; | 1229 | enum_type->deep_const = true; |
| ... | @@ -1370,7 +1418,7 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE | ... | @@ -1370,7 +1418,7 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE |
| 1370 | return; | 1418 | return; |
| 1371 | } | 1419 | } |
| 1372 | 1420 | ||
| 1373 | 1421 | assert(decl_node->type == NodeTypeContainerDecl); | |
| 1374 | assert(struct_type->di_type); | 1422 | assert(struct_type->di_type); |
| 1375 | 1423 | ||
| 1376 | struct_type->deep_const = true; | 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,38 +1544,30 @@ static void get_fully_qualified_decl_name(Buf *buf, AstNode *decl_node, uint8_t |
| 1496 | } | 1544 | } |
| 1497 | 1545 | ||
| 1498 | static void preview_generic_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *node) { | 1546 | static void preview_generic_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 1499 | if (node->type == NodeTypeFnProto) { | 1547 | assert(node->type == NodeTypeContainerDecl); |
| 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 | } | ||
| 1515 | 1548 | ||
| 1516 | node->data.struct_decl.generic_fn_type = get_generic_fn_type(g, node); | 1549 | if (node->data.struct_decl.generic_params_is_var_args) { |
| 1517 | } else { | 1550 | add_node_error(g, node, buf_sprintf("generic parameters cannot be var args")); |
| 1518 | zig_unreachable(); | 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 | static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstNode *proto_node, | 1559 | static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstNode *proto_node, |
| 1524 | BlockContext *containing_context) | 1560 | BlockContext *containing_context) |
| 1525 | { | 1561 | { |
| 1562 | assert(proto_node->type == NodeTypeFnProto); | ||
| 1563 | |||
| 1526 | if (proto_node->data.fn_proto.skip) { | 1564 | if (proto_node->data.fn_proto.skip) { |
| 1527 | return; | 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 | AstNode *parent_decl = proto_node->data.fn_proto.top_level_decl.parent_decl; | 1572 | AstNode *parent_decl = proto_node->data.fn_proto.top_level_decl.parent_decl; |
| 1533 | Buf *proto_name = &proto_node->data.fn_proto.name; | 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,41 +1591,50 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN |
| 1551 | 1591 | ||
| 1552 | get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_'); | 1592 | get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_'); |
| 1553 | 1593 | ||
| 1554 | g->fn_protos.append(fn_table_entry); | 1594 | proto_node->data.fn_proto.fn_table_entry = fn_table_entry; |
| 1555 | |||
| 1556 | if (fn_def_node) { | ||
| 1557 | g->fn_defs.append(fn_table_entry); | ||
| 1558 | } | ||
| 1559 | 1595 | ||
| 1560 | bool is_main_fn = !is_generic_instance && | 1596 | if (is_generic_fn) { |
| 1561 | !parent_decl && (import == g->root_import) && | 1597 | fn_table_entry->type_entry = get_generic_fn_type(g, proto_node); |
| 1562 | buf_eql_str(proto_name, "main"); | ||
| 1563 | if (is_main_fn) { | ||
| 1564 | g->main_fn = fn_table_entry; | ||
| 1565 | } | ||
| 1566 | 1598 | ||
| 1567 | proto_node->data.fn_proto.fn_table_entry = fn_table_entry; | 1599 | if (is_extern || proto_node->data.fn_proto.top_level_decl.visib_mod == VisibModExport) { |
| 1568 | resolve_function_proto(g, proto_node, fn_table_entry, import, containing_context); | 1600 | for (int i = 0; i < proto_node->data.fn_proto.params.length; i += 1) { |
| 1569 | 1601 | AstNode *param_decl_node = proto_node->data.fn_proto.params.at(i); | |
| 1570 | if (is_main_fn && !g->link_libc) { | 1602 | if (param_decl_node->data.param_decl.is_inline) { |
| 1571 | TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void); | 1603 | proto_node->data.fn_proto.skip = true; |
| 1572 | TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; | 1604 | add_node_error(g, param_decl_node, |
| 1573 | if (actual_return_type != err_void) { | 1605 | buf_sprintf("inline parameter not allowed in extern function")); |
| 1574 | AstNode *return_type_node = fn_table_entry->proto_node->data.fn_proto.return_type; | 1606 | } |
| 1575 | add_node_error(g, return_type_node, | 1607 | } |
| 1576 | buf_sprintf("expected return type of main to be '%%void', instead is '%s'", | ||
| 1577 | buf_ptr(&actual_return_type->name))); | ||
| 1578 | } | 1608 | } |
| 1579 | } | ||
| 1580 | } | ||
| 1581 | 1609 | ||
| 1582 | static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *proto_node) { | 1610 | |
| 1583 | if (proto_node->data.fn_proto.generic_params.length > 0) { | ||
| 1584 | return preview_generic_fn_proto(g, import, proto_node); | ||
| 1585 | } else { | 1611 | } else { |
| 1586 | return preview_fn_proto_instance(g, import, proto_node, proto_node->block_context); | 1612 | g->fn_protos.append(fn_table_entry); |
| 1587 | } | 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 | static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { | 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,7 +1732,7 @@ static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) |
| 1683 | 1732 | ||
| 1684 | switch (node->type) { | 1733 | switch (node->type) { |
| 1685 | case NodeTypeFnProto: | 1734 | case NodeTypeFnProto: |
| 1686 | preview_fn_proto(g, import, node); | 1735 | preview_fn_proto_instance(g, import, node, node->block_context); |
| 1687 | break; | 1736 | break; |
| 1688 | case NodeTypeContainerDecl: | 1737 | case NodeTypeContainerDecl: |
| 1689 | resolve_struct_decl(g, import, node); | 1738 | resolve_struct_decl(g, import, node); |
| ... | @@ -2600,7 +2649,11 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i | ... | @@ -2600,7 +2649,11 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i |
| 2600 | 2649 | ||
| 2601 | node->data.field_access_expr.is_member_fn = true; | 2650 | node->data.field_access_expr.is_member_fn = true; |
| 2602 | FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry; | 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 | } else { | 2657 | } else { |
| 2605 | add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'", | 2658 | add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'", |
| 2606 | buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); | 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,13 +3057,11 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod |
| 3004 | VariableTableEntry *var = decl_node->data.variable_declaration.variable; | 3057 | VariableTableEntry *var = decl_node->data.variable_declaration.variable; |
| 3005 | return analyze_var_ref(g, source_node, var, block_context, depends_on_compile_var); | 3058 | return analyze_var_ref(g, source_node, var, block_context, depends_on_compile_var); |
| 3006 | } else if (decl_node->type == NodeTypeFnProto) { | 3059 | } else if (decl_node->type == NodeTypeFnProto) { |
| 3007 | if (decl_node->data.fn_proto.generic_params.length > 0) { | 3060 | FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry; |
| 3008 | TypeTableEntry *type_entry = decl_node->data.fn_proto.generic_fn_type; | 3061 | assert(fn_entry->type_entry); |
| 3009 | assert(type_entry); | 3062 | if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) { |
| 3010 | return resolve_expr_const_val_as_generic_fn(g, source_node, type_entry, depends_on_compile_var); | 3063 | return resolve_expr_const_val_as_generic_fn(g, source_node, fn_entry->type_entry, depends_on_compile_var); |
| 3011 | } else { | 3064 | } else { |
| 3012 | FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry; | ||
| 3013 | assert(fn_entry->type_entry); | ||
| 3014 | return resolve_expr_const_val_as_fn(g, source_node, fn_entry, depends_on_compile_var); | 3065 | return resolve_expr_const_val_as_fn(g, source_node, fn_entry, depends_on_compile_var); |
| 3015 | } | 3066 | } |
| 3016 | } else if (decl_node->type == NodeTypeContainerDecl) { | 3067 | } else if (decl_node->type == NodeTypeContainerDecl) { |
| ... | @@ -5238,6 +5289,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry | ... | @@ -5238,6 +5289,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 5238 | zig_unreachable(); | 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 | static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 5294 | static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 5242 | TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type, | 5295 | TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type, |
| 5243 | AstNode *struct_node) | 5296 | AstNode *struct_node) |
| ... | @@ -5248,26 +5301,30 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, | ... | @@ -5248,26 +5301,30 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 5248 | return fn_type; | 5301 | return fn_type; |
| 5249 | } | 5302 | } |
| 5250 | 5303 | ||
| 5251 | // count parameters | 5304 | // The function call might include inline parameters which we need to ignore according to the |
| 5252 | int src_param_count = fn_type->data.fn.fn_type_id.param_count; | 5305 | // fn_type. |
| 5253 | int actual_param_count = node->data.fn_call_expr.params.length; | 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) { | 5310 | // count parameters |
| 5256 | actual_param_count += 1; | 5311 | int struct_node_1_or_0 = struct_node ? 1 : 0; |
| 5257 | } | 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 | bool ok_invocation = true; | 5316 | bool ok_invocation = true; |
| 5260 | 5317 | ||
| 5261 | if (fn_type->data.fn.fn_type_id.is_var_args) { | 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 | ok_invocation = false; | 5320 | ok_invocation = false; |
| 5264 | add_node_error(g, node, | 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 | ok_invocation = false; | 5325 | ok_invocation = false; |
| 5269 | add_node_error(g, node, | 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 | bool all_args_const_expr = true; | 5330 | bool all_args_const_expr = true; |
| ... | @@ -5281,17 +5338,30 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, | ... | @@ -5281,17 +5338,30 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 5281 | 5338 | ||
| 5282 | // analyze each parameter. in the case of a method, we already analyzed the | 5339 | // analyze each parameter. in the case of a method, we already analyzed the |
| 5283 | // first parameter in order to figure out which struct we were calling a method on. | 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) { | 5341 | int next_type_i = struct_node_1_or_0; |
| 5285 | AstNode **child = &node->data.fn_call_expr.params.at(i); | 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 | // determine the expected type for each parameter | 5345 | // determine the expected type for each parameter |
| 5287 | TypeTableEntry *expected_param_type = nullptr; | 5346 | TypeTableEntry *expected_param_type = nullptr; |
| 5288 | int fn_proto_i = i + (struct_node ? 1 : 0); | 5347 | if (proto_i < src_param_count) { |
| 5289 | if (fn_proto_i < src_param_count) { | 5348 | if (generic_proto_node && |
| 5290 | expected_param_type = fn_type->data.fn.fn_type_id.param_info[fn_proto_i].type; | 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 | if (!const_arg_val->ok) { | 5365 | if (!const_arg_val->ok) { |
| 5296 | all_args_const_expr = false; | 5366 | all_args_const_expr = false; |
| 5297 | } | 5367 | } |
| ... | @@ -5303,7 +5373,6 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, | ... | @@ -5303,7 +5373,6 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 5303 | return return_type; | 5373 | return return_type; |
| 5304 | } | 5374 | } |
| 5305 | 5375 | ||
| 5306 | FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; | ||
| 5307 | ConstExprValue *result_val = &get_resolved_expr(node)->const_val; | 5376 | ConstExprValue *result_val = &get_resolved_expr(node)->const_val; |
| 5308 | if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) { | 5377 | if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && fn_table_entry->want_pure != WantPureFalse) { |
| 5309 | if (fn_table_entry->anal_state == FnAnalStateReady) { | 5378 | if (fn_table_entry->anal_state == FnAnalStateReady) { |
| ... | @@ -5335,14 +5404,103 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, | ... | @@ -5335,14 +5404,103 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 5335 | return return_type; | 5404 | return return_type; |
| 5336 | } | 5405 | } |
| 5337 | 5406 | ||
| 5338 | static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 5407 | static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableEntry *import, |
| 5339 | TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, AstNode *struct_node) | 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 | static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, | 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,14 +5510,8 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp |
| 5352 | assert(generic_fn_type->id == TypeTableEntryIdGenericFn); | 5510 | assert(generic_fn_type->id == TypeTableEntryIdGenericFn); |
| 5353 | 5511 | ||
| 5354 | AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node; | 5512 | AstNode *decl_node = generic_fn_type->data.generic_fn.decl_node; |
| 5355 | ZigList<AstNode *> *generic_params; | 5513 | assert(decl_node->type == NodeTypeContainerDecl); |
| 5356 | if (decl_node->type == NodeTypeFnProto) { | 5514 | ZigList<AstNode *> *generic_params = &decl_node->data.struct_decl.generic_params; |
| 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 | } | ||
| 5363 | 5515 | ||
| 5364 | int expected_param_count = generic_params->length; | 5516 | int expected_param_count = generic_params->length; |
| 5365 | int actual_param_count = node->data.fn_call_expr.params.length; | 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,10 +5557,6 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp |
| 5405 | } else { | 5557 | } else { |
| 5406 | add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression")); | 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 | return g->builtin_types.entry_invalid; | 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,36 +5568,19 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp |
| 5420 | auto entry = g->generic_table.maybe_get(generic_fn_type_id); | 5568 | auto entry = g->generic_table.maybe_get(generic_fn_type_id); |
| 5421 | if (entry) { | 5569 | if (entry) { |
| 5422 | AstNode *impl_decl_node = entry->value; | 5570 | AstNode *impl_decl_node = entry->value; |
| 5423 | if (impl_decl_node->type == NodeTypeFnProto) { | 5571 | assert(impl_decl_node->type == NodeTypeContainerDecl); |
| 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); | ||
| 5447 | TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry; | 5572 | TypeTableEntry *type_entry = impl_decl_node->data.struct_decl.type_entry; |
| 5448 | resolve_struct_type(g, import, type_entry); | ||
| 5449 | return resolve_expr_const_val_as_type(g, node, type_entry, false); | 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 | static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 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,10 +5618,32 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import |
| 5487 | struct_node = nullptr; | 5618 | struct_node = nullptr; |
| 5488 | } | 5619 | } |
| 5489 | 5620 | ||
| 5490 | return analyze_fn_call_raw(g, import, context, expected_type, node, | 5621 | FnTableEntry *fn_table_entry = const_val->data.x_fn; |
| 5491 | const_val->data.x_fn, struct_node); | 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 | } else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) { | 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 | } else { | 5647 | } else { |
| 5495 | add_node_error(g, fn_ref_expr, | 5648 | add_node_error(g, fn_ref_expr, |
| 5496 | buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name))); | 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,7 +6520,9 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 6367 | var->src_arg_index = i; | 6520 | var->src_arg_index = i; |
| 6368 | param_decl_node->data.param_decl.variable = var; | 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 | if (!type->deep_const) { | 6527 | if (!type->deep_const) { |
| 6373 | fn_table_entry->is_pure = false; | 6528 | fn_table_entry->is_pure = false; |
| ... | @@ -6406,11 +6561,11 @@ static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContex | ... | @@ -6406,11 +6561,11 @@ static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContex |
| 6406 | tld->import = import; | 6561 | tld->import = import; |
| 6407 | tld->name = name; | 6562 | tld->name = name; |
| 6408 | 6563 | ||
| 6409 | bool want_as_export = (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport); | 6564 | bool want_to_resolve = (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) || | 6565 | bool is_generic_container = (node->type == NodeTypeContainerDecl && |
| 6411 | (node->type == NodeTypeContainerDecl && node->data.struct_decl.generic_params.length > 0); | 6566 | node->data.struct_decl.generic_params.length > 0); |
| 6412 | if (!is_generic && want_as_export) { | 6567 | if (want_to_resolve && !is_generic_container) { |
| 6413 | g->export_queue.append(node); | 6568 | g->resolve_queue.append(node); |
| 6414 | } | 6569 | } |
| 6415 | 6570 | ||
| 6416 | node->block_context = block_context; | 6571 | node->block_context = block_context; |
| ... | @@ -6425,6 +6580,18 @@ static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContex | ... | @@ -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 | static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { | 6595 | static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { |
| 6429 | switch (node->type) { | 6596 | switch (node->type) { |
| 6430 | case NodeTypeRoot: | 6597 | case NodeTypeRoot: |
| ... | @@ -6467,6 +6634,7 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte | ... | @@ -6467,6 +6634,7 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte |
| 6467 | add_node_error(g, node, buf_sprintf("missing function name")); | 6634 | add_node_error(g, node, buf_sprintf("missing function name")); |
| 6468 | break; | 6635 | break; |
| 6469 | } | 6636 | } |
| 6637 | node->data.fn_proto.inline_arg_count = fn_proto_inline_arg_count(node); | ||
| 6470 | 6638 | ||
| 6471 | add_top_level_decl(g, import, context, node, fn_name); | 6639 | add_top_level_decl(g, import, context, node, fn_name); |
| 6472 | break; | 6640 | break; |
| ... | @@ -6692,8 +6860,8 @@ void semantic_analyze(CodeGen *g) { | ... | @@ -6692,8 +6860,8 @@ void semantic_analyze(CodeGen *g) { |
| 6692 | resolve_use_decl(g, use_decl_node); | 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) { | 6863 | for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) { |
| 6696 | AstNode *decl_node = g->export_queue.at(g->export_queue_index); | 6864 | AstNode *decl_node = g->resolve_queue.at(g->resolve_queue_index); |
| 6697 | bool pointer_only = false; | 6865 | bool pointer_only = false; |
| 6698 | resolve_top_level_decl(g, decl_node, pointer_only); | 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,11 +7151,9 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { |
| 6983 | FnTypeParamInfo *a_param_info = &a->param_info[i]; | 7151 | FnTypeParamInfo *a_param_info = &a->param_info[i]; |
| 6984 | FnTypeParamInfo *b_param_info = &b->param_info[i]; | 7152 | FnTypeParamInfo *b_param_info = &b->param_info[i]; |
| 6985 | 7153 | ||
| 6986 | if (a_param_info->type != b_param_info->type) { | 7154 | if (a_param_info->type != b_param_info->type || |
| 6987 | return false; | 7155 | a_param_info->is_noalias != b_param_info->is_noalias) |
| 6988 | } | 7156 | { |
| 6989 | |||
| 6990 | if (a_param_info->is_noalias != b_param_info->is_noalias) { | ||
| 6991 | return false; | 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,7 +353,8 @@ static void render_node(AstRender *ar, AstNode *node) { |
| 353 | assert(param_decl->type == NodeTypeParamDecl); | 353 | assert(param_decl->type == NodeTypeParamDecl); |
| 354 | if (buf_len(&param_decl->data.param_decl.name) > 0) { | 354 | if (buf_len(&param_decl->data.param_decl.name) > 0) { |
| 355 | const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : ""; | 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 | print_symbol(ar, &param_decl->data.param_decl.name); | 358 | print_symbol(ar, &param_decl->data.param_decl.name); |
| 358 | fprintf(ar->f, ": "); | 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,12 +1062,15 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 1062 | 1062 | ||
| 1063 | TypeTableEntry *fn_type; | 1063 | TypeTableEntry *fn_type; |
| 1064 | LLVMValueRef fn_val; | 1064 | LLVMValueRef fn_val; |
| 1065 | AstNode *generic_proto_node; | ||
| 1065 | if (fn_table_entry) { | 1066 | if (fn_table_entry) { |
| 1066 | fn_val = fn_table_entry->fn_value; | 1067 | fn_val = fn_table_entry->fn_value; |
| 1067 | fn_type = fn_table_entry->type_entry; | 1068 | fn_type = fn_table_entry->type_entry; |
| 1069 | generic_proto_node = fn_table_entry->proto_node->data.fn_proto.generic_proto_node; | ||
| 1068 | } else { | 1070 | } else { |
| 1069 | fn_val = gen_expr(g, fn_ref_expr); | 1071 | fn_val = gen_expr(g, fn_ref_expr); |
| 1070 | fn_type = get_expr_type(fn_ref_expr); | 1072 | fn_type = get_expr_type(fn_ref_expr); |
| 1073 | generic_proto_node = nullptr; | ||
| 1071 | } | 1074 | } |
| 1072 | 1075 | ||
| 1073 | TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type; | 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,8 +1096,14 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 1093 | gen_param_index += 1; | 1096 | gen_param_index += 1; |
| 1094 | } | 1097 | } |
| 1095 | 1098 | ||
| 1096 | for (int i = 0; i < fn_call_param_count; i += 1) { | 1099 | for (int call_i = 0; call_i < fn_call_param_count; call_i += 1) { |
| 1097 | AstNode *expr_node = node->data.fn_call_expr.params.at(i); | 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 | LLVMValueRef param_value = gen_expr(g, expr_node); | 1107 | LLVMValueRef param_value = gen_expr(g, expr_node); |
| 1099 | assert(param_value); | 1108 | assert(param_value); |
| 1100 | TypeTableEntry *param_type = get_expr_type(expr_node); | 1109 | TypeTableEntry *param_type = get_expr_type(expr_node); |
| ... | @@ -3734,7 +3743,7 @@ static void delete_unused_builtin_fns(CodeGen *g) { | ... | @@ -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 | if (g->is_test_build) { | 3747 | if (g->is_test_build) { |
| 3739 | if (fn_entry->is_test) { | 3748 | if (fn_entry->is_test) { |
| 3740 | return false; | 3749 | return false; |
| ... | @@ -3889,7 +3898,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -3889,7 +3898,7 @@ static void do_code_gen(CodeGen *g) { |
| 3889 | // Generate function prototypes | 3898 | // Generate function prototypes |
| 3890 | for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) { | 3899 | for (int fn_proto_i = 0; fn_proto_i < g->fn_protos.length; fn_proto_i += 1) { |
| 3891 | FnTableEntry *fn_table_entry = g->fn_protos.at(fn_proto_i); | 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 | // huge time saver | 3902 | // huge time saver |
| 3894 | LLVMDeleteFunction(fn_table_entry->fn_value); | 3903 | LLVMDeleteFunction(fn_table_entry->fn_value); |
| 3895 | fn_table_entry->fn_value = nullptr; | 3904 | fn_table_entry->fn_value = nullptr; |
| ... | @@ -3995,7 +4004,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -3995,7 +4004,7 @@ static void do_code_gen(CodeGen *g) { |
| 3995 | // Generate function definitions. | 4004 | // Generate function definitions. |
| 3996 | for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { | 4005 | for (int fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { |
| 3997 | FnTableEntry *fn_table_entry = g->fn_defs.at(fn_i); | 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 | // huge time saver | 4008 | // huge time saver |
| 4000 | continue; | 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,9 +884,9 @@ static bool eval_fn_call_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val |
| 884 | 884 | ||
| 885 | int param_count = node->data.fn_call_expr.params.length; | 885 | int param_count = node->data.fn_call_expr.params.length; |
| 886 | ConstExprValue *args = allocate<ConstExprValue>(param_count); | 886 | ConstExprValue *args = allocate<ConstExprValue>(param_count); |
| 887 | for (int i = 0; i < param_count; i += 1) { | 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(i); | 888 | AstNode *param_expr_node = node->data.fn_call_expr.params.at(call_i); |
| 889 | ConstExprValue *param_val = &args[i]; | 889 | ConstExprValue *param_val = &args[call_i]; |
| 890 | if (eval_expr(ef, param_expr_node, param_val)) return true; | 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,6 +1291,13 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { |
| 1291 | } | 1291 | } |
| 1292 | 1292 | ||
| 1293 | static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val) { | 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 | EvalFn ef = {0}; | 1301 | EvalFn ef = {0}; |
| 1295 | ef.root = efr; | 1302 | ef.root = efr; |
| 1296 | ef.fn = fn; | 1303 | ef.fn = fn; |
| ... | @@ -1300,12 +1307,12 @@ static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args | ... | @@ -1300,12 +1307,12 @@ static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args |
| 1300 | root_scope->block_context = fn->fn_def_node->data.fn_def.body->block_context; | 1307 | root_scope->block_context = fn->fn_def_node->data.fn_def.body->block_context; |
| 1301 | ef.scope_stack.append(root_scope); | 1308 | ef.scope_stack.append(root_scope); |
| 1302 | 1309 | ||
| 1303 | int param_count = fn->type_entry->data.fn.fn_type_id.param_count; | 1310 | int param_count = acting_proto_node->data.fn_proto.params.length; |
| 1304 | for (int i = 0; i < param_count; i += 1) { | 1311 | for (int proto_i = 0; proto_i < param_count; proto_i += 1) { |
| 1305 | AstNode *decl_param_node = fn->proto_node->data.fn_proto.params.at(i); | 1312 | AstNode *decl_param_node = acting_proto_node->data.fn_proto.params.at(proto_i); |
| 1306 | assert(decl_param_node->type == NodeTypeParamDecl); | 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 | assert(src_const_val->ok); | 1316 | assert(src_const_val->ok); |
| 1310 | 1317 | ||
| 1311 | root_scope->vars.add_one(); | 1318 | root_scope->vars.add_one(); |
| ... | @@ -1315,7 +1322,6 @@ static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args | ... | @@ -1315,7 +1322,6 @@ static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args |
| 1315 | } | 1322 | } |
| 1316 | 1323 | ||
| 1317 | return eval_expr(&ef, fn->fn_def_node->data.fn_def.body, out_val); | 1324 | return eval_expr(&ef, fn->fn_def_node->data.fn_def.body, out_val); |
| 1318 | |||
| 1319 | } | 1325 | } |
| 1320 | 1326 | ||
| 1321 | bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_val, | 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,9 +1335,16 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va |
| 1329 | efr.call_node = node; | 1335 | efr.call_node = node; |
| 1330 | efr.branch_quota = branch_quota; | 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 | int call_param_count = node->data.fn_call_expr.params.length; | 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; | 1346 | int proto_param_count = acting_proto_node->data.fn_proto.params.length; |
| 1334 | ConstExprValue *args = allocate<ConstExprValue>(type_param_count); | 1347 | ConstExprValue *args = allocate<ConstExprValue>(proto_param_count); |
| 1335 | int next_arg_index = 0; | 1348 | int next_arg_index = 0; |
| 1336 | if (struct_node) { | 1349 | if (struct_node) { |
| 1337 | ConstExprValue *struct_val = &get_resolved_expr(struct_node)->const_val; | 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,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 | static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { | 752 | static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { |
| 753 | Token *token = &pc->tokens->at(*token_index); | 753 | Token *token = &pc->tokens->at(*token_index); |
| ... | @@ -763,6 +763,10 @@ static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { | ... | @@ -763,6 +763,10 @@ static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { |
| 763 | node->data.param_decl.is_noalias = true; | 763 | node->data.param_decl.is_noalias = true; |
| 764 | *token_index += 1; | 764 | *token_index += 1; |
| 765 | token = &pc->tokens->at(*token_index); | 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 | buf_resize(&node->data.param_decl.name, 0); | 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,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 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory, | 2481 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory, |
| 2478 | ZigList<AstNode*> *directives, VisibMod visib_mod) | 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,17 +2506,6 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand |
| 2502 | 2506 | ||
| 2503 | ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args); | 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 | Token *next_token = &pc->tokens->at(*token_index); | 2509 | Token *next_token = &pc->tokens->at(*token_index); |
| 2517 | if (next_token->id == TokenIdArrow) { | 2510 | if (next_token->id == TokenIdArrow) { |
| 2518 | *token_index += 1; | 2511 | *token_index += 1; |
| ... | @@ -2931,7 +2924,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont | ... | @@ -2931,7 +2924,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont |
| 2931 | case NodeTypeFnProto: | 2924 | case NodeTypeFnProto: |
| 2932 | visit_field(&node->data.fn_proto.return_type, visit, context); | 2925 | visit_field(&node->data.fn_proto.return_type, visit, context); |
| 2933 | visit_node_list(node->data.fn_proto.top_level_decl.directives, visit, context); | 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 | visit_node_list(&node->data.fn_proto.params, visit, context); | 2927 | visit_node_list(&node->data.fn_proto.params, visit, context); |
| 2936 | break; | 2928 | break; |
| 2937 | case NodeTypeFnDef: | 2929 | case NodeTypeFnDef: |
| ... | @@ -3123,6 +3115,22 @@ static void clone_subtree_list(ZigList<AstNode *> *dest, ZigList<AstNode *> *src | ... | @@ -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 | static void clone_subtree_list_ptr(ZigList<AstNode *> **dest_ptr, ZigList<AstNode *> *src, | 3134 | static void clone_subtree_list_ptr(ZigList<AstNode *> **dest_ptr, ZigList<AstNode *> *src, |
| 3127 | uint32_t *next_node_index) | 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,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 | if (src) { | 3147 | if (src) { |
| 3138 | *dest = ast_clone_subtree(src, next_node_index); | 3148 | *dest = ast_clone_subtree_special(src, next_node_index, special); |
| 3139 | (*dest)->parent_field = dest; | 3149 | (*dest)->parent_field = dest; |
| 3140 | } else { | 3150 | } else { |
| 3141 | *dest = nullptr; | 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 | static void clone_subtree_tld(TopLevelDecl *dest, TopLevelDecl *src, uint32_t *next_node_index) { | 3159 | static void clone_subtree_tld(TopLevelDecl *dest, TopLevelDecl *src, uint32_t *next_node_index) { |
| 3146 | clone_subtree_list_ptr(&dest->directives, src->directives, next_node_index); | 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 | AstNode *new_node = allocate_nonzero<AstNode>(1); | 3164 | AstNode *new_node = allocate_nonzero<AstNode>(1); |
| 3151 | memcpy(new_node, old_node, sizeof(AstNode)); | 3165 | memcpy(new_node, old_node, sizeof(AstNode)); |
| 3152 | new_node->create_index = *next_node_index; | 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,14 +3177,19 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { |
| 3163 | next_node_index); | 3177 | next_node_index); |
| 3164 | clone_subtree_field(&new_node->data.fn_proto.return_type, old_node->data.fn_proto.return_type, | 3178 | clone_subtree_field(&new_node->data.fn_proto.return_type, old_node->data.fn_proto.return_type, |
| 3165 | next_node_index); | 3179 | next_node_index); |
| 3166 | clone_subtree_list(&new_node->data.fn_proto.generic_params, | 3180 | |
| 3167 | &old_node->data.fn_proto.generic_params, next_node_index); | 3181 | if (special == AstCloneSpecialOmitInlineParams) { |
| 3168 | clone_subtree_list(&new_node->data.fn_proto.params, &old_node->data.fn_proto.params, | 3182 | clone_subtree_list_omit_inline_params(&new_node->data.fn_proto.params, &old_node->data.fn_proto.params, |
| 3169 | next_node_index); | 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 | break; | 3189 | break; |
| 3172 | case NodeTypeFnDef: | 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 | new_node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = new_node; | 3193 | new_node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = new_node; |
| 3175 | clone_subtree_field(&new_node->data.fn_def.body, old_node->data.fn_def.body, next_node_index); | 3194 | clone_subtree_field(&new_node->data.fn_def.body, old_node->data.fn_def.body, next_node_index); |
| 3176 | break; | 3195 | break; |
| ... | @@ -3354,3 +3373,7 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { | ... | @@ -3354,3 +3373,7 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { |
| 3354 | 3373 | ||
| 3355 | return new_node; | 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,6 +25,13 @@ void ast_print(AstNode *node, int indent); |
| 25 | void normalize_parent_ptrs(AstNode *node); | 25 | void normalize_parent_ptrs(AstNode *node); |
| 26 | 26 | ||
| 27 | AstNode *ast_clone_subtree(AstNode *node, uint32_t *next_node_index); | 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 | void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context); | 35 | void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context); |
| 29 | 36 | ||
| 30 | #endif | 37 | #endif |
std/hash_map.zig+10-9| ... | @@ -7,7 +7,7 @@ const want_modification_safety = !@compile_var("is_release"); | ... | @@ -7,7 +7,7 @@ const want_modification_safety = !@compile_var("is_release"); |
| 7 | const debug_u32 = if (want_modification_safety) u32 else void; | 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 | SmallHashMap(K, V, hash, eql, 8); | 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,7 +70,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 70 | 70 | ||
| 71 | pub fn deinit(hm: &Self) { | 71 | pub fn deinit(hm: &Self) { |
| 72 | if (hm.entries.ptr != &hm.prealloc_entries[0]) { | 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,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 | if (old_entries.ptr != &hm.prealloc_entries[0]) { | 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,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 | fn init_capacity(hm: &Self, capacity: isize) -> %void { | 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 | hm.size = 0; | 156 | hm.size = 0; |
| 157 | hm.max_distance_from_start_index = 0; | 157 | hm.max_distance_from_start_index = 0; |
| 158 | for (hm.entries) |*entry| { | 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,7 +180,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 180 | if (entry.distance_from_start_index < distance_from_start_index) { | 180 | if (entry.distance_from_start_index < distance_from_start_index) { |
| 181 | // robin hood to the rescue | 181 | // robin hood to the rescue |
| 182 | const tmp = *entry; | 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 | hm.max_distance_from_start_index, distance_from_start_index); | 184 | hm.max_distance_from_start_index, distance_from_start_index); |
| 185 | *entry = Entry { | 185 | *entry = Entry { |
| 186 | .used = true, | 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,7 +201,8 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b |
| 201 | hm.size += 1; | 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 | *entry = Entry { | 206 | *entry = Entry { |
| 206 | .used = true, | 207 | .used = true, |
| 207 | .distance_from_start_index = distance_from_start_index, | 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,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 | var global_allocator = Allocator { | 234 | var global_allocator = Allocator { |
| 234 | .alloc = global_alloc, | 235 | .alloc_fn = global_alloc, |
| 235 | .realloc = global_realloc, | 236 | .realloc_fn = global_realloc, |
| 236 | .free = global_free, | 237 | .free_fn = global_free, |
| 237 | .context = null, | 238 | .context = null, |
| 238 | }; | 239 | }; |
| 239 | 240 |
std/io.zig+22-32| ... | @@ -69,7 +69,7 @@ pub struct OutStream { | ... | @@ -69,7 +69,7 @@ pub struct OutStream { |
| 69 | const dest_space_left = os.buffer.len - os.index; | 69 | const dest_space_left = os.buffer.len - os.index; |
| 70 | 70 | ||
| 71 | while (src_bytes_left > 0) { | 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 | @memcpy(&os.buffer[os.index], &bytes[src_index], copy_amt); | 73 | @memcpy(&os.buffer[os.index], &bytes[src_index], copy_amt); |
| 74 | os.index += copy_amt; | 74 | os.index += copy_amt; |
| 75 | if (os.index == os.buffer.len) { | 75 | if (os.index == os.buffer.len) { |
| ... | @@ -208,59 +208,47 @@ pub struct InStream { | ... | @@ -208,59 +208,47 @@ pub struct InStream { |
| 208 | } | 208 | } |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | pub error InvalidChar; | 211 | pub fn parse_unsigned(inline T: type, buf: []u8, radix: u8) -> %T { |
| 212 | pub error Overflow; | ||
| 213 | |||
| 214 | pub fn parse_unsigned(T: type)(buf: []u8, radix: u8) -> %T { | ||
| 215 | var x: T = 0; | 212 | var x: T = 0; |
| 216 | 213 | ||
| 217 | for (buf) |c| { | 214 | for (buf) |c| { |
| 218 | const digit = char_to_digit(c); | 215 | const digit = %return char_to_digit(c, radix); |
| 219 | 216 | x = %return math.mul_overflow(T, x, radix); | |
| 220 | if (digit >= radix) { | 217 | x = %return math.add_overflow(T, x, digit); |
| 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 | } | ||
| 233 | } | 218 | } |
| 234 | 219 | ||
| 235 | return x; | 220 | return x; |
| 236 | } | 221 | } |
| 237 | 222 | ||
| 238 | fn char_to_digit(c: u8) -> u8 { | 223 | pub error InvalidChar; |
| 239 | // TODO use switch with range | 224 | fn char_to_digit(c: u8, radix: u8) -> %u8 { |
| 240 | if ('0' <= c && c <= '9') { | 225 | const value = if ('0' <= c && c <= '9') { |
| 241 | c - '0' | 226 | c - '0' |
| 242 | } else if ('A' <= c && c <= 'Z') { | 227 | } else if ('A' <= c && c <= 'Z') { |
| 243 | c - 'A' + 10 | 228 | c - 'A' + 10 |
| 244 | } else if ('a' <= c && c <= 'z') { | 229 | } else if ('a' <= c && c <= 'z') { |
| 245 | c - 'a' + 10 | 230 | c - 'a' + 10 |
| 246 | } else { | 231 | } else { |
| 247 | @max_value(u8) | 232 | return error.InvalidChar; |
| 248 | } | 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 | const uint = @int_type(false, T.bit_count, false); | 238 | const uint = @int_type(false, T.bit_count, false); |
| 253 | if (x < 0) { | 239 | if (x < 0) { |
| 254 | out_buf[0] = '-'; | 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 | } else { | 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 | var buf: [max_u64_base10_digits]u8 = undefined; | 252 | var buf: [max_u64_base10_digits]u8 = undefined; |
| 265 | var a = x; | 253 | var a = x; |
| 266 | var index: isize = buf.len; | 254 | var index: isize = buf.len; |
| ... | @@ -281,7 +269,9 @@ pub fn buf_print_unsigned(T: type)(out_buf: []u8, x: T) -> isize { | ... | @@ -281,7 +269,9 @@ pub fn buf_print_unsigned(T: type)(out_buf: []u8, x: T) -> isize { |
| 281 | return len; | 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 | pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize { | 276 | pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize { |
| 287 | const numExpBits = 11; | 277 | const numExpBits = 11; |
| ... | @@ -409,7 +399,7 @@ pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize { | ... | @@ -409,7 +399,7 @@ pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize { |
| 409 | 399 | ||
| 410 | #attribute("test") | 400 | #attribute("test") |
| 411 | fn parse_u64_digit_too_big() { | 401 | fn parse_u64_digit_too_big() { |
| 412 | parse_unsigned(u64)("123a", 10) %% |err| { | 402 | parse_unsigned(u64, "123a", 10) %% |err| { |
| 413 | if (err == error.InvalidChar) return; | 403 | if (err == error.InvalidChar) return; |
| 414 | unreachable{}; | 404 | unreachable{}; |
| 415 | }; | 405 | }; |
std/list.zig+14-15| ... | @@ -2,59 +2,58 @@ const assert = @import("debug.zig").assert; | ... | @@ -2,59 +2,58 @@ const assert = @import("debug.zig").assert; |
| 2 | const mem = @import("mem.zig"); | 2 | const mem = @import("mem.zig"); |
| 3 | const Allocator = mem.Allocator; | 3 | const Allocator = mem.Allocator; |
| 4 | 4 | ||
| 5 | /* | 5 | pub inline fn List(inline T: type) -> type { |
| 6 | pub fn List(T: type) -> type { | ||
| 7 | SmallList(T, 8) | 6 | SmallList(T, 8) |
| 8 | } | 7 | } |
| 9 | */ | ||
| 10 | 8 | ||
| 11 | pub struct SmallList(T: type, STATIC_SIZE: isize) { | 9 | pub struct SmallList(T: type, STATIC_SIZE: isize) { |
| 10 | const Self = SmallList(T, STATIC_SIZE); | ||
| 11 | |||
| 12 | items: []T, | 12 | items: []T, |
| 13 | length: isize, | 13 | length: isize, |
| 14 | prealloc_items: [STATIC_SIZE]T, | 14 | prealloc_items: [STATIC_SIZE]T, |
| 15 | allocator: &Allocator, | 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 | l.items = l.prealloc_items[0...]; | 18 | l.items = l.prealloc_items[0...]; |
| 19 | l.length = 0; | 19 | l.length = 0; |
| 20 | l.allocator = allocator; | 20 | l.allocator = allocator; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | pub fn deinit(l: &SmallList(T, STATIC_SIZE)) { | 23 | pub fn deinit(l: &Self) { |
| 24 | if (l.items.ptr != &l.prealloc_items[0]) { | 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 | const new_length = l.length + 1; | 30 | const new_length = l.length + 1; |
| 31 | %return l.ensure_capacity(new_length); | 31 | %return l.ensure_capacity(new_length); |
| 32 | l.items[l.length] = item; | 32 | l.items[l.length] = item; |
| 33 | l.length = new_length; | 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 | const old_capacity = l.items.len; | 37 | const old_capacity = l.items.len; |
| 38 | var better_capacity = old_capacity; | 38 | var better_capacity = old_capacity; |
| 39 | while (better_capacity < new_capacity) { | 39 | while (better_capacity < new_capacity) { |
| 40 | better_capacity *= 2; | 40 | better_capacity *= 2; |
| 41 | } | 41 | } |
| 42 | if (better_capacity != old_capacity) { | 42 | if (better_capacity != old_capacity) { |
| 43 | const alloc_bytes = better_capacity * @sizeof(T); | ||
| 44 | if (l.items.ptr == &l.prealloc_items[0]) { | 43 | if (l.items.ptr == &l.prealloc_items[0]) { |
| 45 | l.items = ([]T)(%return l.allocator.alloc(l.allocator, alloc_bytes)); | 44 | l.items = %return l.allocator.alloc(T, better_capacity); |
| 46 | @memcpy(l.items.ptr, &l.prealloc_items[0], old_capacity * @sizeof(T)); | 45 | mem.copy(T, l.items, l.prealloc_items[0...old_capacity]); |
| 47 | } else { | 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 | var global_allocator = Allocator { | 53 | var global_allocator = Allocator { |
| 55 | .alloc = global_alloc, | 54 | .alloc_fn = global_alloc, |
| 56 | .realloc = global_realloc, | 55 | .realloc_fn = global_realloc, |
| 57 | .free = global_free, | 56 | .free_fn = global_free, |
| 58 | .context = null, | 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,10 +26,24 @@ pub fn f64_is_inf(f: f64) -> bool { |
| 26 | f == f64_get_neg_inf() || f == f64_get_pos_inf() | 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 | if (x < y) x else y | 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 | if (x > y) x else y | 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,18 +1,46 @@ |
| 1 | const assert = @import("debug.zig").assert; | 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 | pub error NoMem; | 6 | pub error NoMem; |
| 4 | 7 | ||
| 5 | pub type Context = u8; | 8 | pub type Context = u8; |
| 6 | pub struct Allocator { | 9 | pub struct Allocator { |
| 7 | alloc: fn (self: &Allocator, n: isize) -> %[]u8, | 10 | alloc_fn: fn (self: &Allocator, n: isize) -> %[]u8, |
| 8 | realloc: fn (self: &Allocator, old_mem: []u8, new_size: isize) -> %[]u8, | 11 | realloc_fn: fn (self: &Allocator, old_mem: []u8, new_size: isize) -> %[]u8, |
| 9 | free: fn (self: &Allocator, mem: []u8), | 12 | free_fn: fn (self: &Allocator, mem: []u8), |
| 10 | context: ?&Context, | 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 | /// Copy all of source into dest at position 0. | 41 | /// Copy all of source into dest at position 0. |
| 14 | /// dest.len must be >= source.len. | 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 | assert(dest.len >= source.len); | 44 | assert(dest.len >= source.len); |
| 17 | @memcpy(dest.ptr, source.ptr, @sizeof(T) * source.len); | 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,14 +99,14 @@ pub fn connect_addr(addr: &Address, port: u16) -> %Connection { |
| 99 | const connect_ret = if (addr.family == linux.AF_INET) { | 99 | const connect_ret = if (addr.family == linux.AF_INET) { |
| 100 | var os_addr: linux.sockaddr_in = undefined; | 100 | var os_addr: linux.sockaddr_in = undefined; |
| 101 | os_addr.family = addr.family; | 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 | @memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4); | 103 | @memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4); |
| 104 | @memset(&os_addr.zero, 0, @sizeof(@typeof(os_addr.zero))); | 104 | @memset(&os_addr.zero, 0, @sizeof(@typeof(os_addr.zero))); |
| 105 | linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeof(linux.sockaddr_in)) | 105 | linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeof(linux.sockaddr_in)) |
| 106 | } else if (addr.family == linux.AF_INET6) { | 106 | } else if (addr.family == linux.AF_INET6) { |
| 107 | var os_addr: linux.sockaddr_in6 = undefined; | 107 | var os_addr: linux.sockaddr_in6 = undefined; |
| 108 | os_addr.family = addr.family; | 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 | os_addr.flowinfo = 0; | 110 | os_addr.flowinfo = 0; |
| 111 | os_addr.scope_id = addr.scope_id; | 111 | os_addr.scope_id = addr.scope_id; |
| 112 | @memcpy(&os_addr.addr[0], &addr.addr[0], 16); | 112 | @memcpy(&os_addr.addr[0], &addr.addr[0], 16); |
| ... | @@ -319,7 +319,7 @@ fn parse_ip4(buf: []const u8) -> %u32 { | ... | @@ -319,7 +319,7 @@ fn parse_ip4(buf: []const u8) -> %u32 { |
| 319 | 319 | ||
| 320 | #attribute("test") | 320 | #attribute("test") |
| 321 | fn test_parse_ip4() { | 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 | switch (parse_ip4("256.0.0.1")) { Overflow => {}, else => unreachable {}, } | 323 | switch (parse_ip4("256.0.0.1")) { Overflow => {}, else => unreachable {}, } |
| 324 | switch (parse_ip4("x.0.0.1")) { InvalidChar => {}, else => unreachable {}, } | 324 | switch (parse_ip4("x.0.0.1")) { InvalidChar => {}, else => unreachable {}, } |
| 325 | switch (parse_ip4("127.0.0.1.1")) { JunkAtEnd => {}, else => unreachable {}, } | 325 | switch (parse_ip4("127.0.0.1.1")) { JunkAtEnd => {}, else => unreachable {}, } |
| ... | @@ -352,12 +352,11 @@ fn test_lookup_simple_ip() { | ... | @@ -352,12 +352,11 @@ fn test_lookup_simple_ip() { |
| 352 | } | 352 | } |
| 353 | } | 353 | } |
| 354 | 354 | ||
| 355 | const be_to_host = host_to_be; | 355 | fn swap_if_little_endian(inline T: type, x: T) -> T { |
| 356 | fn host_to_be(T: type)(x: T) -> T { | 356 | if (@compile_var("is_big_endian")) x else endian_swap(T, x) |
| 357 | 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 | const x_slice = ([]u8)((&const x)[0...1]); | 360 | const x_slice = ([]u8)((&const x)[0...1]); |
| 362 | var result: T = undefined; | 361 | var result: T = undefined; |
| 363 | const result_slice = ([]u8)((&result)[0...1]); | 362 | const result_slice = ([]u8)((&result)[0...1]); |
std/str.zig+4-2| ... | @@ -1,8 +1,10 @@ | ... | @@ -1,8 +1,10 @@ |
| 1 | const assert = @import("debug.zig").assert; | 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 | if (a.len != b.len) return false; | 8 | if (a.len != b.len) return false; |
| 7 | for (a) |item, index| { | 9 | for (a) |item, index| { |
| 8 | if (b[index] != item) return false; | 10 | if (b[index] != item) return false; |
std/test_runner.zig+1| ... | @@ -9,6 +9,7 @@ extern var zig_test_fn_list: []TestFn; | ... | @@ -9,6 +9,7 @@ extern var zig_test_fn_list: []TestFn; |
| 9 | 9 | ||
| 10 | pub fn run_tests() -> %void { | 10 | pub fn run_tests() -> %void { |
| 11 | for (zig_test_fn_list) |test_fn, i| { | 11 | for (zig_test_fn_list) |test_fn, i| { |
| 12 | // TODO: print var args | ||
| 12 | %%io.stderr.write("Test "); | 13 | %%io.stderr.write("Test "); |
| 13 | %%io.stderr.print_i64(i + 1); | 14 | %%io.stderr.print_i64(i + 1); |
| 14 | %%io.stderr.write("/"); | 15 | %%io.stderr.write("/"); |
test/run_tests.cpp+24-3| ... | @@ -1181,11 +1181,11 @@ const invalid = foo > foo; | ... | @@ -1181,11 +1181,11 @@ const invalid = foo > foo; |
| 1181 | )SOURCE", 1, ".tmp_source.zig:3:21: error: operator not allowed for type 'fn()'"); | 1181 | )SOURCE", 1, ".tmp_source.zig:3:21: error: operator not allowed for type 'fn()'"); |
| 1182 | 1182 | ||
| 1183 | add_compile_fail_case("generic function instance with non-constant expression", R"SOURCE( | 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 | fn test1(a: i32, b: i32) -> i32 { | 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 | add_compile_fail_case("goto jumping into block", R"SOURCE( | 1190 | add_compile_fail_case("goto jumping into block", R"SOURCE( |
| 1191 | fn f() { | 1191 | fn f() { |
| ... | @@ -1406,6 +1406,27 @@ fn f() { | ... | @@ -1406,6 +1406,27 @@ fn f() { |
| 1406 | } | 1406 | } |
| 1407 | )SOURCE", 1, ".tmp_source.zig:3:13: error: unable to evaluate constant expression"); | 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,17 +712,17 @@ three)"; |
| 712 | 712 | ||
| 713 | #attribute("test") | 713 | #attribute("test") |
| 714 | fn simple_generic_fn() { | 714 | fn simple_generic_fn() { |
| 715 | assert(max(i32)(3, -1) == 3); | 715 | assert(max(i32, 3, -1) == 3); |
| 716 | assert(max(f32)(0.123, 0.456) == 0.456); | 716 | assert(max(f32, 0.123, 0.456) == 0.456); |
| 717 | assert(add(2)(3) == 5); | 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 | return if (a > b) a else b; | 721 | return if (a > b) a else b; |
| 722 | } | 722 | } |
| 723 | 723 | ||
| 724 | fn add(a: i32)(b: i32) -> i32 { | 724 | fn add(inline a: i32, b: i32) -> i32 { |
| 725 | return a + b; | 725 | return @const_eval(a) + b; |
| 726 | } | 726 | } |
| 727 | 727 | ||
| 728 | 728 | ||
| ... | @@ -734,23 +734,18 @@ fn constant_equal_function_pointers() { | ... | @@ -734,23 +734,18 @@ fn constant_equal_function_pointers() { |
| 734 | 734 | ||
| 735 | fn empty_fn() {} | 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 | #attribute("test") | 738 | #attribute("test") |
| 744 | fn generic_malloc_free() { | 739 | fn generic_malloc_free() { |
| 745 | const a = %%mem_alloc(u8)(10); | 740 | const a = %%mem_alloc(u8, 10); |
| 746 | mem_free(u8)(a); | 741 | mem_free(u8, a); |
| 747 | } | 742 | } |
| 748 | const some_mem : [100]u8 = undefined; | 743 | const some_mem : [100]u8 = undefined; |
| 749 | #static_eval_enable(false) | 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 | return (&T)(&some_mem[0])[0...n]; | 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 | #attribute("test") | 751 | #attribute("test") |
| ... | @@ -982,11 +977,11 @@ pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 { | ... | @@ -982,11 +977,11 @@ pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 { |
| 982 | 977 | ||
| 983 | #attribute("test") | 978 | #attribute("test") |
| 984 | fn generic_fn_with_implicit_cast() { | 979 | fn generic_fn_with_implicit_cast() { |
| 985 | assert(get_first_byte(u8)([]u8 {13}) == 13); | 980 | assert(get_first_byte(u8, []u8 {13}) == 13); |
| 986 | assert(get_first_byte(u16)([]u16 {0, 13}) == 0); | 981 | assert(get_first_byte(u16, []u16 {0, 13}) == 0); |
| 987 | } | 982 | } |
| 988 | fn get_byte(ptr: ?&u8) -> u8 {*??ptr} | 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 | get_byte((&u8)(&mem[0])) | 985 | get_byte((&u8)(&mem[0])) |
| 991 | } | 986 | } |
| 992 | 987 | ||
| ... | @@ -1651,9 +1646,9 @@ struct GenericDataThing(count: isize) { | ... | @@ -1651,9 +1646,9 @@ struct GenericDataThing(count: isize) { |
| 1651 | 1646 | ||
| 1652 | #attribute("test") | 1647 | #attribute("test") |
| 1653 | fn use_generic_param_in_generic_param() { | 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 | return a + b; | 1652 | return a + b; |
| 1658 | } | 1653 | } |
| 1659 | 1654 |