| ... | ... | @@ -11,12 +11,6 @@ |
| 11 | 11 | #include "zig_llvm.hpp" |
| 12 | 12 | #include "os.hpp" |
| 13 | 13 | |
| 14 | | struct BlockContext { |
| 15 | | AstNode *node; |
| 16 | | BlockContext *root; |
| 17 | | BlockContext *parent; |
| 18 | | }; |
| 19 | | |
| 20 | 14 | void add_node_error(CodeGen *g, AstNode *node, Buf *msg) { |
| 21 | 15 | ErrorMsg *err = allocate<ErrorMsg>(1); |
| 22 | 16 | err->line_start = node->line; |
| ... | ... | @@ -75,6 +69,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool |
| 75 | 69 | } |
| 76 | 70 | |
| 77 | 71 | static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { |
| 72 | assert(node->type == NodeTypeType); |
| 78 | 73 | assert(!node->codegen_node); |
| 79 | 74 | node->codegen_node = allocate<CodeGenNode>(1); |
| 80 | 75 | TypeNode *type_node = &node->codegen_node->data.type_node; |
| ... | ... | @@ -353,6 +348,30 @@ static void check_type_compatibility(CodeGen *g, AstNode *node, TypeTableEntry * |
| 353 | 348 | add_node_error(g, node, buf_sprintf("type mismatch. expected %s. got %s", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); |
| 354 | 349 | } |
| 355 | 350 | |
| 351 | static BlockContext *new_block_context(AstNode *node, BlockContext *parent) { |
| 352 | BlockContext *context = allocate<BlockContext>(1); |
| 353 | context->node = node; |
| 354 | context->parent = parent; |
| 355 | if (parent != nullptr) |
| 356 | context->root = parent->root; |
| 357 | else |
| 358 | context->root = context; |
| 359 | context->variable_table.init(8); |
| 360 | return context; |
| 361 | } |
| 362 | |
| 363 | static LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { |
| 364 | while (true) { |
| 365 | auto entry = context->variable_table.maybe_get(name); |
| 366 | if (entry != nullptr) |
| 367 | return entry->value; |
| 368 | |
| 369 | context = context->parent; |
| 370 | if (context == nullptr) |
| 371 | return nullptr; |
| 372 | } |
| 373 | } |
| 374 | |
| 356 | 375 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 357 | 376 | TypeTableEntry *expected_type, AstNode *node) |
| 358 | 377 | { |
| ... | ... | @@ -360,7 +379,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 360 | 379 | switch (node->type) { |
| 361 | 380 | case NodeTypeBlock: |
| 362 | 381 | { |
| 363 | | // TODO: nested block scopes |
| 382 | BlockContext *child_context = new_block_context(node, context); |
| 364 | 383 | return_type = g->builtin_types.entry_void; |
| 365 | 384 | for (int i = 0; i < node->data.block.statements.length; i += 1) { |
| 366 | 385 | AstNode *child = node->data.block.statements.at(i); |
| ... | ... | @@ -375,7 +394,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 375 | 394 | add_node_error(g, child, buf_sprintf("unreachable code")); |
| 376 | 395 | break; |
| 377 | 396 | } |
| 378 | | return_type = analyze_expression(g, import, context, nullptr, child); |
| 397 | return_type = analyze_expression(g, import, child_context, nullptr, child); |
| 379 | 398 | } |
| 380 | 399 | break; |
| 381 | 400 | } |
| ... | ... | @@ -402,8 +421,27 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 402 | 421 | } |
| 403 | 422 | case NodeTypeVariableDeclaration: |
| 404 | 423 | { |
| 405 | | zig_panic("TODO: analyze variable declaration"); |
| 424 | AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;; |
| 425 | |
| 426 | TypeTableEntry *explicit_type = variable_declaration->type != nullptr ? |
| 427 | resolve_type(g, variable_declaration->type) : nullptr; |
| 428 | |
| 429 | TypeTableEntry *implicit_type = variable_declaration->expr != nullptr ? |
| 430 | analyze_expression(g, import, context, explicit_type, variable_declaration->expr) : nullptr; |
| 431 | |
| 432 | TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; |
| 433 | assert(type != nullptr); // should have been caught by the parser |
| 406 | 434 | |
| 435 | LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol); |
| 436 | if (existing_variable) { |
| 437 | add_node_error(g, node, buf_sprintf("redeclaration of variable '%s'.", |
| 438 | buf_ptr(&variable_declaration->symbol))); |
| 439 | } else { |
| 440 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); |
| 441 | buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); |
| 442 | variable_entry->type = type; |
| 443 | context->variable_table.put(&variable_entry->name, variable_entry); |
| 444 | } |
| 407 | 445 | return_type = g->builtin_types.entry_void; |
| 408 | 446 | break; |
| 409 | 447 | } |
| ... | ... | @@ -641,6 +679,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 641 | 679 | node->codegen_node = allocate<CodeGenNode>(1); |
| 642 | 680 | } |
| 643 | 681 | node->codegen_node->expr_node.type_entry = return_type; |
| 682 | node->codegen_node->expr_node.block_context = context; |
| 644 | 683 | |
| 645 | 684 | return return_type; |
| 646 | 685 | } |
| ... | ... | @@ -658,19 +697,40 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 658 | 697 | AstNode *fn_proto_node = node->data.fn_def.fn_proto; |
| 659 | 698 | assert(fn_proto_node->type == NodeTypeFnProto); |
| 660 | 699 | |
| 700 | BlockContext *context = new_block_context(node, nullptr); |
| 701 | |
| 661 | 702 | AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto; |
| 662 | 703 | for (int i = 0; i < fn_proto->params.length; i += 1) { |
| 663 | 704 | AstNode *param_decl_node = fn_proto->params.at(i); |
| 664 | 705 | assert(param_decl_node->type == NodeTypeParamDecl); |
| 665 | | // TODO: define local variables for parameters |
| 706 | |
| 707 | // define local variables for parameters |
| 708 | AstNodeParamDecl *param_decl = &param_decl_node->data.param_decl; |
| 709 | assert(param_decl->type->type == NodeTypeType); |
| 710 | TypeTableEntry *type = param_decl->type->codegen_node->data.type_node.entry; |
| 711 | |
| 712 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); |
| 713 | buf_init_from_buf(&variable_entry->name, &param_decl->name); |
| 714 | variable_entry->type = type; |
| 715 | |
| 716 | LocalVariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name); |
| 717 | if (!existing_entry) { |
| 718 | // unique definition |
| 719 | context->variable_table.put(&variable_entry->name, variable_entry); |
| 720 | } else { |
| 721 | add_node_error(g, node, buf_sprintf("redeclaration of parameter '%s'.", |
| 722 | buf_ptr(&existing_entry->name))); |
| 723 | if (existing_entry->type == variable_entry->type) { |
| 724 | // types agree, so the type is probably good enough for the rest of analysis |
| 725 | } else { |
| 726 | // types disagree. don't trust either one of them. |
| 727 | existing_entry->type = g->builtin_types.entry_invalid;; |
| 728 | } |
| 729 | } |
| 666 | 730 | } |
| 667 | 731 | |
| 668 | | BlockContext context; |
| 669 | | context.node = node; |
| 670 | | context.root = &context; |
| 671 | | context.parent = nullptr; |
| 672 | 732 | TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry; |
| 673 | | TypeTableEntry *block_return_type = analyze_expression(g, import, &context, expected_type, node->data.fn_def.body); |
| 733 | TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body); |
| 674 | 734 | |
| 675 | 735 | node->codegen_node = allocate<CodeGenNode>(1); |
| 676 | 736 | node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type; |