Commit b09a0cd072

Andrew Kelley <superjoe30@gmail.com>
2016-01-21 23:23:24
allow constants to have number literal values
also codegen takes advantage of constant expr eval
1 parent 32e2196
src/all_types.hpp
@@ -90,6 +90,7 @@ struct Expr {
     Cast implicit_cast; // happens first
     Cast implicit_maybe_cast; // happens second
 
+    LLVMValueRef const_llvm_val;
     ConstExprValue const_val;
 };
 
@@ -955,6 +956,7 @@ struct CodeGen {
     // there will not be a corresponding fn_defs entry.
     ZigList<FnTableEntry *> fn_protos;
     ZigList<VariableTableEntry *> global_vars;
+    ZigList<Expr *> global_const_list;
 
     OutType out_type;
     FnTableEntry *cur_fn;
src/analyze.cpp
@@ -1667,7 +1667,13 @@ static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node,
 static TypeTableEntry *resolve_expr_const_val_as_other_expr(CodeGen *g, AstNode *node, AstNode *other) {
     Expr *expr = get_resolved_expr(node);
     Expr *other_expr = get_resolved_expr(other);
-    expr->const_val = other_expr->const_val;
+    ConstExprValue *other_const_val;
+    if (other_expr->implicit_maybe_cast.after_type) {
+        other_const_val = &other_expr->implicit_maybe_cast.const_val;
+    } else {
+        other_const_val = &other_expr->const_val;
+    }
+    expr->const_val = *other_const_val;
     return other_expr->type_entry;
 }
 
@@ -1766,8 +1772,14 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
             AstNode *decl_node = var->decl_node;
             if (decl_node->type == NodeTypeVariableDeclaration) {
                 AstNode *expr_node = decl_node->data.variable_declaration.expr;
-                ConstExprValue *const_val = &get_resolved_expr(expr_node)->const_val;
-                if (const_val->ok) {
+                Expr *other_expr = get_resolved_expr(expr_node);
+                ConstExprValue *other_const_val;
+                if (other_expr->implicit_maybe_cast.after_type) {
+                    other_const_val = &other_expr->implicit_maybe_cast.const_val;
+                } else {
+                    other_const_val = &other_expr->const_val;
+                }
+                if (other_const_val->ok) {
                     return resolve_expr_const_val_as_other_expr(g, node, expr_node);
                 }
             }
@@ -2206,6 +2218,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
         bool expr_is_maybe)
 {
     bool is_const = variable_declaration->is_const;
+    bool is_export = (variable_declaration->visib_mod == VisibModExport);
 
     TypeTableEntry *explicit_type = nullptr;
     if (variable_declaration->type != nullptr) {
@@ -2233,7 +2246,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
             add_node_error(g, source_node,
                 buf_sprintf("variable initialization is unreachable"));
             implicit_type = g->builtin_types.entry_invalid;
-        } else if (!is_const &&
+        } else if ((!is_const || is_export) &&
                 (implicit_type->id == TypeTableEntryIdNumLitFloat ||
                  implicit_type->id == TypeTableEntryIdNumLitInt))
         {
@@ -3273,6 +3286,33 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import,
     return g->builtin_types.entry_unreachable;
 }
 
+static bool type_has_codegen_value(TypeTableEntryId id) {
+    switch (id) {
+        case TypeTableEntryIdInvalid:
+        case TypeTableEntryIdMetaType:
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdUnreachable:
+            return false;
+
+        // TODO make num lits return false when we make implicit casts insert ast nodes
+        case TypeTableEntryIdNumLitFloat:
+        case TypeTableEntryIdNumLitInt:
+
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdInt:
+        case TypeTableEntryIdFloat:
+        case TypeTableEntryIdPointer:
+        case TypeTableEntryIdArray:
+        case TypeTableEntryIdStruct:
+        case TypeTableEntryIdMaybe:
+        case TypeTableEntryIdError:
+        case TypeTableEntryIdEnum:
+        case TypeTableEntryIdFn:
+            return true;
+    }
+    zig_unreachable();
+}
+
 static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -3457,14 +3497,22 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
     resolve_type_compatibility(g, context, node, expected_type, return_type);
 
     Expr *expr = get_resolved_expr(node);
-    expr->type_entry = return_type;
+    if (!expr->resolved_type) {
+        expr->resolved_type = return_type;
+    }
+    expr->type_entry = expr->resolved_type;
     expr->block_context = context;
 
+    if (expr->const_val.ok && type_has_codegen_value(expr->resolved_type->id)) {
+        g->global_const_list.append(expr);
+    }
+
+
     if (expr->type_entry->id == TypeTableEntryIdUnreachable) {
         return expr->type_entry;
     }
 
-    /*
+    /* TODO delete this code when we make implicit casts insert ast nodes
     Cast *cast_node = &expr->implicit_cast;
     if (cast_node->after_type) {
         eval_const_expr_implicit_cast(g, import, context, node, cast_node, node);
src/codegen.cpp
@@ -134,38 +134,6 @@ static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_no
     }
 }
 
-static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *expr_node) {
-    Expr *expr = get_resolved_expr(expr_node);
-    TypeTableEntry *type_entry = expr->resolved_type;
-    if (!type_entry) {
-        type_entry = expr->type_entry;
-    }
-    assert(type_entry);
-
-    ConstExprValue *const_val = &expr->const_val;
-
-    assert(const_val->ok);
-
-    if (type_entry->id == TypeTableEntryIdInt) {
-        assert(const_val->data.x_bignum.kind == BigNumKindInt);
-        return LLVMConstInt(type_entry->type_ref,
-            bignum_to_twos_complement(&const_val->data.x_bignum),
-            type_entry->data.integral.is_signed);
-    } else if (type_entry->id == TypeTableEntryIdFloat) {
-        if (const_val->data.x_bignum.kind == BigNumKindFloat) {
-            return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float);
-        } else {
-            int64_t x = const_val->data.x_bignum.data.x_uint;
-            if (const_val->data.x_bignum.is_negative) {
-                x = -x;
-            }
-            return LLVMConstReal(type_entry->type_ref, x);
-        }
-    } else {
-        zig_unreachable();
-    }
-}
-
 static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeFnCallExpr);
     AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
@@ -277,7 +245,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
         case BuiltinFnIdMinValue:
         case BuiltinFnIdMaxValue:
         case BuiltinFnIdMemberCount:
-            return gen_number_literal(g, node);
+            // caught by constant expression eval codegen
+            zig_unreachable();
     }
     zig_unreachable();
 }
@@ -1884,6 +1853,16 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
 }
 
 static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
+    AstNode *init_expr = node->data.variable_declaration.expr;
+    if (node->data.variable_declaration.is_const && init_expr) {
+        TypeTableEntry *init_expr_type = get_expr_type(init_expr);
+        if (init_expr_type->id == TypeTableEntryIdNumLitFloat ||
+            init_expr_type->id == TypeTableEntryIdNumLitInt)
+        {
+            return nullptr;
+        }
+    }
+
     LLVMValueRef init_val;
     return gen_var_decl_raw(g, node, &node->data.variable_declaration,
             get_resolved_expr(node)->block_context, false, &init_val);
@@ -1992,6 +1971,11 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
 }
 
 static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
+    Expr *expr = get_resolved_expr(node);
+    if (expr->const_val.ok) {
+        assert(expr->const_llvm_val);
+        return expr->const_llvm_val;
+    }
     switch (node->type) {
         case NodeTypeBinOpExpr:
             return gen_bin_op_expr(g, node);
@@ -2009,11 +1993,6 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
             return gen_slice_expr(g, node);
         case NodeTypeFieldAccessExpr:
             return gen_field_access_expr(g, node, false);
-        case NodeTypeBoolLiteral:
-            if (node->data.bool_literal.value)
-                return LLVMConstAllOnes(LLVMInt1Type());
-            else
-                return LLVMConstNull(LLVMInt1Type());
         case NodeTypeNullLiteral:
             return gen_null_literal(g, node);
         case NodeTypeIfBoolExpr:
@@ -2026,8 +2005,6 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
             return gen_for_expr(g, node);
         case NodeTypeAsmExpr:
             return gen_asm_expr(g, node);
-        case NodeTypeNumberLiteral:
-            return gen_number_literal(g, node);
         case NodeTypeErrorLiteral:
             return gen_error_literal(g, node);
         case NodeTypeStringLiteral:
@@ -2070,6 +2047,10 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
             return gen_container_init_expr(g, node);
         case NodeTypeSwitchExpr:
             return gen_switch_expr(g, node);
+        case NodeTypeNumberLiteral:
+        case NodeTypeBoolLiteral:
+            // caught by constant expression eval codegen
+            zig_unreachable();
         case NodeTypeRoot:
         case NodeTypeRootExportDecl:
         case NodeTypeFnProto:
@@ -2129,16 +2110,114 @@ static void build_label_blocks(CodeGen *g, AstNode *block_node) {
         label_node->data.label.label_entry->basic_block = LLVMAppendBasicBlock(
                 g->cur_fn->fn_value, buf_ptr(name));
     }
+}
 
+static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val) {
+    assert(const_val->ok);
+
+    if (type_entry->id == TypeTableEntryIdInt) {
+        return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false);
+    } else if (type_entry->id == TypeTableEntryIdFloat) {
+        if (const_val->data.x_bignum.kind == BigNumKindFloat) {
+            return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float);
+        } else {
+            int64_t x = const_val->data.x_bignum.data.x_uint;
+            if (const_val->data.x_bignum.is_negative) {
+                x = -x;
+            }
+            return LLVMConstReal(type_entry->type_ref, x);
+        }
+    } else if (type_entry->id == TypeTableEntryIdBool) {
+        if (const_val->data.x_bool) {
+            return LLVMConstAllOnes(LLVMInt1Type());
+        } else {
+            return LLVMConstNull(LLVMInt1Type());
+        }
+    } else if (type_entry->id == TypeTableEntryIdMaybe) {
+        TypeTableEntry *child_type = type_entry->data.maybe.child_type;
+        LLVMValueRef child_val;
+        LLVMValueRef maybe_val;
+        if (const_val->data.x_maybe) {
+            child_val = gen_const_val(g, child_type, const_val->data.x_maybe);
+            maybe_val = LLVMConstAllOnes(LLVMInt1Type());
+        } else {
+            child_val = LLVMConstNull(child_type->type_ref);
+            maybe_val = LLVMConstNull(LLVMInt1Type());
+        }
+        LLVMValueRef fields[] = {
+            child_val,
+            maybe_val,
+        };
+        return LLVMConstStruct(fields, 2, false);
+    } else if (type_entry->id == TypeTableEntryIdStruct) {
+        zig_panic("TODO");
+    } else if (type_entry->id == TypeTableEntryIdArray) {
+        zig_panic("TODO");
+    } else if (type_entry->id == TypeTableEntryIdEnum) {
+        LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref;
+        LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, const_val->data.x_enum.tag, false);
+        if (type_entry->data.enumeration.gen_field_count == 0) {
+            return tag_value;
+        } else {
+            zig_panic("TODO");
+            /*
+            LLVMValueRef fields[] = {
+                tag_value,
+                union_value,
+            };
+            return LLVMConstStruct(fields, 2, false);
+            */
+        }
+    } else if (type_entry->id == TypeTableEntryIdFn) {
+        return const_val->data.x_fn->fn_value;
+    } else {
+        zig_unreachable();
+    }
+}
+
+static void gen_const_globals(CodeGen *g) {
+    for (int i = 0; i < g->global_const_list.length; i += 1) {
+        Expr *expr = g->global_const_list.at(i);
+        ConstExprValue *const_val = &expr->const_val;
+        assert(const_val->ok);
+        TypeTableEntry *type_entry = expr->resolved_type;
+
+        // TODO delete this if when we make implicit casts insert ast nodes
+        if (type_entry->id == TypeTableEntryIdNumLitFloat ||
+            type_entry->id == TypeTableEntryIdNumLitInt)
+        {
+            continue;
+        }
+
+        if (handle_is_ptr(type_entry)) {
+            LLVMValueRef global_value = LLVMAddGlobal(g->module, type_entry->type_ref, "");
+            LLVMSetLinkage(global_value, LLVMPrivateLinkage);
+            LLVMValueRef init_val = gen_const_val(g, type_entry, const_val);
+            LLVMSetInitializer(global_value, init_val);
+            LLVMSetGlobalConstant(global_value, true);
+            LLVMSetUnnamedAddr(global_value, true);
+            expr->const_llvm_val = global_value;
+        } else {
+            expr->const_llvm_val = gen_const_val(g, type_entry, const_val);
+        }
+    }
 }
 
 static void do_code_gen(CodeGen *g) {
     assert(!g->errors.length);
 
+    gen_const_globals(g);
+
     // Generate module level variables
     for (int i = 0; i < g->global_vars.length; i += 1) {
         VariableTableEntry *var = g->global_vars.at(i);
 
+        if (var->type->id == TypeTableEntryIdNumLitFloat ||
+            var->type->id == TypeTableEntryIdNumLitInt)
+        {
+            continue;
+        }
+
         // TODO if the global is exported, set external linkage
         LLVMValueRef global_value = LLVMAddGlobal(g->module, var->type->type_ref, "");
         LLVMSetLinkage(global_value, LLVMPrivateLinkage);
test/run_tests.cpp
@@ -866,7 +866,7 @@ pub fn main(args: [][]u8) i32 => {
 }
     )SOURCE", "20\n");
 
-    add_simple_case("#min_value() and #max_value()", R"SOURCE(
+    add_simple_case("@min_value() and @max_value()", R"SOURCE(
 import "std.zig";
 pub fn main(args: [][]u8) i32 => {
     print_str("max u8: ");