From f6cbb73c7402fc100bbfb26c1a35c9f23b3f36ff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Dec 2016 21:08:12 -0500 Subject: [PATCH] rewrite scope implementation * now there are not extra unused hash tables * each variable declaration opens a new scope inside a function --- src/all_types.hpp | 99 ++++++++++---- src/analyze.cpp | 326 +++++++++++++++++++++++++--------------------- src/analyze.hpp | 13 +- src/codegen.cpp | 70 ++++++---- src/hash_map.hpp | 9 ++ src/ir.cpp | 173 +++++++++++++++--------- src/parseh.cpp | 6 +- 7 files changed, 425 insertions(+), 271 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index ab00d222d0..b65caeb24e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -31,6 +31,7 @@ struct ConstExprValue; struct IrInstruction; struct IrInstructionCast; struct IrBasicBlock; +struct ScopeDecls; struct IrExecutable { ZigList basic_block_list; @@ -251,8 +252,8 @@ struct AstNodeFnDef { // populated by semantic analyzer TypeTableEntry *implicit_return_type; - // the first child block context - Scope *scope; + Scope *containing_scope; + Scope *child_scope; }; struct AstNodeFnDecl { @@ -638,7 +639,7 @@ struct AstNodeStructDecl { ZigList decls; // populated by semantic analyzer - Scope *scope; + ScopeDecls *decls_scope; TypeTableEntry *type_entry; TypeTableEntry *generic_fn_type; bool skip; @@ -884,7 +885,7 @@ struct TypeTableEntryStruct { uint64_t size_bytes; bool is_invalid; // true if any fields are invalid bool is_slice; - Scope *scope; + ScopeDecls *decls_scope; // set this flag temporarily to detect infinite loops bool embedded_in_current; @@ -910,7 +911,7 @@ struct TypeTableEntryEnum { TypeTableEntry *tag_type; TypeTableEntry *union_type; - Scope *scope; + ScopeDecls *decls_scope; // set this flag temporarily to detect infinite loops bool embedded_in_current; @@ -926,7 +927,7 @@ struct TypeTableEntryUnion { TypeStructField *fields; uint64_t size_bytes; bool is_invalid; // true if any fields are invalid - Scope *scope; + ScopeDecls *decls_scope; // set this flag temporarily to detect infinite loops bool embedded_in_current; @@ -1044,7 +1045,7 @@ struct ImportTableEntry { ZigLLVMDIFile *di_file; Buf *source_code; ZigList *line_offsets; - Scope *scope; + ScopeDecls *decls_scope; AstNode *c_import_node; bool any_imports_failed; @@ -1070,8 +1071,6 @@ struct FnTableEntry { AstNode *proto_node; AstNode *fn_def_node; ImportTableEntry *import_entry; - // Required to be a pre-order traversal of the AST. (parents must come before children) - ZigList all_block_contexts; Buf symbol_name; TypeTableEntry *type_entry; // function type bool internal_linkage; @@ -1310,7 +1309,8 @@ struct VariableTableEntry { ZigLLVMDILocalVariable *di_loc_var; size_t src_arg_index; size_t gen_arg_index; - Scope *scope; + Scope *parent_scope; + Scope *child_scope; LLVMValueRef param_value_ref; bool force_depends_on_compile_var; ImportTableEntry *import; @@ -1334,28 +1334,79 @@ struct LabelTableEntry { struct Scope { AstNode *node; - // any variables that are introduced by this scope - HashMap decl_table; - HashMap var_table; - HashMap label_table; - - // if the block is inside a function, this is the function it is in: - FnTableEntry *fn_entry; - - // if the block has a parent, this is it + // if the scope has a parent, this is it. Every scope has a parent except + // ScopeIdGlobal Scope *parent; - // if break or continue is valid in this context, this is the loop node that - // it would pertain to - AstNode *parent_loop_node; - ZigLLVMDIScope *di_scope; - Buf *c_import_buf; bool safety_off; AstNode *safety_set_node; }; +// This scope comes from global declarations or from +// declarations in a container declaration +// NodeTypeRoot, NodeTypeContainerDecl +struct ScopeDecls { + Scope base; + + HashMap decl_table; +}; + +// This scope comes from a container declaration such as a struct, +// enum, or union. +struct ScopeContainer { + Scope base; + + HashMap decl_table; +}; + +// This scope comes from a block expression in user code. +// NodeTypeBlock +struct ScopeBlock { + Scope base; + + HashMap label_table; +}; + +// This scope is created from every defer expression. +// NodeTypeDefer +struct ScopeDefer { + Scope base; +}; + +// This scope is created for every variable declaration inside an IrExecutable +// NodeTypeVariableDeclaration, NodeTypeParamDecl +struct ScopeVarDecl { + Scope base; + + // The variable that creates this scope + VariableTableEntry *var; +}; + +// This scope is created for a @cImport +// NodeTypeFnCallExpr +struct ScopeCImport { + Scope base; + + Buf c_import_buf; +}; + +// This scope is created for a loop such as for or while in order to +// make break and continue statements work. +// NodeTypeForExpr or NodeTypeWhileExpr +struct ScopeLoop { + Scope base; +}; + +// This scope is created for a function definition. +// NodeTypeFnDef +struct ScopeFnBody { + Scope base; + + FnTableEntry *fn_entry; +}; + enum AtomicOrder { AtomicOrderUnordered, AtomicOrderMonotonic, diff --git a/src/analyze.cpp b/src/analyze.cpp index a0904f283d..baa74365cd 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -115,26 +115,24 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { return entry; } -static Scope **get_container_block_context_ptr(TypeTableEntry *type_entry) { +static ScopeDecls **get_container_scope_ptr(TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdStruct) { - return &type_entry->data.structure.scope; + return &type_entry->data.structure.decls_scope; } else if (type_entry->id == TypeTableEntryIdEnum) { - return &type_entry->data.enumeration.scope; + return &type_entry->data.enumeration.decls_scope; } else if (type_entry->id == TypeTableEntryIdUnion) { - return &type_entry->data.unionation.scope; + return &type_entry->data.unionation.decls_scope; } zig_unreachable(); } -Scope *get_container_block_context(TypeTableEntry *type_entry) { - return *get_container_block_context_ptr(type_entry); +ScopeDecls *get_container_scope(TypeTableEntry *type_entry) { + return *get_container_scope_ptr(type_entry); } -static TypeTableEntry *new_container_type_entry(TypeTableEntryId id, AstNode *source_node, - Scope *parent_context) -{ +static TypeTableEntry *new_container_type_entry(TypeTableEntryId id, AstNode *source_node, Scope *parent_scope) { TypeTableEntry *entry = new_type_table_entry(id); - *get_container_block_context_ptr(entry) = new_scope(source_node, parent_context); + *get_container_scope_ptr(entry) = create_decls_scope(source_node, parent_scope); return entry; } @@ -766,11 +764,11 @@ static TypeTableEntryId container_to_type(ContainerKind kind) { zig_unreachable(); } -TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import, Scope *context, +TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import, Scope *scope, ContainerKind kind, AstNode *decl_node, const char *name) { TypeTableEntryId type_id = container_to_type(kind); - TypeTableEntry *entry = new_container_type_entry(type_id, decl_node, context); + TypeTableEntry *entry = new_container_type_entry(type_id, decl_node, scope); switch (kind) { case ContainerKindStruct: @@ -844,10 +842,10 @@ static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *nod return result; } -static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, Scope *context, +static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, Scope *scope, AstNode *node) { - IrInstruction *result = analyze_const_value(g, context, node, g->builtin_types.entry_type); + IrInstruction *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type); if (result->type_entry->id == TypeTableEntryIdInvalid) return g->builtin_types.entry_invalid; @@ -861,7 +859,7 @@ static bool fn_wants_full_static_eval(FnTableEntry *fn_table_entry) { } // fn_table_entry is populated if and only if there is a function definition for this prototype -static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, Scope *context, +static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, Scope *scope, TypeTableEntry *expected_type, AstNode *node, bool is_naked, bool is_cold, FnTableEntry *fn_table_entry) { assert(node->type == NodeTypeFnProto); @@ -888,7 +886,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor if (fn_proto->skip) { type_entry = g->builtin_types.entry_invalid; } else { - type_entry = analyze_type_expr(g, import, context, child->data.param_decl.type); + type_entry = analyze_type_expr(g, import, scope, child->data.param_decl.type); } switch (type_entry->id) { @@ -951,7 +949,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor if (fn_proto->skip) { fn_type_id.return_type = g->builtin_types.entry_invalid; } else { - fn_type_id.return_type = analyze_type_expr(g, import, context, fn_proto->return_type); + fn_type_id.return_type = analyze_type_expr(g, import, scope, fn_proto->return_type); } switch (fn_type_id.return_type->id) { case TypeTableEntryIdInvalid: @@ -1022,7 +1020,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor } static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, - ImportTableEntry *import, Scope *containing_context) + ImportTableEntry *import, Scope *containing_scope) { assert(node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &node->data.fn_proto; @@ -1037,7 +1035,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t - TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, containing_context, nullptr, node, + TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, containing_scope, nullptr, node, fn_proto->is_nakedcc, fn_proto->is_coldcc, fn_table_entry); fn_table_entry->type_entry = fn_type; @@ -1060,8 +1058,8 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t } if (fn_table_entry->fn_def_node) { - Scope *context = new_scope(fn_table_entry->fn_def_node, containing_context); - fn_table_entry->fn_def_node->data.fn_def.scope = context; + fn_table_entry->fn_def_node->data.fn_def.containing_scope = create_fndef_scope( + fn_table_entry->fn_def_node, containing_scope, fn_table_entry); } if (!fn_wants_full_static_eval(fn_table_entry)) { @@ -1095,23 +1093,6 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim", "true"); ZigLLVMAddFunctionAttr(fn_table_entry->fn_value, "no-frame-pointer-elim-non-leaf", nullptr); } - - if (fn_table_entry->fn_def_node) { - // Add debug info. - unsigned line_number = node->line + 1; - unsigned scope_line = line_number; - bool is_definition = fn_table_entry->fn_def_node != nullptr; - unsigned flags = 0; - bool is_optimized = g->is_release_build; - ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder, - containing_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "", - import->di_file, line_number, - fn_type->di_type, fn_table_entry->internal_linkage, - is_definition, scope_line, flags, is_optimized, nullptr); - - fn_table_entry->fn_def_node->data.fn_def.scope->di_scope = ZigLLVMSubprogramToScope(subprogram); - ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram); - } } } @@ -1151,7 +1132,7 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt uint64_t biggest_align_in_bits = 0; uint64_t biggest_union_member_size_in_bits = 0; - Scope *context = enum_type->data.enumeration.scope; + Scope *scope = &enum_type->data.enumeration.decls_scope->base; // set temporary flag enum_type->data.enumeration.embedded_in_current = true; @@ -1161,7 +1142,7 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt AstNode *field_node = decl_node->data.struct_decl.fields.at(i); TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; type_enum_field->name = field_node->data.struct_field.name; - TypeTableEntry *field_type = analyze_type_expr(g, import, context, + TypeTableEntry *field_type = analyze_type_expr(g, import, scope, field_node->data.struct_field.type); type_enum_field->type_entry = field_type; type_enum_field->value = i; @@ -1337,14 +1318,14 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE // this field should be set to true only during the recursive calls to resolve_struct_type struct_type->data.structure.embedded_in_current = true; - Scope *context = struct_type->data.structure.scope; + Scope *scope = &struct_type->data.structure.decls_scope->base; size_t gen_field_index = 0; for (size_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.struct_decl.fields.at(i); TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; type_struct_field->name = field_node->data.struct_field.name; - TypeTableEntry *field_type = analyze_type_expr(g, import, context, + TypeTableEntry *field_type = analyze_type_expr(g, import, scope, field_node->data.struct_field.type); type_struct_field->type_entry = field_type; type_struct_field->src_index = i; @@ -1463,7 +1444,7 @@ static bool get_is_generic_fn(AstNode *proto_node) { } static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstNode *proto_node, - Scope *containing_context) + Scope *containing_scope) { assert(proto_node->type == NodeTypeFnProto); @@ -1515,7 +1496,7 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN } else { - resolve_function_proto(g, proto_node, fn_table_entry, import, containing_context); + resolve_function_proto(g, proto_node, fn_table_entry, import, containing_scope); if (!fn_wants_full_static_eval(fn_table_entry)) { g->fn_protos.append(fn_table_entry); @@ -1546,7 +1527,7 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN } } -static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, Scope *scope, +static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, ScopeDecls *decls_scope, AstNode *node, Buf *name) { assert(import); @@ -1562,19 +1543,17 @@ static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, Scope *scop g->resolve_queue.append(node); } - node->scope = scope; + node->scope = &decls_scope->base; - auto entry = scope->decl_table.maybe_get(name); + auto entry = decls_scope->decl_table.put_unique(name, node); if (entry) { AstNode *other_decl_node = entry->value; ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(g, msg, other_decl_node, buf_sprintf("previous definition is here")); - } else { - scope->decl_table.put(name, node); } } -static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, Scope *context, AstNode *node) { +static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, ScopeDecls *decls_scope, AstNode *node) { assert(node->type == NodeTypeContainerDecl); if (node->data.struct_decl.type_entry) { @@ -1583,7 +1562,7 @@ static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, Scope *contex } Buf *name = node->data.struct_decl.name; - TypeTableEntry *container_type = get_partial_container_type(g, import, context, + TypeTableEntry *container_type = get_partial_container_type(g, import, &decls_scope->base, node->data.struct_decl.kind, node, buf_ptr(name)); node->data.struct_decl.type_entry = container_type; @@ -1591,8 +1570,8 @@ static void scan_struct_decl(CodeGen *g, ImportTableEntry *import, Scope *contex for (size_t i = 0; i < node->data.struct_decl.decls.length; i += 1) { AstNode *child_node = node->data.struct_decl.decls.at(i); get_as_top_level_decl(child_node)->parent_decl = node; - Scope *child_context = get_container_block_context(container_type); - scan_decls(g, import, child_context, child_node); + ScopeDecls *child_scope = get_container_scope(container_type); + scan_decls(g, import, child_scope, child_node); } } @@ -1644,37 +1623,37 @@ static void preview_error_value_decl(CodeGen *g, AstNode *node) { node->data.error_value_decl.top_level_decl.resolution = TldResolutionOk; } -void scan_decls(CodeGen *g, ImportTableEntry *import, Scope *context, AstNode *node) { +void scan_decls(CodeGen *g, ImportTableEntry *import, ScopeDecls *decls_scope, AstNode *node) { switch (node->type) { case NodeTypeRoot: for (size_t i = 0; i < import->root->data.root.top_level_decls.length; i += 1) { AstNode *child = import->root->data.root.top_level_decls.at(i); - scan_decls(g, import, context, child); + scan_decls(g, import, decls_scope, child); } break; case NodeTypeContainerDecl: { Buf *name = node->data.struct_decl.name; - add_top_level_decl(g, import, context, node, name); + add_top_level_decl(g, import, decls_scope, node, name); if (node->data.struct_decl.generic_params.length == 0) { - scan_struct_decl(g, import, context, node); + scan_struct_decl(g, import, decls_scope, node); } } break; case NodeTypeFnDef: node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = node; - scan_decls(g, import, context, node->data.fn_def.fn_proto); + scan_decls(g, import, decls_scope, node->data.fn_def.fn_proto); break; case NodeTypeVariableDeclaration: { Buf *name = node->data.variable_declaration.symbol; - add_top_level_decl(g, import, context, node, name); + add_top_level_decl(g, import, decls_scope, node, name); break; } case NodeTypeTypeDecl: { Buf *name = node->data.type_decl.symbol; - add_top_level_decl(g, import, context, node, name); + add_top_level_decl(g, import, decls_scope, node, name); break; } case NodeTypeFnProto: @@ -1688,14 +1667,14 @@ void scan_decls(CodeGen *g, ImportTableEntry *import, Scope *context, AstNode *n } count_inline_and_var_args(node); - add_top_level_decl(g, import, context, node, fn_name); + add_top_level_decl(g, import, decls_scope, node, fn_name); break; } case NodeTypeUse: { TopLevelDecl *tld = get_as_top_level_decl(node); tld->import = import; - node->scope = context; + node->scope = &decls_scope->base; g->use_queue.append(node); tld->import->use_decls.append(node); break; @@ -1816,70 +1795,65 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt // 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, - Scope *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node, - bool shadowable) +static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import, + Scope *parent_scope, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node) { VariableTableEntry *variable_entry = allocate(1); variable_entry->type = type_entry; - variable_entry->scope = context; + variable_entry->parent_scope = parent_scope; variable_entry->import = import; - variable_entry->shadowable = shadowable; + variable_entry->shadowable = false; variable_entry->mem_slot_index = SIZE_MAX; - if (name) { - buf_init_from_buf(&variable_entry->name, name); + assert(name); - if (type_entry->id != TypeTableEntryIdInvalid) { - VariableTableEntry *existing_var = find_variable(g, context, name); - if (existing_var && !existing_var->shadowable) { - ErrorMsg *msg = add_node_error(g, source_node, - buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); - add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); + buf_init_from_buf(&variable_entry->name, name); + + if (type_entry->id != TypeTableEntryIdInvalid) { + VariableTableEntry *existing_var = find_variable(g, parent_scope, name); + if (existing_var && !existing_var->shadowable) { + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); + add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); + variable_entry->type = g->builtin_types.entry_invalid; + } else { + auto primitive_table_entry = g->primitive_type_table.maybe_get(name); + if (primitive_table_entry) { + TypeTableEntry *type = primitive_table_entry->value; + add_node_error(g, source_node, + buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->type = g->builtin_types.entry_invalid; } else { - auto primitive_table_entry = g->primitive_type_table.maybe_get(name); - if (primitive_table_entry) { - TypeTableEntry *type = primitive_table_entry->value; - add_node_error(g, source_node, - buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); + AstNode *decl_node = find_decl(parent_scope, name); + if (decl_node && decl_node->type != NodeTypeVariableDeclaration) { + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("redefinition of '%s'", buf_ptr(name))); + add_error_note(g, msg, decl_node, buf_sprintf("previous definition is here")); variable_entry->type = g->builtin_types.entry_invalid; - } else { - AstNode *decl_node = find_decl(context, name); - if (decl_node && decl_node->type != NodeTypeVariableDeclaration) { - ErrorMsg *msg = add_node_error(g, source_node, - buf_sprintf("redefinition of '%s'", buf_ptr(name))); - add_error_note(g, msg, decl_node, buf_sprintf("previous definition is here")); - variable_entry->type = g->builtin_types.entry_invalid; - } } } } + } - context->var_table.put(&variable_entry->name, variable_entry); + Scope *child_scope; + if (source_node->type == NodeTypeParamDecl) { + child_scope = create_var_scope(source_node, parent_scope, variable_entry); } else { - // TODO replace _anon with @anon and make sure all tests still pass - buf_init_from_str(&variable_entry->name, "_anon"); - } - if (context->fn_entry) { - context->fn_entry->variable_list.append(variable_entry); + // it's already in the decls table + child_scope = parent_scope; } + variable_entry->src_is_const = is_const; variable_entry->gen_is_const = is_const; variable_entry->decl_node = source_node; variable_entry->val_node = val_node; + variable_entry->child_scope = child_scope; return variable_entry; } -static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import, - Scope *context, Buf *name, TypeTableEntry *type_entry, bool is_const, AstNode *val_node) -{ - return add_local_var_shadowable(g, source_node, import, context, name, type_entry, is_const, val_node, false); -} - static void resolve_var_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { assert(node->type == NodeTypeVariableDeclaration); @@ -1889,8 +1863,6 @@ static void resolve_var_decl(CodeGen *g, ImportTableEntry *import, AstNode *node 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); @@ -1980,7 +1952,7 @@ void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) { if (node->data.type_decl.override_type) { entry = node->data.type_decl.override_type; } else { - TypeTableEntry *child_type = analyze_type_expr(g, import, import->scope, type_node); + TypeTableEntry *child_type = analyze_type_expr(g, import, &import->decls_scope->base, type_node); if (child_type->id == TypeTableEntryIdInvalid) { entry = child_type; } else { @@ -2170,52 +2142,105 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * return false; } -Scope *new_scope(AstNode *node, Scope *parent) { - Scope *context = allocate(1); - context->node = node; - context->parent = parent; - context->decl_table.init(1); - context->var_table.init(1); - context->label_table.init(1); - - if (parent) { - context->parent_loop_node = parent->parent_loop_node; - context->c_import_buf = parent->c_import_buf; - } - - if (node && node->type == NodeTypeFnDef) { - AstNode *fn_proto_node = node->data.fn_def.fn_proto; - context->fn_entry = fn_proto_node->data.fn_proto.fn_table_entry; - } else if (parent) { - context->fn_entry = parent->fn_entry; - } - - if (context->fn_entry) { - context->fn_entry->all_block_contexts.append(context); - } - - return context; +ScopeDecls *create_decls_scope(AstNode *node, Scope *parent) { + assert(node->type == NodeTypeRoot || node->type == NodeTypeContainerDecl); + ScopeDecls *scope = allocate(1); + scope->base.node = node; + scope->base.parent = parent; + scope->decl_table.init(4); + return scope; } -AstNode *find_decl(Scope *context, Buf *name) { - while (context) { - auto entry = context->decl_table.maybe_get(name); - if (entry) { - return entry->value; +Scope *create_block_scope(AstNode *node, Scope *parent) { + assert(node->type == NodeTypeBlock); + ScopeBlock *scope = allocate(1); + scope->base.node = node; + scope->base.parent = parent; + scope->label_table.init(1); + return &scope->base; +} + +Scope *create_defer_scope(AstNode *node, Scope *parent) { + assert(node->type == NodeTypeDefer); + ScopeDefer *scope = allocate(1); + scope->base.node = node; + scope->base.parent = parent; + return &scope->base; +} + +Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var) { + assert(node->type == NodeTypeVariableDeclaration || node->type == NodeTypeParamDecl); + ScopeVarDecl *scope = allocate(1); + scope->base.node = node; + scope->base.parent = parent; + scope->var = var; + return &scope->base; +} + +Scope *create_cimport_scope(AstNode *node, Scope *parent) { + assert(node->type == NodeTypeFnCallExpr); + ScopeCImport *scope = allocate(1); + scope->base.node = node; + scope->base.parent = parent; + buf_resize(&scope->c_import_buf, 0); + return &scope->base; +} + +Scope *create_loop_scope(AstNode *node, Scope *parent) { + assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr); + ScopeLoop *scope = allocate(1); + scope->base.node = node; + scope->base.parent = parent; + return &scope->base; +} + +Scope *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) { + assert(node->type == NodeTypeFnDef); + ScopeFnBody *scope = allocate(1); + scope->base.node = node; + scope->base.parent = parent; + scope->fn_entry = fn_entry; + return &scope->base; +} + +AstNode *find_decl(Scope *scope, Buf *name) { + while (scope) { + if (scope->node->type == NodeTypeRoot || + scope->node->type == NodeTypeContainerDecl) + { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + auto entry = decls_scope->decl_table.maybe_get(name); + if (entry) + return entry->value; } - context = context->parent; + scope = scope->parent; } return nullptr; } -VariableTableEntry *find_variable(CodeGen *g, Scope *orig_context, Buf *name) { - Scope *context = orig_context; - while (context) { - auto entry = context->var_table.maybe_get(name); - if (entry) { - return entry->value; +VariableTableEntry *find_variable(CodeGen *g, Scope *scope, Buf *name) { + while (scope) { + if (scope->node->type == NodeTypeVariableDeclaration || + scope->node->type == NodeTypeParamDecl) + { + ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; + if (buf_eql_buf(name, &var_scope->var->name)) + return var_scope->var; + } else if (scope->node->type == NodeTypeRoot || + scope->node->type == NodeTypeContainerDecl) + { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + auto entry = decls_scope->decl_table.maybe_get(name); + if (entry) { + AstNode *decl_node = entry->value; + if (decl_node->type == NodeTypeVariableDeclaration) { + VariableTableEntry *var = decl_node->data.variable_declaration.variable; + if (var) + return var; + } + } } - context = context->parent; + scope = scope->parent; } return nullptr; @@ -2355,7 +2380,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { } fn_table_entry->anal_state = FnAnalStateProbing; - Scope *context = node->data.fn_def.scope; + Scope *child_scope = node->data.fn_def.containing_scope; TypeTableEntry *fn_type = fn_table_entry->type_entry; FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; @@ -2381,16 +2406,20 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { add_node_error(g, param_decl_node, buf_sprintf("missing parameter name")); } - VariableTableEntry *var = add_local_var(g, param_decl_node, import, context, param_decl->name, + VariableTableEntry *var = add_local_var(g, param_decl_node, import, child_scope, param_decl->name, type, true, nullptr); var->src_arg_index = i; param_decl_node->data.param_decl.variable = var; + child_scope = var->child_scope; + fn_table_entry->variable_list.append(var); if (fn_type->data.fn.gen_param_info) { var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; } } + node->data.fn_def.child_scope = child_scope; + TypeTableEntry *expected_type = fn_type->data.fn.fn_type_id.return_type; if (fn_type->data.fn.fn_type_id.is_extern && handle_is_ptr(expected_type)) { @@ -2456,7 +2485,7 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * continue; } if (target_tld->visib_mod != VisibModPrivate) { - auto existing_entry = tld->import->scope->decl_table.maybe_get(target_tld->name); + auto existing_entry = tld->import->decls_scope->decl_table.put_unique(target_tld->name, decl_node); if (existing_entry) { AstNode *existing_decl = existing_entry->value; if (existing_decl != decl_node) { @@ -2466,8 +2495,6 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * add_error_note(g, msg, existing_decl, buf_sprintf("previous definition here")); add_error_note(g, msg, decl_node, buf_sprintf("imported definition here")); } - } else { - tld->import->scope->decl_table.put(target_tld->name, decl_node); } } } @@ -2494,7 +2521,7 @@ void preview_use_decl(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeUse); TopLevelDecl *tld = get_as_top_level_decl(node); - IrInstruction *result = analyze_const_value(g, tld->import->scope, node->data.use.expr, + IrInstruction *result = analyze_const_value(g, &tld->import->decls_scope->base, node->data.use.expr, g->builtin_types.entry_namespace); if (result->type_entry->id == TypeTableEntryIdInvalid) tld->import->any_imports_failed = true; @@ -2553,8 +2580,7 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, g->import_table.put(abs_full_path, import_entry); g->import_queue.append(import_entry); - import_entry->scope = new_scope(import_entry->root, nullptr); - import_entry->scope->di_scope = ZigLLVMFileToScope(import_entry->di_file); + import_entry->decls_scope = create_decls_scope(import_entry->root, nullptr); assert(import_entry->root->type == NodeTypeRoot); @@ -2581,7 +2607,7 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, void semantic_analyze(CodeGen *g) { for (; g->import_queue_index < g->import_queue.length; g->import_queue_index += 1) { ImportTableEntry *import = g->import_queue.at(g->import_queue_index); - scan_decls(g, import, import->scope, import->root); + scan_decls(g, import, import->decls_scope, import->root); } for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) { diff --git a/src/analyze.hpp b/src/analyze.hpp index 6376a90544..4201643876 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -15,7 +15,6 @@ ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg); TypeTableEntry *new_type_table_entry(TypeTableEntryId id); TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); -Scope *new_scope(AstNode *node, Scope *parent); bool is_node_void_expr(AstNode *node); uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry); TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, size_t size_in_bits); @@ -59,11 +58,19 @@ TypeTableEntry *container_ref_type(TypeTableEntry *type_entry); bool type_is_complete(TypeTableEntry *type_entry); void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry); TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name); -Scope *get_container_block_context(TypeTableEntry *type_entry); +ScopeDecls *get_container_scope(TypeTableEntry *type_entry); TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name); bool is_container_ref(TypeTableEntry *type_entry); -void scan_decls(CodeGen *g, ImportTableEntry *import, Scope *context, AstNode *node); +void scan_decls(CodeGen *g, ImportTableEntry *import, ScopeDecls *decls_scope, AstNode *node); void preview_use_decl(CodeGen *g, AstNode *node); void resolve_use_decl(CodeGen *g, AstNode *node); +ScopeDecls *create_decls_scope(AstNode *node, Scope *parent); +Scope *create_block_scope(AstNode *node, Scope *parent); +Scope *create_defer_scope(AstNode *node, Scope *parent); +Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var); +Scope *create_cimport_scope(AstNode *node, Scope *parent); +Scope *create_loop_scope(AstNode *node, Scope *parent); +Scope *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index ebf5ae530e..26ca633b8d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -227,9 +227,47 @@ void codegen_set_rdynamic(CodeGen *g, bool rdynamic) { static void render_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val); static void render_const_val_global(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val); +static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { + if (scope->di_scope) + return scope->di_scope; + + if (scope->node->type == NodeTypeFnDef) { + assert(scope->parent); + ScopeFnBody *fn_scope = (ScopeFnBody *)scope; + FnTableEntry *fn_table_entry = fn_scope->fn_entry; + unsigned line_number = fn_table_entry->proto_node->line + 1; + unsigned scope_line = line_number; + bool is_definition = fn_table_entry->fn_def_node != nullptr; + unsigned flags = 0; + bool is_optimized = g->is_release_build; + ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder, + get_di_scope(g, scope->parent), buf_ptr(&fn_table_entry->symbol_name), "", + scope->node->owner->di_file, line_number, + fn_table_entry->type_entry->di_type, fn_table_entry->internal_linkage, + is_definition, scope_line, flags, is_optimized, nullptr); + + scope->di_scope = ZigLLVMSubprogramToScope(subprogram); + ZigLLVMFnSetSubprogram(fn_table_entry->fn_value, subprogram); + } else if (scope->node->type == NodeTypeRoot || + scope->node->type == NodeTypeContainerDecl) + { + scope->di_scope = ZigLLVMFileToScope(scope->node->owner->di_file); + } else { + assert(scope->parent); + ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder, + get_di_scope(g, scope->parent), + scope->node->owner->di_file, + scope->node->line + 1, + scope->node->column + 1); + scope->di_scope = ZigLLVMLexicalBlockToScope(di_block); + } + + return scope->di_scope; +} + static void set_debug_source_node(CodeGen *g, AstNode *node) { assert(node->scope); - ZigLLVMSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, node->scope->di_scope); + ZigLLVMSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, get_di_scope(g, node->scope)); } static void clear_debug_source_node(CodeGen *g) { @@ -558,10 +596,9 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, } static void gen_var_debug_decl(CodeGen *g, VariableTableEntry *var) { - Scope *scope = var->scope; AstNode *source_node = var->decl_node; ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc(source_node->line + 1, source_node->column + 1, - scope->di_scope); + get_di_scope(g, var->parent_scope)); ZigLLVMInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc, LLVMGetInsertBlock(g->builder)); } @@ -2133,8 +2170,7 @@ static void gen_global_var(CodeGen *g, VariableTableEntry *var, LLVMValueRef ini assert(var->import); assert(type_entry); bool is_local_to_unit = true; - ZigLLVMCreateGlobalVariable(g->dbuilder, - var->scope->di_scope, buf_ptr(&var->name), + ZigLLVMCreateGlobalVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), buf_ptr(&var->name), var->import->di_file, var->decl_node->line + 1, type_entry->di_type, is_local_to_unit, init_val); } @@ -2328,24 +2364,6 @@ static void do_code_gen(CodeGen *g) { AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; build_all_basic_blocks(g, fn_table_entry); - - - // Set up debug info for blocks - for (size_t bc_i = 0; bc_i < fn_table_entry->all_block_contexts.length; bc_i += 1) { - Scope *scope = fn_table_entry->all_block_contexts.at(bc_i); - - if (!scope->di_scope) { - ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder, - scope->parent->di_scope, - import->di_file, - scope->node->line + 1, - scope->node->column + 1); - scope->di_scope = ZigLLVMLexicalBlockToScope(di_block); - } - - - } - clear_debug_source_node(g); // allocate temporary stack data @@ -2383,7 +2401,7 @@ static void do_code_gen(CodeGen *g) { if (var->is_inline) continue; - if (var->scope->node->type == NodeTypeFnDef) { + if (var->parent_scope->node->type == NodeTypeFnDef) { assert(var->gen_arg_index != SIZE_MAX); TypeTableEntry *gen_type; if (handle_is_ptr(var->type)) { @@ -2395,7 +2413,7 @@ static void do_code_gen(CodeGen *g) { unsigned align_bytes = ZigLLVMGetPrefTypeAlignment(g->target_data_ref, var->type->type_ref); LLVMSetAlignment(var->value_ref, align_bytes); } - var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, var->scope->di_scope, + var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), import->di_file, var->decl_node->line + 1, gen_type->di_type, !g->strip_debug_symbols, 0, var->gen_arg_index + 1); @@ -2406,7 +2424,7 @@ static void do_code_gen(CodeGen *g) { unsigned align_bytes = ZigLLVMGetPrefTypeAlignment(g->target_data_ref, var->type->type_ref); LLVMSetAlignment(var->value_ref, align_bytes); - var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, var->scope->di_scope, + var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), import->di_file, var->decl_node->line + 1, var->type->di_type, !g->strip_debug_symbols, 0); } diff --git a/src/hash_map.hpp b/src/hash_map.hpp index 9c9939e4cc..51ec352eda 100644 --- a/src/hash_map.hpp +++ b/src/hash_map.hpp @@ -61,6 +61,15 @@ public: } } + Entry *put_unique(const K &key, const V &value) { + // TODO make this more efficient + Entry *entry = internal_get(key); + if (entry) + return entry; + put(key, value); + return nullptr; + } + const V &get(const K &key) const { Entry *entry = internal_get(key); if (!entry) diff --git a/src/ir.cpp b/src/ir.cpp index ecf70f69ff..54fa8d9f0e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1211,28 +1211,39 @@ static IrInstruction *ir_build_ref_from(IrBuilder *irb, IrInstruction *old_instr return new_instruction; } -static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_block, Scope *outer_block, +static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers, bool gen_maybe_defers) { - while (inner_block != outer_block) { - if (inner_block->node->type == NodeTypeDefer && - ((inner_block->node->data.defer.kind == ReturnKindUnconditional) || - (gen_error_defers && inner_block->node->data.defer.kind == ReturnKindError) || - (gen_maybe_defers && inner_block->node->data.defer.kind == ReturnKindMaybe))) + while (inner_scope != outer_scope) { + if (inner_scope->node->type == NodeTypeDefer && + ((inner_scope->node->data.defer.kind == ReturnKindUnconditional) || + (gen_error_defers && inner_scope->node->data.defer.kind == ReturnKindError) || + (gen_maybe_defers && inner_scope->node->data.defer.kind == ReturnKindMaybe))) { - AstNode *defer_expr_node = inner_block->node->data.defer.expr; + AstNode *defer_expr_node = inner_scope->node->data.defer.expr; ir_gen_node(irb, defer_expr_node, defer_expr_node->scope); } - inner_block = inner_block->parent; + inner_scope = inner_scope->parent; } } +static FnTableEntry *scope_fn_entry(Scope *scope) { + while (scope) { + if (scope->node->type == NodeTypeFnDef) { + ScopeFnBody *fn_scope = (ScopeFnBody *)scope; + return fn_scope->fn_entry; + } + scope = scope->parent; + } + return nullptr; +} + static IrInstruction *ir_gen_return(IrBuilder *irb, AstNode *node) { assert(node->type == NodeTypeReturnExpr); Scope *scope = node->scope; - if (!scope->fn_entry) { + if (!scope_fn_entry(scope)) { add_node_error(irb->codegen, node, buf_sprintf("return expression outside function definition")); return irb->codegen->invalid_instruction; } @@ -1243,7 +1254,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, AstNode *node) { { IrInstruction *return_value; if (expr_node) { - return_value = ir_gen_node(irb, expr_node, scope); + return_value = ir_gen_node(irb, expr_node, node->scope); } else { return_value = ir_build_const_void(irb, node); } @@ -1264,11 +1275,11 @@ static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) { irb->current_basic_block = basic_block; } -static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, Scope *scope, +static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, bool is_inline) { VariableTableEntry *variable_entry = allocate(1); - variable_entry->scope = scope; + variable_entry->parent_scope = parent_scope; variable_entry->import = node->owner; variable_entry->shadowable = is_shadowable; variable_entry->mem_slot_index = SIZE_MAX; @@ -1277,7 +1288,7 @@ static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, Scope if (name) { buf_init_from_buf(&variable_entry->name, name); - VariableTableEntry *existing_var = find_variable(codegen, scope, name); + VariableTableEntry *existing_var = find_variable(codegen, parent_scope, name); if (existing_var && !existing_var->shadowable) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); @@ -1291,7 +1302,7 @@ static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, Scope buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->type = codegen->builtin_types.entry_invalid; } else { - AstNode *decl_node = find_decl(scope, name); + AstNode *decl_node = find_decl(parent_scope, name); if (decl_node && decl_node->type != NodeTypeVariableDeclaration) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); @@ -1301,28 +1312,32 @@ static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, Scope } } - scope->var_table.put(&variable_entry->name, variable_entry); } else { assert(is_shadowable); - // TODO replace _anon with @anon and make sure all tests still pass + // TODO make this name not actually be in scope. user should be able to make a variable called "_anon" + // might already be solved, let's just make sure it has test coverage + // maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables buf_init_from_str(&variable_entry->name, "_anon"); } variable_entry->src_is_const = src_is_const; variable_entry->gen_is_const = gen_is_const; variable_entry->decl_node = node; + variable_entry->child_scope = create_var_scope(node, parent_scope, variable_entry); 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, Scope *scope, Buf *name, +// After you call this function var->child_scope has the variable in scope +static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, bool is_inline) { VariableTableEntry *var = add_local_var(irb->codegen, node, scope, name, src_is_const, gen_is_const, is_shadowable, is_inline); if (is_inline || gen_is_const) var->mem_slot_index = exec_next_mem_slot(irb->exec); + assert(var->child_scope); return var; } @@ -1330,7 +1345,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, AstNode *block_node) { assert(block_node->type == NodeTypeBlock); Scope *parent_scope = block_node->scope; - Scope *outer_block_scope = new_scope(block_node, parent_scope); + Scope *outer_block_scope = create_block_scope(block_node, parent_scope); Scope *child_scope = outer_block_scope; IrInstruction *return_value = nullptr; @@ -1341,6 +1356,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, AstNode *block_node) { // defer starts a new block context child_scope = statement_node->data.defer.child_block; assert(child_scope); + } else if (return_value->id == IrInstructionIdDeclVar) { + IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)return_value; + child_scope = decl_var_instruction->var->child_scope; } } @@ -1760,7 +1778,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) { if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - if (node->scope->fn_entry) { + if (scope_fn_entry(node->scope)) { add_node_error(irb->codegen, node, buf_sprintf("import valid only at top level scope")); return irb->codegen->invalid_instruction; } @@ -1999,8 +2017,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, AstNode *node) { bool is_const = variable_declaration->is_const; bool is_extern = variable_declaration->is_extern; bool is_inline = ir_should_inline(irb) || variable_declaration->is_inline; - VariableTableEntry *var = ir_add_local_var(irb, node, node->scope, + VariableTableEntry *var = ir_create_var(irb, node, node->scope, variable_declaration->symbol, is_const, is_const, is_shadowable, is_inline); + // we detect IrInstructionIdDeclVar in gen_block to make sure the next node + // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { var->type = irb->codegen->builtin_types.entry_invalid; @@ -2079,14 +2099,15 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, AstNode *node) { } bool is_inline = ir_should_inline(irb) || node->data.for_expr.is_inline; - Scope *child_scope = new_scope(node, parent_scope); - child_scope->parent_loop_node = node; + Scope *child_scope = create_loop_scope(node, parent_scope); elem_node->scope = child_scope; // TODO make it an error to write to element variable or i variable. Buf *elem_var_name = elem_node->data.symbol_expr.symbol; - node->data.for_expr.elem_var = ir_add_local_var(irb, elem_node, child_scope, elem_var_name, + node->data.for_expr.elem_var = ir_create_var(irb, elem_node, child_scope, elem_var_name, true, false, false, is_inline); + child_scope = node->data.for_expr.elem_var->child_scope; + IrInstruction *undefined_value = ir_build_const_undefined(irb, elem_node); ir_build_var_decl(irb, elem_node, node->data.for_expr.elem_var, elem_var_type, undefined_value); IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, node, node->data.for_expr.elem_var); @@ -2096,13 +2117,15 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, AstNode *node) { index_var_source_node = index_node; Buf *index_var_name = index_node->data.symbol_expr.symbol; index_node->scope = child_scope; - node->data.for_expr.index_var = ir_add_local_var(irb, index_node, child_scope, index_var_name, + node->data.for_expr.index_var = ir_create_var(irb, index_node, child_scope, index_var_name, true, false, false, is_inline); } else { index_var_source_node = node; - node->data.for_expr.index_var = ir_add_local_var(irb, node, child_scope, nullptr, + node->data.for_expr.index_var = ir_create_var(irb, node, child_scope, nullptr, true, false, true, is_inline); } + child_scope = node->data.for_expr.index_var->child_scope; + IrInstruction *usize = ir_build_const_type(irb, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, node, 0); IrInstruction *one = ir_build_const_usize(irb, node, 1); @@ -2159,10 +2182,11 @@ static IrInstruction *ir_gen_this_literal(IrBuilder *irb, AstNode *node) { if (!scope->parent) return ir_build_const_import(irb, node, node->owner); - if (scope->fn_entry && (!scope->parent->fn_entry || - (scope->parent->parent && !scope->parent->parent->fn_entry))) + FnTableEntry *fn_entry = scope_fn_entry(scope); + if (fn_entry && scope->parent && scope->parent->parent && + !scope_fn_entry(scope->parent->parent)) { - return ir_build_const_fn(irb, node, scope->fn_entry); + return ir_build_const_fn(irb, node, fn_entry); } if (scope->node->type == NodeTypeContainerDecl) { @@ -2308,15 +2332,15 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, AstNode *node) { if (var_type == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } - Scope *child_scope = new_scope(node, node->scope); bool is_shadowable = false; bool is_const = var_decl->is_const; - VariableTableEntry *var = ir_add_local_var(irb, node, child_scope, + VariableTableEntry *var = ir_create_var(irb, node, node->scope, var_decl->symbol, is_const, is_const, is_shadowable, is_inline); + IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, node, expr_value, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, node, var_ptr_value); ir_build_var_decl(irb, node, var, var_type, var_value); - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, child_scope); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var->child_scope); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; @@ -2360,11 +2384,11 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, AstNode *switch_node, AstNo Buf *var_name = var_symbol_node->data.symbol_expr.symbol; bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr; - child_scope = new_scope(switch_node, switch_node->scope); bool is_shadowable = false; bool is_const = true; - VariableTableEntry *var = ir_add_local_var(irb, var_symbol_node, child_scope, + VariableTableEntry *var = ir_create_var(irb, var_symbol_node, switch_node->scope, var_name, is_const, is_const, is_shadowable, is_inline); + child_scope = var->child_scope; IrInstruction *var_value; if (prong_value) { IrInstruction *var_ptr_value = ir_build_switch_var(irb, var_symbol_node, target_value_ptr, prong_value); @@ -2542,14 +2566,25 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) { return ir_build_phi(irb, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } -static LabelTableEntry *find_label(IrExecutable *exec, Scope *orig_context, Buf *name) { - Scope *context = orig_context; - while (context) { - auto entry = context->label_table.maybe_get(name); - if (entry) { - return entry->value; +static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) { + while (scope) { + if (scope->node->type == NodeTypeBlock) { + ScopeBlock *block_scope = (ScopeBlock *)scope; + auto entry = block_scope->label_table.maybe_get(name); + if (entry) + return entry->value; } - context = context->parent; + scope = scope->parent; + } + + return nullptr; +} + +static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) { + while (scope) { + if (scope->node->type == NodeTypeBlock) + return (ScopeBlock *)scope; + scope = scope->parent; } return nullptr; } @@ -2571,7 +2606,8 @@ static IrInstruction *ir_gen_label(IrBuilder *irb, AstNode *node) { add_error_note(irb->codegen, msg, existing_label->decl_node, buf_sprintf("other label here")); return irb->codegen->invalid_instruction; } else { - node->scope->label_table.put(label_name, label); + ScopeBlock *scope_block = find_block_scope(irb->exec, node->scope); + scope_block->label_table.put(label_name, label); } bool is_inline = ir_should_inline(irb); @@ -2778,9 +2814,9 @@ IrInstruction *ir_gen_fn(CodeGen *codegn, FnTableEntry *fn_entry) { assert(fn_def_node->type == NodeTypeFnDef); AstNode *body_node = fn_def_node->data.fn_def.body; - Scope *scope = fn_def_node->data.fn_def.scope; + Scope *child_scope = fn_def_node->data.fn_def.child_scope; - return ir_gen(codegn, body_node, scope, ir_executable); + return ir_gen(codegn, body_node, child_scope, ir_executable); } static IrInstruction *ir_eval_fn(IrAnalyze *ira, IrInstruction *source_instruction, @@ -3023,8 +3059,10 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst } else { IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->source_node, wanted_type, value, cast_op); result->type_entry = wanted_type; - if (need_alloca && source_instr->source_node->scope->fn_entry) { - source_instr->source_node->scope->fn_entry->alloca_list.append(result); + if (need_alloca) { + FnTableEntry *fn_entry = scope_fn_entry(source_instr->source_node->scope); + if (fn_entry) + fn_entry->alloca_list.append(result); } return result; } @@ -3550,7 +3588,8 @@ static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_inst ir_link_new_instruction(value, source_instruction); return ptr_type; } else { - FnTableEntry *fn_entry = source_instruction->source_node->scope->fn_entry; + FnTableEntry *fn_entry = scope_fn_entry(source_instruction->source_node->scope); + assert(fn_entry); IrInstruction *new_instruction = ir_build_ref_from(&ira->new_irb, source_instruction, value); fn_entry->alloca_list.append(new_instruction); return ptr_type; @@ -4098,8 +4137,9 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc ir_build_var_decl_from(&ira->new_irb, &decl_var_instruction->base, var, var_type, casted_init_value); Scope *scope = decl_var_instruction->base.source_node->scope; - if (scope->fn_entry) - scope->fn_entry->variable_list.append(var); + FnTableEntry *fn_entry = scope_fn_entry(scope); + if (fn_entry) + fn_entry->variable_list.append(var); return ira->codegen->builtin_types.entry_void; } @@ -4200,8 +4240,11 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, fn_entry, fn_ref, call_param_count, casted_args); - if (type_has_bits(return_type) && handle_is_ptr(return_type)) - call_instruction->base.source_node->scope->fn_entry->alloca_list.append(new_call_instruction); + if (type_has_bits(return_type) && handle_is_ptr(return_type)) { + FnTableEntry *fn_entry = scope_fn_entry(call_instruction->base.source_node->scope); + assert(fn_entry); + fn_entry->alloca_list.append(new_call_instruction); + } return ir_finish_anal(ira, return_type); } @@ -4732,7 +4775,8 @@ static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruc return var->type; ConstExprValue *mem_slot = nullptr; - if (var->scope->fn_entry) { + FnTableEntry *fn_entry = scope_fn_entry(var->parent_scope); + if (fn_entry) { // TODO once the analyze code is fully ported over to IR we won't need this SIZE_MAX thing. if (var->mem_slot_index != SIZE_MAX) mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; @@ -4879,9 +4923,8 @@ static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira, IrInstruction *container_ptr, TypeTableEntry *container_type) { if (!is_slice(bare_struct_type)) { - Scope *container_block_context = get_container_block_context(bare_struct_type); - assert(container_block_context); - auto entry = container_block_context->decl_table.maybe_get(field_name); + ScopeDecls *container_scope = get_container_scope(bare_struct_type); + auto entry = container_scope->decl_table.maybe_get(field_name); AstNode *fn_decl_node = entry ? entry->value : nullptr; if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) { resolve_top_level_decl(ira->codegen, fn_decl_node, false); @@ -5023,8 +5066,8 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } else if (child_type->id == TypeTableEntryIdEnum) { zig_panic("TODO enum type field"); } else if (child_type->id == TypeTableEntryIdStruct) { - Scope *container_block_context = get_container_block_context(child_type); - auto entry = container_block_context->decl_table.maybe_get(field_name); + ScopeDecls *container_scope = get_container_scope(child_type); + auto entry = container_scope->decl_table.maybe_get(field_name); AstNode *decl_node = entry ? entry->value : nullptr; if (decl_node) { bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var; @@ -5055,7 +5098,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru ImportTableEntry *namespace_import = namespace_val->data.x_import; bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var; - AstNode *decl_node = find_decl(namespace_import->scope, field_name); + AstNode *decl_node = find_decl(&namespace_import->decls_scope->base, field_name); if (!decl_node) { // we must now resolve all the use decls for (size_t i = 0; i < namespace_import->use_decls.length; i += 1) { @@ -5066,7 +5109,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } resolve_use_decl(ira->codegen, use_decl_node); } - decl_node = find_decl(namespace_import->scope, field_name); + decl_node = find_decl(&namespace_import->decls_scope->base, field_name); } if (decl_node) { TopLevelDecl *tld = get_as_top_level_decl(decl_node); @@ -5327,15 +5370,15 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira, if (target_type->id == TypeTableEntryIdBlock) { target_context = target_val->data.x_block; } else if (target_type->id == TypeTableEntryIdFn) { - target_context = target_val->data.x_fn->fn_def_node->data.fn_def.scope; + target_context = target_val->data.x_fn->fn_def_node->data.fn_def.child_scope; } else if (target_type->id == TypeTableEntryIdMetaType) { TypeTableEntry *type_arg = target_val->data.x_type; if (type_arg->id == TypeTableEntryIdStruct) { - target_context = type_arg->data.structure.scope; + target_context = &type_arg->data.structure.decls_scope->base; } else if (type_arg->id == TypeTableEntryIdEnum) { - target_context = type_arg->data.enumeration.scope; + target_context = &type_arg->data.enumeration.decls_scope->base; } else if (type_arg->id == TypeTableEntryIdUnion) { - target_context = type_arg->data.unionation.scope; + target_context = &type_arg->data.unionation.decls_scope->base; } else { add_node_error(ira->codegen, target_instruction->source_node, buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name))); @@ -5966,7 +6009,7 @@ static TypeTableEntry *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructi ImportTableEntry *target_import = add_source_file(ira->codegen, target_package, abs_full_path, search_dir, import_target_path, import_code); - scan_decls(ira->codegen, target_import, target_import->scope, target_import->root); + scan_decls(ira->codegen, target_import, target_import->decls_scope, target_import->root); ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base, depends_on_compile_var); out_val->data.x_import = target_import; @@ -6021,7 +6064,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru IrInstructionStructInitField *new_fields = allocate(actual_field_count); - FnTableEntry *fn_entry = instruction->source_node->scope->fn_entry; + FnTableEntry *fn_entry = scope_fn_entry(instruction->source_node->scope); bool outside_fn = (fn_entry == nullptr); ConstExprValue const_val = {}; @@ -6124,7 +6167,7 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira const_val.data.x_array.elements = allocate(elem_count); const_val.data.x_array.size = elem_count; - FnTableEntry *fn_entry = instruction->base.source_node->scope->fn_entry; + FnTableEntry *fn_entry = scope_fn_entry(instruction->base.source_node->scope); bool outside_fn = (fn_entry == nullptr); IrInstruction **new_items = allocate(elem_count); diff --git a/src/parseh.cpp b/src/parseh.cpp index 7629a524fb..8d7e2fbce3 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -817,7 +817,7 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) const EnumDecl *enum_def = enum_decl->getDefinition(); if (!enum_def) { TypeTableEntry *enum_type = get_partial_container_type(c->codegen, c->import, - c->import->scope, + &c->import->decls_scope->base, ContainerKindEnum, c->source_node, buf_ptr(full_type_name)); c->enum_type_table.put(bare_name, enum_type); c->decl_table.put(enum_decl, enum_type); @@ -842,7 +842,7 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) if (pure_enum) { TypeTableEntry *enum_type = get_partial_container_type(c->codegen, c->import, - c->import->scope, + &c->import->decls_scope->base, ContainerKindEnum, c->source_node, buf_ptr(full_type_name)); c->enum_type_table.put(bare_name, enum_type); c->decl_table.put(enum_decl, enum_type); @@ -1002,7 +1002,7 @@ static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_ TypeTableEntry *struct_type = get_partial_container_type(c->codegen, c->import, - c->import->scope, ContainerKindStruct, c->source_node, buf_ptr(full_type_name)); + &c->import->decls_scope->base, ContainerKindStruct, c->source_node, buf_ptr(full_type_name)); c->struct_type_table.put(bare_name, struct_type); c->decl_table.put(record_decl, struct_type);