// checker.hpp struct Type; struct Entity; struct Scope; struct DeclInfo; struct AstFile; struct Checker; struct CheckerInfo; struct CheckerContext; enum AddressingMode; struct TypeAndValue; // ExprInfo stores information used for "untyped" expressions struct ExprInfo { AddressingMode mode; Type * type; ExactValue value; bool is_lhs; // Debug info }; gb_inline ExprInfo make_expr_info(AddressingMode mode, Type *type, ExactValue value, bool is_lhs) { ExprInfo ei = {}; 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_CheckScopeDecls = 1<<5, }; enum BuiltinProcPkg { BuiltinProcPkg_builtin, BuiltinProcPkg_intrinsics, }; struct BuiltinProc { String name; isize arg_count; bool variadic; ExprKind kind; BuiltinProcPkg pkg; }; enum BuiltinProcId { BuiltinProc_Invalid, BuiltinProc_len, BuiltinProc_cap, BuiltinProc_size_of, BuiltinProc_align_of, BuiltinProc_offset_of, BuiltinProc_type_of, BuiltinProc_type_info_of, BuiltinProc_typeid_of, BuiltinProc_swizzle, BuiltinProc_complex, BuiltinProc_real, BuiltinProc_imag, BuiltinProc_conj, BuiltinProc_expand_to_tuple, BuiltinProc_min, BuiltinProc_max, BuiltinProc_abs, BuiltinProc_clamp, BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures // "Intrinsics" BuiltinProc_atomic_fence, BuiltinProc_atomic_fence_acq, BuiltinProc_atomic_fence_rel, BuiltinProc_atomic_fence_acqrel, BuiltinProc_atomic_store, BuiltinProc_atomic_store_rel, BuiltinProc_atomic_store_relaxed, BuiltinProc_atomic_store_unordered, BuiltinProc_atomic_load, BuiltinProc_atomic_load_acq, BuiltinProc_atomic_load_relaxed, BuiltinProc_atomic_load_unordered, BuiltinProc_atomic_add, BuiltinProc_atomic_add_acq, BuiltinProc_atomic_add_rel, BuiltinProc_atomic_add_acqrel, BuiltinProc_atomic_add_relaxed, BuiltinProc_atomic_sub, BuiltinProc_atomic_sub_acq, BuiltinProc_atomic_sub_rel, BuiltinProc_atomic_sub_acqrel, BuiltinProc_atomic_sub_relaxed, BuiltinProc_atomic_and, BuiltinProc_atomic_and_acq, BuiltinProc_atomic_and_rel, BuiltinProc_atomic_and_acqrel, BuiltinProc_atomic_and_relaxed, BuiltinProc_atomic_nand, BuiltinProc_atomic_nand_acq, BuiltinProc_atomic_nand_rel, BuiltinProc_atomic_nand_acqrel, BuiltinProc_atomic_nand_relaxed, BuiltinProc_atomic_or, BuiltinProc_atomic_or_acq, BuiltinProc_atomic_or_rel, BuiltinProc_atomic_or_acqrel, BuiltinProc_atomic_or_relaxed, BuiltinProc_atomic_xor, BuiltinProc_atomic_xor_acq, BuiltinProc_atomic_xor_rel, BuiltinProc_atomic_xor_acqrel, BuiltinProc_atomic_xor_relaxed, BuiltinProc_atomic_xchg, BuiltinProc_atomic_xchg_acq, BuiltinProc_atomic_xchg_rel, BuiltinProc_atomic_xchg_acqrel, BuiltinProc_atomic_xchg_relaxed, BuiltinProc_atomic_cxchg, BuiltinProc_atomic_cxchg_acq, BuiltinProc_atomic_cxchg_rel, BuiltinProc_atomic_cxchg_acqrel, BuiltinProc_atomic_cxchg_relaxed, BuiltinProc_atomic_cxchg_failrelaxed, BuiltinProc_atomic_cxchg_failacq, BuiltinProc_atomic_cxchg_acq_failrelaxed, BuiltinProc_atomic_cxchg_acqrel_failrelaxed, BuiltinProc_atomic_cxchgweak, BuiltinProc_atomic_cxchgweak_acq, BuiltinProc_atomic_cxchgweak_rel, BuiltinProc_atomic_cxchgweak_acqrel, BuiltinProc_atomic_cxchgweak_relaxed, BuiltinProc_atomic_cxchgweak_failrelaxed, BuiltinProc_atomic_cxchgweak_failacq, BuiltinProc_atomic_cxchgweak_acq_failrelaxed, BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed, BuiltinProc_COUNT, }; gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_builtin}, {STR_LIT("len"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("cap"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("size_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("align_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("offset_of"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("type_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("type_info_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("typeid_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("complex"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("real"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("imag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("min"), 2, true, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("max"), 2, true, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("abs"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("clamp"), 3, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE // "Intrinsics" {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, }; // 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; }; struct AttributeContext { String link_name; String link_prefix; isize init_expr_list_count; String thread_local_model; String deprecated_message; }; 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, ExactValue value, AttributeContext *ac) typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc); void check_decl_attributes(CheckerContext *c, Array const &attributes, DeclAttributeProc *proc, AttributeContext *ac); // 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 Scope * scope; Entity *entity; Ast * type_expr; Ast * init_expr; Array attributes; Ast * proc_lit; // Ast_ProcLit Type * gen_proc_type; // Precalculated bool is_using; PtrSet deps; PtrSet type_info_deps; Array labels; }; // 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 { ScopeFlag_Pkg = 1<<1, ScopeFlag_Global = 1<<2, ScopeFlag_File = 1<<3, ScopeFlag_Init = 1<<4, ScopeFlag_Proc = 1<<5, ScopeFlag_Type = 1<<6, ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes }; struct Scope { Ast * node; Scope * parent; Scope * prev; Scope * next; Scope * first_child; Scope * last_child; Map elements; // Key: String Array delayed_directives; Array delayed_imports; PtrSet imported; i32 flags; // ScopeFlag union { AstPackage *pkg; AstFile * file; }; }; 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; }; struct ForeignContext { Ast * curr_library; ProcCallingConvention default_cc; String link_prefix; bool in_export; }; typedef Array CheckerTypePath; typedef Array CheckerPolyPath; // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Map untyped; // Key: Ast * | Expression -> ExprInfo // NOTE(bill): This needs to be a map and not on the Ast // as it needs to be iterated across Map files; // Key: String (full path) Map packages; // Key: String (full path) Map foreigns; // Key: String Array definitions; Array entities; Array variable_init_order; Map > gen_procs; // Key: Ast * | Identifier -> Entity Map > gen_types; // Key: Type * Array type_info_types; Map type_info_map; // Key: Type * AstPackage * builtin_package; AstPackage * runtime_package; Scope * init_scope; Entity * entry_point; PtrSet minimum_dependency_set; PtrSet minimum_dependency_type_info_set; }; struct CheckerContext { Checker * checker; CheckerInfo * info; AstPackage * pkg; AstFile * file; Scope * scope; DeclInfo * decl; u32 stmt_state_flags; bool in_defer; // TODO(bill): Actually handle correctly Type * type_hint; String proc_name; DeclInfo * curr_proc_decl; Type * curr_proc_sig; bool in_proc_sig; ForeignContext foreign_context; gbAllocator allocator; CheckerTypePath *type_path; isize type_level; // TODO(bill): Actually handle correctly CheckerPolyPath *poly_path; isize poly_level; // TODO(bill): Actually handle correctly bool in_enum_type; bool collect_delayed_decls; bool allow_polymorphic_types; bool no_polymorphic_errors; bool in_polymorphic_specialization; Scope * polymorphic_scope; }; struct Checker { Parser * parser; CheckerInfo info; Array procs_to_check; gbAllocator allocator; CheckerContext init_ctx; }; gb_global AstPackage *builtin_pkg = nullptr; gb_global AstPackage *intrinsics_pkg = nullptr; HashKey hash_node (Ast *node) { return hash_pointer(node); } HashKey hash_ast_file (AstFile *file) { return hash_pointer(file); } HashKey hash_entity (Entity *e) { return hash_pointer(e); } HashKey hash_type (Type *t) { return hash_pointer(t); } HashKey hash_decl_info(DeclInfo *decl) { return hash_pointer(decl); } // CheckerInfo API TypeAndValue type_and_value_of_expr (Ast *expr); Type * type_of_expr (Ast *expr); Entity * entity_of_ident (Ast *identifier); Entity * implicit_entity_of_node(Ast *clause); Scope * scope_of_node (Ast *node); DeclInfo * decl_info_of_ident (Ast *ident); DeclInfo * decl_info_of_entity (Entity * e); AstFile * ast_file_of_filename (CheckerInfo *i, String filename); // IMPORTANT: Only to use once checking is done isize type_info_index (CheckerInfo *i, Type * type, bool error_on_failure = true); // Will return nullptr if not found Entity *entity_of_node(Ast *expr); Entity *scope_lookup_current(Scope *s, String name); Entity *scope_lookup (Scope *s, String name); void scope_lookup_parent (Scope *s, String name, Scope **scope_, Entity **entity_); Entity *scope_insert (Scope *s, Entity *entity); ExprInfo *check_get_expr_info (CheckerInfo *i, Ast *expr); void check_set_expr_info (CheckerInfo *i, Ast *expr, ExprInfo info); void check_remove_expr_info (CheckerInfo *i, Ast *expr); void add_untyped (CheckerInfo *i, Ast *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value); void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value); void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity); void add_implicit_entity (CheckerContext *c, Ast *node, Entity *e); void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d); void add_type_info_type (CheckerContext *c, Type *t); void check_add_import_decl(CheckerContext *c, Ast *decl); void check_add_foreign_import_decl(CheckerContext *c, Ast *decl); bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false); void check_collect_entities(CheckerContext *c, Array const &nodes); void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws); void check_delayed_file_import_entity(CheckerContext *c, Ast *decl); CheckerTypePath *new_checker_type_path(); void destroy_checker_type_path(CheckerTypePath *tp); void check_type_path_push(CheckerContext *c, Entity *e); Entity *check_type_path_pop (CheckerContext *c); CheckerPolyPath *new_checker_poly_path(); void destroy_checker_poly_path(CheckerPolyPath *); void check_poly_path_push(CheckerContext *c, Type *t); Type *check_poly_path_pop (CheckerContext *c);