From 2dc4ac0e21939c440135131212ac564197c61e30 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Feb 2016 18:33:04 -0700 Subject: [PATCH] add @compile_var("os") and @compile_var("arch") --- src/all_types.hpp | 4 +++ src/analyze.cpp | 76 +++++++++++++++++++++++++++++++++++++++++--- src/codegen.cpp | 59 +++++++++++++++++++++++++++++++++- src/main.cpp | 6 ++-- src/target.cpp | 13 ++++++-- src/target.hpp | 1 + test/self_hosted.zig | 9 ++---- 7 files changed, 150 insertions(+), 18 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 492a0e0593..9285e71c29 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1108,6 +1108,8 @@ struct CodeGen { TypeTableEntry *entry_num_lit_float; TypeTableEntry *entry_undef; TypeTableEntry *entry_pure_error; + TypeTableEntry *entry_os_enum; + TypeTableEntry *entry_arch_enum; } builtin_types; ZigTarget zig_target; @@ -1126,6 +1128,8 @@ struct CodeGen { Buf triple_str; bool is_release_build; bool is_test_build; + uint32_t target_os_index; + uint32_t target_arch_index; LLVMTargetMachineRef target_machine; LLVMZigDIFile *dummy_di_file; bool is_native_target; diff --git a/src/analyze.cpp b/src/analyze.cpp index 02056c1beb..6650e0ed06 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3526,6 +3526,8 @@ static TypeTableEntry *analyze_if(CodeGen *g, ImportTableEntry *import, BlockCon ConstExprValue *const_val = &get_resolved_expr(node)->const_val; *const_val = *other_const_val; + // the condition depends on a compile var, so the entire if statement does too + const_val->depends_on_compile_var = true; return result_type; } @@ -4233,6 +4235,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry } ConstExprValue *const_val = &get_resolved_expr(node)->const_val; + const_val->ok = true; const_val->depends_on_compile_var = true; if (buf_eql_str(&var_name, "is_big_endian")) { @@ -4242,7 +4245,11 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry } else if (buf_eql_str(&var_name, "is_test")) { return resolve_expr_const_val_as_bool(g, node, g->is_test_build, true); } else if (buf_eql_str(&var_name, "os")) { - zig_panic("TODO"); + const_val->data.x_enum.tag = g->target_os_index; + return g->builtin_types.entry_os_enum; + } else if (buf_eql_str(&var_name, "arch")) { + const_val->data.x_enum.tag = g->target_arch_index; + return g->builtin_types.entry_arch_enum; } else { add_node_error(g, *str_node, buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(&var_name))); @@ -4638,17 +4645,18 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { - AstNode *expr_node = node->data.switch_expr.expr; - TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, expr_node); + AstNode **expr_node = &node->data.switch_expr.expr; + TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node); int prong_count = node->data.switch_expr.prongs.length; AstNode **peer_nodes = allocate(prong_count); TypeTableEntry **peer_types = allocate(prong_count); + bool any_errors = false; if (expr_type->id == TypeTableEntryIdInvalid) { return expr_type; } else if (expr_type->id == TypeTableEntryIdUnreachable) { - add_node_error(g, first_executing_node(expr_node), + add_node_error(g, first_executing_node(*expr_node), buf_sprintf("switch on unreachable expression not allowed")); return g->builtin_types.entry_invalid; } else { @@ -4666,6 +4674,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, if (prong_node->data.switch_prong.items.length == 0) { if (else_prong) { add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression")); + any_errors = true; } else { else_prong = prong_node; } @@ -4700,14 +4709,17 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, add_node_error(g, item_node, buf_sprintf("duplicate switch value: '%s'", buf_ptr(type_enum_field->name))); + any_errors = true; } } else { add_node_error(g, item_node, buf_sprintf("enum '%s' has no field '%s'", buf_ptr(&expr_type->name), buf_ptr(field_name))); + any_errors = true; } } else { add_node_error(g, item_node, buf_sprintf("expected enum tag name")); + any_errors = true; } } else { 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, if (!const_val->ok) { add_node_error(g, item_node, buf_sprintf("unable to resolve constant expression")); + any_errors = true; } } } @@ -4751,11 +4764,64 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, add_node_error(g, node, buf_sprintf("enumeration value '%s' not handled in switch", buf_ptr(expr_type->data.enumeration.fields[i].name))); + any_errors = true; } } } } - return resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count); + + if (any_errors) { + return g->builtin_types.entry_invalid; + } + + if (prong_count == 0) { + add_node_error(g, node, buf_sprintf("switch statement has no prongs")); + return g->builtin_types.entry_invalid; + } + + TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node, + peer_nodes, peer_types, prong_count); + + if (resolved_type->id == TypeTableEntryIdInvalid) { + return resolved_type; + } + + ConstExprValue *expr_val = &get_resolved_expr(*expr_node)->const_val; + if (!expr_val->ok) { + return resolved_type; + } + + if (expr_val->ok && !expr_val->depends_on_compile_var) { + add_node_error(g, first_executing_node(*expr_node), + buf_sprintf("value is constant; unnecessary switch statement")); + } + + if (!expr_val->ok) { + return resolved_type; + } + + ConstExprValue *const_val = &get_resolved_expr(node)->const_val; + + for (int prong_i = 0; prong_i < prong_count; prong_i += 1) { + AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); + for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) { + AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); + if (expr_type->id == TypeTableEntryIdEnum) { + TypeEnumField *type_enum_field = item_node->data.symbol_expr.enum_field; + if (expr_val->data.x_enum.tag == type_enum_field->value) { + *const_val = get_resolved_expr(peer_nodes[prong_i])->const_val; + const_val->ok = true; + // the target expr depends on a compile var, so the entire if statement does too + const_val->depends_on_compile_var = true; + return resolved_type; + } + } else { + zig_panic("TODO determine if const exprs are equal"); + } + } + } + + zig_unreachable(); } static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, diff --git a/src/codegen.cpp b/src/codegen.cpp index c6fd87fa5d..4dc5c18557 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -352,10 +352,11 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { case BuiltinFnIdMinValue: case BuiltinFnIdMaxValue: case BuiltinFnIdMemberCount: - case BuiltinFnIdCompileVar: case BuiltinFnIdConstEval: // caught by constant expression eval codegen zig_unreachable(); + case BuiltinFnIdCompileVar: + return nullptr; } zig_unreachable(); } @@ -3443,6 +3444,62 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_i16 = get_int_type(g, true, 16); g->builtin_types.entry_i32 = get_int_type(g, true, 32); g->builtin_types.entry_i64 = get_int_type(g, true, 64); + + { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); + entry->zero_bits = true; // only allowed at compile time + buf_init_from_str(&entry->name, "@OS"); + uint32_t field_count = target_os_count(); + entry->data.enumeration.field_count = field_count; + entry->data.enumeration.fields = allocate(field_count); + for (uint32_t i = 0; i < field_count; i += 1) { + TypeEnumField *type_enum_field = &entry->data.enumeration.fields[i]; + ZigLLVM_OSType os_type = get_target_os(i); + type_enum_field->name = buf_create_from_str(get_target_os_name(os_type)); + type_enum_field->value = i; + + if (os_type == g->zig_target.os) { + g->target_os_index = i; + } + } + entry->data.enumeration.complete = true; + + TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); + entry->data.enumeration.tag_type = tag_type_entry; + + g->builtin_types.entry_os_enum = entry; + } + + { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); + entry->zero_bits = true; // only allowed at compile time + buf_init_from_str(&entry->name, "@Arch"); + uint32_t field_count = target_arch_count(); + entry->data.enumeration.field_count = field_count; + entry->data.enumeration.fields = allocate(field_count); + for (uint32_t i = 0; i < field_count; i += 1) { + TypeEnumField *type_enum_field = &entry->data.enumeration.fields[i]; + const ArchType *arch_type = get_target_arch(i); + type_enum_field->name = buf_alloc(); + buf_resize(type_enum_field->name, 50); + get_arch_name(buf_ptr(type_enum_field->name), arch_type); + buf_resize(type_enum_field->name, strlen(buf_ptr(type_enum_field->name))); + + type_enum_field->value = i; + + if (arch_type->arch == g->zig_target.arch.arch && + arch_type->sub_arch == g->zig_target.arch.sub_arch) + { + g->target_arch_index = i; + } + } + entry->data.enumeration.complete = true; + + TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count); + entry->data.enumeration.tag_type = tag_type_entry; + + g->builtin_types.entry_arch_enum = entry; + } } diff --git a/src/main.cpp b/src/main.cpp index 3cc7fdd9d2..44ac144dd0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,11 +58,11 @@ static int print_target_list(FILE *f) { int arch_count = target_arch_count(); for (int arch_i = 0; arch_i < arch_count; arch_i += 1) { const ArchType *arch = get_target_arch(arch_i); - const char *sub_arch_str = (arch->sub_arch == ZigLLVM_NoSubArch) ? - "" : ZigLLVMGetSubArchTypeName(arch->sub_arch); + char arch_name[50]; + get_arch_name(arch_name, arch); const char *native_str = (native.arch.arch == arch->arch && native.arch.sub_arch == arch->sub_arch) ? " (native)" : ""; - fprintf(f, " %s%s%s\n", ZigLLVMGetArchTypeName(arch->arch), sub_arch_str, native_str); + fprintf(f, " %s%s\n", arch_name, native_str); } fprintf(f, "\nOperating Systems:\n"); diff --git a/src/target.cpp b/src/target.cpp index 67f30f13cb..0bc4c0406d 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -181,13 +181,20 @@ void get_unknown_target(ZigTarget *target) { target->oformat = ZigLLVM_UnknownObjectFormat; } +static void get_arch_name_raw(char *out_str, ZigLLVM_ArchType arch, ZigLLVM_SubArchType sub_arch) { + const char *sub_str = (sub_arch == ZigLLVM_NoSubArch) ? "" : ZigLLVMGetSubArchTypeName(sub_arch); + sprintf(out_str, "%s%s", ZigLLVMGetArchTypeName(arch), sub_str); +} + +void get_arch_name(char *out_str, const ArchType *arch) { + return get_arch_name_raw(out_str, arch->arch, arch->sub_arch); +} + int parse_target_arch(const char *str, ArchType *out_arch) { for (int i = 0; i < array_length(arch_list); i += 1) { const ArchType *arch = &arch_list[i]; char arch_name[50]; - const char *sub_str = (arch->sub_arch == ZigLLVM_NoSubArch) ? - "" : ZigLLVMGetSubArchTypeName(arch->sub_arch); - sprintf(arch_name, "%s%s", ZigLLVMGetArchTypeName(arch->arch), sub_str); + get_arch_name_raw(arch_name, arch->arch, arch->sub_arch); if (strcmp(arch_name, str) == 0) { *out_arch = *arch; return 0; diff --git a/src/target.hpp b/src/target.hpp index f858bfb2af..198fe4041c 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -27,6 +27,7 @@ struct ZigTarget { int target_arch_count(void); const ArchType *get_target_arch(int index); +void get_arch_name(char *out_str, const ArchType *arch); int target_vendor_count(void); ZigLLVM_VendorType get_target_vendor(int index); diff --git a/test/self_hosted.zig b/test/self_hosted.zig index d117b19e8e..87fb71fb24 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -308,11 +308,6 @@ fn non_const_cast_bool_to_int(t: bool, f: bool) { #attribute("test") fn switch_on_enum() { const fruit = Fruit.Orange; - switch (fruit) { - Apple => unreachable{}, - Orange => {}, - Banana => unreachable{}, - } non_const_switch_on_enum(fruit); } enum Fruit { @@ -330,7 +325,9 @@ fn non_const_switch_on_enum(fruit: Fruit) { #attribute("test") fn switch_statement() { - const foo = SwitchStatmentFoo.C; + non_const_switch(SwitchStatmentFoo.C); +} +fn non_const_switch(foo: SwitchStatmentFoo) { const val: i32 = switch (foo) { A => 1, B => 2,