Commit afc5507b64

Andrew Kelley <superjoe30@gmail.com>
2016-10-30 01:14:48
ir: ability to modify global vars
1 parent 8e2804e
src/analyze.cpp
@@ -49,6 +49,9 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte
 static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);
 static void resolve_use_decl(CodeGen *g, AstNode *node);
 static void preview_use_decl(CodeGen *g, AstNode *node);
+static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
+        BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const,
+        AstNode *val_node);
 
 AstNode *first_executing_node(AstNode *node) {
     switch (node->type) {
@@ -1677,6 +1680,129 @@ static void preview_error_value_decl(CodeGen *g, AstNode *node) {
     node->data.error_value_decl.top_level_decl.resolution = TldResolutionOk;
 }
 
+TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) {
+    TypeTableEntry *underlying_type = get_underlying_type(type_entry);
+    switch (underlying_type->id) {
+        case TypeTableEntryIdTypeDecl:
+            zig_unreachable();
+        case TypeTableEntryIdInvalid:
+            return g->builtin_types.entry_invalid;
+        case TypeTableEntryIdUnreachable:
+        case TypeTableEntryIdVar:
+        case TypeTableEntryIdNumLitFloat:
+        case TypeTableEntryIdNumLitInt:
+        case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
+        case TypeTableEntryIdBlock:
+            add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed",
+                buf_ptr(&underlying_type->name)));
+            return g->builtin_types.entry_invalid;
+        case TypeTableEntryIdNamespace:
+        case TypeTableEntryIdMetaType:
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdInt:
+        case TypeTableEntryIdFloat:
+        case TypeTableEntryIdPointer:
+        case TypeTableEntryIdArray:
+        case TypeTableEntryIdStruct:
+        case TypeTableEntryIdMaybe:
+        case TypeTableEntryIdErrorUnion:
+        case TypeTableEntryIdPureError:
+        case TypeTableEntryIdEnum:
+        case TypeTableEntryIdUnion:
+        case TypeTableEntryIdFn:
+        case TypeTableEntryIdGenericFn:
+            return type_entry;
+    }
+    zig_unreachable();
+}
+
+static void resolve_var_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
+    assert(node->type == NodeTypeVariableDeclaration);
+
+    AstNodeVariableDeclaration *var_decl = &node->data.variable_declaration;
+    BlockContext *scope = node->block_context;
+    bool is_const = var_decl->is_const;
+    bool is_export = (var_decl->top_level_decl.visib_mod == VisibModExport);
+    bool is_extern = var_decl->is_extern;
+
+    assert(!scope->fn_entry);
+
+    TypeTableEntry *explicit_type = nullptr;
+    if (var_decl->type) {
+        TypeTableEntry *proposed_type = analyze_type_expr(g, import, scope, var_decl->type);
+        explicit_type = validate_var_type(g, var_decl->type, proposed_type);
+    }
+
+    TypeTableEntry *implicit_type = nullptr;
+    if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) {
+        implicit_type = explicit_type;
+    } else if (var_decl->expr) {
+        IrExecutable ir_executable = {0};
+        IrExecutable analyzed_executable = {0};
+        IrInstruction *result = ir_gen(g, var_decl->expr, scope, &ir_executable);
+        if (result == g->invalid_instruction) {
+            // ignore the poison value
+            implicit_type = g->builtin_types.entry_invalid;
+        } else {
+            if (g->verbose) {
+                fprintf(stderr, "var %s = {\n", buf_ptr(var_decl->symbol));
+                ir_print(stderr, &ir_executable, 4);
+                fprintf(stderr, "}\n");
+            }
+            implicit_type = ir_analyze(g, &ir_executable, &analyzed_executable,
+                explicit_type, var_decl->type);
+            if (g->verbose) {
+                fprintf(stderr, "var %s = { // (analyzed)\n", buf_ptr(var_decl->symbol));
+                ir_print(stderr, &analyzed_executable, 4);
+                fprintf(stderr, "}\n");
+            }
+
+            if (implicit_type->id == TypeTableEntryIdUnreachable) {
+                add_node_error(g, node,
+                    buf_sprintf("variable initialization is unreachable"));
+                implicit_type = g->builtin_types.entry_invalid;
+            } else if ((!is_const || is_export) &&
+                    (implicit_type->id == TypeTableEntryIdNumLitFloat ||
+                    implicit_type->id == TypeTableEntryIdNumLitInt))
+            {
+                add_node_error(g, node, buf_sprintf("unable to infer variable type"));
+                implicit_type = g->builtin_types.entry_invalid;
+            } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) {
+                add_node_error(g, node, buf_sprintf("variable of type 'type' must be constant"));
+                implicit_type = g->builtin_types.entry_invalid;
+            }
+            if (implicit_type->id != TypeTableEntryIdInvalid) {
+                Expr *expr = get_resolved_expr(var_decl->expr);
+                IrInstruction *result = ir_exec_const_result(&analyzed_executable);
+                if (result) {
+                    assert(result->static_value.ok);
+                    expr->const_val = result->static_value;
+                    expr->type_entry = result->type_entry;
+                } else {
+                    add_node_error(g, first_executing_node(var_decl->expr),
+                        buf_sprintf("global variable initializer requires constant expression"));
+                    implicit_type = g->builtin_types.entry_invalid;
+                }
+            }
+        }
+    } else if (!is_extern) {
+        add_node_error(g, node, buf_sprintf("variables must be initialized"));
+        implicit_type = g->builtin_types.entry_invalid;
+    }
+
+    TypeTableEntry *type = explicit_type ? explicit_type : implicit_type;
+    assert(type != nullptr); // should have been caught by the parser
+
+    VariableTableEntry *var = add_local_var(g, node, import, scope,
+            var_decl->symbol, type, is_const, var_decl->expr);
+
+    var_decl->variable = var;
+
+    g->global_vars.append(var);
+}
+
 void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) {
     TopLevelDecl *tld = get_as_top_level_decl(node);
     if (tld->resolution != TldResolutionUnresolved) {
@@ -1705,14 +1831,8 @@ void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) {
             resolve_struct_decl(g, import, node);
             break;
         case NodeTypeVariableDeclaration:
-            {
-                AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
-                VariableTableEntry *var = analyze_variable_declaration_raw(g, import, node->block_context,
-                        node, variable_declaration, false, node, false);
-
-                g->global_vars.append(var);
-                break;
-            }
+            resolve_var_decl(g, import, node);
+            break;
         case NodeTypeTypeDecl:
             {
                 AstNode *type_node = node->data.type_decl.child_type;
@@ -3566,6 +3686,7 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
 }
 
 // Set name to nullptr to make the variable anonymous (not visible to programmer).
+// TODO merge with definition of add_local_var in ir.cpp
 static VariableTableEntry *add_local_var_shadowable(CodeGen *g, AstNode *source_node, ImportTableEntry *import,
         BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node,
         bool shadowable)
src/analyze.hpp
@@ -58,5 +58,6 @@ void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only);
 TopLevelDecl *get_as_top_level_decl(AstNode *node);
 void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node);
 bool type_is_codegen_pointer(TypeTableEntry *type);
+TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry);
 
 #endif
src/codegen.cpp
@@ -2885,7 +2885,7 @@ static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrIn
     VariableTableEntry *var = instruction->var;
     if (type_has_bits(var->type)) {
         assert(var->value_ref);
-        return get_handle_value(g, var->value_ref, var->type);
+        return var->value_ref;
     } else {
         return nullptr;
     }
src/ir.cpp
@@ -617,39 +617,38 @@ static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
     irb->current_basic_block = basic_block;
 }
 
-// Set name to nullptr to make the variable anonymous (not visible to programmer).
-static VariableTableEntry *ir_add_local_var(IrBuilder *irb, AstNode *node, Buf *name,
+static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, Buf *name,
         bool is_const, bool is_shadowable)
 {
     VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1);
     variable_entry->block_context = node->block_context;
     variable_entry->import = node->owner;
     variable_entry->shadowable = is_shadowable;
-    variable_entry->mem_slot_index = exec_next_mem_slot(irb->exec);
+    variable_entry->mem_slot_index = SIZE_MAX;
 
     if (name) {
         buf_init_from_buf(&variable_entry->name, name);
 
-        VariableTableEntry *existing_var = find_variable(irb->codegen, node->block_context, name);
+        VariableTableEntry *existing_var = find_variable(codegen, node->block_context, name);
         if (existing_var && !existing_var->shadowable) {
-            ErrorMsg *msg = add_node_error(irb->codegen, node,
+            ErrorMsg *msg = add_node_error(codegen, node,
                     buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
-            add_error_note(irb->codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
-            variable_entry->type = irb->codegen->builtin_types.entry_invalid;
+            add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
+            variable_entry->type = codegen->builtin_types.entry_invalid;
         } else {
-            auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(name);
+            auto primitive_table_entry = codegen->primitive_type_table.maybe_get(name);
             if (primitive_table_entry) {
                 TypeTableEntry *type = primitive_table_entry->value;
-                add_node_error(irb->codegen, node,
+                add_node_error(codegen, node,
                         buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
-                variable_entry->type = irb->codegen->builtin_types.entry_invalid;
+                variable_entry->type = codegen->builtin_types.entry_invalid;
             } else {
                 AstNode *decl_node = find_decl(node->block_context, name);
                 if (decl_node && decl_node->type != NodeTypeVariableDeclaration) {
-                    ErrorMsg *msg = add_node_error(irb->codegen, node,
+                    ErrorMsg *msg = add_node_error(codegen, node,
                             buf_sprintf("redefinition of '%s'", buf_ptr(name)));
-                    add_error_note(irb->codegen, msg, decl_node, buf_sprintf("previous definition is here"));
-                    variable_entry->type = irb->codegen->builtin_types.entry_invalid;
+                    add_error_note(codegen, msg, decl_node, buf_sprintf("previous definition is here"));
+                    variable_entry->type = codegen->builtin_types.entry_invalid;
                 }
             }
         }
@@ -666,6 +665,15 @@ static VariableTableEntry *ir_add_local_var(IrBuilder *irb, AstNode *node, Buf *
     return variable_entry;
 }
 
+// Set name to nullptr to make the variable anonymous (not visible to programmer).
+static VariableTableEntry *ir_add_local_var(IrBuilder *irb, AstNode *node, Buf *name,
+        bool is_const, bool is_shadowable)
+{
+    VariableTableEntry *var = add_local_var(irb->codegen, node, name, is_const, is_shadowable);
+    var->mem_slot_index = exec_next_mem_slot(irb->exec);
+    return var;
+}
+
 static IrInstruction *ir_gen_block(IrBuilder *irb, AstNode *block_node) {
     assert(block_node->type == NodeTypeBlock);
 
@@ -1224,6 +1232,13 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
             return ir_gen_array_access(irb, node, lval);
         case NodeTypeUnwrapErrorExpr:
         case NodeTypeReturnExpr:
+        // TODO
+    //if (!scope->fn_entry) {
+    //    add_node_error(ira->codegen, source_node,
+    //        buf_sprintf("return expression outside function definition"));
+    //    return ira->codegen->builtin_types.entry_invalid;
+    //}
+
         case NodeTypeDefer:
         case NodeTypeSliceExpr:
         case NodeTypeFieldAccessExpr:
@@ -1933,20 +1948,12 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value,
     zig_unreachable();
 }
 
-static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *return_instruction) {
-    AstNode *source_node = return_instruction->base.source_node;
-    BlockContext *scope = source_node->block_context;
-    if (!scope->fn_entry) {
-        add_node_error(ira->codegen, source_node, buf_sprintf("return expression outside function definition"));
-        return ira->codegen->builtin_types.entry_invalid;
-    }
-
-    TypeTableEntry *expected_return_type = scope->fn_entry->type_entry->data.fn.fn_type_id.return_type;
-
-    IrInstruction *value = ir_get_casted_value(ira, return_instruction->value->other, expected_return_type);
-    if (value == ira->codegen->invalid_instruction) {
+static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira,
+    IrInstructionReturn *return_instruction)
+{
+    IrInstruction *value = ir_get_casted_value(ira, return_instruction->value->other, ira->explicit_return_type);
+    if (value == ira->codegen->invalid_instruction)
         return ira->codegen->builtin_types.entry_invalid;
-    }
     ira->implicit_return_type_list.append(value);
 
     IrInstruction *new_instruction = ir_build_return_from(&ira->new_irb, &return_instruction->base, value);
@@ -2333,43 +2340,10 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
     IrInstruction *var_type = nullptr;
     if (decl_var_instruction->var_type != nullptr) {
         var_type = decl_var_instruction->var_type->other;
-        explicit_type = ir_get_canonical_type(ira, var_type);
-        switch (explicit_type->id) {
-            case TypeTableEntryIdTypeDecl:
-                zig_unreachable();
-            case TypeTableEntryIdInvalid:
-                explicit_type = ira->codegen->builtin_types.entry_invalid;
-                break;
-            case TypeTableEntryIdUnreachable:
-            case TypeTableEntryIdVar:
-            case TypeTableEntryIdNumLitFloat:
-            case TypeTableEntryIdNumLitInt:
-            case TypeTableEntryIdUndefLit:
-            case TypeTableEntryIdNullLit:
-            case TypeTableEntryIdBlock:
-                add_node_error(ira->codegen, var_type->source_node,
-                    buf_sprintf("variable of type '%s' not allowed", buf_ptr(&explicit_type->name)));
-                explicit_type = ira->codegen->builtin_types.entry_invalid;
-                break;
-            case TypeTableEntryIdNamespace:
-            case TypeTableEntryIdMetaType:
-            case TypeTableEntryIdVoid:
-            case TypeTableEntryIdBool:
-            case TypeTableEntryIdInt:
-            case TypeTableEntryIdFloat:
-            case TypeTableEntryIdPointer:
-            case TypeTableEntryIdArray:
-            case TypeTableEntryIdStruct:
-            case TypeTableEntryIdMaybe:
-            case TypeTableEntryIdErrorUnion:
-            case TypeTableEntryIdPureError:
-            case TypeTableEntryIdEnum:
-            case TypeTableEntryIdUnion:
-            case TypeTableEntryIdFn:
-            case TypeTableEntryIdGenericFn:
-                // OK
-                break;
-        }
+        TypeTableEntry *proposed_type = ir_get_canonical_type(ira, var_type);
+        explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type);
+        if (explicit_type->id == TypeTableEntryIdInvalid)
+            return explicit_type;
     }
 
     IrInstruction *init_value = decl_var_instruction->init_value->other;
@@ -4306,3 +4280,21 @@ bool ir_has_side_effects(IrInstruction *instruction) {
     }
     zig_unreachable();
 }
+
+IrInstruction *ir_exec_const_result(IrExecutable *exec) {
+    if (exec->basic_block_list.length != 1)
+        return nullptr;
+
+    IrBasicBlock *bb = exec->basic_block_list.at(0);
+    if (bb->instruction_list.length != 1)
+        return nullptr;
+
+    IrInstruction *only_inst = bb->instruction_list.at(0);
+    if (only_inst->id != IrInstructionIdReturn)
+        return nullptr;
+
+    IrInstructionReturn *ret_inst = (IrInstructionReturn *)only_inst;
+    IrInstruction *value = ret_inst->value;
+    assert(value->static_value.ok);
+    return value;
+}
src/ir.hpp
@@ -16,6 +16,8 @@ IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry);
 TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable,
         TypeTableEntry *expected_type, AstNode *expected_type_source_node);
 
+IrInstruction *ir_exec_const_result(IrExecutable *exec);
+
 bool ir_has_side_effects(IrInstruction *instruction);
 
 #endif