Commit afc5507b64
Changed files (5)
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