ir: ability to modify global vars

This commit is contained in:
Andrew Kelley 2016-10-29 19:14:48 -04:00
parent 8e2804efa1
commit afc5507b64
5 changed files with 188 additions and 72 deletions

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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"));
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;
}
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) {
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;
}

View File

@ -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