aboutsummaryrefslogtreecommitdiff
path: root/src/checker.hpp
blob: 58ac8beb51ce93b7c47b18f2f2238085a03298b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
// 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,
	BuiltinProcPkg_COUNT
};

String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = {
	str_lit("builtin"),
	str_lit("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;
};


enum InstrumentationFlag : i32 {
	Instrumentation_Enabled  = -1,
	Instrumentation_Default  = 0,
	Instrumentation_Disabled = +1,
};

struct AttributeContext {
	String  link_name;
	String  link_prefix;
	String  link_suffix;
	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;
	bool    entry_point_only      : 1;
	bool    instrumentation_enter : 1;
	bool    instrumentation_exit  : 1;
	bool    no_sanitize_address   : 1;
	bool    no_sanitize_memory    : 1;
	bool    rodata                : 1;
	bool    ignore_duplicates     : 1;
	u32 optimization_mode; // ProcedureOptimizationMode
	i64 foreign_import_priority_index;
	String extra_linker_flags;
	InstrumentationFlag no_instrumentation;

	String  objc_class;
	String  objc_name;
	String  objc_selector;
	Type *  objc_type;
	Type *  objc_superclass;
	Type *  objc_ivar;
	Entity *objc_context_provider;
	bool    objc_is_class_method;
	bool    objc_is_implementation;     // This struct or proc provides a class/method implementation, not a binding to an existing type.
	bool    objc_is_disabled_implement; // This means the method explicitly set @objc_implement to false so it won't be inferred from the class' attribute.

	String require_target_feature; // required by the target micro-architecture
	String enable_target_feature;  // will be enabled for the procedure only

	bool   raddbg_type_view;
	String raddbg_type_view_string;
};

gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix, String link_suffix) {
	AttributeContext ac = {};
	ac.link_prefix = link_prefix;
	ac.link_suffix = link_suffix;
	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<Ast *> const &attributes, DeclAttributeProc *proc, AttributeContext *ac);

#include "name_canonicalization.hpp"

enum ProcCheckedState : u8 {
	ProcCheckedState_Unchecked,
	ProcCheckedState_InProgress,
	ProcCheckedState_Checked,

	ProcCheckedState_COUNT
};

char const *ProcCheckedState_strings[ProcCheckedState_COUNT] {
	"Unchecked",
	"In Progress",
	"Checked",
};

struct VariadicReuseData {
	Type *slice_type; // ..elem_type
	i64 max_count;
};

// 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<Ast *>  attributes;
	Ast *         proc_lit;      // Ast_ProcLit
	Type *        gen_proc_type; // Precalculated

	bool          is_using;
	bool          where_clauses_evaluated;
	bool          foreign_require_results;
	std::atomic<ProcCheckedState> proc_checked_state;

	BlockingMutex proc_checked_mutex;
	isize         defer_used;
	bool          defer_use_checked;

	CommentGroup *comment;
	CommentGroup *docs;

	RwMutex          deps_mutex;
	PtrSet<Entity *> deps;

	RwMutex type_info_deps_mutex;
	TypeSet type_info_deps;

	BlockingMutex type_and_value_mutex;

	Array<BlockLabel> labels;

	i32 scope_index;

	Array<VariadicReuseData> variadic_reuses;
	i64 variadic_reuse_max_bytes;
	i64 variadic_reuse_max_align;

	// 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<Scope *> next;
	std::atomic<Scope *> head_child;

	i32 index; // within a procedure

	RwMutex mutex;
	StringMap<Entity *> elements;
	PtrSet<Scope *> imported;

	DeclInfo *decl_info;

	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;
	String                link_suffix;
	EntityVisiblityKind   visibility_kind;
	bool                  require_results;
};

typedef Array<Entity *> CheckerTypePath;
typedef Array<Type *>   CheckerPolyPath;

struct AtomOpMapEntry {
	u32  kind;
	Ast *node;
};


struct CheckerContext;

struct UntypedExprInfo {
	Ast *expr;
	ExprInfo *info;
};

typedef PtrMap<Ast *, ExprInfo *> UntypedExprInfoMap; 

enum ObjcMsgKind : u32 {
	ObjcMsg_normal,
	ObjcMsg_fpret,
	ObjcMsg_fp2ret,
	ObjcMsg_stret,
};
struct ObjcMsgData {
	ObjcMsgKind kind;
	Type *proc_type;
};

struct ObjcMethodData {
	AttributeContext ac;
	Entity *proc_entity;
};

enum LoadFileTier {
	LoadFileTier_Invalid,
	LoadFileTier_Exists,
	LoadFileTier_Contents,
};

struct LoadFileCache {
	LoadFileTier   tier;
	bool           exists;
	String         path;
	gbFileError    file_error;
	String         data;
	StringMap<u64> hashes;
};


struct LoadDirectoryFile {
	String file_name;
	String data;
};

struct LoadDirectoryCache {
	String                 path;
	gbFileError            file_error;
	Array<LoadFileCache *> files;
};


struct GenProcsData {
	Array<Entity *> procs;
	RwMutex         mutex;
};

struct GenTypesData {
	Array<Entity *> types;
	RecursiveMutex  mutex;
};

struct Defineable {
	String        name;
	ExactValue    default_value;
	TokenPos      pos;
	CommentGroup *docs;

	// These strings are only computed from previous fields when defineables are being shown or exported.
	String default_value_str;
	String pos_str;
};

struct RaddbgTypeView {
	Type * type;
	String view;
};

// 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;
	BlockingMutex minimum_dependency_type_info_mutex;
	PtrMap</*type info hash*/u64, /*min dep index*/isize> min_dep_type_info_index_map;
	TypeSet             min_dep_type_info_set;
	Array<TypeInfoPair> type_info_types_hash_map; // 2 * type_info_types.count


	Array<Entity *> testing_procedures;
	Array<Entity *> init_procedures;
	Array<Entity *> fini_procedures;

	Array<Entity *> definitions;
	Array<Entity *> entities;
	Array<Entity *> required_foreign_imports_through_force;

	BlockingMutex     defineables_mutex;
	Array<Defineable> defineables;


	// 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

	BlockingMutex                  gen_types_mutex;
	PtrMap<Type *, GenTypesData *> gen_types;

	// BlockingMutex type_info_mutex; // NOT recursive
	// Array<TypeInfoPair> type_info_types;
	// PtrMap<Type *, isize> type_info_map;
	// TypeSet type_info_set;

	BlockingMutex foreign_mutex; // NOT recursive
	StringMap<Entity *> foreigns;

	MPSCQueue<Entity *> definition_queue;
	MPSCQueue<Entity *> entity_queue;
	MPSCQueue<Entity *> required_global_variable_queue;
	MPSCQueue<Entity *> required_foreign_imports_through_force_queue;
	MPSCQueue<Entity *> foreign_imports_to_check_fullpaths;
	MPSCQueue<Entity *> foreign_decls_to_check;

	MPSCQueue<RaddbgTypeView> raddbg_type_views_queue;
	Array<RaddbgTypeView> raddbg_type_views;

	MPSCQueue<Ast *> intrinsics_entry_point_usage;

	BlockingMutex objc_objc_msgSend_mutex;
	PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;

	BlockingMutex objc_class_name_mutex;
	StringSet obcj_class_name_set;
	MPSCQueue<Entity *> objc_class_implementations;

	BlockingMutex objc_method_mutex;
	PtrMap<Type *, Array<ObjcMethodData>> objc_method_implementations;


	BlockingMutex load_file_mutex;
	StringMap<LoadFileCache *> load_file_cache;

	BlockingMutex all_procedures_mutex;
	Array<ProcInfo *> all_procedures;

	BlockingMutex instrumentation_mutex;
	Entity *instrumentation_enter_entity;
	Entity *instrumentation_exit_entity;


	BlockingMutex                       load_directory_mutex;
	StringMap<LoadDirectoryCache *>     load_directory_cache;
	PtrMap<Ast *, LoadDirectoryCache *> load_directory_map; // Key: Ast_CallExpr *


};

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;
	Ast *          type_hint_expr;

	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       in_proc_group;
	bool       collect_delayed_decls;
	bool       allow_polymorphic_types;
	bool       disallow_polymorphic_return_types; // NOTE(zen3ger): no poly type decl in return types
	bool       no_polymorphic_errors;
	bool       hide_polymorphic_errors;
	bool       in_polymorphic_specialization;
	bool       allow_arrow_right_selector_expr;
	u8         bit_field_bit_size;
	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<Entity *> procs_with_deferred_to_check;
	MPSCQueue<Entity *> procs_with_objc_context_provider_to_check;
	Array<ProcInfo *> procs_to_check;

	BlockingMutex nested_proc_lits_mutex;
	Array<DeclInfo *> nested_proc_lits;


	MPSCQueue<UntypedExprInfo> global_untyped_queue;
	MPSCQueue<Type *> soa_types_to_complete;
};



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);
gb_internal isize        type_info_index        (CheckerInfo *info, TypeInfoPair pair, 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<Ast *> 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);


gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type);


gb_internal void init_map_internal_types(Type *type);