| author | |
| committer | |
| log | 77ffb5075bd550893b9f6ac99f151b1d55a8040e |
| tree | d7cde8b3527f2458f216562f42b5957fefc477e9 |
| parent | 91101f08c24a931e8e0ecbe46d80df759135332c |
* Directives can have arbitrary expressions as parameters
* Fix switch statement not generating code sometimes
* Rename "main" fn in bootstrap.zig to "zig_user_main" to
avoid name collisions
* codegen: fix badref when unreachable is last thing in an
expression
* support #condition directive on exported functions8 files changed, 208 insertions(+), 141 deletions(-)
doc/langref.md+1-1| ... | @@ -31,7 +31,7 @@ ExternDecl = "extern" (FnProto | VariableDeclaration) ";" | ... | @@ -31,7 +31,7 @@ ExternDecl = "extern" (FnProto | VariableDeclaration) ";" |
| 31 | 31 | ||
| 32 | FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr) | 32 | FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr) |
| 33 | 33 | ||
| 34 | Directive = "#" "Symbol" "(" "String" ")" | 34 | Directive = "#" "Symbol" "(" Expression ")" |
| 35 | 35 | ||
| 36 | VisibleMod = "pub" | "export" | 36 | VisibleMod = "pub" | "export" |
| 37 | 37 |
src/all_types.hpp+2-1| ... | @@ -427,7 +427,7 @@ struct AstNodeFieldAccessExpr { | ... | @@ -427,7 +427,7 @@ struct AstNodeFieldAccessExpr { |
| 427 | 427 | ||
| 428 | struct AstNodeDirective { | 428 | struct AstNodeDirective { |
| 429 | Buf name; | 429 | Buf name; |
| 430 | Buf param; | 430 | AstNode *expr; |
| 431 | }; | 431 | }; |
| 432 | 432 | ||
| 433 | struct AstNodeRootExportDecl { | 433 | struct AstNodeRootExportDecl { |
| ... | @@ -526,6 +526,7 @@ struct AstNodeSwitchExpr { | ... | @@ -526,6 +526,7 @@ struct AstNodeSwitchExpr { |
| 526 | 526 | ||
| 527 | // populated by semantic analyzer | 527 | // populated by semantic analyzer |
| 528 | Expr resolved_expr; | 528 | Expr resolved_expr; |
| 529 | int const_chosen_prong_index; | ||
| 529 | }; | 530 | }; |
| 530 | 531 | ||
| 531 | struct AstNodeSwitchProng { | 532 | struct AstNodeSwitchProng { |
src/analyze.cpp+110-56| ... | @@ -816,6 +816,53 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor | ... | @@ -816,6 +816,53 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor |
| 816 | return get_fn_type(g, &fn_type_id); | 816 | return get_fn_type(g, &fn_type_id); |
| 817 | } | 817 | } |
| 818 | 818 | ||
| 819 | static Buf *resolve_const_expr_str(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode **node) { | ||
| 820 | TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); | ||
| 821 | TypeTableEntry *resolved_type = analyze_expression(g, import, context, str_type, *node); | ||
| 822 | |||
| 823 | if (resolved_type->id == TypeTableEntryIdInvalid) { | ||
| 824 | return nullptr; | ||
| 825 | } | ||
| 826 | |||
| 827 | ConstExprValue *const_str_val = &get_resolved_expr(*node)->const_val; | ||
| 828 | |||
| 829 | if (!const_str_val->ok) { | ||
| 830 | add_node_error(g, *node, buf_sprintf("unable to resolve constant expression")); | ||
| 831 | return nullptr; | ||
| 832 | } | ||
| 833 | |||
| 834 | ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0]; | ||
| 835 | uint64_t len = ptr_field->data.x_ptr.len; | ||
| 836 | Buf *result = buf_alloc(); | ||
| 837 | for (uint64_t i = 0; i < len; i += 1) { | ||
| 838 | ConstExprValue *char_val = ptr_field->data.x_ptr.ptr[i]; | ||
| 839 | uint64_t big_c = char_val->data.x_bignum.data.x_uint; | ||
| 840 | assert(big_c <= UINT8_MAX); | ||
| 841 | uint8_t c = big_c; | ||
| 842 | buf_append_char(result, c); | ||
| 843 | } | ||
| 844 | return result; | ||
| 845 | } | ||
| 846 | |||
| 847 | static bool resolve_const_expr_bool(CodeGen *g, ImportTableEntry *import, BlockContext *context, | ||
| 848 | AstNode **node, bool *value) | ||
| 849 | { | ||
| 850 | TypeTableEntry *resolved_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, *node); | ||
| 851 | |||
| 852 | if (resolved_type->id == TypeTableEntryIdInvalid) { | ||
| 853 | return false; | ||
| 854 | } | ||
| 855 | |||
| 856 | ConstExprValue *const_bool_val = &get_resolved_expr(*node)->const_val; | ||
| 857 | |||
| 858 | if (!const_bool_val->ok) { | ||
| 859 | add_node_error(g, *node, buf_sprintf("unable to resolve constant expression")); | ||
| 860 | return false; | ||
| 861 | } | ||
| 862 | |||
| 863 | *value = const_bool_val->data.x_bool; | ||
| 864 | return true; | ||
| 865 | } | ||
| 819 | 866 | ||
| 820 | static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, | 867 | static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, |
| 821 | ImportTableEntry *import) | 868 | ImportTableEntry *import) |
| ... | @@ -839,23 +886,38 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t | ... | @@ -839,23 +886,38 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 839 | Buf *name = &directive_node->data.directive.name; | 886 | Buf *name = &directive_node->data.directive.name; |
| 840 | 887 | ||
| 841 | if (buf_eql_str(name, "attribute")) { | 888 | if (buf_eql_str(name, "attribute")) { |
| 842 | Buf *attr_name = &directive_node->data.directive.param; | ||
| 843 | if (fn_table_entry->fn_def_node) { | 889 | if (fn_table_entry->fn_def_node) { |
| 844 | if (buf_eql_str(attr_name, "naked")) { | 890 | Buf *attr_name = resolve_const_expr_str(g, import, import->block_context, |
| 845 | is_naked = true; | 891 | &directive_node->data.directive.expr); |
| 846 | } else if (buf_eql_str(attr_name, "cold")) { | 892 | if (attr_name) { |
| 847 | is_cold = true; | 893 | if (buf_eql_str(attr_name, "naked")) { |
| 848 | } else if (buf_eql_str(attr_name, "test")) { | 894 | is_naked = true; |
| 849 | is_test = true; | 895 | } else if (buf_eql_str(attr_name, "cold")) { |
| 850 | g->test_fn_count += 1; | 896 | is_cold = true; |
| 851 | } else { | 897 | } else if (buf_eql_str(attr_name, "test")) { |
| 852 | add_node_error(g, directive_node, | 898 | is_test = true; |
| 853 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | 899 | g->test_fn_count += 1; |
| 900 | } else { | ||
| 901 | add_node_error(g, directive_node, | ||
| 902 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | ||
| 903 | } | ||
| 854 | } | 904 | } |
| 855 | } else { | 905 | } else { |
| 856 | add_node_error(g, directive_node, | 906 | add_node_error(g, directive_node, |
| 857 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | 907 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); |
| 858 | } | 908 | } |
| 909 | } else if (buf_eql_str(name, "condition")) { | ||
| 910 | if (fn_proto->visib_mod == VisibModExport) { | ||
| 911 | bool include; | ||
| 912 | bool ok = resolve_const_expr_bool(g, import, import->block_context, | ||
| 913 | &directive_node->data.directive.expr, &include); | ||
| 914 | if (ok && !include) { | ||
| 915 | fn_proto->visib_mod = VisibModPub; | ||
| 916 | } | ||
| 917 | } else { | ||
| 918 | add_node_error(g, directive_node, | ||
| 919 | buf_sprintf("#condition valid only on exported symbols")); | ||
| 920 | } | ||
| 859 | } else { | 921 | } else { |
| 860 | add_node_error(g, directive_node, | 922 | add_node_error(g, directive_node, |
| 861 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | 923 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); |
| ... | @@ -863,6 +925,15 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t | ... | @@ -863,6 +925,15 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 863 | } | 925 | } |
| 864 | } | 926 | } |
| 865 | 927 | ||
| 928 | bool is_internal = (fn_proto->visib_mod != VisibModExport); | ||
| 929 | bool is_c_compat = !is_internal || fn_proto->is_extern; | ||
| 930 | fn_table_entry->internal_linkage = !is_c_compat; | ||
| 931 | if (!is_internal) { | ||
| 932 | fn_table_entry->ref_count += 1; | ||
| 933 | } | ||
| 934 | |||
| 935 | |||
| 936 | |||
| 866 | TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, import->block_context, nullptr, node, | 937 | TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, import->block_context, nullptr, node, |
| 867 | is_naked, is_cold); | 938 | is_naked, is_cold); |
| 868 | 939 | ||
| ... | @@ -1242,8 +1313,6 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, | ... | @@ -1242,8 +1313,6 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, |
| 1242 | 1313 | ||
| 1243 | auto entry = fn_table->maybe_get(proto_name); | 1314 | auto entry = fn_table->maybe_get(proto_name); |
| 1244 | bool skip = false; | 1315 | bool skip = false; |
| 1245 | bool is_internal = (proto_node->data.fn_proto.visib_mod != VisibModExport); | ||
| 1246 | bool is_c_compat = !is_internal || is_extern; | ||
| 1247 | bool is_pub = (proto_node->data.fn_proto.visib_mod != VisibModPrivate); | 1316 | bool is_pub = (proto_node->data.fn_proto.visib_mod != VisibModPrivate); |
| 1248 | if (entry) { | 1317 | if (entry) { |
| 1249 | add_node_error(g, proto_node, | 1318 | add_node_error(g, proto_node, |
| ... | @@ -1263,10 +1332,8 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, | ... | @@ -1263,10 +1332,8 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, |
| 1263 | fn_table_entry->import_entry = import; | 1332 | fn_table_entry->import_entry = import; |
| 1264 | fn_table_entry->proto_node = proto_node; | 1333 | fn_table_entry->proto_node = proto_node; |
| 1265 | fn_table_entry->fn_def_node = fn_def_node; | 1334 | fn_table_entry->fn_def_node = fn_def_node; |
| 1266 | fn_table_entry->internal_linkage = !is_c_compat; | ||
| 1267 | fn_table_entry->is_extern = is_extern; | 1335 | fn_table_entry->is_extern = is_extern; |
| 1268 | fn_table_entry->member_of_struct = struct_type; | 1336 | fn_table_entry->member_of_struct = struct_type; |
| 1269 | fn_table_entry->ref_count = (proto_node->data.fn_proto.visib_mod == VisibModExport) ? 1 : 0; | ||
| 1270 | 1337 | ||
| 1271 | if (struct_type) { | 1338 | if (struct_type) { |
| 1272 | buf_resize(&fn_table_entry->symbol_name, 0); | 1339 | buf_resize(&fn_table_entry->symbol_name, 0); |
| ... | @@ -1290,7 +1357,7 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, | ... | @@ -1290,7 +1357,7 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, |
| 1290 | g->main_fn = fn_table_entry; | 1357 | g->main_fn = fn_table_entry; |
| 1291 | 1358 | ||
| 1292 | if (g->bootstrap_import && !g->is_test_build) { | 1359 | if (g->bootstrap_import && !g->is_test_build) { |
| 1293 | g->bootstrap_import->fn_table.put(proto_name, fn_table_entry); | 1360 | g->bootstrap_import->fn_table.put(buf_create_from_str("zig_user_main"), fn_table_entry); |
| 1294 | } | 1361 | } |
| 1295 | } | 1362 | } |
| 1296 | bool is_test_main_fn = !struct_type && (import == g->test_runner_import) && buf_eql_str(proto_name, "main"); | 1363 | bool is_test_main_fn = !struct_type && (import == g->test_runner_import) && buf_eql_str(proto_name, "main"); |
| ... | @@ -4246,54 +4313,33 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry | ... | @@ -4246,54 +4313,33 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4246 | { | 4313 | { |
| 4247 | AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field; | 4314 | AstNode **str_node = node->data.fn_call_expr.params.at(0)->parent_field; |
| 4248 | 4315 | ||
| 4249 | TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); | 4316 | Buf *var_name = resolve_const_expr_str(g, import, context, str_node); |
| 4250 | TypeTableEntry *resolved_type = analyze_expression(g, import, context, str_type, *str_node); | 4317 | if (!var_name) { |
| 4251 | 4318 | return g->builtin_types.entry_invalid; | |
| 4252 | if (resolved_type->id == TypeTableEntryIdInvalid) { | ||
| 4253 | return resolved_type; | ||
| 4254 | } | ||
| 4255 | |||
| 4256 | ConstExprValue *const_str_val = &get_resolved_expr(*str_node)->const_val; | ||
| 4257 | |||
| 4258 | if (!const_str_val->ok) { | ||
| 4259 | add_node_error(g, *str_node, buf_sprintf("@compile_var requires constant expression")); | ||
| 4260 | return g->builtin_types.entry_void; | ||
| 4261 | } | ||
| 4262 | |||
| 4263 | ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0]; | ||
| 4264 | uint64_t len = ptr_field->data.x_ptr.len; | ||
| 4265 | Buf var_name = BUF_INIT; | ||
| 4266 | buf_resize(&var_name, 0); | ||
| 4267 | for (uint64_t i = 0; i < len; i += 1) { | ||
| 4268 | ConstExprValue *char_val = ptr_field->data.x_ptr.ptr[i]; | ||
| 4269 | uint64_t big_c = char_val->data.x_bignum.data.x_uint; | ||
| 4270 | assert(big_c <= UINT8_MAX); | ||
| 4271 | uint8_t c = big_c; | ||
| 4272 | buf_append_char(&var_name, c); | ||
| 4273 | } | 4319 | } |
| 4274 | 4320 | ||
| 4275 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | 4321 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; |
| 4276 | const_val->ok = true; | 4322 | const_val->ok = true; |
| 4277 | const_val->depends_on_compile_var = true; | 4323 | const_val->depends_on_compile_var = true; |
| 4278 | 4324 | ||
| 4279 | if (buf_eql_str(&var_name, "is_big_endian")) { | 4325 | if (buf_eql_str(var_name, "is_big_endian")) { |
| 4280 | return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true); | 4326 | return resolve_expr_const_val_as_bool(g, node, g->is_big_endian, true); |
| 4281 | } else if (buf_eql_str(&var_name, "is_release")) { | 4327 | } else if (buf_eql_str(var_name, "is_release")) { |
| 4282 | return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true); | 4328 | return resolve_expr_const_val_as_bool(g, node, g->is_release_build, true); |
| 4283 | } else if (buf_eql_str(&var_name, "is_test")) { | 4329 | } else if (buf_eql_str(var_name, "is_test")) { |
| 4284 | return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true); | 4330 | return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true); |
| 4285 | } else if (buf_eql_str(&var_name, "os")) { | 4331 | } else if (buf_eql_str(var_name, "os")) { |
| 4286 | const_val->data.x_enum.tag = g->target_os_index; | 4332 | const_val->data.x_enum.tag = g->target_os_index; |
| 4287 | return g->builtin_types.entry_os_enum; | 4333 | return g->builtin_types.entry_os_enum; |
| 4288 | } else if (buf_eql_str(&var_name, "arch")) { | 4334 | } else if (buf_eql_str(var_name, "arch")) { |
| 4289 | const_val->data.x_enum.tag = g->target_arch_index; | 4335 | const_val->data.x_enum.tag = g->target_arch_index; |
| 4290 | return g->builtin_types.entry_arch_enum; | 4336 | return g->builtin_types.entry_arch_enum; |
| 4291 | } else if (buf_eql_str(&var_name, "environ")) { | 4337 | } else if (buf_eql_str(var_name, "environ")) { |
| 4292 | const_val->data.x_enum.tag = g->target_environ_index; | 4338 | const_val->data.x_enum.tag = g->target_environ_index; |
| 4293 | return g->builtin_types.entry_environ_enum; | 4339 | return g->builtin_types.entry_environ_enum; |
| 4294 | } else { | 4340 | } else { |
| 4295 | add_node_error(g, *str_node, | 4341 | add_node_error(g, *str_node, |
| 4296 | buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name))); | 4342 | buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(var_name))); |
| 4297 | return g->builtin_types.entry_invalid; | 4343 | return g->builtin_types.entry_invalid; |
| 4298 | } | 4344 | } |
| 4299 | } | 4345 | } |
| ... | @@ -4740,7 +4786,8 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -4740,7 +4786,8 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4740 | field_use_counts = allocate<int>(expr_type->data.enumeration.field_count); | 4786 | field_use_counts = allocate<int>(expr_type->data.enumeration.field_count); |
| 4741 | } | 4787 | } |
| 4742 | 4788 | ||
| 4743 | int const_chosen_prong_index = -1; | 4789 | int *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index; |
| 4790 | *const_chosen_prong_index = -1; | ||
| 4744 | AstNode *else_prong = nullptr; | 4791 | AstNode *else_prong = nullptr; |
| 4745 | for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { | 4792 | for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { |
| 4746 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); | 4793 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); |
| ... | @@ -4756,8 +4803,8 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -4756,8 +4803,8 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4756 | } | 4803 | } |
| 4757 | var_type = expr_type; | 4804 | var_type = expr_type; |
| 4758 | var_is_target_expr = true; | 4805 | var_is_target_expr = true; |
| 4759 | if (const_chosen_prong_index == -1) { | 4806 | if (*const_chosen_prong_index == -1 && expr_val->ok) { |
| 4760 | const_chosen_prong_index = prong_i; | 4807 | *const_chosen_prong_index = prong_i; |
| 4761 | } | 4808 | } |
| 4762 | } else { | 4809 | } else { |
| 4763 | bool all_agree_on_var_type = true; | 4810 | bool all_agree_on_var_type = true; |
| ... | @@ -4792,7 +4839,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -4792,7 +4839,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4792 | } | 4839 | } |
| 4793 | if (!any_errors && expr_val->ok) { | 4840 | if (!any_errors && expr_val->ok) { |
| 4794 | if (expr_val->data.x_enum.tag == type_enum_field->value) { | 4841 | if (expr_val->data.x_enum.tag == type_enum_field->value) { |
| 4795 | const_chosen_prong_index = prong_i; | 4842 | *const_chosen_prong_index = prong_i; |
| 4796 | } | 4843 | } |
| 4797 | } | 4844 | } |
| 4798 | } else { | 4845 | } else { |
| ... | @@ -4844,7 +4891,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -4844,7 +4891,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4844 | for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { | 4891 | for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { |
| 4845 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); | 4892 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); |
| 4846 | BlockContext *child_context = prong_node->data.switch_prong.block_context; | 4893 | BlockContext *child_context = prong_node->data.switch_prong.block_context; |
| 4847 | child_context->codegen_excluded = expr_val->ok && (const_chosen_prong_index != prong_i); | 4894 | child_context->codegen_excluded = expr_val->ok && (*const_chosen_prong_index != prong_i); |
| 4848 | 4895 | ||
| 4849 | peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type, | 4896 | peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type, |
| 4850 | prong_node->data.switch_prong.expr); | 4897 | prong_node->data.switch_prong.expr); |
| ... | @@ -4872,10 +4919,9 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -4872,10 +4919,9 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4872 | } | 4919 | } |
| 4873 | 4920 | ||
| 4874 | if (expr_val->ok) { | 4921 | if (expr_val->ok) { |
| 4875 | assert(const_chosen_prong_index != -1); | 4922 | assert(*const_chosen_prong_index != -1); |
| 4876 | 4923 | ||
| 4877 | *const_val = get_resolved_expr(peer_nodes[const_chosen_prong_index])->const_val; | 4924 | *const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val; |
| 4878 | const_val->ok = true; | ||
| 4879 | // the target expr depends on a compile var, | 4925 | // the target expr depends on a compile var, |
| 4880 | // so the entire if statement does too | 4926 | // so the entire if statement does too |
| 4881 | const_val->depends_on_compile_var = true; | 4927 | const_val->depends_on_compile_var = true; |
| ... | @@ -5490,6 +5536,12 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode | ... | @@ -5490,6 +5536,12 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 5490 | AstNode *param = node->data.fn_proto.params.at(i); | 5536 | AstNode *param = node->data.fn_proto.params.at(i); |
| 5491 | collect_expr_decl_deps(g, import, param, decl_node); | 5537 | collect_expr_decl_deps(g, import, param, decl_node); |
| 5492 | } | 5538 | } |
| 5539 | if (node->data.fn_proto.directives) { | ||
| 5540 | for (int i = 0; i < node->data.fn_proto.directives->length; i += 1) { | ||
| 5541 | AstNode *directive = node->data.fn_proto.directives->at(i); | ||
| 5542 | collect_expr_decl_deps(g, import, directive, decl_node); | ||
| 5543 | } | ||
| 5544 | } | ||
| 5493 | collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node); | 5545 | collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node); |
| 5494 | break; | 5546 | break; |
| 5495 | case NodeTypeParamDecl: | 5547 | case NodeTypeParamDecl: |
| ... | @@ -5498,12 +5550,14 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode | ... | @@ -5498,12 +5550,14 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 5498 | case NodeTypeTypeDecl: | 5550 | case NodeTypeTypeDecl: |
| 5499 | collect_expr_decl_deps(g, import, node->data.type_decl.child_type, decl_node); | 5551 | collect_expr_decl_deps(g, import, node->data.type_decl.child_type, decl_node); |
| 5500 | break; | 5552 | break; |
| 5553 | case NodeTypeDirective: | ||
| 5554 | collect_expr_decl_deps(g, import, node->data.directive.expr, decl_node); | ||
| 5555 | break; | ||
| 5501 | case NodeTypeVariableDeclaration: | 5556 | case NodeTypeVariableDeclaration: |
| 5502 | case NodeTypeRootExportDecl: | 5557 | case NodeTypeRootExportDecl: |
| 5503 | case NodeTypeFnDef: | 5558 | case NodeTypeFnDef: |
| 5504 | case NodeTypeRoot: | 5559 | case NodeTypeRoot: |
| 5505 | case NodeTypeFnDecl: | 5560 | case NodeTypeFnDecl: |
| 5506 | case NodeTypeDirective: | ||
| 5507 | case NodeTypeImport: | 5561 | case NodeTypeImport: |
| 5508 | case NodeTypeCImport: | 5562 | case NodeTypeCImport: |
| 5509 | case NodeTypeLabel: | 5563 | case NodeTypeLabel: |
src/ast_render.cpp+4-2| ... | @@ -339,6 +339,7 @@ void ast_print(FILE *f, AstNode *node, int indent) { | ... | @@ -339,6 +339,7 @@ void ast_print(FILE *f, AstNode *node, int indent) { |
| 339 | break; | 339 | break; |
| 340 | case NodeTypeDirective: | 340 | case NodeTypeDirective: |
| 341 | fprintf(f, "%s\n", node_type_str(node->type)); | 341 | fprintf(f, "%s\n", node_type_str(node->type)); |
| 342 | ast_print(f, node->data.directive.expr, indent + 2); | ||
| 342 | break; | 343 | break; |
| 343 | case NodeTypePrefixOpExpr: | 344 | case NodeTypePrefixOpExpr: |
| 344 | fprintf(f, "%s %s\n", node_type_str(node->type), | 345 | fprintf(f, "%s %s\n", node_type_str(node->type), |
| ... | @@ -631,8 +632,9 @@ static void render_node(AstRender *ar, AstNode *node) { | ... | @@ -631,8 +632,9 @@ static void render_node(AstRender *ar, AstNode *node) { |
| 631 | fprintf(ar->f, "}"); | 632 | fprintf(ar->f, "}"); |
| 632 | break; | 633 | break; |
| 633 | case NodeTypeDirective: | 634 | case NodeTypeDirective: |
| 634 | fprintf(ar->f, "#%s(\"%s\")\n", buf_ptr(&node->data.directive.name), | 635 | fprintf(ar->f, "#%s(", buf_ptr(&node->data.directive.name)); |
| 635 | buf_ptr(&node->data.directive.param)); | 636 | render_node(ar, node->data.directive.expr); |
| 637 | fprintf(ar->f, ")\n"); | ||
| 636 | break; | 638 | break; |
| 637 | case NodeTypeReturnExpr: | 639 | case NodeTypeReturnExpr: |
| 638 | zig_panic("TODO"); | 640 | zig_panic("TODO"); |
src/codegen.cpp+24-11| ... | @@ -2150,7 +2150,8 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) { | ... | @@ -2150,7 +2150,8 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) { |
| 2150 | if (!g->is_release_build) { | 2150 | if (!g->is_release_build) { |
| 2151 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); | 2151 | LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 2152 | } | 2152 | } |
| 2153 | return LLVMBuildUnreachable(g->builder); | 2153 | LLVMBuildUnreachable(g->builder); |
| 2154 | return nullptr; | ||
| 2154 | } else if (type_entry->id == TypeTableEntryIdVoid) { | 2155 | } else if (type_entry->id == TypeTableEntryIdVoid) { |
| 2155 | assert(node->data.container_init_expr.entries.length == 0); | 2156 | assert(node->data.container_init_expr.entries.length == 0); |
| 2156 | return nullptr; | 2157 | return nullptr; |
| ... | @@ -2487,6 +2488,13 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { | ... | @@ -2487,6 +2488,13 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { |
| 2487 | static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { | 2488 | static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { |
| 2488 | assert(node->type == NodeTypeSwitchExpr); | 2489 | assert(node->type == NodeTypeSwitchExpr); |
| 2489 | 2490 | ||
| 2491 | if (node->data.switch_expr.const_chosen_prong_index >= 0) { | ||
| 2492 | AstNode *prong_node = node->data.switch_expr.prongs.at(node->data.switch_expr.const_chosen_prong_index); | ||
| 2493 | assert(prong_node->type == NodeTypeSwitchProng); | ||
| 2494 | AstNode *prong_expr = prong_node->data.switch_prong.expr; | ||
| 2495 | return gen_expr(g, prong_expr); | ||
| 2496 | } | ||
| 2497 | |||
| 2490 | TypeTableEntry *target_type = get_expr_type(node->data.switch_expr.expr); | 2498 | TypeTableEntry *target_type = get_expr_type(node->data.switch_expr.expr); |
| 2491 | LLVMValueRef target_value_handle = gen_expr(g, node->data.switch_expr.expr); | 2499 | LLVMValueRef target_value_handle = gen_expr(g, node->data.switch_expr.expr); |
| 2492 | LLVMValueRef target_value; | 2500 | LLVMValueRef target_value; |
| ... | @@ -3877,18 +3885,23 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, | ... | @@ -3877,18 +3885,23 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, |
| 3877 | for (int i = 0; i < directives->length; i += 1) { | 3885 | for (int i = 0; i < directives->length; i += 1) { |
| 3878 | AstNode *directive_node = directives->at(i); | 3886 | AstNode *directive_node = directives->at(i); |
| 3879 | Buf *name = &directive_node->data.directive.name; | 3887 | Buf *name = &directive_node->data.directive.name; |
| 3880 | Buf *param = &directive_node->data.directive.param; | 3888 | AstNode *param_node = directive_node->data.directive.expr; |
| 3881 | if (buf_eql_str(name, "version")) { | 3889 | assert(param_node->type == NodeTypeStringLiteral); |
| 3882 | set_root_export_version(g, param, directive_node); | 3890 | Buf *param = &param_node->data.string_literal.buf; |
| 3883 | } else if (buf_eql_str(name, "link")) { | 3891 | |
| 3884 | if (buf_eql_str(param, "c")) { | 3892 | if (param) { |
| 3885 | g->link_libc = true; | 3893 | if (buf_eql_str(name, "version")) { |
| 3894 | set_root_export_version(g, param, directive_node); | ||
| 3895 | } else if (buf_eql_str(name, "link")) { | ||
| 3896 | if (buf_eql_str(param, "c")) { | ||
| 3897 | g->link_libc = true; | ||
| 3898 | } else { | ||
| 3899 | g->link_libs.append(param); | ||
| 3900 | } | ||
| 3886 | } else { | 3901 | } else { |
| 3887 | g->link_libs.append(param); | 3902 | add_node_error(g, directive_node, |
| 3903 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | ||
| 3888 | } | 3904 | } |
| 3889 | } else { | ||
| 3890 | add_node_error(g, directive_node, | ||
| 3891 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | ||
| 3892 | } | 3905 | } |
| 3893 | } | 3906 | } |
| 3894 | } | 3907 | } |
src/link.cpp+39-44| ... | @@ -580,35 +580,33 @@ static void construct_linker_job_darwin(LinkJob *lj) { | ... | @@ -580,35 +580,33 @@ static void construct_linker_job_darwin(LinkJob *lj) { |
| 580 | lj->args.append("-o"); | 580 | lj->args.append("-o"); |
| 581 | lj->args.append(buf_ptr(&lj->out_file)); | 581 | lj->args.append(buf_ptr(&lj->out_file)); |
| 582 | 582 | ||
| 583 | if (lj->link_in_crt) { | 583 | if (shared) { |
| 584 | if (shared) { | 584 | zig_panic("TODO"); |
| 585 | zig_panic("TODO"); | 585 | } else if (g->is_static) { |
| 586 | } else if (g->is_static) { | 586 | lj->args.append("-lcrt0.o"); |
| 587 | lj->args.append("-lcrt0.o"); | 587 | } else { |
| 588 | } else { | 588 | switch (platform.kind) { |
| 589 | switch (platform.kind) { | 589 | case MacOS: |
| 590 | case MacOS: | 590 | if (darwin_version_lt(&platform, 10, 5)) { |
| 591 | if (darwin_version_lt(&platform, 10, 5)) { | 591 | lj->args.append("-lcrt1.o"); |
| 592 | lj->args.append("-lcrt1.o"); | 592 | } else if (darwin_version_lt(&platform, 10, 6)) { |
| 593 | } else if (darwin_version_lt(&platform, 10, 6)) { | 593 | lj->args.append("-lcrt1.10.5.o"); |
| 594 | lj->args.append("-lcrt1.10.5.o"); | 594 | } else if (darwin_version_lt(&platform, 10, 8)) { |
| 595 | } else if (darwin_version_lt(&platform, 10, 8)) { | 595 | lj->args.append("-lcrt1.10.6.o"); |
| 596 | lj->args.append("-lcrt1.10.6.o"); | 596 | } |
| 597 | } | 597 | break; |
| 598 | break; | 598 | case IPhoneOS: |
| 599 | case IPhoneOS: | 599 | if (g->zig_target.arch.arch == ZigLLVM_aarch64) { |
| 600 | if (g->zig_target.arch.arch == ZigLLVM_aarch64) { | 600 | // iOS does not need any crt1 files for arm64 |
| 601 | // iOS does not need any crt1 files for arm64 | 601 | } else if (darwin_version_lt(&platform, 3, 1)) { |
| 602 | } else if (darwin_version_lt(&platform, 3, 1)) { | 602 | lj->args.append("-lcrt1.o"); |
| 603 | lj->args.append("-lcrt1.o"); | 603 | } else if (darwin_version_lt(&platform, 6, 0)) { |
| 604 | } else if (darwin_version_lt(&platform, 6, 0)) { | 604 | lj->args.append("-lcrt1.3.1.o"); |
| 605 | lj->args.append("-lcrt1.3.1.o"); | 605 | } |
| 606 | } | 606 | break; |
| 607 | break; | 607 | case IPhoneOSSimulator: |
| 608 | case IPhoneOSSimulator: | 608 | // no crt1.o needed |
| 609 | // no crt1.o needed | 609 | break; |
| 610 | break; | ||
| 611 | } | ||
| 612 | } | 610 | } |
| 613 | } | 611 | } |
| 614 | 612 | ||
| ... | @@ -620,29 +618,26 @@ static void construct_linker_job_darwin(LinkJob *lj) { | ... | @@ -620,29 +618,26 @@ static void construct_linker_job_darwin(LinkJob *lj) { |
| 620 | 618 | ||
| 621 | lj->args.append((const char *)buf_ptr(&lj->out_file_o)); | 619 | lj->args.append((const char *)buf_ptr(&lj->out_file_o)); |
| 622 | 620 | ||
| 623 | if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { | ||
| 624 | Buf *builtin_o_path = build_o(g, "builtin"); | ||
| 625 | lj->args.append(buf_ptr(builtin_o_path)); | ||
| 626 | } | ||
| 627 | |||
| 628 | for (int i = 0; i < g->link_libs.length; i += 1) { | 621 | for (int i = 0; i < g->link_libs.length; i += 1) { |
| 629 | Buf *link_lib = g->link_libs.at(i); | 622 | Buf *link_lib = g->link_libs.at(i); |
| 630 | Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib)); | 623 | Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib)); |
| 631 | lj->args.append(buf_ptr(arg)); | 624 | lj->args.append(buf_ptr(arg)); |
| 632 | } | 625 | } |
| 633 | 626 | ||
| 634 | if (g->link_libc) { | 627 | // on Darwin, libSystem has libc in it, but also you have to use it |
| 635 | lj->args.append("-lSystem"); | 628 | // to make syscalls because the syscall numbers are not documented |
| 629 | // and change between versions. | ||
| 630 | // so we always link against libSystem | ||
| 631 | lj->args.append("-lSystem"); | ||
| 636 | 632 | ||
| 637 | if (platform.kind == MacOS) { | 633 | if (platform.kind == MacOS) { |
| 638 | if (darwin_version_lt(&platform, 10, 5)) { | 634 | if (darwin_version_lt(&platform, 10, 5)) { |
| 639 | lj->args.append("-lgcc_s.10.4"); | 635 | lj->args.append("-lgcc_s.10.4"); |
| 640 | } else if (darwin_version_lt(&platform, 10, 6)) { | 636 | } else if (darwin_version_lt(&platform, 10, 6)) { |
| 641 | lj->args.append("-lgcc_s.10.5"); | 637 | lj->args.append("-lgcc_s.10.5"); |
| 642 | } | ||
| 643 | } else { | ||
| 644 | zig_panic("TODO"); | ||
| 645 | } | 638 | } |
| 639 | } else { | ||
| 640 | zig_panic("TODO"); | ||
| 646 | } | 641 | } |
| 647 | 642 | ||
| 648 | } | 643 | } |
src/parser.cpp+8-21| ... | @@ -499,6 +499,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo | ... | @@ -499,6 +499,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo |
| 499 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory, | 499 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory, |
| 500 | ZigList<AstNode*> *directives, VisibMod visib_mod); | 500 | ZigList<AstNode*> *directives, VisibMod visib_mod); |
| 501 | static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index); | 501 | static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index); |
| 502 | static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool mandatory); | ||
| 502 | 503 | ||
| 503 | static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { | 504 | static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { |
| 504 | if (token->id == token_id) { | 505 | if (token->id == token_id) { |
| ... | @@ -517,33 +518,19 @@ static Token *ast_eat_token(ParseContext *pc, int *token_index, TokenId token_id | ... | @@ -517,33 +518,19 @@ static Token *ast_eat_token(ParseContext *pc, int *token_index, TokenId token_id |
| 517 | return token; | 518 | return token; |
| 518 | } | 519 | } |
| 519 | 520 | ||
| 520 | 521 | /* | |
| 522 | Directive = "#" "Symbol" "(" Expression ")" | ||
| 523 | */ | ||
| 521 | static AstNode *ast_parse_directive(ParseContext *pc, int *token_index) { | 524 | static AstNode *ast_parse_directive(ParseContext *pc, int *token_index) { |
| 522 | Token *number_sign = &pc->tokens->at(*token_index); | 525 | Token *number_sign = ast_eat_token(pc, token_index, TokenIdNumberSign); |
| 523 | *token_index += 1; | ||
| 524 | ast_expect_token(pc, number_sign, TokenIdNumberSign); | ||
| 525 | 526 | ||
| 526 | AstNode *node = ast_create_node(pc, NodeTypeDirective, number_sign); | 527 | AstNode *node = ast_create_node(pc, NodeTypeDirective, number_sign); |
| 527 | 528 | ||
| 528 | Token *name_symbol = &pc->tokens->at(*token_index); | 529 | Token *name_symbol = ast_eat_token(pc, token_index, TokenIdSymbol); |
| 529 | *token_index += 1; | ||
| 530 | ast_expect_token(pc, name_symbol, TokenIdSymbol); | ||
| 531 | 530 | ||
| 532 | ast_buf_from_token(pc, name_symbol, &node->data.directive.name); | 531 | ast_buf_from_token(pc, name_symbol, &node->data.directive.name); |
| 533 | 532 | ||
| 534 | Token *l_paren = &pc->tokens->at(*token_index); | 533 | node->data.directive.expr = ast_parse_grouped_expr(pc, token_index, true); |
| 535 | *token_index += 1; | ||
| 536 | ast_expect_token(pc, l_paren, TokenIdLParen); | ||
| 537 | |||
| 538 | Token *param_str = &pc->tokens->at(*token_index); | ||
| 539 | *token_index += 1; | ||
| 540 | ast_expect_token(pc, param_str, TokenIdStringLiteral); | ||
| 541 | |||
| 542 | parse_string_literal(pc, param_str, &node->data.directive.param, nullptr, nullptr); | ||
| 543 | |||
| 544 | Token *r_paren = &pc->tokens->at(*token_index); | ||
| 545 | *token_index += 1; | ||
| 546 | ast_expect_token(pc, r_paren, TokenIdRParen); | ||
| 547 | 534 | ||
| 548 | normalize_parent_ptrs(node); | 535 | normalize_parent_ptrs(node); |
| 549 | return node; | 536 | return node; |
| ... | @@ -2741,7 +2728,7 @@ void normalize_parent_ptrs(AstNode *node) { | ... | @@ -2741,7 +2728,7 @@ void normalize_parent_ptrs(AstNode *node) { |
| 2741 | set_list_fields(&node->data.block.statements); | 2728 | set_list_fields(&node->data.block.statements); |
| 2742 | break; | 2729 | break; |
| 2743 | case NodeTypeDirective: | 2730 | case NodeTypeDirective: |
| 2744 | // none | 2731 | set_field(&node->data.directive.expr); |
| 2745 | break; | 2732 | break; |
| 2746 | case NodeTypeReturnExpr: | 2733 | case NodeTypeReturnExpr: |
| 2747 | set_field(&node->data.return_expr.expr); | 2734 | set_field(&node->data.return_expr.expr); |
std/bootstrap.zig+20-5| ... | @@ -1,24 +1,28 @@ | ... | @@ -1,24 +1,28 @@ |
| 1 | import "syscall.zig"; | 1 | import "syscall.zig"; |
| 2 | 2 | ||
| 3 | // The compiler treats this file special by implicitly importing the function `main` | 3 | // The compiler treats this file special by implicitly importing the function `main` |
| 4 | // from the root source file. | 4 | // from the root source file as the symbol `zig_user_main`. |
| 5 | |||
| 6 | const want_start_symbol = switch(@compile_var("os")) { | ||
| 7 | linux => true, | ||
| 8 | else => false, | ||
| 9 | }; | ||
| 10 | const want_main_symbol = !want_start_symbol; | ||
| 5 | 11 | ||
| 6 | var argc: isize = undefined; | 12 | var argc: isize = undefined; |
| 7 | var argv: &&u8 = undefined; | 13 | var argv: &&u8 = undefined; |
| 8 | var env: &&u8 = undefined; | ||
| 9 | 14 | ||
| 10 | #attribute("naked") | 15 | #attribute("naked") |
| 16 | #condition(want_start_symbol) | ||
| 11 | export fn _start() -> unreachable { | 17 | export fn _start() -> unreachable { |
| 12 | switch (@compile_var("arch")) { | 18 | switch (@compile_var("arch")) { |
| 13 | x86_64 => { | 19 | x86_64 => { |
| 14 | argc = asm("mov (%%rsp), %[argc]": [argc] "=r" (-> isize)); | 20 | argc = asm("mov (%%rsp), %[argc]": [argc] "=r" (-> isize)); |
| 15 | argv = asm("lea 0x8(%%rsp), %[argv]": [argv] "=r" (-> &&u8)); | 21 | argv = asm("lea 0x8(%%rsp), %[argv]": [argv] "=r" (-> &&u8)); |
| 16 | env = asm("lea 0x10(%%rsp,[argc],8), %[env]": [env] "=r" (-> &&u8): [argc] "r" (argc)); | ||
| 17 | }, | 22 | }, |
| 18 | i386 => { | 23 | i386 => { |
| 19 | argc = asm("mov (%%esp), %[argc]": [argc] "=r" (-> isize)); | 24 | argc = asm("mov (%%esp), %[argc]": [argc] "=r" (-> isize)); |
| 20 | argv = asm("lea 0x4(%%esp), %[argv]": [argv] "=r" (-> &&u8)); | 25 | argv = asm("lea 0x4(%%esp), %[argv]": [argv] "=r" (-> &&u8)); |
| 21 | env = asm("lea 0x8(%%esp,%[argc],4), %[env]": [env] "=r" (-> &&u8): [argc] "r" (argc)); | ||
| 22 | }, | 26 | }, |
| 23 | else => unreachable{}, | 27 | else => unreachable{}, |
| 24 | } | 28 | } |
| ... | @@ -39,6 +43,17 @@ fn call_main() -> unreachable { | ... | @@ -39,6 +43,17 @@ fn call_main() -> unreachable { |
| 39 | const ptr = argv[i]; | 43 | const ptr = argv[i]; |
| 40 | args[i] = ptr[0...strlen(ptr)]; | 44 | args[i] = ptr[0...strlen(ptr)]; |
| 41 | } | 45 | } |
| 42 | main(args) %% exit(1); | 46 | zig_user_main(args) %% exit(1); |
| 43 | exit(0); | 47 | exit(0); |
| 44 | } | 48 | } |
| 49 | |||
| 50 | #condition(want_main_symbol) | ||
| 51 | export fn main(argc: i32, argv: &&u8) -> i32 { | ||
| 52 | var args: [argc][]u8 = undefined; | ||
| 53 | for (args) |arg, i| { | ||
| 54 | const ptr = argv[i]; | ||
| 55 | args[i] = ptr[0...strlen(ptr)]; | ||
| 56 | } | ||
| 57 | zig_user_main(args) %% return 1; | ||
| 58 | return 0; | ||
| 59 | } |