mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
ir: ability to modify global vars
This commit is contained in:
parent
8e2804efa1
commit
afc5507b64
137
src/analyze.cpp
137
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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
118
src/ir.cpp
118
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"));
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user