// checker.hpp struct Type; struct Entity; struct Scope; struct DeclInfo; struct AstFile; struct Checker; struct CheckerInfo; struct CheckerContext; enum AddressingMode : u8; struct TypeAndValue; // ExprInfo stores information used for "untyped" expressions struct ExprInfo { AddressingMode mode; bool is_lhs; // Debug info Type * type; ExactValue value; }; gb_internal gb_inline ExprInfo *make_expr_info(AddressingMode mode, Type *type, ExactValue const &value, bool is_lhs) { ExprInfo *ei = gb_alloc_item(permanent_allocator(), ExprInfo); ei->mode = mode; ei->type = type; ei->value = value; ei->is_lhs = is_lhs; return ei; } enum ExprKind { Expr_Expr, Expr_Stmt, }; // Statements and Declarations enum StmtFlag { Stmt_BreakAllowed = 1<<0, Stmt_ContinueAllowed = 1<<1, Stmt_FallthroughAllowed = 1<<2, Stmt_TypeSwitch = 1<<4, Stmt_CheckScopeDecls = 1<<5, }; enum BuiltinProcPkg { BuiltinProcPkg_builtin, BuiltinProcPkg_intrinsics, }; struct BuiltinProc { String name; isize arg_count; bool variadic; ExprKind kind; BuiltinProcPkg pkg; bool diverging; bool ignore_results; // ignores require results handling }; #include "checker_builtin_procs.hpp" // Operand is used as an intermediate value whilst checking // Operands store an addressing mode, the expression being evaluated, // its type and node, and other specific information for certain // addressing modes // Its zero-value is a valid "invalid operand" struct Operand { AddressingMode mode; Type * type; ExactValue value; Ast * expr; BuiltinProcId builtin_id; Entity * proc_group; }; struct BlockLabel { String name; Ast *label; // Ast_Label; }; enum DeferredProcedureKind { DeferredProcedure_none, DeferredProcedure_in, DeferredProcedure_out, DeferredProcedure_in_out, DeferredProcedure_in_by_ptr, DeferredProcedure_out_by_ptr, DeferredProcedure_in_out_by_ptr, }; struct DeferredProcedure { DeferredProcedureKind kind; Entity *entity; }; struct AttributeContext { String link_name; String link_prefix; String link_section; String linkage; isize init_expr_list_count; String thread_local_model; String deprecated_message; String warning_message; DeferredProcedure deferred_procedure; bool is_export : 1; bool is_static : 1; bool require_results : 1; bool require_declaration : 1; bool has_disabled_proc : 1; bool disabled_proc : 1; bool test : 1; bool init : 1; bool fini : 1; bool set_cold : 1; u32 optimization_mode; // ProcedureOptimizationMode i64 foreign_import_priority_index; String extra_linker_flags; String objc_class; String objc_name; bool objc_is_class_method; Type * objc_type; String require_target_feature; // required by the target micro-architecture String enable_target_feature; // will be enabled for the procedure only }; gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix) { AttributeContext ac = {}; ac.link_prefix = link_prefix; return ac; } #define DECL_ATTRIBUTE_PROC(_name) bool _name(CheckerContext *c, Ast *elem, String name, Ast *value, AttributeContext *ac) typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc); gb_internal void check_decl_attributes(CheckerContext *c, Array const &attributes, DeclAttributeProc *proc, AttributeContext *ac); enum ProcCheckedState : u8 { ProcCheckedState_Unchecked, ProcCheckedState_InProgress, ProcCheckedState_Checked, ProcCheckedState_COUNT }; char const *ProcCheckedState_strings[ProcCheckedState_COUNT] { "Unchecked", "In Progress", "Checked", }; // DeclInfo is used to store information of certain declarations to allow for "any order" usage struct DeclInfo { DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment BlockingMutex next_mutex; DeclInfo * next_child; DeclInfo * next_sibling; Scope * scope; Entity *entity; Ast * decl_node; Ast * type_expr; Ast * init_expr; Array attributes; Ast * proc_lit; // Ast_ProcLit Type * gen_proc_type; // Precalculated bool is_using; bool where_clauses_evaluated; std::atomic proc_checked_state; BlockingMutex proc_checked_mutex; isize defer_used; bool defer_use_checked; CommentGroup *comment; CommentGroup *docs; RwMutex deps_mutex; PtrSet deps; RwMutex type_info_deps_mutex; PtrSet type_info_deps; BlockingMutex type_and_value_mutex; Array labels; // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time struct lbModule *code_gen_module; }; // ProcInfo stores the information needed for checking a procedure struct ProcInfo { AstFile * file; Token token; DeclInfo *decl; Type * type; // Type_Procedure Ast * body; // Ast_BlockStmt u64 tags; bool generated_from_polymorphic; Ast * poly_def_node; }; enum ScopeFlag : i32 { ScopeFlag_Pkg = 1<<1, ScopeFlag_Builtin = 1<<2, ScopeFlag_Global = 1<<3, ScopeFlag_File = 1<<4, ScopeFlag_Init = 1<<5, ScopeFlag_Proc = 1<<6, ScopeFlag_Type = 1<<7, ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes ScopeFlag_ContextDefined = 1<<16, }; enum { DEFAULT_SCOPE_CAPACITY = 32 }; struct Scope { Ast * node; Scope * parent; std::atomic next; std::atomic head_child; RwMutex mutex; StringMap elements; PtrSet imported; i32 flags; // ScopeFlag union { AstPackage *pkg; AstFile * file; Entity * procedure_entity; }; }; struct EntityGraphNode; typedef PtrSet EntityGraphNodeSet; struct EntityGraphNode { Entity * entity; // Procedure, Variable, Constant EntityGraphNodeSet pred; EntityGraphNodeSet succ; isize index; // Index in array/queue isize dep_count; }; struct ImportGraphNode; typedef PtrSet ImportGraphNodeSet; struct ImportGraphNode { AstPackage * pkg; Scope * scope; ImportGraphNodeSet pred; ImportGraphNodeSet succ; isize index; // Index in array/queue isize dep_count; }; enum EntityVisiblityKind { EntityVisiblity_Public, EntityVisiblity_PrivateToPackage, EntityVisiblity_PrivateToFile, }; struct ForeignContext { Ast * curr_library; ProcCallingConvention default_cc; String link_prefix; EntityVisiblityKind visibility_kind; }; typedef Array CheckerTypePath; typedef Array CheckerPolyPath; struct AtomOpMapEntry { u32 kind; Ast *node; }; struct CheckerContext; struct UntypedExprInfo { Ast *expr; ExprInfo *info; }; typedef PtrMap UntypedExprInfoMap; enum ObjcMsgKind : u32 { ObjcMsg_normal, ObjcMsg_fpret, ObjcMsg_fp2ret, ObjcMsg_stret, }; struct ObjcMsgData { ObjcMsgKind kind; Type *proc_type; }; struct LoadFileCache { String path; gbFileError file_error; String data; StringMap hashes; }; struct GenProcsData { Array procs; RwMutex mutex; }; struct GenTypesData { Array types; RwMutex mutex; }; // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Checker *checker; StringMap files; // Key (full path) StringMap packages; // Key (full path) Array variable_init_order; AstPackage * builtin_package; AstPackage * runtime_package; AstPackage * init_package; Scope * init_scope; Entity * entry_point; PtrSet minimum_dependency_set; PtrMap minimum_dependency_type_info_set; Array testing_procedures; Array init_procedures; Array fini_procedures; Array definitions; Array entities; Array required_foreign_imports_through_force; // Below are accessed within procedures RwMutex global_untyped_mutex; UntypedExprInfoMap global_untyped; // NOTE(bill): This needs to be a map and not on the Ast // as it needs to be iterated across afterwards BlockingMutex builtin_mutex; BlockingMutex type_and_value_mutex; RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files RwMutex gen_types_mutex; PtrMap gen_types; BlockingMutex type_info_mutex; // NOT recursive Array type_info_types; PtrMap type_info_map; BlockingMutex foreign_mutex; // NOT recursive StringMap foreigns; MPSCQueue definition_queue; MPSCQueue entity_queue; MPSCQueue required_global_variable_queue; MPSCQueue required_foreign_imports_through_force_queue; MPSCQueue intrinsics_entry_point_usage; BlockingMutex objc_types_mutex; PtrMap objc_msgSend_types; BlockingMutex load_file_mutex; StringMap load_file_cache; BlockingMutex all_procedures_mutex; Array all_procedures; }; struct CheckerContext { // Order matters here BlockingMutex mutex; Checker * checker; CheckerInfo * info; AstPackage * pkg; AstFile * file; Scope * scope; DeclInfo * decl; // Order doesn't matter after this u32 state_flags; bool in_defer; Type * type_hint; String proc_name; DeclInfo * curr_proc_decl; Type * curr_proc_sig; ProcCallingConvention curr_proc_calling_convention; bool in_proc_sig; ForeignContext foreign_context; CheckerTypePath *type_path; isize type_level; UntypedExprInfoMap *untyped; #define MAX_INLINE_FOR_DEPTH 1024ll i64 inline_for_depth; u32 stmt_flags; bool in_enum_type; bool collect_delayed_decls; bool allow_polymorphic_types; bool no_polymorphic_errors; bool hide_polymorphic_errors; bool in_polymorphic_specialization; bool allow_arrow_right_selector_expr; Scope * polymorphic_scope; Ast *assignment_lhs_hint; }; gb_internal u64 check_vet_flags(CheckerContext *c); gb_internal u64 check_vet_flags(Ast *node); struct Checker { Parser * parser; CheckerInfo info; CheckerContext builtin_ctx; MPSCQueue procs_with_deferred_to_check; Array procs_to_check; BlockingMutex nested_proc_lits_mutex; Array nested_proc_lits; MPSCQueue global_untyped_queue; }; gb_global AstPackage *builtin_pkg = nullptr; gb_global AstPackage *intrinsics_pkg = nullptr; gb_global AstPackage *config_pkg = nullptr; // CheckerInfo API gb_internal TypeAndValue type_and_value_of_expr (Ast *expr); gb_internal Type * type_of_expr (Ast *expr); gb_internal Entity * implicit_entity_of_node(Ast *clause); gb_internal DeclInfo * decl_info_of_ident (Ast *ident); gb_internal DeclInfo * decl_info_of_entity (Entity * e); gb_internal AstFile * ast_file_of_filename (CheckerInfo *i, String filename); // IMPORTANT: Only to use once checking is done gb_internal isize type_info_index (CheckerInfo *i, Type *type, bool error_on_failure); // Will return nullptr if not found gb_internal Entity *entity_of_node(Ast *expr); gb_internal Entity *scope_lookup_current(Scope *s, String const &name); gb_internal Entity *scope_lookup (Scope *s, String const &name); gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_); gb_internal Entity *scope_insert (Scope *s, Entity *entity); gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value); gb_internal ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr); gb_internal void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue const &value); gb_internal void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity); gb_internal void add_implicit_entity (CheckerContext *c, Ast *node, Entity *e); gb_internal void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported=true); gb_internal void add_type_info_type (CheckerContext *c, Type *t); gb_internal void check_add_import_decl(CheckerContext *c, Ast *decl); gb_internal void check_add_foreign_import_decl(CheckerContext *c, Ast *decl); gb_internal void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type); gb_internal void check_const_decl(CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type); gb_internal void check_type_decl(CheckerContext *c, Entity *e, Ast *type_expr, Type *def); gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false); gb_internal void check_collect_entities(CheckerContext *c, Slice const &nodes); gb_internal void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws); gb_internal void check_delayed_file_import_entity(CheckerContext *c, Ast *decl); gb_internal CheckerTypePath *new_checker_type_path(); gb_internal void destroy_checker_type_path(CheckerTypePath *tp); gb_internal void check_type_path_push(CheckerContext *c, Entity *e); gb_internal Entity *check_type_path_pop (CheckerContext *c); gb_internal void init_core_context(Checker *c); gb_internal void init_mem_allocator(Checker *c); gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped);