Files
Odin/src/checker.hpp

437 lines
12 KiB
C++

// 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 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;
};
#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,
};
struct DeferredProcedure {
DeferredProcedureKind kind;
Entity *entity;
};
struct AttributeContext {
bool is_export;
bool is_static;
bool require_results;
bool require_declaration;
bool has_disabled_proc;
bool disabled_proc;
bool test;
bool set_cold;
String link_name;
String link_prefix;
String link_section;
isize init_expr_list_count;
String thread_local_model;
String deprecated_message;
DeferredProcedure deferred_procedure;
u32 optimization_mode; // ProcedureOptimizationMode
};
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);
void check_decl_attributes(CheckerContext *c, Array<Ast *> 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 * decl_node;
Ast * type_expr;
Ast * init_expr;
Array<Ast *> attributes;
Ast * proc_lit; // Ast_ProcLit
Type * gen_proc_type; // Precalculated
bool is_using;
bool where_clauses_evaluated;
bool proc_checked;
CommentGroup *comment;
CommentGroup *docs;
PtrSet<Entity *> deps;
PtrSet<Type *> type_info_deps;
Array<BlockLabel> 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 : 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 = 29 };
struct Scope {
Ast * node;
Scope * parent;
Scope * prev;
Scope * next;
Scope * first_child;
Scope * last_child;
StringMap<Entity *> elements;
Array<Ast *> delayed_directives;
Array<Ast *> delayed_imports;
PtrSet<Scope *> imported;
i32 flags; // ScopeFlag
union {
AstPackage *pkg;
AstFile * file;
Entity * procedure_entity;
};
};
struct EntityGraphNode;
typedef PtrSet<EntityGraphNode *> 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<ImportGraphNode *> 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<Entity *> CheckerTypePath;
typedef Array<Type *> CheckerPolyPath;
struct AtomOpMapEntry {
u32 kind;
Ast *node;
};
struct CheckerContext;
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
Checker *checker;
StringMap<AstFile *> files; // Key (full path)
StringMap<AstPackage *> packages; // Key (full path)
Array<DeclInfo *> variable_init_order;
AstPackage * builtin_package;
AstPackage * runtime_package;
AstPackage * init_package;
Scope * init_scope;
Entity * entry_point;
PtrSet<Entity *> minimum_dependency_set;
PtrSet<isize> minimum_dependency_type_info_set;
Array<Entity *> testing_procedures;
// Below are accessed within procedures
// NOTE(bill): If the semantic checker (check_proc_body) is to ever to be multithreaded,
// these variables will be of contention
gbMutex untyped_mutex;
gbMutex gen_procs_mutex;
gbMutex gen_types_mutex;
gbMutex type_info_mutex;
gbMutex deps_mutex;
gbMutex identifier_uses_mutex;
gbMutex entity_mutex;
gbMutex foreign_mutex;
gbMutex scope_mutex;
Map<ExprInfo *> 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<Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity
Map<Array<Entity *> > gen_types; // Key: Type *
Array<Type *> type_info_types;
Map<isize> type_info_map; // Key: Type *
bool allow_identifier_uses;
Array<Ast *> identifier_uses; // only used by 'odin query'
Array<Entity *> definitions;
Array<Entity *> entities;
StringMap<Entity *> foreigns;
Array<Entity *> required_global_variables;
Array<Entity *> required_foreign_imports_through_force;
};
struct CheckerContext {
Checker * checker;
CheckerInfo * info;
AstPackage * pkg;
AstFile * file;
Scope * scope;
DeclInfo * decl;
u32 state_flags;
bool in_defer; // TODO(bill): Actually handle correctly
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; // TODO(bill): Actually handle correctly
CheckerPolyPath *poly_path;
isize poly_level; // TODO(bill): Actually handle correctly
#define MAX_INLINE_FOR_DEPTH 1024ll
i64 inline_for_depth;
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;
};
struct Checker {
Parser * parser;
CheckerInfo info;
CheckerContext builtin_ctx;
MPMCQueue<Entity *> procs_with_deferred_to_check;
MPMCQueue<ProcInfo *> procs_to_check_queue;
gbSemaphore procs_to_check_semaphore;
};
gb_global AstPackage *builtin_pkg = nullptr;
gb_global AstPackage *intrinsics_pkg = nullptr;
gb_global AstPackage *config_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 * 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);
// Will return nullptr if not found
Entity *entity_of_node(Ast *expr);
Entity *scope_lookup_current(Scope *s, String const &name);
Entity *scope_lookup (Scope *s, String const &name);
void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_);
Entity *scope_insert (Scope *s, Entity *entity);
ExprInfo *check_get_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, bool is_exported=true);
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);
void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type);
void check_const_decl(CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type);
void check_type_decl(CheckerContext *c, Entity *e, Ast *type_expr, Type *def);
bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
void check_collect_entities(CheckerContext *c, Slice<Ast *> 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);
void init_core_context(Checker *c);
void init_mem_allocator(Checker *c);