Commit 2dc4ac0e21

Andrew Kelley <superjoe30@gmail.com>
2016-02-12 02:33:04
add @compile_var("os") and @compile_var("arch")
1 parent 08eb194
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;
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<AstNode*>(prong_count);
     TypeTableEntry **peer_types = allocate<TypeTableEntry*>(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;
+                }
+            }
+        }
+    }
+
+    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");
             }
         }
     }
-    return resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count);
+
+    zig_unreachable();
 }
 
 static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
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<TypeEnumField>(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<TypeEnumField>(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;
+    }
 }
 
 
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");
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;
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);
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,