| author | |
| committer | |
| log | 2dc4ac0e21939c440135131212ac564197c61e30 |
| tree | 8e34d358b152bb82fed363609a49711070d930ea |
| parent | 08eb19456b319cc5b0c287773d52850f12f2bcb5 |
7 files changed, 150 insertions(+), 18 deletions(-)
src/all_types.hpp+4| ... | ... | @@ -1108,6 +1108,8 @@ struct CodeGen { |
| 1108 | 1108 | TypeTableEntry *entry_num_lit_float; |
| 1109 | 1109 | TypeTableEntry *entry_undef; |
| 1110 | 1110 | TypeTableEntry *entry_pure_error; |
| 1111 | TypeTableEntry *entry_os_enum; | |
| 1112 | TypeTableEntry *entry_arch_enum; | |
| 1111 | 1113 | } builtin_types; |
| 1112 | 1114 | |
| 1113 | 1115 | ZigTarget zig_target; |
| ... | ... | @@ -1126,6 +1128,8 @@ struct CodeGen { |
| 1126 | 1128 | Buf triple_str; |
| 1127 | 1129 | bool is_release_build; |
| 1128 | 1130 | bool is_test_build; |
| 1131 | uint32_t target_os_index; | |
| 1132 | uint32_t target_arch_index; | |
| 1129 | 1133 | LLVMTargetMachineRef target_machine; |
| 1130 | 1134 | LLVMZigDIFile *dummy_di_file; |
| 1131 | 1135 | bool is_native_target; |
src/analyze.cpp+71-5| ... | ... | @@ -3526,6 +3526,8 @@ static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockCon |
| 3526 | 3526 | |
| 3527 | 3527 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; |
| 3528 | 3528 | *const_val = *other_const_val; |
| 3529 | // the condition depends on a compile var, so the entire if statement does too | |
| 3530 | const_val->depends_on_compile_var = true; | |
| 3529 | 3531 | return result_type; |
| 3530 | 3532 | } |
| 3531 | 3533 | |
| ... | ... | @@ -4233,6 +4235,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4233 | 4235 | } |
| 4234 | 4236 | |
| 4235 | 4237 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; |
| 4238 | const_val->ok = true; | |
| 4236 | 4239 | const_val->depends_on_compile_var = true; |
| 4237 | 4240 | |
| 4238 | 4241 | if (buf_eql_str(&var_name, "is_big_endian")) { |
| ... | ... | @@ -4242,7 +4245,11 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4242 | 4245 | } else if (buf_eql_str(&var_name, "is_test")) { |
| 4243 | 4246 | return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true); |
| 4244 | 4247 | } else if (buf_eql_str(&var_name, "os")) { |
| 4245 | zig_panic("TODO"); | |
| 4248 | const_val->data.x_enum.tag = g->target_os_index; | |
| 4249 | return g->builtin_types.entry_os_enum; | |
| 4250 | } else if (buf_eql_str(&var_name, "arch")) { | |
| 4251 | const_val->data.x_enum.tag = g->target_arch_index; | |
| 4252 | return g->builtin_types.entry_arch_enum; | |
| 4246 | 4253 | } else { |
| 4247 | 4254 | add_node_error(g, *str_node, |
| 4248 | 4255 | buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name))); |
| ... | ... | @@ -4638,17 +4645,18 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo |
| 4638 | 4645 | static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 4639 | 4646 | TypeTableEntry *expected_type, AstNode *node) |
| 4640 | 4647 | { |
| 4641 | AstNode *expr_node = node->data.switch_expr.expr; | |
| 4642 | TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, expr_node); | |
| 4648 | AstNode **expr_node = &node->data.switch_expr.expr; | |
| 4649 | TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node); | |
| 4643 | 4650 | |
| 4644 | 4651 | int prong_count = node->data.switch_expr.prongs.length; |
| 4645 | 4652 | AstNode **peer_nodes = allocate<AstNode*>(prong_count); |
| 4646 | 4653 | TypeTableEntry **peer_types = allocate<TypeTableEntry*>(prong_count); |
| 4647 | 4654 | |
| 4655 | bool any_errors = false; | |
| 4648 | 4656 | if (expr_type->id == TypeTableEntryIdInvalid) { |
| 4649 | 4657 | return expr_type; |
| 4650 | 4658 | } else if (expr_type->id == TypeTableEntryIdUnreachable) { |
| 4651 | add_node_error(g, first_executing_node(expr_node), | |
| 4659 | add_node_error(g, first_executing_node(*expr_node), | |
| 4652 | 4660 | buf_sprintf("switch on unreachable expression not allowed")); |
| 4653 | 4661 | return g->builtin_types.entry_invalid; |
| 4654 | 4662 | } else { |
| ... | ... | @@ -4666,6 +4674,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4666 | 4674 | if (prong_node->data.switch_prong.items.length == 0) { |
| 4667 | 4675 | if (else_prong) { |
| 4668 | 4676 | add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression")); |
| 4677 | any_errors = true; | |
| 4669 | 4678 | } else { |
| 4670 | 4679 | else_prong = prong_node; |
| 4671 | 4680 | } |
| ... | ... | @@ -4700,14 +4709,17 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4700 | 4709 | add_node_error(g, item_node, |
| 4701 | 4710 | buf_sprintf("duplicate switch value: '%s'", |
| 4702 | 4711 | buf_ptr(type_enum_field->name))); |
| 4712 | any_errors = true; | |
| 4703 | 4713 | } |
| 4704 | 4714 | } else { |
| 4705 | 4715 | add_node_error(g, item_node, |
| 4706 | 4716 | buf_sprintf("enum '%s' has no field '%s'", |
| 4707 | 4717 | buf_ptr(&expr_type->name), buf_ptr(field_name))); |
| 4718 | any_errors = true; | |
| 4708 | 4719 | } |
| 4709 | 4720 | } else { |
| 4710 | 4721 | add_node_error(g, item_node, buf_sprintf("expected enum tag name")); |
| 4722 | any_errors = true; | |
| 4711 | 4723 | } |
| 4712 | 4724 | } else { |
| 4713 | 4725 | TypeTableEntry *item_type = analyze_expression(g, import, context, expr_type, item_node); |
| ... | ... | @@ -4716,6 +4728,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4716 | 4728 | if (!const_val->ok) { |
| 4717 | 4729 | add_node_error(g, item_node, |
| 4718 | 4730 | buf_sprintf("unable to resolve constant expression")); |
| 4731 | any_errors = true; | |
| 4719 | 4732 | } |
| 4720 | 4733 | } |
| 4721 | 4734 | } |
| ... | ... | @@ -4751,11 +4764,64 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, |
| 4751 | 4764 | add_node_error(g, node, |
| 4752 | 4765 | buf_sprintf("enumeration value '%s' not handled in switch", |
| 4753 | 4766 | buf_ptr(expr_type->data.enumeration.fields[i].name))); |
| 4767 | any_errors = true; | |
| 4768 | } | |
| 4769 | } | |
| 4770 | } | |
| 4771 | } | |
| 4772 | ||
| 4773 | if (any_errors) { | |
| 4774 | return g->builtin_types.entry_invalid; | |
| 4775 | } | |
| 4776 | ||
| 4777 | if (prong_count == 0) { | |
| 4778 | add_node_error(g, node, buf_sprintf("switch statement has no prongs")); | |
| 4779 | return g->builtin_types.entry_invalid; | |
| 4780 | } | |
| 4781 | ||
| 4782 | TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node, | |
| 4783 | peer_nodes, peer_types, prong_count); | |
| 4784 | ||
| 4785 | if (resolved_type->id == TypeTableEntryIdInvalid) { | |
| 4786 | return resolved_type; | |
| 4787 | } | |
| 4788 | ||
| 4789 | ConstExprValue *expr_val = &get_resolved_expr(*expr_node)->const_val; | |
| 4790 | if (!expr_val->ok) { | |
| 4791 | return resolved_type; | |
| 4792 | } | |
| 4793 | ||
| 4794 | if (expr_val->ok && !expr_val->depends_on_compile_var) { | |
| 4795 | add_node_error(g, first_executing_node(*expr_node), | |
| 4796 | buf_sprintf("value is constant; unnecessary switch statement")); | |
| 4797 | } | |
| 4798 | ||
| 4799 | if (!expr_val->ok) { | |
| 4800 | return resolved_type; | |
| 4801 | } | |
| 4802 | ||
| 4803 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | |
| 4804 | ||
| 4805 | for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { | |
| 4806 | AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); | |
| 4807 | for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) { | |
| 4808 | AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); | |
| 4809 | if (expr_type->id == TypeTableEntryIdEnum) { | |
| 4810 | TypeEnumField *type_enum_field = item_node->data.symbol_expr.enum_field; | |
| 4811 | if (expr_val->data.x_enum.tag == type_enum_field->value) { | |
| 4812 | *const_val = get_resolved_expr(peer_nodes[prong_i])->const_val; | |
| 4813 | const_val->ok = true; | |
| 4814 | // the target expr depends on a compile var, so the entire if statement does too | |
| 4815 | const_val->depends_on_compile_var = true; | |
| 4816 | return resolved_type; | |
| 4754 | 4817 | } |
| 4818 | } else { | |
| 4819 | zig_panic("TODO determine if const exprs are equal"); | |
| 4755 | 4820 | } |
| 4756 | 4821 | } |
| 4757 | 4822 | } |
| 4758 | return resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count); | |
| 4823 | ||
| 4824 | zig_unreachable(); | |
| 4759 | 4825 | } |
| 4760 | 4826 | |
| 4761 | 4827 | static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
src/codegen.cpp+58-1| ... | ... | @@ -352,10 +352,11 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { |
| 352 | 352 | case BuiltinFnIdMinValue: |
| 353 | 353 | case BuiltinFnIdMaxValue: |
| 354 | 354 | case BuiltinFnIdMemberCount: |
| 355 | case BuiltinFnIdCompileVar: | |
| 356 | 355 | case BuiltinFnIdConstEval: |
| 357 | 356 | // caught by constant expression eval codegen |
| 358 | 357 | zig_unreachable(); |
| 358 | case BuiltinFnIdCompileVar: | |
| 359 | return nullptr; | |
| 359 | 360 | } |
| 360 | 361 | zig_unreachable(); |
| 361 | 362 | } |
| ... | ... | @@ -3443,6 +3444,62 @@ static void define_builtin_types(CodeGen *g) { |
| 3443 | 3444 | g->builtin_types.entry_i16 = get_int_type(g, true, 16); |
| 3444 | 3445 | g->builtin_types.entry_i32 = get_int_type(g, true, 32); |
| 3445 | 3446 | g->builtin_types.entry_i64 = get_int_type(g, true, 64); |
| 3447 | ||
| 3448 | { | |
| 3449 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); | |
| 3450 | entry->zero_bits = true; // only allowed at compile time | |
| 3451 | buf_init_from_str(&entry->name, "@OS"); | |
| 3452 | uint32_t field_count = target_os_count(); | |
| 3453 | entry->data.enumeration.field_count = field_count; | |
| 3454 | entry->data.enumeration.fields = allocate<TypeEnumField>(field_count); | |
| 3455 | for (uint32_t i = 0; i < field_count; i += 1) { | |
| 3456 | TypeEnumField *type_enum_field = &entry->data.enumeration.fields[i]; | |
| 3457 | ZigLLVM_OSType os_type = get_target_os(i); | |
| 3458 | type_enum_field->name = buf_create_from_str(get_target_os_name(os_type)); | |
| 3459 | type_enum_field->value = i; | |
| 3460 | ||
| 3461 | if (os_type == g->zig_target.os) { | |
| 3462 | g->target_os_index = i; | |
| 3463 | } | |
| 3464 | } | |
| 3465 | entry->data.enumeration.complete = true; | |
| 3466 | ||
| 3467 | TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); | |
| 3468 | entry->data.enumeration.tag_type = tag_type_entry; | |
| 3469 | ||
| 3470 | g->builtin_types.entry_os_enum = entry; | |
| 3471 | } | |
| 3472 | ||
| 3473 | { | |
| 3474 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); | |
| 3475 | entry->zero_bits = true; // only allowed at compile time | |
| 3476 | buf_init_from_str(&entry->name, "@Arch"); | |
| 3477 | uint32_t field_count = target_arch_count(); | |
| 3478 | entry->data.enumeration.field_count = field_count; | |
| 3479 | entry->data.enumeration.fields = allocate<TypeEnumField>(field_count); | |
| 3480 | for (uint32_t i = 0; i < field_count; i += 1) { | |
| 3481 | TypeEnumField *type_enum_field = &entry->data.enumeration.fields[i]; | |
| 3482 | const ArchType *arch_type = get_target_arch(i); | |
| 3483 | type_enum_field->name = buf_alloc(); | |
| 3484 | buf_resize(type_enum_field->name, 50); | |
| 3485 | get_arch_name(buf_ptr(type_enum_field->name), arch_type); | |
| 3486 | buf_resize(type_enum_field->name, strlen(buf_ptr(type_enum_field->name))); | |
| 3487 | ||
| 3488 | type_enum_field->value = i; | |
| 3489 | ||
| 3490 | if (arch_type->arch == g->zig_target.arch.arch && | |
| 3491 | arch_type->sub_arch == g->zig_target.arch.sub_arch) | |
| 3492 | { | |
| 3493 | g->target_arch_index = i; | |
| 3494 | } | |
| 3495 | } | |
| 3496 | entry->data.enumeration.complete = true; | |
| 3497 | ||
| 3498 | TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); | |
| 3499 | entry->data.enumeration.tag_type = tag_type_entry; | |
| 3500 | ||
| 3501 | g->builtin_types.entry_arch_enum = entry; | |
| 3502 | } | |
| 3446 | 3503 | } |
| 3447 | 3504 | |
| 3448 | 3505 |
src/main.cpp+3-3| ... | ... | @@ -58,11 +58,11 @@ static int print_target_list(FILE *f) { |
| 58 | 58 | int arch_count = target_arch_count(); |
| 59 | 59 | for (int arch_i = 0; arch_i < arch_count; arch_i += 1) { |
| 60 | 60 | const ArchType *arch = get_target_arch(arch_i); |
| 61 | const char *sub_arch_str = (arch->sub_arch == ZigLLVM_NoSubArch) ? | |
| 62 | "" : ZigLLVMGetSubArchTypeName(arch->sub_arch); | |
| 61 | char arch_name[50]; | |
| 62 | get_arch_name(arch_name, arch); | |
| 63 | 63 | const char *native_str = (native.arch.arch == arch->arch && native.arch.sub_arch == arch->sub_arch) ? |
| 64 | 64 | " (native)" : ""; |
| 65 | fprintf(f, " %s%s%s\n", ZigLLVMGetArchTypeName(arch->arch), sub_arch_str, native_str); | |
| 65 | fprintf(f, " %s%s\n", arch_name, native_str); | |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | 68 | fprintf(f, "\nOperating Systems:\n"); |
src/target.cpp+10-3| ... | ... | @@ -181,13 +181,20 @@ void get_unknown_target(ZigTarget *target) { |
| 181 | 181 | target->oformat = ZigLLVM_UnknownObjectFormat; |
| 182 | 182 | } |
| 183 | 183 | |
| 184 | static void get_arch_name_raw(char *out_str, ZigLLVM_ArchType arch, ZigLLVM_SubArchType sub_arch) { | |
| 185 | const char *sub_str = (sub_arch == ZigLLVM_NoSubArch) ? "" : ZigLLVMGetSubArchTypeName(sub_arch); | |
| 186 | sprintf(out_str, "%s%s", ZigLLVMGetArchTypeName(arch), sub_str); | |
| 187 | } | |
| 188 | ||
| 189 | void get_arch_name(char *out_str, const ArchType *arch) { | |
| 190 | return get_arch_name_raw(out_str, arch->arch, arch->sub_arch); | |
| 191 | } | |
| 192 | ||
| 184 | 193 | int parse_target_arch(const char *str, ArchType *out_arch) { |
| 185 | 194 | for (int i = 0; i < array_length(arch_list); i += 1) { |
| 186 | 195 | const ArchType *arch = &arch_list[i]; |
| 187 | 196 | char arch_name[50]; |
| 188 | const char *sub_str = (arch->sub_arch == ZigLLVM_NoSubArch) ? | |
| 189 | "" : ZigLLVMGetSubArchTypeName(arch->sub_arch); | |
| 190 | sprintf(arch_name, "%s%s", ZigLLVMGetArchTypeName(arch->arch), sub_str); | |
| 197 | get_arch_name_raw(arch_name, arch->arch, arch->sub_arch); | |
| 191 | 198 | if (strcmp(arch_name, str) == 0) { |
| 192 | 199 | *out_arch = *arch; |
| 193 | 200 | return 0; |
src/target.hpp+1| ... | ... | @@ -27,6 +27,7 @@ struct ZigTarget { |
| 27 | 27 | |
| 28 | 28 | int target_arch_count(void); |
| 29 | 29 | const ArchType *get_target_arch(int index); |
| 30 | void get_arch_name(char *out_str, const ArchType *arch); | |
| 30 | 31 | |
| 31 | 32 | int target_vendor_count(void); |
| 32 | 33 | ZigLLVM_VendorType get_target_vendor(int index); |
test/self_hosted.zig+3-6| ... | ... | @@ -308,11 +308,6 @@ fn non_const_cast_bool_to_int(t: bool, f: bool) { |
| 308 | 308 | #attribute("test") |
| 309 | 309 | fn switch_on_enum() { |
| 310 | 310 | const fruit = Fruit.Orange; |
| 311 | switch (fruit) { | |
| 312 | Apple => unreachable{}, | |
| 313 | Orange => {}, | |
| 314 | Banana => unreachable{}, | |
| 315 | } | |
| 316 | 311 | non_const_switch_on_enum(fruit); |
| 317 | 312 | } |
| 318 | 313 | enum Fruit { |
| ... | ... | @@ -330,7 +325,9 @@ fn non_const_switch_on_enum(fruit: Fruit) { |
| 330 | 325 | |
| 331 | 326 | #attribute("test") |
| 332 | 327 | fn switch_statement() { |
| 333 | const foo = SwitchStatmentFoo.C; | |
| 328 | non_const_switch(SwitchStatmentFoo.C); | |
| 329 | } | |
| 330 | fn non_const_switch(foo: SwitchStatmentFoo) { | |
| 334 | 331 | const val: i32 = switch (foo) { |
| 335 | 332 | A => 1, |
| 336 | 333 | B => 2, |