mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-09 15:19:07 +00:00
analysis for variable declaration, but not variable reference
This commit is contained in:
parent
f8ca6c70c7
commit
cb69cb0f26
2 changed files with 90 additions and 15 deletions
|
|
@ -11,12 +11,6 @@
|
||||||
#include "zig_llvm.hpp"
|
#include "zig_llvm.hpp"
|
||||||
#include "os.hpp"
|
#include "os.hpp"
|
||||||
|
|
||||||
struct BlockContext {
|
|
||||||
AstNode *node;
|
|
||||||
BlockContext *root;
|
|
||||||
BlockContext *parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
void add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
|
void add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
|
||||||
ErrorMsg *err = allocate<ErrorMsg>(1);
|
ErrorMsg *err = allocate<ErrorMsg>(1);
|
||||||
err->line_start = node->line;
|
err->line_start = node->line;
|
||||||
|
|
@ -75,6 +69,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
|
static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
|
||||||
|
assert(node->type == NodeTypeType);
|
||||||
assert(!node->codegen_node);
|
assert(!node->codegen_node);
|
||||||
node->codegen_node = allocate<CodeGenNode>(1);
|
node->codegen_node = allocate<CodeGenNode>(1);
|
||||||
TypeNode *type_node = &node->codegen_node->data.type_node;
|
TypeNode *type_node = &node->codegen_node->data.type_node;
|
||||||
|
|
@ -353,6 +348,30 @@ static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry *
|
||||||
add_node_error(g, node, buf_sprintf("type mismatch. expected %s. got %s", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
|
add_node_error(g, node, buf_sprintf("type mismatch. expected %s. got %s", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
|
||||||
|
BlockContext *context = allocate<BlockContext>(1);
|
||||||
|
context->node = node;
|
||||||
|
context->parent = parent;
|
||||||
|
if (parent != nullptr)
|
||||||
|
context->root = parent->root;
|
||||||
|
else
|
||||||
|
context->root = context;
|
||||||
|
context->variable_table.init(8);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) {
|
||||||
|
while (true) {
|
||||||
|
auto entry = context->variable_table.maybe_get(name);
|
||||||
|
if (entry != nullptr)
|
||||||
|
return entry->value;
|
||||||
|
|
||||||
|
context = context->parent;
|
||||||
|
if (context == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||||
TypeTableEntry *expected_type, AstNode *node)
|
TypeTableEntry *expected_type, AstNode *node)
|
||||||
{
|
{
|
||||||
|
|
@ -360,7 +379,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case NodeTypeBlock:
|
case NodeTypeBlock:
|
||||||
{
|
{
|
||||||
// TODO: nested block scopes
|
BlockContext *child_context = new_block_context(node, context);
|
||||||
return_type = g->builtin_types.entry_void;
|
return_type = g->builtin_types.entry_void;
|
||||||
for (int i = 0; i < node->data.block.statements.length; i += 1) {
|
for (int i = 0; i < node->data.block.statements.length; i += 1) {
|
||||||
AstNode *child = node->data.block.statements.at(i);
|
AstNode *child = node->data.block.statements.at(i);
|
||||||
|
|
@ -375,7 +394,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||||
add_node_error(g, child, buf_sprintf("unreachable code"));
|
add_node_error(g, child, buf_sprintf("unreachable code"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return_type = analyze_expression(g, import, context, nullptr, child);
|
return_type = analyze_expression(g, import, child_context, nullptr, child);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -402,8 +421,27 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||||
}
|
}
|
||||||
case NodeTypeVariableDeclaration:
|
case NodeTypeVariableDeclaration:
|
||||||
{
|
{
|
||||||
zig_panic("TODO: analyze variable declaration");
|
AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;;
|
||||||
|
|
||||||
|
TypeTableEntry *explicit_type = variable_declaration->type != nullptr ?
|
||||||
|
resolve_type(g, variable_declaration->type) : nullptr;
|
||||||
|
|
||||||
|
TypeTableEntry *implicit_type = variable_declaration->expr != nullptr ?
|
||||||
|
analyze_expression(g, import, context, explicit_type, variable_declaration->expr) : nullptr;
|
||||||
|
|
||||||
|
TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
|
||||||
|
assert(type != nullptr); // should have been caught by the parser
|
||||||
|
|
||||||
|
LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol);
|
||||||
|
if (existing_variable) {
|
||||||
|
add_node_error(g, node, buf_sprintf("redeclaration of variable '%s'.",
|
||||||
|
buf_ptr(&variable_declaration->symbol)));
|
||||||
|
} else {
|
||||||
|
LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
|
||||||
|
buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
|
||||||
|
variable_entry->type = type;
|
||||||
|
context->variable_table.put(&variable_entry->name, variable_entry);
|
||||||
|
}
|
||||||
return_type = g->builtin_types.entry_void;
|
return_type = g->builtin_types.entry_void;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -641,6 +679,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
|
||||||
node->codegen_node = allocate<CodeGenNode>(1);
|
node->codegen_node = allocate<CodeGenNode>(1);
|
||||||
}
|
}
|
||||||
node->codegen_node->expr_node.type_entry = return_type;
|
node->codegen_node->expr_node.type_entry = return_type;
|
||||||
|
node->codegen_node->expr_node.block_context = context;
|
||||||
|
|
||||||
return return_type;
|
return return_type;
|
||||||
}
|
}
|
||||||
|
|
@ -658,19 +697,40 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
|
||||||
AstNode *fn_proto_node = node->data.fn_def.fn_proto;
|
AstNode *fn_proto_node = node->data.fn_def.fn_proto;
|
||||||
assert(fn_proto_node->type == NodeTypeFnProto);
|
assert(fn_proto_node->type == NodeTypeFnProto);
|
||||||
|
|
||||||
|
BlockContext *context = new_block_context(node, nullptr);
|
||||||
|
|
||||||
AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
|
AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
|
||||||
for (int i = 0; i < fn_proto->params.length; i += 1) {
|
for (int i = 0; i < fn_proto->params.length; i += 1) {
|
||||||
AstNode *param_decl_node = fn_proto->params.at(i);
|
AstNode *param_decl_node = fn_proto->params.at(i);
|
||||||
assert(param_decl_node->type == NodeTypeParamDecl);
|
assert(param_decl_node->type == NodeTypeParamDecl);
|
||||||
// TODO: define local variables for parameters
|
|
||||||
|
// define local variables for parameters
|
||||||
|
AstNodeParamDecl *param_decl = ¶m_decl_node->data.param_decl;
|
||||||
|
assert(param_decl->type->type == NodeTypeType);
|
||||||
|
TypeTableEntry *type = param_decl->type->codegen_node->data.type_node.entry;
|
||||||
|
|
||||||
|
LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
|
||||||
|
buf_init_from_buf(&variable_entry->name, ¶m_decl->name);
|
||||||
|
variable_entry->type = type;
|
||||||
|
|
||||||
|
LocalVariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name);
|
||||||
|
if (!existing_entry) {
|
||||||
|
// unique definition
|
||||||
|
context->variable_table.put(&variable_entry->name, variable_entry);
|
||||||
|
} else {
|
||||||
|
add_node_error(g, node, buf_sprintf("redeclaration of parameter '%s'.",
|
||||||
|
buf_ptr(&existing_entry->name)));
|
||||||
|
if (existing_entry->type == variable_entry->type) {
|
||||||
|
// types agree, so the type is probably good enough for the rest of analysis
|
||||||
|
} else {
|
||||||
|
// types disagree. don't trust either one of them.
|
||||||
|
existing_entry->type = g->builtin_types.entry_invalid;;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockContext context;
|
|
||||||
context.node = node;
|
|
||||||
context.root = &context;
|
|
||||||
context.parent = nullptr;
|
|
||||||
TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;
|
TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;
|
||||||
TypeTableEntry *block_return_type = analyze_expression(g, import, &context, expected_type, node->data.fn_def.body);
|
TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
|
||||||
|
|
||||||
node->codegen_node = allocate<CodeGenNode>(1);
|
node->codegen_node = allocate<CodeGenNode>(1);
|
||||||
node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;
|
node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,18 @@ struct CodeGen {
|
||||||
ImportTableEntry *root_import;
|
ImportTableEntry *root_import;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LocalVariableTableEntry {
|
||||||
|
Buf name;
|
||||||
|
TypeTableEntry *type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlockContext {
|
||||||
|
AstNode *node; // either NodeTypeFnDef or NodeTypeBlock
|
||||||
|
BlockContext *root; // always points to the BlockContext with the NodeTypeFnDef
|
||||||
|
BlockContext *parent; // nullptr when this is the root
|
||||||
|
HashMap<Buf *, LocalVariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
|
||||||
|
};
|
||||||
|
|
||||||
struct TypeNode {
|
struct TypeNode {
|
||||||
TypeTableEntry *entry;
|
TypeTableEntry *entry;
|
||||||
};
|
};
|
||||||
|
|
@ -133,6 +145,9 @@ struct FnDefNode {
|
||||||
|
|
||||||
struct ExprNode {
|
struct ExprNode {
|
||||||
TypeTableEntry *type_entry;
|
TypeTableEntry *type_entry;
|
||||||
|
// the context in which this expression is evaluated.
|
||||||
|
// for blocks, this points to the containing scope, not the block's own scope for its children.
|
||||||
|
BlockContext *block_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CodeGenNode {
|
struct CodeGenNode {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue