• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tbklang / tlang / #973

03 Jan 2025 09:39AM UTC coverage: 79.97% (-0.4%) from 80.342%
#973

push

coveralls-ruby

deavmi
Dep-gen

- Added missing import

4839 of 6051 relevant lines covered (79.97%)

368.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.41
/source/tlang/compiler/typecheck/core.d
1
module tlang.compiler.typecheck.core;
2

3
import tlang.compiler.symbols.check;
4
import tlang.compiler.symbols.data;
5
import std.conv : to, ConvException;
6
import std.string;
7
import std.stdio;
8
import tlang.misc.logging;
9
import tlang.compiler.parsing.core;
10
import tlang.compiler.typecheck.resolution;
11
import tlang.compiler.typecheck.exceptions;
12
import tlang.compiler.symbols.typing.core;
13
import tlang.compiler.typecheck.dependency.core;
14
import tlang.compiler.codegen.instruction;
15
import std.container.slist;
16
import std.algorithm : reverse;
17
import tlang.compiler.typecheck.meta;
18
import tlang.compiler.configuration;
19
import tlang.compiler.core;
20
import tlang.compiler.typecheck.dependency.store.interfaces : IFuncDefStore;
21
import tlang.compiler.typecheck.dependency.store.impls : FuncDefStore;
22
import tlang.compiler.typecheck.dependency.pool.interfaces;
23
import tlang.compiler.typecheck.dependency.pool.impls;
24
import tlang.compiler.typecheck.strings;
25
import tlang.compiler.symbols.strings;
26

27
/**
28
* The Parser only makes sure syntax
29
* is adhered to (and, well, partially)
30
* as it would allow string+string
31
* for example
32
*
33
*/
34
public final class TypeChecker
35
{
36
    /** 
37
     * The compiler instance
38
     */
39
    private Compiler compiler;
40

41
    /** 
42
     * The compiler configuration
43
     */
44
    private CompilerConfiguration config;
45

46
    /** 
47
     * The container of the program
48
     */
49
    private Program program;
50

51
    /** 
52
     * The name resolver
53
     */
54
    private Resolver resolver;
55

56
    /** 
57
     * The meta-programming processor
58
     */
59
    private MetaProcessor meta;
60

61
    /** 
62
     * String pool
63
     */
64
    private StringPool s_pool;
65

66
    /** 
67
     * Constructs a new `TypeChecker` with the given
68
     * compiler instance
69
     *
70
     * Params:
71
     *   compiler = the `Compiler` instance
72
     */
73
    this(Compiler compiler)
83✔
74
    {
75
        this.compiler = compiler;
83✔
76
        this.config = compiler.getConfig();
83✔
77
        this.program = compiler.getProgram();
83✔
78

79
        this.resolver = new Resolver(program, this);
83✔
80
        this.meta = new MetaProcessor(this, true);
83✔
81
    }
82

83
    /** 
84
     * Returns the compiler configuration
85
     *
86
     * Returns: the `CompilerConfguration`
87
     */
88
    public CompilerConfiguration getConfig()
89
    {
90
        return config;
83✔
91
    }
92

93
    /** 
94
     * Returns the program instance
95
     *
96
     * Returns: the `Program`
97
     */
98
    public Program getProgram()
99
    {
100
        return this.program;
530✔
101
    }
102

103
    /** 
104
     * Crashes the type checker with an expectation message
105
     * by throwing a new `TypeCheckerException`.
106
     *
107
     * Params:
108
     *   message = the expectation message
109
     */
110
    public void expect(string message)
111
    {
112
        throw new TypeCheckerException(this, TypeCheckerException.TypecheckError.GENERAL_ERROR, message);
×
113
    }
114

115
    /**
116
    * I guess this should be called rather
117
    * when processing assignments but I also
118
    * think we need something like it for
119
    * class initializations first rather than
120
    * variable expressions in assignments 
121
    * (which should probably use some other
122
    * function to check that then)
123
    */
124
    public void dependencyCheck()
125
    {
126
        // TODO: Ensure this is CORRECT! (MODMAN)
127
        /* Check declaration and definition types */
128
        foreach(Module curModule; this.program.getModules())
336✔
129
        {
130
            checkDefinitionTypes(curModule);
57✔
131
        }
132
        
133
        // TODO: Ensure this is CORRECT! (MODMAN)
134
        /* TODO: Implement me */
135
        foreach(Module curModule; this.program.getModules())
336✔
136
        {
137
            checkClassInherit(curModule);
57✔
138
        }
139

140
        /**
141
        * Dependency tree generation
142
        *
143
        * Currently this generates a dependency tree
144
        * just for the module, the tree must be run
145
        * through after wards to make it
146
        * non-cyclic
147
        *
148
        */
149

150
        /* Create the dependency generator */
151
        IPoolManager poolManager = new PoolManager();
55✔
152
        IFuncDefStore funcDefStore = new FuncDefStore(this, poolManager);
55✔
153
        DNodeGenerator dNodeGenerator = new DNodeGenerator(this, poolManager, funcDefStore);
55✔
154

155
        /* Generate the dependency tree */
156
        DNode rootNode = dNodeGenerator.generate(); /* TODO: This should make it acyclic */
55✔
157

158
        /** 
159
         * TODO: Because we get a `Program` DNode out
160
         * of this we should perform linearization on
161
         * each sub-node and then process those seperately
162
         */
163
        foreach(DNode modDep; rootNode.getDeps())
316✔
164
        {
165
            Module mod = cast(Module)modDep.getEntity();
57✔
166
            assert(mod);
57✔
167
            DEBUG(format("Dependency node entry point mod: %s", modDep));
57✔
168

169
            // Linearize this module's dependencies
170
            modDep.performLinearization();
57✔
171

172
            // Print the dep tree
173
            string modTree = modDep.getTree();
57✔
174
            DEBUG(format("\n%s", modTree));
57✔
175

176
            // Get the linerization
177
            DNode[] modActions = modDep.getLinearizedNodes();
57✔
178

179
            // Perform typecheck/codegen for this
180
            doTypeCheck(modActions);
57✔
181

182
            /** 
183
             * After having done the typecheck/codegen
184
             * there would be instructions in the
185
             * `codeQueue`. We must extract these
186
             * now, clear the `codeQueue` and save
187
             * the extracted stuff to something
188
             * which maps `Module -> Instruction[]`
189
             */
190
            scratchToModQueue(mod);
56✔
191
            assert(codeQueue.empty() == true);
56✔
192

193
            /**
194
             * We must now find the function
195
             * definitions that belong to this
196
             * `Module` and process those
197
             * by generating the dependencies
198
             * for them
199
             */
200
            FunctionData[string] modFuncDefs = funcDefStore.grabFunctionDefs(mod);
56✔
201
            DEBUG(format("Defined functions for module '%s': %s", mod, modFuncDefs));
56✔
202
            foreach(FunctionData curFD; modFuncDefs.values)
384✔
203
            {
204
                assert(codeQueue.empty() == true);
78✔
205

206
                /* Generate the dependency tree */
207
                DNode funcNode = curFD.generate();
78✔
208
                
209
                /* Perform the linearization to the dependency tree */
210
                funcNode.performLinearization();
78✔
211

212
                /* Get the action-list (linearised bottom up graph) */
213
                DNode[] actionListFunc = funcNode.getLinearizedNodes();
78✔
214

215
                //TODO: Would this not mess with our queues?
216
                doTypeCheck(actionListFunc);
78✔
217
                DEBUG(funcNode.getTree());
69✔
218

219
                // The current code queue would be the function's body instructions
220
                // a.k.a. the `codeQueue`
221
                // functionBodies[funcData.name] = codeQueue;
222

223

224
                // The call to `doTypeCheck()` above adds to this queue
225
                // so we should clean it out before the next run
226
                //
227
                // NOTE: Static allocations in? Well, we don't clean init queue
228
                // so is it fine then? We now have seperate dependency trees,
229
                // we should make checking methods that check the `initQueue`
230
                // whenever we come past a `ClassStaticNode` for example
231
                // codeQueue.clear();
232

233
                /**
234
                 * Copy over the current function's
235
                 * instructions which make part of
236
                 * its definition, clear the scratchpad
237
                 * `codeQueue` and map to these
238
                 * instructions to the `ModuleQueue`
239
                 */
240
                funcScratchToModQueue(mod, curFD);
69✔
241
                assert(codeQueue.empty() == true);
69✔
242
            }
243

244
            /** 
245
             * Copy the `initQueue` instructions over
246
             * to the `ModuleQueue` for the current
247
             * module and clear the queue for the
248
             * next module round
249
             */
250
            initsScratchToModQueue(mod);
47✔
251
        }
252

253
        /* Collect statistics */
254
        doPostChecks();
45✔
255
    }
256

257
    /** 
258
     * These are just checks we run for the convenience
259
     * of the user. They do not manipulate anything but
260
     * rather provide statistics
261
     */
262
    private void doPostChecks()
263
    {
264
        /** 
265
         * Find the variables which were declared but never used
266
         */
267
        if(this.config.hasConfig("typecheck:warnUnusedVars") && this.config.getConfig("typecheck:warnUnusedVars").flag())
90✔
268
        {
269
            Variable[] unusedVariables = getUnusedVariables();
45✔
270
            WARN("There are "~to!(string)(unusedVariables.length)~" unused variables");
45✔
271
            if(unusedVariables.length)
45✔
272
            {
273
                foreach(Variable unusedVariable; unusedVariables)
192✔
274
                {
275
                    // TODO: Get a nicer name, full path-based
276
                    INFO("Variable '"~to!(string)(unusedVariable.getName())~"' is declared but never used");
41✔
277
                }
278
            }
279
        }
280
    }
281

282
    /** 
283
     * Associates various instruction
284
     * sets with a given `Module`
285
     */
286
    private struct ModuleQueue
287
    {
288
        private Module owner;
289
        private Instruction[] codeInstrs;
290
        private Instruction[][string] functionBodyCodeQueues;
291
        private Instruction[] initInstrs;
292

293
        this(Module owner)
56✔
294
        {
295
            this.owner = owner;
56✔
296
        }
297

298
        public void setCode(Instruction[] instructions)
299
        {
300
            this.codeInstrs = instructions;
56✔
301
        }
302

303
        public Instruction[] getCode()
304
        {
305
            return this.codeInstrs;
23✔
306
        }
307

308
        public void setFunctionDeclInstr(string functionName, Instruction[] bodyInstrs)
309
        {
310
            this.functionBodyCodeQueues[functionName] = bodyInstrs;
69✔
311
        }
312

313
        public Instruction[][string] getFunctionDefinitions()
314
        {
315
            return this.functionBodyCodeQueues;
204✔
316
        }
317

318
        public void setInit(Instruction[] instructions)
319
        {
320
            this.initInstrs = instructions;
47✔
321
        }
322

323
        public Instruction[] getInitInstrs()
324
        {
325
            return this.initInstrs;
23✔
326
        }
327
    }
328

329
    private ModuleQueue[Module] moduleQueues;
330

331
    /** 
332
     * Gets the `ModuleQueue*` for the given
333
     * `Module` and creates one if it does
334
     * not yet already exist
335
     *
336
     * Params:
337
     *   owner = the `Module`
338
     * Returns: a `ModuleQueue*`
339
     */
340
    private ModuleQueue* getModQueueFor(Module owner)
341
    {
342
        // Find entry
343
        ModuleQueue* modQ = owner in this.moduleQueues;
478✔
344

345
        // If not there, make it
346
        if(!modQ)
478✔
347
        {
348
            this.moduleQueues[owner] = ModuleQueue(owner);
56✔
349
            return getModQueueFor(owner);
56✔
350
        }
351

352
        return modQ;
422✔
353
    }
354

355
    /** 
356
     * Takes the current scratchpad `codeQueue`,
357
     * copies its instructions, clears it
358
     * and then creates a new `ModuleQueue`
359
     * entry for it and adds it to the
360
     * `moduleQueues` array
361
     *
362
     * Params:
363
     *   owner = the owner `Module` to
364
     * associat with the current code
365
     * queue
366
     */
367
    private void scratchToModQueue(Module owner)
368
    {
369
        // Extract a copy
370
        Instruction[] copyQueue;
56✔
371
        foreach(Instruction instr; this.codeQueue)
220✔
372
        {
373
            copyQueue ~= instr;
36✔
374
        }
375

376
        // Clear the scratchpad `codeQueue`
377
        this.codeQueue.clear();
56✔
378

379
        // Get the module queue
380
        ModuleQueue* modQ = getModQueueFor(owner);
56✔
381
        assert(modQ);
56✔
382

383
        // Set the `code` instructions
384
        modQ.setCode(copyQueue);
56✔
385
    }
386

387
    private void funcScratchToModQueue(Module owner, FunctionData fd)
388
    {
389
        // Extract a copy
390
        Instruction[] copyQueue;
69✔
391
        foreach(Instruction instr; this.codeQueue)
585✔
392
        {
393
            copyQueue ~= instr;
149✔
394
            DEBUG(format("FuncDef (%s): Adding body instruction: %s", fd.getName(), instr));
149✔
395
        }
396

397
        // Clear the scratchpad `codeQueue`
398
        this.codeQueue.clear();
69✔
399

400
        // Get the module queue
401
        ModuleQueue* modQ = getModQueueFor(owner);
69✔
402
        assert(modQ);
69✔
403

404
        // Set this function definition's instructions
405
        modQ.setFunctionDeclInstr(fd.getName(), copyQueue);
69✔
406
    }
407

408
    private void initsScratchToModQueue(Module owner)
409
    {
410
        // Extract a copy
411
        Instruction[] copyQueue;
47✔
412
        foreach(Instruction instr; this.initQueue)
94✔
413
        {
414
            copyQueue ~= instr;
×
415
        }
416

417
        // Clear the scratchpad `initQueue`
418
        this.initQueue.clear();
47✔
419

420
        // Get the module queue
421
        ModuleQueue* modQ = getModQueueFor(owner);
47✔
422
        assert(modQ);
47✔
423

424
        // Set the `init` instructions
425
        modQ.setInit(copyQueue);
47✔
426
    }
427
    
428
    /** 
429
     * Concrete queues
430
     *
431
     * These queues below are finalized and not used as a scratchpad.
432
     *
433
     * 1. Global code queue
434
     *     - This accounts for the globals needing to be executed
435
     * 2. Function body code queues
436
     *     - This accounts for (every) function definition's code queue
437
     */
438
    private Instruction[] globalCodeQueue;
439
    private Instruction[][string] functionBodyCodeQueues;
440

441
    public Instruction[] getGlobalCodeQueue(Module owner)
442
    {
443
        // Find the module queue
444
        ModuleQueue* modQ = getModQueueFor(owner);
23✔
445
        assert(modQ);
23✔
446

447
        return modQ.getCode();
23✔
448
    }
449

450
    public Instruction[][string] getFunctionBodyCodeQueues(Module owner)
451
    {
452
        // Find the module queue
453
        ModuleQueue* modQ = getModQueueFor(owner);
204✔
454
        assert(modQ);
204✔
455

456
        return modQ.getFunctionDefinitions();
204✔
457
    }
458

459

460
    
461

462

463
    /* Main code queue (used for temporary passes) */
464
    private SList!(Instruction) codeQueue; //TODO: Rename to `currentCodeQueue`
465

466
    /* Initialization queue */
467
    private SList!(Instruction) initQueue;
468

469

470
    //TODO: CHange to oneshot in the function
471
    public Instruction[] getInitQueue(Module owner)
472
    {
473
        // Find the module queue
474
        ModuleQueue* modQ = getModQueueFor(owner);
23✔
475
        assert(modQ);
23✔
476

477
        return modQ.getInitInstrs();
23✔
478
    }
479

480
    /* Adds an initialization instruction to the initialization queue (at the back) */
481
    public void addInit(Instruction initInstruction)
482
    {
483
        initQueue.insertAfter(initQueue[], initInstruction);
×
484
    }
485

486
    /*
487
    * Prints the current contents of the init-queue
488
    */
489
    public void printInitQueue()
490
    {
491
        import std.range : walkLength;
492
        ulong i = 0;
125✔
493
        foreach(Instruction instruction; initQueue)
250✔
494
        {
495
            DEBUG("InitQueue: "~to!(string)(i+1)~"/"~to!(string)(walkLength(initQueue[]))~": "~instruction.toString());
×
496
            i++;
×
497
        }
498
    }
499

500
    /* Adds an instruction to the front of code queue */
501
    public void addInstr(Instruction inst)
502
    {
503
        codeQueue.insert(inst);
521✔
504
    }
505

506
    /* Adds an instruction to the back of the code queue */
507
    public void addInstrB(Instruction inst)
508
    {
509
        codeQueue.insertAfter(codeQueue[], inst);
227✔
510
    }
511

512
    /* Removes the instruction at the front of the code queue and returns it */
513
    public Instruction popInstr()
514
    {
515
        Instruction poppedInstr;
521✔
516

517
        if(!codeQueue.empty)
521✔
518
        {
519
            poppedInstr = codeQueue.front();
521✔
520
            codeQueue.removeFront();
521✔
521
        }
522
        
523
        return poppedInstr;
521✔
524
    }
525

526
    /* Pops from the tail of the code queue and returns it */
527
    public Instruction tailPopInstr()
528
    {
529
        Instruction poppedInstr;
28✔
530

531
        if(!codeQueue.empty)
28✔
532
        {
533
            // Perhaps there is a nicer way to tail popping
534
            codeQueue.reverse();
28✔
535
            poppedInstr = codeQueue.front();
28✔
536
            codeQueue.removeFront();
28✔
537
            codeQueue.reverse();
28✔
538
        }
539

540
        return poppedInstr;
28✔
541
    }
542

543
    public bool isInstrEmpty()
544
    {
545
        return codeQueue.empty;
64✔
546
    }
547

548
    /*
549
    * Prints the current contents of the code-queue
550
    */
551
    public void printCodeQueue()
552
    {
553
        import std.range : walkLength;
554
        ulong i = 0;
1,055✔
555
        foreach(Instruction instruction; codeQueue)
10,453✔
556
        {
557
            DEBUG(to!(string)(i+1)~"/"~to!(string)(walkLength(codeQueue[]))~": "~instruction.toString());
2,781✔
558
            i++;
2,781✔
559
        }
560
    }
561

562
    /** 
563
     * 🧠️ Feature: Universal coercion and type enforcer
564
     *
565
     * This tests two DIFFERENT types to see if they are:
566
     * 
567
     * 1. The same type (and if not, don't attempt coercion)
568
     * 2. The same type (and if not, ATTEMPT coercion)
569
     */
570
    unittest
571
    {
572
        import tlang.compiler.symbols.typing.core;
573

574
        File dummyFile;
2✔
575
        Compiler dummyCompiler = new Compiler("", "legitidk.t", dummyFile);
1✔
576
        TypeChecker tc = new TypeChecker(dummyCompiler);
1✔
577

578
        /* To type is `t1` */
579
        Type t1 = getBuiltInType(tc, tc.getProgram(), "uint");
1✔
580
        assert(t1);
1✔
581

582
        /* We will comapre `t2` to `t1` */
583
        Type t2 = getBuiltInType(tc, tc.getProgram(), "ubyte");
1✔
584
        assert(t2);
1✔
585
        Value v2 = new LiteralValue("25", t2);
1✔
586
        
587
        // Ensure instruction v2's type is `ubyte`
588
        assert(tc.isSameType(t2, v2.getInstrType()));
1✔
589

590

591
        try
592
        {
593
            // Try type match them, if initially fails then try coercion
594
            // ... (This should FAIL due to type mismatch and coercion disallowed)
595
            tc.typeEnforce(t1, v2, v2, false);
1✔
596
            assert(false);
597
        }
598
        catch(TypeMismatchException mismatch)
599
        {
600
            Type expectedType = mismatch.getExpectedType();
1✔
601
            Type attemptedType = mismatch.getAttemptedType();
1✔
602
            assert(tc.isSameType(expectedType, getBuiltInType(tc, tc.getProgram(), "uint")));
1✔
603
            assert(tc.isSameType(attemptedType, getBuiltInType(tc, tc.getProgram(), "ubyte")));
1✔
604
        }
605

606

607

608
        // Try type match them, if initially fails then try coercion
609
        // ... (This should pass due to its coercibility)
610
        tc.typeEnforce(t1, v2, v2, true);
1✔
611

612
        // This should have updated `v2`'s type to type `t1`
613
        t2 = v2.getInstrType();
1✔
614
        assert(tc.isSameType(t1, t2));
1✔
615
    }
616

617
    /** 
618
     * 🧠️ Feature: Universal coercion and type enforcer
619
     *
620
     * This tests two EQUAL/SAME types to see if they are:
621
     * 
622
     * 1. The same type
623
     */
624
    unittest
625
    {
626
        import tlang.compiler.symbols.typing.core;
627

628
        File dummyFile;
2✔
629
        Compiler dummyCompiler = new Compiler("", "legitidk.t", dummyFile);
1✔
630
        TypeChecker tc = new TypeChecker(dummyCompiler);
1✔
631

632
        /* To type is `t1` */
633
        Type t1 = getBuiltInType(tc, tc.getProgram(), "uint");
1✔
634
        assert(t1);
1✔
635

636
        /* We will comapre `t2` to `t1` */
637
        Type t2 = getBuiltInType(tc, tc.getProgram(), "uint");
1✔
638
        assert(t2);
1✔
639
        Value v2 = new LiteralValue("25", t2);
1✔
640
        
641
        // Ensure instruction v2's type is `uint`
642
        assert(tc.isSameType(t2, v2.getInstrType()));
1✔
643

644

645
        // This should not fail (no coercion needed in either)
646
        tc.typeEnforce(t1, v2, v2, false);
1✔
647
        tc.typeEnforce(t1, v2, v2, true);
1✔
648
    }
649

650
    // FIXME: I should re-write the below. It is now incorrect
651
    // ... as I DO ALLOW coercion of non literal-based instructions
652
    // ... now - so it fails because it is using an older specification
653
    // ... of TLang
654
    // /** 
655
    //  * 🧠️ Feature: Universal coercion and type enforcer
656
    //  *
657
    //  * This tests a failing case (read for details)
658
    //  */
659
    // unittest
660
    // {
661
    //     /** 
662
    //      * Create a simple program with
663
    //      * a function that returns an uint
664
    //      * and a variable of type ubyte
665
    //      */
666
    //     Module testModule = new Module("myModule");
667
    //     TypeChecker tc = new TypeChecker(testModule);
668

669
    //     /* Add the variable */
670
    //     Variable myVar = new Variable("ubyte", "myVar");
671
    //     myVar.parentTo(testModule);
672
    //     testModule.addStatement(myVar);
673

674
    //     /* Add the function with a return expression */
675
    //     VariableExpression retExp = new VariableExpression("myVar");
676
    //     ReturnStmt retStmt = new ReturnStmt(retExp);
677
    //     Function myFunc = new Function("function", "uint", [retStmt], []);
678
    //     retStmt.parentTo(myFunc);
679
    //     testModule.addStatement(myFunc);
680
    //     myFunc.parentTo(testModule);
681

682

683
    //     /* Now let's play with this as if the code-queue processor was present */
684

685

686
    //     /* Create a variable fetch instruction for the `myVar` variable */
687
    //     Value varFetch = new FetchValueVar("myVar", 1);
688
    //     varFetch.setInstrType(tc.getType(testModule, myVar.getType()));
689
    
690
    //     /** 
691
    //      * Create a ReturnInstruction now based on `function`'s return type
692
    //      *
693
    //      * 1) The ay we did this when we only have the `ReturnStmt` on the code-queue
694
    //      * is by finding the ReturnStmt's parent (the Function) and getting its type.
695
    //      *
696
    //      * 2) We must now "pop" the `varFetch` instruction from the stack and compare types.
697
    //      *
698
    //      * 3) If the type enforcement is fine, then let's check that they are equal
699
    //      *
700
    //      */
701

702
    //     // 1)
703
    //     Function returnStmtContainer = cast(Function)retStmt.parentOf();
704
    //     Type funcReturnType = tc.getType(testModule, returnStmtContainer.getType());
705

706
    //     // 2) The enforcement will fail as coercion of non-literals is NOT allowed
707
    //     try
708
    //     {
709
    //         tc.typeEnforce(funcReturnType, varFetch, true);
710
    //         assert(false);
711
    //     }
712
    //     catch(CoercionException e)
713
    //     {
714
    //         assert(true);
715
    //     }
716
        
717

718
    //     // 3) The types should not be the same
719
    //     assert(!tc.isSameType(funcReturnType, varFetch.getInstrType()));
720
    // }
721

722
    /** 
723
     * 🧠️ Feature: Universal coercion and type enforcer
724
     *
725
     * This tests a passing case (read for details)
726
     */
727
    unittest
728
    {
729
        /** 
730
         * Create a simple program with
731
         * a function that returns an uint
732
         * and an expression of type ubyte
733
         */
734
        File dummyFile;
2✔
735
        Compiler compiler = new Compiler("", "", dummyFile);
1✔
736

737
        Program program = new Program();
1✔
738
        Module testModule = new Module("myModule");
1✔
739
        program.addModule(testModule);
1✔
740
        compiler.setProgram(program);
1✔
741
        
742
        TypeChecker tc = new TypeChecker(compiler);
1✔
743

744

745
        /* Add the function with a return expression */
746
        NumberLiteral retExp = new IntegerLiteral("21", IntegerLiteralEncoding.UNSIGNED_INTEGER);
1✔
747
        ReturnStmt retStmt = new ReturnStmt(retExp);
1✔
748
        Function myFunc = new Function("function", "uint", [retStmt], []);
1✔
749
        retStmt.parentTo(myFunc);
1✔
750
        testModule.addStatement(myFunc);
1✔
751
        myFunc.parentTo(testModule);
1✔
752

753

754
        /* Now let's play with this as if the code-queue processor was present */
755

756

757
        /* Create a new LiteralValue instruction with our literal and of type `ubyte` */
758
        Type literalType = tc.getType(testModule, "ubyte");
1✔
759
        Value literalValue = new LiteralValue(retExp.getNumber(), literalType);
1✔
760
    
761
        /** 
762
         * Create a ReturnInstruction now based on `function`'s return type
763
         *
764
         * 1) The ay we did this when we only have the `ReturnStmt` on the code-queue
765
         * is by finding the ReturnStmt's parent (the Function) and getting its type.
766
         *
767
         * 2) We must now "pop" the `literalValue` instruction from the stack and compare types.
768
         *
769
         * 3) If the type enforcement is fine, then let's check that they are equal
770
         *
771
         */
772

773
        // 1)
774
        Function returnStmtContainer = cast(Function)retStmt.parentOf();
1✔
775
        Type funcReturnType = tc.getType(testModule, returnStmtContainer.getType());
1✔
776

777
        // 2)
778
        tc.typeEnforce(funcReturnType, literalValue, literalValue, true);
1✔
779

780
        // 3) 
781
        assert(tc.isSameType(funcReturnType, literalValue.getInstrType()));
1✔
782
    }
783

784
    /** 
785
     * For: 🧠️ Feature: Universal coercion
786
     *
787
     * Given a Type `t1` and a `Value`-based instruction, if the
788
     * type of the `Value`-based instruction is the same as that
789
     * of the provided type, `t1`, then the function returns cleanly
790
     * without throwing any exceptions and will not fill in the `ref`
791
     * argument.
792
     *
793
     * If the types do NOT match then and cerocion is disallowed then
794
     * an exception is thrown.
795
     * 
796
     * If the types do NOT match and coercion is allowed then coercion
797
     * is attempted. If coercion fails an exception is thrown, else
798
     * it will place a `CastedValueInstruction` into the memory referrred
799
     * to by the `ref` parameter. It is this instruction that will contain
800
     * the action to cast the instruction to the coerced type.
801
     *
802
     * In the case that coercion is disabled then mismatched types results
803
     * in false being returned.
804
     *
805
     * Params:
806
     *   t1 = To-type (will coerce towards if requested)
807
     *   v2 = the `Value`-instruction
808
     *   ref coerceInstruction = the place to store the `CastedValueInstruction` in if coercion succeeds
809
     *                          (this will just be `v2` itself if the types are the same exactly)
810
     *   allowCoercion = whether or not at attempt coercion on initial type mismatch (default: `false`)
811
     *
812
     * Throws:
813
     *   TypeMismatchException if coercion is disallowed and the types are not equal
814
     * Throws:
815
     *   CoercionException if the types were not equal to begin with, coercion was allowed
816
     * but failed to coerce
817
     */
818
    private void typeEnforce(Type t1, Value v2, ref Value coerceInstruction, bool allowCoercion = false)
819
    {
820
        /* Debugging */
821
        string dbgHeader = "typeEnforce(t1="~t1.toString()~", v2="~v2.toString()~", attemptCoerce="~to!(string)(allowCoercion)~"): ";
218✔
822
        DEBUG(dbgHeader~"Entering");
218✔
823
        scope(exit)
824
        {
825
            DEBUG(dbgHeader~"Leaving");
218✔
826
        }
827

828
        /* Extract the original types of `v2` */
829
        Type t2 = v2.getInstrType();
218✔
830
        
831

832
        /* Check if the types are equal */
833
        if(isSameType(t1, t2))
218✔
834
        {
835
            // Do nothing
836
        }
837
        /* If the types are NOT the same */
838
        else
839
        {
840
            /* If coercion is allowed */
841
            if(allowCoercion)
56✔
842
            {
843
                /* If coerion fails, it would throw an exception */
844
                CastedValueInstruction coerceCastInstr = attemptCoercion(t1, v2);
55✔
845
                coerceInstruction = coerceCastInstr;
45✔
846
            }
847
            /* If coercion is not allowed, then we failed */
848
            else
849
            {
850
                throw new TypeMismatchException(this, t1, t2);
1✔
851
            }
852
        }
853
    }
854

855
    /** 
856
     * Compares the two types for equality
857
     *
858
     * Params:
859
     *   type1 = the first type
860
     *   type2 = the second type
861
     *
862
     * Returns: true if the types are equal, false otherwise
863
     */
864
    private bool isSameType(Type type1, Type type2)
865
    {
866
        bool same = false;
633✔
867

868
        // NOTE: We compare actual types, then check which type
869
        // ... the order is important due to type hierachy
870

871
        /* Handling for pointers */
872
        if(typeid(type1) == typeid(type2) && cast(Pointer)type1 !is null)
1,208✔
873
        {
874
            Pointer p1 = cast(Pointer)type1, p2 = cast(Pointer)type2;
132✔
875

876
            /* Now check that both of their referred types are the same */
877
            return isSameType(p1.getReferredType(), p2.getReferredType());
66✔
878
        }
879
        /* Handling for Integers */
880
        else if(typeid(type1) == typeid(type2) && cast(Integer)type1 !is null)
1,076✔
881
        {
882
            Integer i1 = cast(Integer)type1, i2 = cast(Integer)type2;
1,016✔
883

884
            /* Both same size? */
885
            if(i1.getSize() == i2.getSize())
508✔
886
            {
887
                /* Matching signedness ? */
888
                same =  i1.isSigned() == i2.isSigned();
424✔
889
            }
890
            /* Size mismatch */
891
            else
892
            {
893
                same = false;
84✔
894
            }
895
        }
896
        /* Handling for all other cases */
897
        else if(typeid(type1) == typeid(type2))
59✔
898
        {
899
            return true;
1✔
900
        }
901

902
        ERROR("isSameType("~to!(string)(type1)~","~to!(string)(type2)~"): "~to!(string)(same));
566✔
903
        return same;
566✔
904
    }
905

906

907
    /** 
908
     * Given a type to try coerce towards and a literal value
909
     * instruction, this will check whether the literal itself
910
     * is within the range whereby it may be coerced
911
     *
912
     * Params:
913
     *   toType = the type to try coercing towards
914
     *   literalInstr = the literal to apply a range check to
915
     */
916
    private bool isCoercibleRange(Type toType, Value literalInstr)
917
    {
918
        // You should only be calling this on either a `LiteralValue`
919
        // ... or a `LiteralValueFloat` instruction
920
        // TODO: Add support for UnaryOpInstr (where the inner type is then)
921
        // ... one of the above
922
        assert(cast(LiteralValue)literalInstr || cast(LiteralValueFloat)literalInstr || cast(UnaryOpInstr)literalInstr);
43✔
923

924
        // LiteralValue (integer literal instructions)
925
        if(cast(LiteralValue)literalInstr)
33✔
926
        {
927
            LiteralValue integerLiteral = cast(LiteralValue)literalInstr;
28✔
928
            string literal = integerLiteral.getLiteralValue();
28✔
929

930
            // NOTE (X-platform): For cross-platform sake we should change the `ulong` to `size_t`
931
            ulong literalValue = to!(ulong)(literal);
28✔
932

933
            if(isSameType(toType, getType(null, "ubyte")))
28✔
934
            {
935
                if(literalValue >= 0 && literalValue <= 255)
32✔
936
                {
937
                    // Valid coercion
938
                    return true;
14✔
939
                }
940
                else
941
                {
942
                    // Invalid coercion
943
                    return false;
2✔
944
                }
945
            }
946
            else if(isSameType(toType, getType(null, "ushort")))
12✔
947
            {
948
                if(literalValue >= 0 && literalValue <= 65_535)
×
949
                {
950
                    // Valid coercion
951
                    return true;
×
952
                }
953
                else
954
                {
955
                    // Invalid coercion
956
                    return false;
×
957
                }
958
            }
959
            else if(isSameType(toType, getType(null, "uint")))
12✔
960
            {
961
                if(literalValue >= 0 && literalValue <= 4_294_967_295)
4✔
962
                {
963
                    // Valid coercion
964
                    return true;
2✔
965
                }
966
                else
967
                {
968
                    // Invalid coercion
969
                    return false;
×
970
                }
971
            }
972
            else if(isSameType(toType, getType(null, "ulong")))
10✔
973
            {
974
                if(literalValue >= 0 && literalValue <= 18_446_744_073_709_551_615)
×
975
                {
976
                    // Valid coercion
977
                    return true;
×
978
                }
979
                else
980
                {
981
                    // Invalid coercion
982
                    return false;
×
983
                }
984
            }
985
            // Handling for signed bytes [0, 127]
986
            else if(isSameType(toType, getType(null, "byte")))
10✔
987
            {
988
                if(literalValue >= 0 && literalValue <= 127)
16✔
989
                {
990
                    // Valid coercion
991
                    return true;
8✔
992
                }
993
                else
994
                {
995
                    // Invalid coercion
996
                    return false;
×
997
                }
998
            }
999
            // Handling for signed shorts [0, 32_767]
1000
            else if(isSameType(toType, getType(null, "short")))
2✔
1001
            {
1002
                if(literalValue >= 0 && literalValue <= 32_767)
×
1003
                {
1004
                    // Valid coercion
1005
                    return true;
×
1006
                }
1007
                else
1008
                {
1009
                    // Invalid coercion
1010
                    return false;
×
1011
                }
1012
            }
1013
            // Handling for signed integers [0, 2_147_483_647]
1014
            else if(isSameType(toType, getType(null, "int")))
2✔
1015
            {
1016
                if(literalValue >= 0 && literalValue <= 2_147_483_647)
×
1017
                {
1018
                    // Valid coercion
1019
                    return true;
×
1020
                }
1021
                else
1022
                {
1023
                    // Invalid coercion
1024
                    return false;
×
1025
                }
1026
            }
1027
            // Handling for signed longs [0, 9_223_372_036_854_775_807]
1028
            else if(isSameType(toType, getType(null, "long")))
2✔
1029
            {
1030
                if(literalValue >= 0 && literalValue <= 9_223_372_036_854_775_807)
4✔
1031
                {
1032
                    // Valid coercion
1033
                    return true;
2✔
1034
                }
1035
                else
1036
                {
1037
                    // Invalid coercion
1038
                    return false;
×
1039
                }
1040
            }
1041
        }
1042
        // LiteralValue (integer literal instructions)
1043
        else if(cast(LiteralValueFloat)literalInstr)
5✔
1044
        {
1045
            
1046
        }
1047
        // Unary operator
1048
        else
1049
        {
1050
            UnaryOpInstr unaryOpLiteral = cast(UnaryOpInstr)literalInstr;
5✔
1051
            assert(unaryOpLiteral.getOperator() == SymbolType.SUB);
5✔
1052

1053
            Value operandInstr = unaryOpLiteral.getOperand();
5✔
1054

1055
            // LiteralValue (integer literal instructions) with subtraction infront
1056
            if(cast(LiteralValue)operandInstr)
5✔
1057
            {
1058
                LiteralValue theLiteral = cast(LiteralValue)operandInstr;
5✔
1059

1060
                // Then the actual literal will be `-<value>`
1061
                string negativeLiteral = "-"~theLiteral.getLiteralValue();
5✔
1062
                DEBUG("Negated literal: "~negativeLiteral);
5✔
1063

1064
                // NOTE (X-platform): For cross-platform sake we should change the `long` to `ssize_t`
1065
                long literalValue = to!(long)(negativeLiteral);
5✔
1066

1067
                if(isSameType(toType, getType(null, "byte")))
5✔
1068
                {
1069
                    if(literalValue >= -128 && literalValue <= 127)
6✔
1070
                    {
1071
                        // Valid coercion
1072
                        return true;
3✔
1073
                    }
1074
                    else
1075
                    {
1076
                        // Invalid coercion
1077
                        return false;
×
1078
                    }
1079
                }
1080
                else if(isSameType(toType, getType(null, "short")))
2✔
1081
                {
1082
                    if(literalValue >= -32_768 && literalValue <= 32_767)
×
1083
                    {
1084
                        // Valid coercion
1085
                        return true;
×
1086
                    }
1087
                    else
1088
                    {
1089
                        // Invalid coercion
1090
                        return false;
×
1091
                    }
1092
                }
1093
                else if(isSameType(toType, getType(null, "int")))
2✔
1094
                {
1095
                    if(literalValue >= -2_147_483_648 && literalValue <= 2_147_483_647)
×
1096
                    {
1097
                        // Valid coercion
1098
                        return true;
×
1099
                    }
1100
                    else
1101
                    {
1102
                        // Invalid coercion
1103
                        return false;
×
1104
                    }
1105
                }
1106
                else if(isSameType(toType, getType(null, "long")))
2✔
1107
                {
1108
                    if(literalValue >= -9_223_372_036_854_775_808 && literalValue <= 9_223_372_036_854_775_807)
2✔
1109
                    {
1110
                        // Valid coercion
1111
                        return true;
1✔
1112
                    }
1113
                    else
1114
                    {
1115
                        // Invalid coercion
1116
                        return false;
×
1117
                    }
1118
                }
1119
            }
1120
            // LiteralValue (integer literal instructions) with subtraction infront
1121
            else
1122
            {
1123

1124
            }
1125
        }
1126

1127

1128
        return false;
1✔
1129
    }
1130

1131
    /** 
1132
     * Checks if the provided type refers to a `StackArray`
1133
     *
1134
     * Params:
1135
     *   typeIn = the `Type` to test
1136
     * Returns: `true` if it refers to a `StackArray`,
1137
     * `false` otherwise
1138
     */
1139
    private bool isStackArrayType(Type typeIn)
1140
    {
1141
        return cast(StackArray)typeIn !is null;
55✔
1142
    }
1143

1144
    /** 
1145
     * Attempts to perform coercion of the provided Value-instruction
1146
     * with respect to the provided to-type.
1147
     * 
1148
     * This should only be called if the types do not match.
1149
     * 
1150
     *
1151
     * Params:
1152
     *   toType = the type to attempt coercing the instruction to
1153
     *   providedInstruction = instruction to coerce
1154
     * Throws:
1155
     *   CoercionException if we cannot coerce to the given to-type
1156
     * Returns:
1157
     *   the `CastedValueInstruction` on success
1158
     */
1159
    private CastedValueInstruction attemptCoercion(Type toType, Value providedInstruction)
1160
    {
1161
        DEBUG("VibeCheck?");
55✔
1162

1163
        /* Extract the type of the provided instruction */
1164
        Type providedType = providedInstruction.getInstrType();
55✔
1165

1166

1167
        /**
1168
         * ==== Stack-array to pointer coercion ====
1169
         *
1170
         * If the provided-type is a `StackArray`
1171
         * and the to-type is a `Pointer`.
1172
         *
1173
         * if this is the case we must cast the `StackArray`
1174
         * to a new `Pointer` type of its component type
1175
         */
1176
        if(isStackArrayType(providedType) && isPointerType(toType))
67✔
1177
        {
1178
            // Extract the pointer (to-type's)  referred type
1179
            Pointer toTypePointer = cast(Pointer)toType;
11✔
1180
            Type toTypeReferred = toTypePointer.getReferredType();
11✔
1181

1182
            // Extract the stack array's component type
1183
            StackArray providedTypeStkArr = cast(StackArray)providedType;
11✔
1184
            Type stackArrCompType = providedTypeStkArr.getComponentType();       
11✔
1185

1186
            // We still need the component type to match the to-type's referred type
1187
            if(isSameType(stackArrCompType, toTypeReferred))
11✔
1188
            {
1189
                DEBUG("Stack-array ('"~providedInstruction.toString()~"' coercion from type '"~providedType.getName()~"' to type of '"~toType.getName()~"' allowed :)");
8✔
1190

1191
                // Return a cast instruction to the to-type
1192
                return new CastedValueInstruction(providedInstruction, toType);
8✔
1193
            }
1194
            // If not, error, impossible to coerce
1195
            else
1196
            {
1197
                throw new CoercionException(this, toType, providedType, "Cannot coerce a stack array with component type not matching the provided to-type of the pointer type trying to be coerced towards");
3✔
1198
            } 
1199
        }
1200
        /** 
1201
         * ==== Pointer coerion check first ====
1202
         *
1203
         * If the to-type is a Pointer
1204
         * If the incoming provided-type is an Integer (non-pointer though)
1205
         *
1206
         * This is the case where an Integer [non-pointer though] (provided-type)
1207
         * must be coerced to a Pointer (to-type)
1208
         */
1209
        else if(isIntegralTypeButNotPointer(providedType) && isPointerType(toType))
87✔
1210
        {
1211
            // throw new CoercionException(this, toType, providedType, "Yolo baggins, we still need to implement dis");
1212

1213
            // Return a cast instruction to the to-type
1214
            return new CastedValueInstruction(providedInstruction, toType);
5✔
1215
        }
1216
        // If it is a LiteralValue (integer literal) (support for issue #94)
1217
        else if(cast(LiteralValue)providedInstruction)
39✔
1218
        {
1219
            // TODO: Add a check for if these types are both atleast integral (as in the Variable's type)
1220
            // ... THEN (TODO): Check if range makes sense
1221
            bool isIntegral = !(cast(Integer)toType is null); // Integrality check
28✔
1222

1223
            if(isIntegral)
28✔
1224
            {
1225
                bool isCoercible = isCoercibleRange(toType, providedInstruction); // TODO: Range check
28✔
1226

1227
                if(isCoercible)
28✔
1228
                {
1229
                    // TODO: Coerce here by changing the embedded instruction's type (I think this makes sense)
1230
                    // ... as during code emit that is what will be hoisted out and checked regarding its type
1231
                    // NOTE: Referrring to same type should not be a problem (see #96 Question 1)
1232
                    // providedInstruction.setInstrType(toType);
1233

1234
                    // Return a cast instruction to the to-type
1235
                    return new CastedValueInstruction(providedInstruction, toType);
26✔
1236
                }
1237
                else
1238
                {
1239
                    throw new CoercionException(this, toType, providedType, "Not coercible (range violation)");
2✔
1240
                }
1241
            }
1242
            else
1243
            {
1244
                throw new CoercionException(this, toType, providedType, "Not coercible (lacking integral var type)");
×
1245
            }
1246
            
1247
        }
1248
        // If it is a LiteralValueFloat (support for issue #94)
1249
        else if(cast(LiteralValueFloat)providedInstruction)
11✔
1250
        {
1251
            ERROR("Coercion not yet supported for floating point literals");
×
1252
            assert(false);
1253
        }
1254
        // Unary operator (specifically with a minus)
1255
        else if(cast(UnaryOpInstr)providedInstruction)
11✔
1256
        {
1257
            UnaryOpInstr unaryOpInstr = cast(UnaryOpInstr)providedInstruction;
5✔
1258

1259
            if(unaryOpInstr.getOperator() == SymbolType.SUB)
5✔
1260
            {
1261
                Value operandInstr = unaryOpInstr.getOperand();
5✔
1262

1263
                // If it is a negative LiteralValue (integer literal)
1264
                if(cast(LiteralValue)operandInstr)
5✔
1265
                {
1266
                    bool isIntegral = !(cast(Integer)toType is null);
5✔
1267

1268
                    if(isIntegral)
5✔
1269
                    {
1270
                        LiteralValue literalValue = cast(LiteralValue)operandInstr;
5✔
1271

1272
                        
1273

1274
                        bool isCoercible = isCoercibleRange(toType, providedInstruction); // TODO: Range check
5✔
1275

1276
                        if(isCoercible)
5✔
1277
                        {
1278
                            // TODO: Coerce here by changing the embedded instruction's type (I think this makes sense)
1279
                            // ... as during code emit that is what will be hoisted out and checked regarding its type
1280
                            // NOTE: Referrring to same type should not be a problem (see #96 Question 1)
1281
                            // providedInstruction.setInstrType(toType);
1282

1283
                            // Return a cast instruction to the to-type
1284
                            return new CastedValueInstruction(providedInstruction, toType);
4✔
1285
                        }
1286
                        else
1287
                        {
1288
                            throw new CoercionException(this, toType, providedType, "Not coercible (range violation)");
1✔
1289
                        }
1290

1291

1292

1293
                        // TODO: Implement things here
1294
                        // gprintln("Please implement coercing checking for negative integer literals", DebugType.ERROR);
1295
                        // assert(false);
1296
                    }
1297
                    else
1298
                    {
1299
                        ERROR("Yo, 'fix me', just throw an exception thing ain't integral, too lazy to write it now");
×
1300
                        assert(false);
1301
                    }
1302
                }
1303
                // If it is a negative LiteralValueFloat (floating-point literal)
1304
                else if(cast(LiteralValueFloat)operandInstr)
×
1305
                {
1306
                    ERROR("Coercion not yet supported for floating point literals");
×
1307
                    assert(false);
1308
                }
1309
                // If anything else is embedded
1310
                else
1311
                {
1312
                    throw new CoercionException(this, toType, providedType, "Not coercible (lacking integral var type)");
×
1313
                }
1314
            }
1315
            else
1316
            {
1317
                throw new CoercionException(this, toType, providedType, "Cannot coerce a non minus unary operation");
×
1318
            }
1319
        }
1320
        /** 
1321
         * If we arrive at this case then it is not any special literal
1322
         * handling, rather we need to check promotion rules and on
1323
         * cast-shortening - we raise an error
1324
         */
1325
        else
1326
        {
1327
            /** 
1328
             * If the incoming type is `Number`
1329
             * and the `toType` is `Number`
1330
             */
1331
            if(cast(Number)providedType && cast(Number)toType)
11✔
1332
            {
1333
                Number providedNumericType = cast(Number)providedType;
5✔
1334
                Number toNumericType = cast(Number)toType;
5✔
1335

1336
                /**
1337
                 * If the provided type is less than or equal
1338
                 * in size to that of the to-type
1339
                 */
1340
                if(providedNumericType.getSize() <= toNumericType.getSize())
5✔
1341
                {
1342
                    // providedInstruction.setInstrType(toType);
1343
                    // Return a cast instruction to the to-type
1344
                    return new CastedValueInstruction(providedInstruction, toType);
2✔
1345
                }
1346
                /** 
1347
                 * If the incoming type is bigger than the toType
1348
                 *
1349
                 * E.g.
1350
                 * ```
1351
                 * long i = 2;
1352
                 * byte i1 = i;
1353
                 * ```
1354
                 */
1355
                else
1356
                {
1357
                    throw new CoercionException(this, toType, providedType, "Loss of size would occur");
3✔
1358
                }
1359
            }
1360
            else
1361
            {
1362
                ERROR("Mashallah why are we here? BECAUSE we should just use ze-value-based genral case!: "~providedInstruction.classinfo.toString());
1✔
1363
                throw new CoercionException(this, toType, providedType);
1✔
1364
            }
1365
        }
1366
    }
1367

1368
    /** 
1369
     * Determines whether the provided Value-instruction refers
1370
     * to a StackArray. This is used for array indexing checks,
1371
     * to disambiguate between pointer-arrays and stack-based
1372
     * arrays.
1373
     *
1374
     * Params:
1375
     *   valInstr = the Value-based instruction to inspect
1376
     * Returns: true if the FetchValInstr refers to a stack-array,
1377
     * false otherwise
1378
     */
1379
    private bool isStackArrayIndex(Value valInstr)
1380
    {
1381
        // TODO: Rename
1382
        Value indexToInstr = valInstr;
42✔
1383

1384
        /* We need a `FetchValueInstruction` as the first condition */
1385
        FetchValueVar potFVV = cast(FetchValueVar)indexToInstr;
42✔
1386
        if(potFVV)
42✔
1387
        {
1388
            /** 
1389
                * Obtain the array variable being referred to
1390
                * and obtain it's declared type
1391
                */
1392
            Context potFVVCtx = potFVV.getContext();
36✔
1393
            Variable potStackArrVar = cast(Variable)resolver.resolveBest(potFVVCtx.getContainer(), potFVV.varName);
36✔
1394
            Type variableDeclaredType = getType(potFVVCtx.getContainer(), potStackArrVar.getType());
36✔
1395

1396
            /**
1397
            * If the type is `StackArray`
1398
            */
1399
            if(cast(StackArray)variableDeclaredType)
36✔
1400
            {
1401
                return true;
24✔
1402
            }
1403
        }
1404

1405
        return false;
18✔
1406
    }
1407

1408
    /** 
1409
     * Used to check if the type of the argument being passed into
1410
     * a function call is a stack array and if the function's parameter
1411
     * type is a pointer then this will check if the component type
1412
     * of the stack array is the same as that of the pointer
1413
     *
1414
     * Params:
1415
     *   parameterType = the function's parameter typoe
1416
     *   argumentType = the argument's type
1417
     *   outputType = variable to place updated type into
1418
     *
1419
     * Returns: true if the so, false otherwise
1420
     */
1421
    private bool canCoerceStackArray(Type parameterType, Type argumentType, ref Type outputType)
1422
    {
1423
        // If the argument being passed in is a stack array
1424
        if(cast(StackArray)argumentType)
×
1425
        {
1426
            StackArray stackArrayType = cast(StackArray)argumentType;
×
1427

1428
            // Get the component type of the stack array
1429
            Type stackArrCompType = stackArrayType.getComponentType();
×
1430

1431
            // Now check if the parameter is a pointer type
1432
            if(cast(Pointer)parameterType)
×
1433
            {
1434
                Pointer parameterPointerCompType = cast(Pointer)parameterType;
×
1435

1436
                // Now create a new type for the stack array which is
1437
                // effectively <stackArrayType>*
1438
                Type stackArrayTypeCoerced = new Pointer(stackArrCompType);
×
1439
                outputType = stackArrayTypeCoerced;
×
1440

1441
                // If the coerced stack array's component type is the same as the pointer's component type
1442
                return isSameType(parameterPointerCompType, stackArrayTypeCoerced);
×
1443
            }
1444
            // If not, then return false immedtaiely
1445
            else
1446
            {
1447
                return false;
×
1448
            }
1449
        }
1450
        // If not, then immediately return false
1451
        else
1452
        {   
1453
            return false;
×
1454
        }
1455
    }
1456

1457
    /**
1458
     * Given two Value-based instructions this will firstly check if
1459
     * at least one of the two is of type Pointer, then checks if the
1460
     * remaining instruction is an of type Integer - the remaining instruction
1461
     * will then be coerced into a pointer.
1462
     *
1463
     * If both are Pointers, neither are pointers or one or the other is
1464
     * a Pointer and another is non-Integer then nothing will be coerced.
1465
     * and this function is effectively a no-op.
1466
     *
1467
     * Params:
1468
     *   vInstr1 = the first instruction
1469
     *   vInstr2 = the second instruction
1470
     */
1471
    private void attemptPointerAriehmeticCoercion(Value vInstr1, Value vInstr2)
1472
    {
1473
        // Get the types of `vInstr1` and `vInstr2` respectively
1474
        Type t1 = vInstr1.getInstrType();
×
1475
        Type t2 = vInstr2.getInstrType();
×
1476

1477
        // TODO: Check if T1 is a pointer and then if T2 is an integer make it a pointer
1478
        if(cast(Pointer)t1 && cast(Integer)t2)
×
1479
        {
1480
            Pointer t1Ptr = cast(Pointer)t1;
×
1481

1482
            Type coercedType = new Pointer(t1Ptr.getReferredType());
×
1483
            vInstr2.setInstrType(coercedType);
×
1484
        }
1485
        // TODO: Else check if T2 is a pointer and then if T1 is an integer and make it a pointer
1486
        else if(cast(Pointer)t2 && cast(Integer)t2)
×
1487
        {
1488
            Pointer t2Ptr = cast(Pointer)t2;
×
1489

1490
            Type coercedType = new Pointer(t2Ptr.getReferredType());
×
1491
            vInstr1.setInstrType(coercedType);
×
1492
        }
1493
        else if(cast(Pointer)t1 && cast(Pointer)t2)
×
1494
        {
1495
            // Do nothing
1496
            // TODO: Remove this branch
1497
        }
1498
        else
1499
        {
1500
            // Do nothing
1501
        }
1502
    }
1503

1504
    /** 
1505
     * Checks if the given `Type` is a pointer-type
1506
     *
1507
     * Params:
1508
     *   typeIn = the `Type` to check
1509
     * Returns: `true` if the type is a `Pointer`,
1510
     * `false` otherwise
1511
     */
1512
    public bool isPointerType(Type typeIn)
1513
    {
1514
        return typeid(typeIn) == typeid(Pointer);
585✔
1515
    }
1516

1517
    /** 
1518
     * Checks if the given `Type` is an integral type
1519
     * (a kind-of `Integer`) HOWEVER that it is not
1520
     * a `Pointer` (recall all ptrs are integers)
1521
     *
1522
     * Params:
1523
     *   typeIn = the `Type` to check
1524
     * Returns: `true` if integral (not pointer) type,
1525
     * `false` otherwise
1526
     */
1527
    public bool isIntegralTypeButNotPointer(Type typeIn)
1528
    {
1529
        return cast(Integer)typeIn && !isPointerType(typeIn);
529✔
1530
    }
1531

1532

1533
    /** 
1534
     * Determines the biggest of the two `Integer`-based types
1535
     * and returns that one.
1536
     *
1537
     * If neither is bigger than the other then the first is
1538
     * returned.
1539
     *
1540
     * Please do not pass in `Pointer` types here - NOT the
1541
     * intended usage (even though allowed).
1542
     *
1543
     * Params:
1544
     *   integralType1 = the first `Integer`-based type to test
1545
     *   integralType2 = the second `Integer`-based type to test
1546
     * Returns: the biggest `Integer` type
1547
     */
1548
    private Integer biggerOfTheTwo(Integer integralType1, Integer integralType2)
1549
    {
1550
        // Sanity check, please don't pass Pointer types in here
1551
        // as that isn't the intended usage
1552
        assert(!isPointerType(integralType1) && !isPointerType(integralType2));
×
1553

1554
        if(integralType1.getSize() > integralType2.getSize())
×
1555
        {
1556
            return integralType1;
×
1557
        }
1558
        else if(integralType1.getSize() < integralType2.getSize())
×
1559
        {
1560
            return integralType2;
×
1561
        }
1562
        else
1563
        {
1564
            return integralType1;
×
1565
        }
1566
    }
1567

1568

1569
    public void typeCheckThing(DNode dnode)
1570
    {
1571
        DEBUG("typeCheckThing(): "~dnode.toString());
896✔
1572

1573
        /* ExpressionDNodes */
1574
        if(cast(tlang.compiler.typecheck.dependency.expression.ExpressionDNode)dnode)
896✔
1575
        {
1576
            tlang.compiler.typecheck.dependency.expression.ExpressionDNode expDNode = cast(tlang.compiler.typecheck.dependency.expression.ExpressionDNode)dnode;
507✔
1577

1578
            Statement statement = expDNode.getEntity();
507✔
1579
            DEBUG("Hdfsfdjfds"~to!(string)(statement));
507✔
1580

1581
            /* Dependent on the type of Statement */
1582

1583
            if(cast(NumberLiteral)statement)
507✔
1584
            {
1585
                /**
1586
                * Codegen
1587
                *
1588
                * TODO: We just assume (for integers) byte size 4?
1589
                * 
1590
                * Generate the correct value instruction depending
1591
                * on the number literal's type
1592
                */
1593
                Value valInstr;
192✔
1594

1595
                /* Generate a LiteralValue (IntegerLiteral) */
1596
                if(cast(IntegerLiteral)statement)
192✔
1597
                {
1598
                    IntegerLiteral integerLitreal = cast(IntegerLiteral)statement;
192✔
1599

1600
                    /**
1601
                     * Determine the type of this value instruction by finding
1602
                     * the encoding of the integer literal (part of doing issue #94)
1603
                     */
1604
                    Type literalEncodingType;
192✔
1605
                    if(integerLitreal.getEncoding() == IntegerLiteralEncoding.SIGNED_INTEGER)
192✔
1606
                    {
1607
                        literalEncodingType = getType(this.program, "int");
183✔
1608
                    }
1609
                    else if(integerLitreal.getEncoding() == IntegerLiteralEncoding.UNSIGNED_INTEGER)
9✔
1610
                    {
1611
                        literalEncodingType = getType(this.program, "uint");
2✔
1612
                    }
1613
                    else if(integerLitreal.getEncoding() == IntegerLiteralEncoding.SIGNED_LONG)
7✔
1614
                    {
1615
                        literalEncodingType = getType(this.program, "long");
2✔
1616
                    }
1617
                    else if(integerLitreal.getEncoding() == IntegerLiteralEncoding.UNSIGNED_LONG)
5✔
1618
                    {
1619
                        literalEncodingType = getType(this.program, "ulong");
5✔
1620
                    }
1621
                    assert(literalEncodingType);
192✔
1622

1623
                    // TODO: Insert getEncoding stuff here
1624
                    LiteralValue litValInstr = new LiteralValue(integerLitreal.getNumber(), literalEncodingType);
192✔
1625

1626
                    valInstr = litValInstr;
192✔
1627

1628
                    // TODO: Insert get encoding stuff here
1629
                }
1630
                /* Generate a LiteralValueFloat (FloatingLiteral) */
1631
                else
1632
                {
1633
                    FloatingLiteral floatLiteral = cast(FloatingLiteral)statement;
×
1634

1635
                    ERROR("We haven't sorted ouyt literal encoding for floating onts yet (null below hey!)");
×
1636
                    Type bruhType = null;
×
1637
                    assert(bruhType);
×
1638
                    
1639
                    LiteralValueFloat litValInstr = new LiteralValueFloat(floatLiteral.getNumber(), bruhType);
×
1640

1641
                    valInstr = litValInstr;
×
1642

1643
                    // TODO: Insert get encoding stuff here
1644
                }
1645
                
1646
                addInstr(valInstr);
192✔
1647
            }
1648
            /* String literal */
1649
            else if(cast(StringExpression)statement)
315✔
1650
            {
1651
                DEBUG("Typecheck(): String literal processing...");
×
1652

1653
                StringExpression str_exp = cast(StringExpression)statement;
×
1654
                Context str_ctx = str_exp.getContext();
×
1655
                assert(str_ctx);
×
1656
                DEBUG("String literal: ", str_exp);
×
1657
                // string strLit = str_exp.getStringLiteral();
1658
                StringInfo str_data = str_exp.data();
×
1659

1660
                // pool a string node here (this will be used for the instruction reference)
1661
                // FIXME: implement me
1662
                // StringNode* s_node = s_pool.pool(str_exp);
1663
                // DEBUG("string node obtained: ", *s_node, " @", s_node);
1664
                
1665

1666
                
1667
                /**
1668
                 * Add the instruction and pass the literal to it.
1669
                 * The instruction type for this `Value`-based instruction
1670
                 * is that of a `ubyte*` as a string literal is
1671
                 * to be interpreted as a pointer to a `ubyte`
1672
                 * representing the first byte of the character
1673
                 * string stored _somewhere_ in memory
1674
                 */
1675
                StringLiteral strLitInstr = new StringLiteral(str_data);
×
1676
                strLitInstr.setInstrType(getType(str_ctx.getContainer(), "ubyte*"));
×
1677
                addInstr(strLitInstr);
×
1678
            }
1679
            else if(cast(VariableExpression)statement)
315✔
1680
            {
1681

1682
                DEBUG("Yaa, it's rewind time");
149✔
1683
                auto g  = cast(VariableExpression)statement;
149✔
1684
                assert(g);
149✔
1685

1686
                /* FIXME: It would seem that g.getContext() is returning null, so within function body's context is not being set */
1687
                DEBUG("VarExp: "~g.getName());
149✔
1688
                DEBUG(g.getContext());
149✔
1689
                auto gVar = cast(TypedEntity)resolver.resolveBest(g.getContext().getContainer(), g.getName());
149✔
1690
                DEBUG("gVar nullity?: "~to!(string)(gVar is null));
149✔
1691

1692
                /* TODO; Above crashes when it is a container, eish baba - from dependency generation with `TestClass.P.h` */
1693
                string variableName = resolver.generateName(this.program, gVar);
149✔
1694

1695
                DEBUG("VarName: "~variableName);
149✔
1696
                DEBUG("Halo");
149✔
1697

1698
                DEBUG("Yaa, it's rewind time1: "~to!(string)(gVar.getType()));
149✔
1699
                DEBUG("Yaa, it's rewind time2: "~to!(string)(gVar.getContext()));
149✔
1700
                
1701
                /* TODO: Above TYpedEntity check */
1702
                /* TODO: still wip the expresison parser */
1703

1704
                /* TODO: TYpe needs ansatz too `.updateName()` call */
1705
                Type variableType = getType(gVar.getContext().getContainer(), gVar.getType());
149✔
1706

1707
                DEBUG("Yaa, it's rewind time");
149✔
1708

1709

1710
                /**
1711
                * Codegen
1712
                *
1713
                * FIXME: Add type info, length
1714
                *
1715
                * 1. Generate the instruction
1716
                * 2. Set the Context of it to where the VariableExpression occurred
1717
                */
1718
                FetchValueVar fVV = new FetchValueVar(variableName, 4);
149✔
1719
                fVV.setContext(g.getContext());
149✔
1720

1721

1722
                addInstr(fVV);
149✔
1723

1724
                /* The type of a FetchValueInstruction is the type of the variable being fetched */
1725
                fVV.setInstrType(variableType);
149✔
1726
            }
1727
            // else if(cast()) !!!! Continue here 
1728
            else if(cast(BinaryOperatorExpression)statement)
166✔
1729
            {
1730
                BinaryOperatorExpression binOpExp = cast(BinaryOperatorExpression)statement;
77✔
1731
                SymbolType binOperator = binOpExp.getOperator();
77✔
1732
            
1733

1734
                /**
1735
                * Codegen/Type checking
1736
                *
1737
                * Retrieve the two Value Instructions
1738
                *
1739
                * They would be placed as if they were on stack
1740
                * hence we need to burger-flip them around (swap)
1741
                */
1742
                Value vRhsInstr = cast(Value)popInstr();
77✔
1743
                Value vLhsInstr = cast(Value)popInstr();
77✔
1744

1745
                Type vRhsType = vRhsInstr.getInstrType();
77✔
1746
                Type vLhsType = vLhsInstr.getInstrType();
77✔
1747

1748

1749
                /** 
1750
                 * ==== Pointer coercion ====
1751
                 *
1752
                 * We now need to determine if our bianry operation:
1753
                 *
1754
                 * `a + b`
1755
                 *
1756
                 * Is a case where `a` is a Pointer and `b` is an Integer
1757
                 * OR if `a` is an Integer and `b` is a Pointer
1758
                 * But exclusive OR wise
1759
                 *
1760
                 * And only THEN must we coerce-cast the non-pointer one
1761
                 *
1762
                 * Last case is if the above two are not true.
1763
                 */
1764
                if(isPointerType(vLhsType) && isIntegralTypeButNotPointer(vRhsType)) // <a> is Pointer, <b> is Integer
82✔
1765
                {
1766
                    // Coerce right-hand side towards left-hand side
1767
                    typeEnforce(vLhsType, vRhsInstr, vRhsInstr, true);
5✔
1768
                }
1769
                else if(isIntegralTypeButNotPointer(vLhsType) && isPointerType(vRhsType)) // <a> is Integer, <b> is Pointer
144✔
1770
                {
1771
                    // Coerce the left-hand side towards the right-hand side
1772
                    typeEnforce(vRhsType, vLhsInstr, vLhsInstr, true);
×
1773
                }
1774
                // If left and right operands are integral
1775
                else if(isIntegralTypeButNotPointer(vLhsType) && isIntegralTypeButNotPointer(vRhsType))
144✔
1776
                {
1777
                    /**
1778
                     * If one of the instructions if a `LiteralValue` (a numeric literal)
1779
                     * and another is not then coerce the literal to the other instruction's
1780
                     * type.
1781
                     *
1782
                     * If the above is NOT true then:
1783
                     *
1784
                     * Coerce the instruction which is the smaller of the two.
1785
                     *
1786
                     * If they are equal then:
1787
                     *
1788
                     *      If one is signed and the other unsigned then coerce
1789
                     *      signed to unsigned
1790
                     */
1791
                    Integer vLhsTypeIntegral = cast(Integer)vLhsType;
72✔
1792
                    assert(vLhsTypeIntegral);
72✔
1793
                    Integer vRhsTypeIntegral = cast(Integer)vRhsType;
72✔
1794
                    assert(vRhsTypeIntegral);
72✔
1795

1796
                    if(cast(LiteralValue)vLhsInstr || cast(LiteralValue)vRhsInstr)
117✔
1797
                    {
1798
                        // Type enforce left-hand instruction to right-hand instruction
1799
                        if(cast(LiteralValue)vLhsInstr && cast(LiteralValue)vRhsInstr is null)
72✔
1800
                        {
1801
                            typeEnforce(vRhsTypeIntegral, vLhsInstr, vLhsInstr, true);
17✔
1802
                        }
1803
                        // Type enforce right-hand instruction to left-hand instruction
1804
                        else if(cast(LiteralValue)vLhsInstr is null && cast(LiteralValue)vRhsInstr)
46✔
1805
                        {
1806
                            typeEnforce(vLhsTypeIntegral, vRhsInstr, vRhsInstr, true);
18✔
1807
                        }
1808
                        // Both are literal values
1809
                        else
1810
                        {
1811
                            // Do nothing
1812
                        }
1813
                    }
1814
                    else if(vLhsTypeIntegral.getSize() < vRhsTypeIntegral.getSize())
27✔
1815
                    {
1816
                        typeEnforce(vRhsTypeIntegral, vLhsInstr, vLhsInstr, true);
×
1817
                    }
1818
                    else if(vLhsTypeIntegral.getSize() > vRhsTypeIntegral.getSize())
27✔
1819
                    {
1820
                        typeEnforce(vLhsTypeIntegral, vRhsInstr, vRhsInstr, true);
×
1821
                    }
1822
                    else
1823
                    {
1824
                        if(vLhsTypeIntegral.isSigned() && !vRhsTypeIntegral.isSigned())
53✔
1825
                        {
1826
                            typeEnforce(vRhsTypeIntegral, vLhsInstr, vLhsInstr, true);
×
1827
                        }
1828
                        else if(!vLhsTypeIntegral.isSigned() && vRhsTypeIntegral.isSigned())
28✔
1829
                        {
1830
                            typeEnforce(vLhsTypeIntegral, vRhsInstr, vRhsInstr, true);
×
1831
                        }
1832
                        else
1833
                        {
1834
                            // Do nothing if they are the same type
1835
                        }
1836
                    }
1837
                }
1838
                else
1839
                {
1840
                    // See issue #141: Binary Operators support for non-Integer types (https://deavmi.assigned.network/git/tlang/tlang/issues/141)
1841
                    ERROR("FIXME: We need to add support for this, class equality, and others like floats");
×
1842
                }
1843

1844
                
1845
                /**
1846
                 * Refresh types as instructions may have changed in
1847
                 * the above enforcement call
1848
                 */
1849
                vRhsType = vRhsInstr.getInstrType();
77✔
1850
                vLhsType = vLhsInstr.getInstrType();
77✔
1851

1852
                /** 
1853
                 * We now will check to make sure the types
1854
                 * match, if not an error is thrown.
1855
                 *
1856
                 * We will also then set the instruction's
1857
                 * type to one of the two (they're the same
1858
                 * so it isn't as if it matters). But the
1859
                 * resulting instruction should be of the type
1860
                 * of its components - that's the logic.
1861
                 */
1862
                Type chosenType;
77✔
1863
                if(isSameType(vLhsType, vRhsType))
77✔
1864
                {
1865
                    /* Left type + Right type = left/right type (just use left - it doesn't matter) */
1866
                    chosenType = vLhsType;
77✔
1867
                }
1868
                else
1869
                {
1870
                    throw new TypeMismatchException(this, vLhsType, vRhsType, "Binary operator expression requires both types be same");
×
1871
                }
1872
                
1873
                BinOpInstr addInst = new BinOpInstr(vLhsInstr, vRhsInstr, binOperator);
77✔
1874
                addInstr(addInst);
77✔
1875

1876
                /* Set the Value instruction's type */
1877
                addInst.setInstrType(chosenType);
77✔
1878
            }
1879
            /* Unary operator expressions */
1880
            else if(cast(UnaryOperatorExpression)statement)
89✔
1881
            {
1882
                UnaryOperatorExpression unaryOpExp = cast(UnaryOperatorExpression)statement;
21✔
1883
                SymbolType unaryOperator = unaryOpExp.getOperator();
21✔
1884
                
1885
                /* The type of the eventual UnaryOpInstr */
1886
                Type unaryOpType;
21✔
1887
                
1888

1889
                /**
1890
                * Typechecking (TODO)
1891
                */
1892
                Value expInstr = cast(Value)popInstr();
21✔
1893
                Type expType = expInstr.getInstrType();
21✔
1894

1895
                /* TODO: Ad type check for operator */
1896

1897
                /* If the unary operation is an arithmetic one */
1898
                if(unaryOperator == SymbolType.ADD || unaryOperator == SymbolType.SUB)
42✔
1899
                {
1900
                    /* TODO: I guess any type fr */
1901

1902
                    if(unaryOperator == SymbolType.SUB)
5✔
1903
                    {
1904
                        // TODO: Note below is a legitimately good question, given a type
1905
                        // ... <valueType>, what does applying a `-` infront of it (`-<valueType>`)
1906
                        // ... mean in terms of its type?
1907
                        //
1908
                        // ... Does it remain the same type? We ask because of literal encoding.
1909
                        // ... I believe the best way forward would be specifically to handle
1910
                        // ... cases where `cast(LiteralValue)expInstr` is true here - just
1911
                        // ... as we had the special handling for it in `NumberLiteral` statements
1912
                        // ... before.
1913
                        if(cast(LiteralValue)expInstr)
5✔
1914
                        {
1915
                            LiteralValue literalValue = cast(LiteralValue)expInstr;
5✔
1916
                            string literalValueStr = literalValue.getLiteralValue();
5✔
1917
                            ulong literalValueNumber = to!(ulong)(literalValueStr); // TODO: Add a conv check for overflow
5✔
1918

1919
                            if(literalValueNumber >= 9_223_372_036_854_775_808)
5✔
1920
                            {
1921
                                // TODO: I don't think we are meant to be doing the below, atleast for coercive cases
1922
                                // TODO: make this error nicer
1923
                                // throw new TypeCheckerException(this, TypeCheckerException.TypecheckError.GENERAL_ERROR, "Cannot represent -"~literalValueStr~" as too big");
1924
                            }
1925
                            // TODO: Check case of literal being 9223372036854775808 or above
1926
                            // ... and having a `-` infront of it, then disallow
1927

1928
                            // TODO: Remove the below (just for now)
1929
                            unaryOpType = expType;
5✔
1930
                        }
1931
                        else
1932
                        {
1933
                            // Else just copy the tyoe of the expInstr over
1934
                            unaryOpType = expType;
×
1935
                        }
1936
                    }
1937
                    else
1938
                    {
1939
                        // Else just copy the tyoe of the expInstr over
1940
                        unaryOpType = expType;
×
1941
                    }
1942
                }
1943
                /* If pointer dereference */
1944
                else if(unaryOperator == SymbolType.STAR)
16✔
1945
                {
1946
                    DEBUG("Type popped: "~to!(string)(expType));
3✔
1947

1948
                    // Okay, so yes, we would pop `ptr`'s type as `int*` which is correct
1949
                    // but now, we must a.) ensure that IS the case and b.)
1950
                    // push the type of `<type>` with one star less on as we are derefrencing `ptr`
1951
                    Type derefPointerType;
3✔
1952
                    if(cast(Pointer)expType)
3✔
1953
                    {
1954
                        Pointer pointerType = cast(Pointer)expType;
3✔
1955
                        
1956
                        // Get the type being referred to
1957
                        Type referredType = pointerType.getReferredType();
3✔
1958

1959
                        unaryOpType = referredType;
3✔
1960
                    }
1961
                    else
1962
                    {
1963
                        throw new TypeCheckerException(this, TypeCheckerException.TypecheckError.GENERAL_ERROR, "You cannot dereference a type that is not a pointer type!");
×
1964
                    }
1965
                }
1966
                /* If pointer create `&` */
1967
                else if(unaryOperator == SymbolType.AMPERSAND)
13✔
1968
                {
1969
                    /**
1970
                    * If the type popped from the stack was `<type>` then push
1971
                    * a new type onto the stack which is `<type>*`
1972
                    */
1973
                    Type ptrType = new Pointer(expType);
13✔
1974
                    unaryOpType = ptrType;
13✔
1975
                }
1976
                /* This should never occur */
1977
                else
1978
                {
1979
                    ERROR("UnaryOperatorExpression: This should NEVER happen: "~to!(string)(unaryOperator));
×
1980
                    assert(false);
1981
                }
1982
                
1983

1984
             
1985
                
1986

1987
                // TODO: For type checking and semantics we should be checking WHAT is being ampersanded
1988
                // ... as in we should only be allowing Ident's to be ampersanded, not, for example, literals
1989
                // ... such a check can be accomplished via runtime type information of the instruction above
1990
                
1991
                
1992
                UnaryOpInstr addInst = new UnaryOpInstr(expInstr, unaryOperator);
21✔
1993
                DEBUG("Made unaryop instr: "~to!(string)(addInst));
21✔
1994
                addInstr(addInst);
21✔
1995

1996
                addInst.setInstrType(unaryOpType);
21✔
1997
            }
1998
            /* Function calls */
1999
            else if(cast(FunctionCall)statement)
68✔
2000
            {
2001
                // gprintln("FuncCall hehe (REMOVE AFTER DONE)");
2002

2003
                FunctionCall funcCall = cast(FunctionCall)statement;
40✔
2004

2005
                // Find the top-level container of the function being called
2006
                // and then use this as the container to resolve our function
2007
                // being-called to (as a starting point)
2008
                Module belongsTo = cast(Module)resolver.findContainerOfType(Module.classinfo, statement);
40✔
2009
                assert(belongsTo);
40✔
2010

2011
                /* TODO: Look up func def to know when popping stops (types-based delimiting) */
2012
                Function func = cast(Function)resolver.resolveBest(belongsTo, funcCall.getName());
40✔
2013
                assert(func);
40✔
2014
                VariableParameter[] paremeters = func.getParams();
40✔
2015

2016

2017
                /* TODO: Pass in FUnction, so we get function's body for calling too */
2018
                DEBUG(format("funcCall.getName() %s", funcCall.getName()));
40✔
2019
                FuncCallInstr funcCallInstr = new FuncCallInstr(funcCall.getName(), paremeters.length);
40✔
2020
                ERROR("Name of func call: "~func.getName());
40✔
2021

2022
                /* If there are paremeters for this function (as per definition) */
2023
                if(!paremeters.length)
40✔
2024
                {
2025
                    ERROR("No parameters for deez nuts: "~func.getName());
11✔
2026
                }
2027
                /* Pop all args per type */
2028
                else
2029
                {
2030
                    ulong parmCount = paremeters.length-1;
29✔
2031
                    ERROR("Kachow: "~to!(string)(parmCount));
29✔
2032

2033
                    while(!isInstrEmpty())
64✔
2034
                    {
2035
                        Instruction instr = popInstr();
60✔
2036
                        
2037
                        Value valueInstr = cast(Value)instr;
60✔
2038
                        
2039

2040
                        /* Must be a value instruction */
2041
                        if(valueInstr && parmCount!=-1)
110✔
2042
                        {
2043
                            /* TODO: Determine type and match up */
2044
                            DEBUG("Yeah");
39✔
2045
                            DEBUG(valueInstr);
39✔
2046
                            Type argType = valueInstr.getInstrType();
39✔
2047
                            // gprintln(argType);
2048

2049
                            Variable parameter = paremeters[parmCount];
39✔
2050
                            // gprintln(parameter);
2051
                            
2052

2053
                            Type parmType = getType(func.parentOf(), parameter.getType());
39✔
2054
                            // gprintln("FuncCall(Actual): "~argType.getName());
2055
                            // gprintln("FuncCall(Formal): "~parmType.getName());
2056
                            // gprintln("FuncCall(Actual): "~valueInstr.toString());
2057

2058
                            /* Scratch type used only for stack-array coercion */
2059
                            Type coercionScratchType;
39✔
2060

2061

2062

2063
                            /**
2064
                             * We need to enforce the `valueInstr`'s' (the `Value`-based
2065
                             * instruction being passed as an argument) type to be that
2066
                             * of the `parmType` (the function's parameter type)
2067
                             */
2068
                            typeEnforce(parmType, valueInstr, valueInstr, true);
39✔
2069

2070
                            /**
2071
                             * Refresh the `argType` as `valueInstr` may have been
2072
                             * updated and we need the new type
2073
                             */
2074
                            argType = valueInstr.getInstrType();
35✔
2075
                            
2076

2077
                            // Sanity check
2078
                            assert(isSameType(argType, parmType));
35✔
2079

2080
                            
2081
                            /* Add the instruction into the FunctionCallInstr */
2082
                            funcCallInstr.setEvalInstr(parmCount, valueInstr);
35✔
2083
                            DEBUG(funcCallInstr.getEvaluationInstructions());
35✔
2084
                            
2085
                            /* Decrement the parameter index (right-to-left, so move to left) */
2086
                            parmCount--;
35✔
2087
                        }
2088
                        else
2089
                        {
2090
                            // TODO: This should enver happen, see book and remove soon (see Cleanup: Remove any pushbacks #101)
2091
                            /* Push it back */
2092
                            addInstr(instr);
21✔
2093
                            break;
21✔
2094
                        }
2095
                    }
2096
                }
2097

2098
                
2099
                
2100
                
2101

2102
                /**
2103
                * Codegen
2104
                *
2105
                * 1. Create FuncCallInstr
2106
                * 2. Evaluate args and process them?! wait done elsewhere yeah!!!
2107
                * 3. Pop args into here
2108
                * 4. addInstr(combining those args)
2109
                *   4.1. If this is a statement-level function then `addInstrB()` is used
2110
                * 5. Done
2111
                */
2112
                funcCallInstr.setContext(funcCall.getContext());
36✔
2113

2114
                // If not a statement-level function call then it is an expression
2115
                // ... and ought to be placed at the top of the stack for later consumption
2116
                if(!funcCall.isStatementLevelFuncCall())
36✔
2117
                {
2118
                    addInstr(funcCallInstr);
33✔
2119
                }
2120
                // If this IS a statement-level function call then it is not meant
2121
                // ... to be placed on the top of the stack as it won't be consumed later,
2122
                // ... rather it is finalised and should be added to the back of the code queue
2123
                else
2124
                {
2125
                    addInstrB(funcCallInstr);
3✔
2126

2127
                    // We also, for emitter, must transfer this flag over by
2128
                    // ... marking this function call instruction as statement-level
2129
                    funcCallInstr.markStatementLevel();
3✔
2130
                }
2131

2132
                /* Set the Value instruction's type */
2133
                Type funcCallInstrType = getType(func.parentOf(), func.getType());
36✔
2134
                funcCallInstr.setInstrType(funcCallInstrType);
36✔
2135
            }
2136
            /* Type cast operator */
2137
            else if(cast(CastedExpression)statement)
28✔
2138
            {
2139
                CastedExpression castedExpression = cast(CastedExpression)statement;
10✔
2140
                DEBUG("Context: "~to!(string)(castedExpression.context));
10✔
2141
                DEBUG("ParentOf: "~to!(string)(castedExpression.parentOf()));
10✔
2142
                
2143
                /* Extract the type that the cast is casting towards */
2144
                Type castToType = getType(castedExpression.context.container, castedExpression.getToType());
10✔
2145

2146

2147
                /**
2148
                * Codegen
2149
                *
2150
                * 1. Pop off the current value instruction corresponding to the embedding
2151
                * 2. Create a new CastedValueInstruction instruction
2152
                * 3. Set the context
2153
                * 4. Add to front of code queue
2154
                */
2155
                Value uncastedInstruction = cast(Value)popInstr();
10✔
2156
                assert(uncastedInstruction);
10✔
2157

2158
                /* Extract the type of the expression being casted */
2159
                Type typeBeingCasted = uncastedInstruction.getInstrType();
10✔
2160
                DEBUG("TypeCast [FromType: "~to!(string)(typeBeingCasted)~", ToType: "~to!(string)(castToType)~"]");
10✔
2161
                
2162

2163
                printCodeQueue();
10✔
2164

2165
                // TODO: Remove the `castToType` argument, this should be solely based off of the `.type` (as set below)
2166
                CastedValueInstruction castedValueInstruction = new CastedValueInstruction(uncastedInstruction, castToType);
10✔
2167
                castedValueInstruction.setContext(castedExpression.context);
10✔
2168

2169
                addInstr(castedValueInstruction);
10✔
2170

2171
                /* The type of the cats expression is that of the type it casts to */
2172
                castedValueInstruction.setInstrType(castToType);
10✔
2173
            }
2174
            /* ArrayIndex */
2175
            else if(cast(ArrayIndex)statement)
18✔
2176
            {
2177
                ArrayIndex arrayIndex = cast(ArrayIndex)statement;
18✔
2178
                Type accessType;
18✔
2179

2180
                /* Pop the thing being indexed (the indexTo expression) */
2181
                Value indexToInstr = cast(Value)popInstr();
18✔
2182
                Type indexToType = indexToInstr.getInstrType();
18✔
2183
                assert(indexToType);
18✔
2184
                DEBUG("ArrayIndex: Type of `indexToInstr`: "~indexToType.toString());
18✔
2185

2186
                /* Pop the index instruction (the index expression) */
2187
                Value indexInstr = cast(Value)popInstr();
18✔
2188
                Type indexType = indexInstr.getInstrType();
18✔
2189
                assert(indexType);
18✔
2190

2191

2192
                // TODO: Type check here the `indexToInstr` ensure that it is an array
2193
                // TODO: Type check the indexInstr and ensure it is an integral type (index can not be anything else)
2194

2195
                // TODO: We need iets different for stack-arrays here
2196
                
2197

2198

2199
                /* Final Instruction generated */
2200
                Instruction generatedInstruction;
18✔
2201

2202

2203
                // // TODO: We need to add a check here for if the `arrayRefInstruction` is a name
2204
                // // ... and if so if its type is `StackArray`, else we will enter the wrong thing below
2205

2206
                // TODO: Look up based on the name of the `FetchValueInstruction` (so if it is)
2207
                // ... AND if it refers to a stack array
2208
                bool isStackArray = isStackArrayIndex(indexToInstr);
18✔
2209
                ERROR("isStackArray (being indexed-on)?: "~to!(string)(isStackArray));
18✔
2210

2211

2212
               
2213
                // /* The type of what is being indexed on */
2214
                // Type indexingOnType = arrayRefInstruction.getInstrType();
2215
                // gprintln("Indexing-on type: "~indexingOnType.toString(), DebugType.WARNING);
2216

2217

2218
                /* Stack-array type `<compnentType>[<size>]` */
2219
                if(isStackArray)
18✔
2220
                {
2221
                    StackArray stackArray = cast(StackArray)indexToType;
12✔
2222
                    accessType = stackArray.getComponentType();
12✔
2223
                    DEBUG("ArrayIndex: Stack-array access");
12✔
2224

2225

2226
                    ERROR("<<<<<<<< STCK ARRAY INDEX CODE GEN >>>>>>>>");
12✔
2227

2228

2229

2230
                    /**
2231
                    * Codegen and type checking
2232
                    *
2233
                    * 1. Set the type (TODO)
2234
                    * 2. Set the context (TODO)
2235
                    */
2236
                    StackArrayIndexInstruction stackArrayIndexInstr = new StackArrayIndexInstruction(indexToInstr, indexInstr);
12✔
2237
                    stackArrayIndexInstr.setInstrType(accessType);
12✔
2238
                    stackArrayIndexInstr.setContext(arrayIndex.context);
12✔
2239

2240
                    ERROR("IndexTo: "~indexToInstr.toString());
12✔
2241
                    ERROR("Index: "~indexInstr.toString());
12✔
2242
                    ERROR("Stack ARray type: "~stackArray.getComponentType().toString());
12✔
2243

2244
                    
2245

2246
                    // assert(false);
2247
                    generatedInstruction = stackArrayIndexInstr;
12✔
2248
                }
2249
                /* Array type `<componentType>[]` */
2250
                else if(cast(Pointer)indexToType)
6✔
2251
                {
2252
                    DEBUG("ArrayIndex: Pointer access");
6✔
2253

2254
                    Pointer pointer = cast(Pointer)indexToType;
6✔
2255
                    accessType = pointer.getReferredType();
6✔
2256

2257
                    /**
2258
                    * Codegen and type checking
2259
                    *
2260
                    * 1. Embed the index instruction and indexed-to instruction
2261
                    * 2. Set the type of this instruction to the type of the array's component type
2262
                    * 3. (TODO) Set the context
2263
                    */
2264
                    ArrayIndexInstruction arrayIndexInstr = new ArrayIndexInstruction(indexToInstr, indexInstr);
6✔
2265
                    arrayIndexInstr.setInstrType(accessType);
6✔
2266

2267
                    generatedInstruction = arrayIndexInstr;
6✔
2268
                }
2269
                else
2270
                {
2271
                    // TODO: Throw an error here
2272
                    // throw new TypeMismatchException()
2273
                    ERROR("Indexing to an entity other than a stack array or pointer!");
×
2274
                    assert(false);
2275
                }
2276

2277

2278

2279
                // TODO: context (arrayIndex)
2280

2281
                DEBUG("ArrayIndex: [toInstr: "~indexToInstr.toString()~", indexInstr: "~indexInstr.toString()~"]");
18✔
2282

2283
                ERROR("Array index not yet supported");
18✔
2284
                // assert(false);
2285

2286
                addInstr(generatedInstruction);
18✔
2287
            }
2288
            else
2289
            {
2290
                ERROR("This ain't it chief");
×
2291
                assert(false);
2292
            }
2293
        }
2294
        /* TODO: Add support */
2295
        /**
2296
        * TODO: We need to emit different code dependeing on variable declaration TYPE
2297
        * We could use context for this, ClassVariableDec vs ModuleVariableDec
2298
        */
2299
        else if(cast(tlang.compiler.typecheck.dependency.variables.StaticVariableDeclaration)dnode)
389✔
2300
        {
2301
            /* TODO: Add skipping if context is within a class */
2302
            /* We need to wait for class static node, to do an InitInstruction (static init) */
2303
            /* It probably makes sense , IDK, we need to allocate both classes */
2304

2305
            /**
2306
            * Codegen
2307
            *
2308
            * Emit a variable declaration instruction
2309
            */
2310
            Variable variablePNode = cast(Variable)dnode.getEntity();
96✔
2311
            DEBUG("HELLO FELLA");
96✔
2312

2313
            string variableName = resolver.generateName(this.program, variablePNode);
96✔
2314
            DEBUG("HELLO FELLA (name): "~variableName);
96✔
2315
            
2316

2317
            Type variableDeclarationType = getType(variablePNode.context.container, variablePNode.getType());
96✔
2318

2319

2320
            // Check if this variable declaration has an assignment attached
2321
            Value assignmentInstr;
96✔
2322
            if(variablePNode.getAssignment())
96✔
2323
            {
2324
                Instruction poppedInstr = popInstr();
50✔
2325
                assert(poppedInstr);
50✔
2326

2327
                // Obtain the value instruction of the variable assignment
2328
                // ... along with the assignment's type
2329
                assignmentInstr = cast(Value)poppedInstr;
50✔
2330
                assert(assignmentInstr);
50✔
2331
                Type assignmentType = assignmentInstr.getInstrType();
50✔
2332

2333

2334

2335
                /** 
2336
                 * Here we can call the `typeEnforce` with the popped
2337
                 * `Value` instruction and the type to coerce to
2338
                 * (our variable's type)
2339
                 */
2340
                typeEnforce(variableDeclarationType, assignmentInstr, assignmentInstr, true);
50✔
2341
                assert(isSameType(variableDeclarationType, assignmentInstr.getInstrType())); // Sanity check
48✔
2342
            }
2343

2344
            /* Generate a variable declaration instruction and add it to the codequeue */
2345
            VariableDeclaration varDecInstr = new VariableDeclaration(variableName, 4, variableDeclarationType, assignmentInstr);
94✔
2346
            varDecInstr.setContext(variablePNode.context);
94✔
2347
            addInstrB(varDecInstr);
94✔
2348
        }
2349
        /* TODO: Add class init, see #8 */
2350
        else if(cast(tlang.compiler.typecheck.dependency.classes.classStaticDep.ClassStaticNode)dnode)
293✔
2351
        {
2352
            /* Extract the class node and create a static allocation instruction out of it */
2353
            Clazz clazzPNode = cast(Clazz)dnode.getEntity();
×
2354
            string clazzName = resolver.generateName(this.program, clazzPNode);
×
2355
            ClassStaticInitAllocate clazzStaticInitAllocInstr = new ClassStaticInitAllocate(clazzName);
×
2356

2357
            /* Add this static initialization to the list of global allocations required */
2358
            addInit(clazzStaticInitAllocInstr);
×
2359
        }
2360
        /* It will pop a bunch of shiiit */
2361
        /* TODO: ANy statement */
2362
        else if(cast(tlang.compiler.typecheck.dependency.core.DNode)dnode)
293✔
2363
        {
2364
            /* TODO: Get the STatement */
2365
            Statement statement = dnode.getEntity();
293✔
2366

2367
            DEBUG("Generic DNode typecheck(): Begin (examine: "~to!(string)(dnode)~" )");
293✔
2368

2369

2370
            /* VariableAssignmentStdAlone */
2371
            if(cast(VariableAssignmentStdAlone)statement)
293✔
2372
            {
2373
                VariableAssignmentStdAlone vasa = cast(VariableAssignmentStdAlone)statement;
46✔
2374
                string variableName = vasa.getVariableName();
46✔
2375

2376
                /* Extract information about the variable declaration of the avriable being assigned to */
2377
                Context variableContext = vasa.getContext();
46✔
2378
                Variable variable = cast(Variable)resolver.resolveBest(variableContext.container, variableName);
46✔
2379
                Type variableDeclarationType = getType(variableContext.container, variable.getType());
46✔
2380

2381
                /**
2382
                * Codegen
2383
                *
2384
                * 1. Get the variable's name
2385
                * 2. Pop Value-instruction
2386
                * 3. Generate VarAssignInstruction with Value-instruction
2387
                */
2388
                Instruction instr = popInstr();
46✔
2389
                assert(instr);
46✔
2390
                Value assignmentInstr = cast(Value)instr;
46✔
2391
                assert(assignmentInstr);
46✔
2392

2393
                
2394
                Type assignmentType = assignmentInstr.getInstrType();
46✔
2395
                assert(assignmentType);
46✔
2396

2397

2398
                /**
2399
                 * Here we will do the enforcing of the types
2400
                 *
2401
                 * Will will allow coercion of the provided
2402
                 * type (the value being assigned to our variable)
2403
                 * to the to-type (our Variable's declared type)
2404
                 */
2405
                typeEnforce(variableDeclarationType, assignmentInstr, assignmentInstr, true);
46✔
2406
                assert(isSameType(variableDeclarationType, assignmentInstr.getInstrType())); // Sanity check
44✔
2407

2408
                /* Generate a variable assignment instruction and add it to the codequeue */
2409
                VariableAssignmentInstr vAInstr = new VariableAssignmentInstr(variableName, assignmentInstr);
44✔
2410
                vAInstr.setContext(vasa.getContext());
44✔
2411
                addInstrB(vAInstr);
44✔
2412
            }
2413
            /**
2414
            * Return statement (ReturnStmt)
2415
            */
2416
            else if(cast(ReturnStmt)statement)
247✔
2417
            {
2418
                ReturnStmt returnStatement = cast(ReturnStmt)statement;
39✔
2419
                Function funcContainer = cast(Function)resolver.findContainerOfType(Function.classinfo, returnStatement);
39✔
2420

2421
                /* Generated return instruction */
2422
                ReturnInstruction returnInstr;
39✔
2423

2424
                /**
2425
                 * Ensure that the `ReturnStmt` is finally parented
2426
                 * by a `Function`
2427
                 */
2428
                if(!funcContainer)
39✔
2429
                {
2430
                    throw new TypeCheckerException(this, TypeCheckerException.TypecheckError.GENERAL_ERROR, "A return statement can only appear in the body of a function");
×
2431
                }
2432

2433
                /**
2434
                 * Extract information about the finally-parented `Function`
2435
                 */
2436
                string functionName = resolver.generateName(funcContainer.parentOf(), funcContainer);
39✔
2437
                Type functionReturnType = getType(funcContainer, funcContainer.getType());
39✔
2438
                
2439

2440
                /**
2441
                * Codegen
2442
                *
2443
                * (1 and 2 only apply for return statements with an expression)
2444
                *
2445
                * 1. Pop the expression on the stack
2446
                * 2. Create a new ReturnInstruction with the expression instruction
2447
                * embedded in it
2448
                */
2449

2450
                /* If the function's return type is void */
2451
                if(isSameType(functionReturnType, getType(cast(Container)funcContainer, "void")))
39✔
2452
                {
2453
                    /* It is an error to have a return expression if function is return void */
2454
                    if(returnStatement.hasReturnExpression())
1✔
2455
                    {
2456
                        throw new TypeCheckerException(this, TypeCheckerException.TypecheckError.GENERAL_ERROR, "Function '"~functionName~"' of type void cannot have a return expression");
×
2457
                    }
2458
                    /* If we don't have an expression (expected) */
2459
                    else
2460
                    {
2461
                        /* Generate the instruction */
2462
                        returnInstr = new ReturnInstruction();
1✔
2463
                    }
2464
                }
2465
                /* If there is a non-void return type */
2466
                else
2467
                {
2468
                    /* We should have an expression in the non-void case */
2469
                    if(returnStatement.hasReturnExpression())
38✔
2470
                    {
2471
                        Value returnExpressionInstr = cast(Value)popInstr();
38✔
2472
                        assert(returnExpressionInstr);
38✔
2473
                        Type returnExpressionInstrType = returnExpressionInstr.getInstrType();
38✔
2474

2475
                        /**
2476
                         * Type check the return expression's type with that of the containing
2477
                         * function's retur type, if they don't match attempt coercion.
2478
                         *
2479
                         * On type check failure, an exception is thrown.
2480
                         *
2481
                         * On success, the `retjrnExpressionInstr` MAY be updated and then
2482
                         * we continue on.
2483
                         */
2484
                        typeEnforce(functionReturnType, returnExpressionInstr, returnExpressionInstr, true);
38✔
2485

2486
                        /* Generate the instruction */
2487
                        returnInstr = new ReturnInstruction(returnExpressionInstr);
36✔
2488
                    }
2489
                    /* If not then this is an error */
2490
                    else
2491
                    {
2492
                        throw new TypeCheckerException(this, TypeCheckerException.TypecheckError.GENERAL_ERROR, "Function '"~functionName~"' of has a type therefore it requires an expression in the return statement");
×
2493
                    }
2494
                }
2495
                
2496
                /** 
2497
                 * Codegen (continued)
2498
                 *
2499
                 * 3. Set the Context of the instruction
2500
                 * 4. Add this instruction back
2501
                 */
2502
                returnInstr.setContext(returnStatement.getContext());
37✔
2503
                addInstrB(returnInstr);
37✔
2504
            }
2505
            /**
2506
            * If statement (IfStatement)
2507
            */
2508
            else if(cast(IfStatement)statement)
208✔
2509
            {
2510
                IfStatement ifStatement = cast(IfStatement)statement;
8✔
2511
                BranchInstruction[] branchInstructions;
8✔
2512

2513
                /* Get the if statement's branches */
2514
                Branch[] branches = ifStatement.getBranches();
8✔
2515
                assert(branches.length > 0);
8✔
2516

2517
                /**
2518
                * 1. These would be added stack wise, so we need to pop them like backwards
2519
                * 2. Then a reversal at the end (generated instructions list)
2520
                *
2521
                * FIXME: EIther used siggned or the hack below lmao, out of boounds
2522
                */
2523
                for(ulong branchIdx = branches.length-1; true; branchIdx--)
30✔
2524
                {
2525
                    Branch branch = branches[branchIdx];
15✔
2526

2527
                    // Pop off an expression instruction (if it exists)
2528
                    Value branchConditionInstr;
15✔
2529
                    if(branch.hasCondition())
15✔
2530
                    {
2531
                        Instruction instr = popInstr();
11✔
2532
                        DEBUG("BranchIdx: "~to!(string)(branchIdx));
11✔
2533
                        DEBUG("Instr is: "~to!(string)(instr));
11✔
2534
                        branchConditionInstr = cast(Value)instr;
11✔
2535
                        assert(branchConditionInstr);
11✔
2536
                    }
2537

2538
                    // Get the number of body instructions to pop
2539
                    ulong bodyCount = branch.getBody().length;
15✔
2540
                    ulong i = 0;
15✔
2541
                    Instruction[] bodyInstructions;
15✔
2542

2543
                    
2544
                    while(i < bodyCount)
32✔
2545
                    {
2546
                        Instruction bodyInstr = tailPopInstr();
17✔
2547
                        bodyInstructions~=bodyInstr;
17✔
2548

2549
                        DEBUG("tailPopp'd("~to!(string)(i)~"/"~to!(string)(bodyCount-1)~"): "~to!(string)(bodyInstr));
17✔
2550

2551
                        i++;
17✔
2552
                    }
2553

2554
                    // Reverse the body instructions (correct ordering)
2555
                    bodyInstructions=reverse(bodyInstructions);
15✔
2556

2557
                    // Create the branch instruction (coupling the condition instruction and body instructions)
2558
                    branchInstructions~=new BranchInstruction(branchConditionInstr, bodyInstructions);
15✔
2559

2560
                    
2561

2562
                    if(branchIdx == 0)
15✔
2563
                    {
2564
                        break;
8✔
2565
                    }
2566
                }
2567

2568
                // Reverse the list to be in the correct order (it was computed backwards)
2569
                branchInstructions=reverse(branchInstructions);
8✔
2570

2571
                /**
2572
                * Code gen
2573
                *
2574
                * 1. Create the IfStatementInstruction containing the BranchInstruction[](s)
2575
                * 2. Set the context
2576
                * 3. Add the instruction
2577
                */
2578
                IfStatementInstruction ifStatementInstruction = new IfStatementInstruction(branchInstructions);
8✔
2579
                ifStatementInstruction.setContext(ifStatement.getContext());
8✔
2580
                addInstrB(ifStatementInstruction);
8✔
2581

2582
                DEBUG("If!");
8✔
2583
            }
2584
            /**
2585
            * While loop (WhileLoop)
2586
            */
2587
            else if(cast(WhileLoop)statement)
200✔
2588
            {
2589
                WhileLoop whileLoop = cast(WhileLoop)statement;
1✔
2590

2591
                // FIXME: Do-while loops are still being considered in terms of dependency construction
2592
                if(whileLoop.isDoWhile)
1✔
2593
                {
2594
                    DEBUG("Still looking at dependency construction in this thing (do while loops )");
×
2595
                    assert(false);
2596
                }
2597

2598
                Branch branch = whileLoop.getBranch();
1✔
2599

2600
                /* The condition `Value` instruction should be on the stack */
2601
                Value valueInstrCondition = cast(Value)popInstr();
1✔
2602
                assert(valueInstrCondition);
1✔
2603

2604
                /* Process the body of the while-loop with tail-popping followed by a reverse */
2605
                Instruction[] bodyInstructions;
1✔
2606
                ulong bodyLen = branch.getBody().length;
1✔
2607
                ulong bodyIdx = 0;
1✔
2608
            
2609
                while(bodyIdx < bodyLen)
6✔
2610
                {
2611
                    Instruction bodyInstr = tailPopInstr();
5✔
2612
                    bodyInstructions~=bodyInstr;
5✔
2613
                    bodyIdx++;
5✔
2614
                }
2615

2616
                // Reverse the list to be in the correct order (it was computed backwards)
2617
                bodyInstructions=reverse(bodyInstructions);
1✔
2618

2619

2620
                // Create a branch instruction coupling the condition instruction + body instructions (in corrected order)
2621
                BranchInstruction branchInstr = new BranchInstruction(valueInstrCondition, bodyInstructions);
1✔
2622

2623

2624
                /**
2625
                * Code gen
2626
                *
2627
                * 1. Create the WhileLoopInstruction containing the BranchInstruction
2628
                * 2. Set the context
2629
                * 3. Add the instruction
2630
                */
2631
                WhileLoopInstruction whileLoopInstruction = new WhileLoopInstruction(branchInstr);
1✔
2632
                whileLoopInstruction.setContext(whileLoop.getContext());
1✔
2633
                addInstrB(whileLoopInstruction);
1✔
2634
            }
2635
            /**
2636
            * For loop (ForLoop)
2637
            */
2638
            else if(cast(ForLoop)statement)
199✔
2639
            {
2640
                ForLoop forLoop = cast(ForLoop)statement;
2✔
2641

2642
                /* Pop-off the Value-instruction for the condition */
2643
                Value valueInstrCondition = cast(Value)popInstr();
2✔
2644
                assert(valueInstrCondition);
2✔
2645

2646
                /* Calculate the number of instructions representing the body to tailPopInstr() */
2647
                ulong bodyTailPopNumber = forLoop.getBranch().getStatements().length;
2✔
2648
                DEBUG("bodyTailPopNumber: "~to!(string)(bodyTailPopNumber));
2✔
2649

2650
                /* Pop off the body instructions, then reverse final list */
2651
                Instruction[] bodyInstructions;
2✔
2652
                for(ulong idx = 0; idx < bodyTailPopNumber; idx++)
12✔
2653
                {
2654
                    bodyInstructions ~= tailPopInstr();
4✔
2655
                }
2656
                bodyInstructions = reverse(bodyInstructions);
2✔
2657

2658
                // Create a branch instruction coupling the condition instruction + body instructions (in corrected order)
2659
                BranchInstruction branchInstr = new BranchInstruction(valueInstrCondition, bodyInstructions);
2✔
2660

2661

2662
                /* If there is a pre-run instruction */
2663
                Instruction preRunInstruction;
2✔
2664
                if(forLoop.hasPreRunStatement())
2✔
2665
                {
2666
                    preRunInstruction = tailPopInstr();
2✔
2667
                }
2668

2669
                /**
2670
                * Code gen
2671
                *
2672
                * 1. Create the ForLoopInstruction containing the BranchInstruction and
2673
                * preRunInstruction
2674
                * 2. Set the context
2675
                * 3. Add the instruction
2676
                */
2677
                ForLoopInstruction forLoopInstruction = new ForLoopInstruction(branchInstr, preRunInstruction);
2✔
2678
                forLoopInstruction.setContext(forLoop.context);
2✔
2679
                addInstrB(forLoopInstruction);
2✔
2680
            }
2681
            /* Branch */
2682
            else if(cast(Branch)statement)
197✔
2683
            {
2684
                Branch branch = cast(Branch)statement;
18✔
2685

2686
                DEBUG("Look at that y'all, cause this is it: "~to!(string)(branch));
18✔
2687
            }
2688
            /**
2689
            * Dereferencing pointer assignment statement (PointerDereferenceAssignment)
2690
            */
2691
            else if(cast(PointerDereferenceAssignment)statement)
179✔
2692
            {
2693
                PointerDereferenceAssignment ptrDerefAss = cast(PointerDereferenceAssignment)statement;
6✔
2694
                
2695
                /* Pop off the pointer dereference expression instruction (LHS) */
2696
                Value lhsPtrExprInstr = cast(Value)popInstr();
6✔
2697
                assert(lhsPtrExprInstr);
6✔
2698

2699
                /* Pop off the assignment instruction (RHS expression) */
2700
                Value rhsExprInstr = cast(Value)popInstr();
6✔
2701
                assert(rhsExprInstr);
6✔
2702

2703
                /**
2704
                * Code gen
2705
                *
2706
                * 1. Create the PointerDereferenceAssignmentInstruction containing the `lhsPtrExprInstr`
2707
                * and `rhsExprInstr`. Also set the pointer depth.
2708
                * 2. Set the context
2709
                * 3. Add the instruction
2710
                */
2711
                PointerDereferenceAssignmentInstruction pointerDereferenceAssignmentInstruction = new PointerDereferenceAssignmentInstruction(lhsPtrExprInstr, rhsExprInstr, ptrDerefAss.getDerefCount());
6✔
2712
                pointerDereferenceAssignmentInstruction.setContext(ptrDerefAss.context);
6✔
2713
                addInstrB(pointerDereferenceAssignmentInstruction);
6✔
2714
            }
2715
            /**
2716
            * Discard statement (DiscardStatement)
2717
            */
2718
            else if(cast(DiscardStatement)statement)
173✔
2719
            {
2720
                DiscardStatement discardStatement = cast(DiscardStatement)statement;
8✔
2721

2722
                /* Pop off a Value instruction */
2723
                Value exprInstr = cast(Value)popInstr();
8✔
2724
                assert(exprInstr);
8✔
2725

2726
                /**
2727
                * Code gen
2728
                *
2729
                * 1. Create the DiscardInstruction containing the Value instruction
2730
                * `exprInstr`
2731
                * 2. Set the context
2732
                * 3. Add the instruction
2733
                */
2734
                DiscardInstruction discardInstruction = new DiscardInstruction(exprInstr);
8✔
2735
                discardInstruction.setContext(discardStatement.context);
8✔
2736
                addInstrB(discardInstruction);
8✔
2737
            }
2738
            /**
2739
            * Array assignments (ArrayAssignment)
2740
            */
2741
            else if(cast(ArrayAssignment)statement)
165✔
2742
            {
2743
                ArrayAssignment arrayAssignment = cast(ArrayAssignment)statement;
24✔
2744

2745
                ERROR("Note, dependency processing of ArrayAssignment is not yet implemented, recall seggy");
24✔
2746
                printCodeQueue();
24✔
2747

2748
                // TODO: We need to implement this, what should we put here
2749
                // ... we also should be setting the correct types if need be
2750

2751
                /**
2752
                 * At this point the code queue top of stack should look like this
2753
                 * (as a requirement for Array assignments) (top-to-bottom)
2754
                 *
2755
                 * 1. Index instruction
2756
                 * 2. Array name instruction
2757
                 * 3. Assigment expression instruction
2758
                 */
2759
                Value indexInstruction = cast(Value)popInstr();
24✔
2760

2761
                
2762
                // FIXME: Actually this may not always be the case, the name fetching makes sense
2763
                // ... for stack arrays but not pointer ones where the arrayRef may be generated
2764
                // ... from something else.
2765
                Value arrayRefInstruction = cast(Value)popInstr();
24✔
2766
                Value assignmentInstr = cast(Value)popInstr();
24✔
2767

2768
                WARN("indexInstruction: "~indexInstruction.toString());
24✔
2769
                WARN("arrayRefInstruction: "~arrayRefInstruction.toString());
24✔
2770
                WARN("assignmentInstr: "~assignmentInstr.toString());
24✔
2771

2772

2773
                /* Final Instruction generated */
2774
                Instruction generatedInstruction;
24✔
2775

2776

2777
                // TODO: We need to add a check here for if the `arrayRefInstruction` is a name
2778
                // ... and if so if its type is `StackArray`, else we will enter the wrong thing below
2779
                bool isStackArray = isStackArrayIndex(arrayRefInstruction);
24✔
2780
                ERROR("isStackArray (being assigned to)?: "~to!(string)(isStackArray));
24✔
2781

2782

2783
               
2784
                /* The type of what is being indexed on */
2785
                Type indexingOnType = arrayRefInstruction.getInstrType();
24✔
2786
                WARN("Indexing-on type: "~indexingOnType.toString());
24✔
2787
                WARN("Indexing-on type: "~indexingOnType.classinfo.toString());
24✔
2788

2789
                
2790
                /* Stack-array type `<compnentType>[<size>]` */
2791
                if(isStackArray)
24✔
2792
                {
2793
                    // TODO: Crashing here currently with `simple_stack_arrays2.t`
2794
                    // gprint("arrayRefInstruction: ");
2795
                    // gprintln(arrayRefInstruction);
2796
    
2797
                    // StackArrayIndexInstruction stackArrayIndex = cast(StackArrayIndexInstruction)arrayRefInstruction;
2798
                    
2799
                    FetchValueVar arrayFetch = cast(FetchValueVar)arrayRefInstruction;
12✔
2800

2801
                    /** 
2802
                     * Hoist out the declared stack array variable
2803
                     */
2804
                    Context stackVarContext = arrayFetch.getContext();
12✔
2805
                    assert(stackVarContext); //TODO: We must set the Context when we make the `StackArrayIndexInstruction`
12✔
2806
                    
2807
                    Variable arrayVariable = cast(Variable)resolver.resolveBest(stackVarContext.container, arrayFetch.varName);
12✔
2808
                    Type arrayVariableDeclarationType = getType(stackVarContext.container, arrayVariable.getType());
12✔
2809

2810
                    ERROR("TODO: We are still working on generating an assignment instruction for assigning to stack arrays");
12✔
2811
                    ERROR("TODO: Implement instruction generation for stack-based arrays");
12✔
2812

2813
                    // TODO: Use StackArrayIndexAssignmentInstruction
2814
                    StackArrayIndexAssignmentInstruction stackAssignmentInstr = new StackArrayIndexAssignmentInstruction(arrayFetch.varName, indexInstruction, assignmentInstr);
12✔
2815

2816
                    // TODO: See issue on `Stack-array support` for what to do next
2817
                    // assert(false);
2818
                    generatedInstruction = stackAssignmentInstr;
12✔
2819

2820
                    // TODO: Set context
2821
                    /* Set the context */
2822
                    stackAssignmentInstr.setContext(arrayAssignment.getContext());
12✔
2823

2824

2825
                    DEBUG(">>>>> "~stackAssignmentInstr.toString());
12✔
2826
                    DEBUG("Assigning into this array: "~to!(string)(assignmentInstr));
12✔
2827
                    // assert(false);
2828
                }
2829
                /* Array type `<componentType>[]` */
2830
                else if(cast(Pointer)indexingOnType)
12✔
2831
                {
2832
                    // TODO: Update this and don't use pointer dereference assignment
2833
                    /** 
2834
                     * Create a new pointer dereference assignment instruction
2835
                     *
2836
                     * 1. The deref is level 1 (as array index == one `*`)
2837
                     * 2. The left-hand side is to be `new ArrayIndexInstruction(arrayRefInstruction, indexInstruction)`
2838
                     * 3. Assignment expression is to be `assignmentInstr`
2839
                     */
2840
                    // NOTE: We couple arrBasePtr+offset (index) using an ArrayIndexInstruction (optimization/code-reuse)
2841
                    ArrayIndexInstruction arrIndex = new ArrayIndexInstruction(arrayRefInstruction, indexInstruction);
12✔
2842
                    ArrayIndexAssignmentInstruction arrDerefAssInstr = new ArrayIndexAssignmentInstruction(arrIndex, assignmentInstr);
12✔
2843

2844
                    ERROR("TODO: Implement instruction generation for pointer-based arrays");
12✔
2845
                    generatedInstruction = arrDerefAssInstr;
12✔
2846
                    // assert(false);
2847

2848
                    // TODO: Set context
2849
                }
2850
                // TODO: handle this error (if even possible?)
2851
                else
2852
                {
2853
                    assert(false);
2854
                }
2855

2856
                assert(generatedInstruction !is null);
24✔
2857

2858
                /* Add the instruction */
2859
                addInstrB(generatedInstruction);
24✔
2860
            }
2861
            /* Case of no matches */
2862
            else
2863
            {
2864
                WARN("NO MATCHES FIX ME FOR: "~to!(string)(statement));
141✔
2865
            }
2866
        }
2867
        
2868

2869

2870
    }
2871

2872
    /**
2873
    * Perform type-checking and code-generation
2874
    * on the provided linearized dependency tree
2875
    */
2876
    private void doTypeCheck(DNode[] actionList)
2877
    {
2878
        /* Print the action list provided to us */
2879
        DEBUG("Action list: "~to!(string)(actionList));
135✔
2880

2881
        /**
2882
        * Loop through each dependency-node in the action list
2883
        * and perform the type-checking/code generation
2884
        */
2885
        foreach(DNode node; actionList)
3,073✔
2886
        {
2887
            DEBUG("Process: "~to!(string)(node));
896✔
2888

2889
            /* Print the code queue each time */
2890
            DEBUG("sdfhjkhdsfjhfdsj 1");
896✔
2891
            printCodeQueue();
896✔
2892
            DEBUG("sdfhjkhdsfjhfdsj 2");
896✔
2893

2894
            /* Type-check/code-gen this node */
2895
            typeCheckThing(node);
896✔
2896
            writeln("--------------");
886✔
2897
        }
2898

2899

2900
        writeln("\n################# Results from type-checking/code-generation #################\n");
125✔
2901

2902
        
2903
        /* Print the init queue */
2904
        DEBUG("<<<<< FINAL ALLOCATE QUEUE >>>>>");
125✔
2905
        printInitQueue();
125✔
2906

2907
        /* Print the code queue */
2908
        DEBUG("<<<<< FINAL CODE QUEUE >>>>>");
125✔
2909
        printCodeQueue();
125✔
2910
    }
2911

2912
    /**
2913
    * Given a type as a string this
2914
    * returns the actual type
2915
    *
2916
    * If not found then null is returned
2917
    */
2918
    public Type getType(Container c, string typeString)
2919
    {
2920
        Type foundType;
1,320✔
2921

2922
        /* Check if the type is built-in */
2923
        foundType = getBuiltInType(this, c, typeString);
1,320✔
2924

2925
        /* If it isn't then check for a type (resolve it) */
2926
        if(!foundType)
1,320✔
2927
        {
2928
            foundType = cast(Type)resolver.resolveBest(c, typeString);
×
2929
        }
2930
        
2931
        return foundType;
1,320✔
2932
    }
2933

2934
    // TODO: What actually is the point of this? It literally generates a `Class[]`
2935
    // ... and then tosses it after returning. (See issue "Dead code tracking" #83)
2936
    private void checkDefinitionTypes(Container c)
2937
    {
2938
        /* Check variables and functions (TypedEntities) declarations */
2939
        // checkTypedEntitiesTypeNames(c);
2940

2941
        /* Check class inheritance types */
2942
        Clazz[] classes;
57✔
2943

2944
        foreach (Statement statement; c.getStatements())
528✔
2945
        {
2946
            if (statement !is null && cast(Clazz) statement)
238✔
2947
            {
2948
                classes ~= cast(Clazz) statement;
2✔
2949
            }
2950
        }
2951
    }
2952

2953
    /**
2954
    * Begins the type checking process
2955
    */
2956
    public void beginCheck()
2957
    {
2958
        // TODO: Ensure this is CORRECT! (MODMAN)
2959
        /* Run the meta-processor on the AST tree (starting from the Module) */
2960
        meta.process(this.program);
67✔
2961

2962
        // TODO: Ensure this is CORRECT! (MODMAN)
2963
        /* Process all pseudo entities of the program's modules */
2964
        foreach(Statement curModule; this.program.getStatements())
408✔
2965
        {
2966
            processPseudoEntities(cast(Module)curModule);
69✔
2967
        }
2968

2969
        // TODO: Ensure this is CORRECT! (MODMAN)
2970
        /**
2971
        * Make sure there are no name collisions anywhere
2972
        * in the Module with an order of precedence of
2973
        * Classes being declared before Functions and
2974
        * Functions before Variables
2975
        */
2976
        foreach(Statement curModule; this.program.getStatements())
384✔
2977
        {
2978
            checkContainerCollision(cast(Module)curModule); /* TODO: Rename checkContainerCollision */
69✔
2979
        }
2980

2981
        /* TODO: Now that everything is defined, no collision */
2982
        /* TODO: Do actual type checking and declarations */
2983
        dependencyCheck();
55✔
2984
    }
2985

2986
    private void processPseudoEntities(Container c)
2987
    {
2988
        /* Collect all `extern` declarations */
2989
        ExternStmt[] externDeclarations;
69✔
2990
        foreach(Statement curStatement; c.getStatements())
606✔
2991
        {
2992
            if(cast(ExternStmt)curStatement)
133✔
2993
            {
2994
                externDeclarations ~= cast(ExternStmt)curStatement;
×
2995
            }
2996
        }
2997

2998
        // TODO: We could remove them from the container too, means less loops in dependency/core.d
2999

3000
        /* Add each Entity to the container */
3001
        foreach(ExternStmt curExternStmt; externDeclarations)
207✔
3002
        {
3003
            SymbolType externType = curExternStmt.getExternType();
×
3004
            string externalSymbolName = curExternStmt.getExternalName();
×
3005
            Entity pseudoEntity = curExternStmt.getPseudoEntity();
×
3006

3007
            /* Set the embedded pseudo entity's parent to that of the container */
3008
            pseudoEntity.parentTo(c);
×
3009

3010
            c.addStatements([pseudoEntity]);
×
3011

3012
            assert(this.getResolver().resolveBest(c, externalSymbolName));
×
3013
        }
3014

3015

3016
        
3017
    }
3018

3019
    private void checkClassInherit(Container c)
3020
    {
3021
        /* Get all types (Clazz so far) */
3022
        Clazz[] classTypes;
61✔
3023

3024
        foreach (Statement statement; c.getStatements())
555✔
3025
        {
3026
            if (statement !is null && cast(Clazz) statement)
248✔
3027
            {
3028
                classTypes ~= cast(Clazz) statement;
4✔
3029
            }
3030
        }
3031

3032
        /* Process each Clazz */
3033
        foreach (Clazz clazz; classTypes)
195✔
3034
        {
3035
            /* Get the current class's parent */
3036
            string[] parentClasses = clazz.getInherit();
4✔
3037
            DEBUG("Class: " ~ clazz.getName() ~ ": ParentInheritList: " ~ to!(
4✔
3038
                    string)(parentClasses));
3039

3040
            /* Try resolve all of these */
3041
            foreach (string parent; parentClasses)
12✔
3042
            {
3043
                /* Find the named entity */
3044
                Entity namedEntity;
×
3045

3046
                /* Check if the name is rooted */
3047
                string[] dotPath = split(parent, '.');
×
3048
                DEBUG(dotPath.length);
×
3049

3050
                /* Resolve the name */
3051
                namedEntity = resolver.resolveBest(c, parent);
×
3052

3053
                /* If the entity exists */
3054
                if (namedEntity)
×
3055
                {
3056
                    /* Check if it is a Class, if so non-null */
3057
                    Clazz parentEntity = cast(Clazz) namedEntity;
×
3058

3059
                    /* Only inherit from class or (TODO: interfaces) */
3060
                    if (parentEntity)
×
3061
                    {
3062
                        /* Make sure it is not myself */
3063
                        if (parentEntity != clazz)
×
3064
                        {
3065
                            /* TODO: Add loop checking here */
3066
                        }
3067
                        else
3068
                        {
3069
                            expect("Cannot inherit from self");
×
3070
                        }
3071
                    }
3072
                    /* Error */
3073
                else
3074
                    {
3075
                        expect("Can only inherit from classes");
×
3076
                    }
3077
                }
3078
                /* If the entity doesn't exist then it is an error */
3079
                else
3080
                {
3081
                    expect("Could not find any entity named " ~ parent);
×
3082
                }
3083
            }
3084
        }
3085

3086
        /* Once processing is done, apply recursively */
3087
        foreach (Clazz clazz; classTypes)
195✔
3088
        {
3089
            checkClassInherit(clazz);
4✔
3090
        }
3091

3092
    }
3093

3094
    private void checkClasses(Container c)
3095
    {
3096
        /**
3097
        * Make sure no duplicate types (classes) defined
3098
        * within same Container
3099
        */
3100
        checkClassNames(c);
×
3101

3102
        /**
3103
        * Now that everything is neat and tidy
3104
        * let's check class properties like inheritance
3105
        * names
3106
        */
3107
        checkClassInherit(c);
×
3108
    }
3109

3110
    public Resolver getResolver()
3111
    {
3112
        return resolver;
1,164✔
3113
    }
3114

3115
    /**
3116
    * Given a Container `c` this will check all
3117
    * members of said Container and make sure
3118
    * none of them have a name that conflicts
3119
    * with any other member in said Container
3120
    * nor uses the same name AS the Container
3121
    * itself.
3122
    *
3123
    * Errors are printed when a member has a name
3124
    * of a previously defined member
3125
    *
3126
    * Errors are printed if the memeber shares a
3127
    * name with the container
3128
    *
3129
    * If the above 2 are false then a last check
3130
    * happens to check if the current Entity
3131
    * that just passed these checks is itself a
3132
    * Container, if not, then we do nothing and
3133
    * go onto processing the next Entity that is
3134
    * a member of Container `c` (we stay at the
3135
    * same level), HOWEVER if so, we then recursively
3136
    * call `checkContainer` on said Entity and the
3137
    * logic above applies again
3138
    *
3139
    * FIXME:
3140
    *
3141
    * (MODMAN) We need to know WHICH `Module` we
3142
    * are currently examining when we do this such
3143
    * that we can then fix the other `resolver`
3144
    * calls when we `generateName`(s), else
3145
    * we use the old `modulle` which is null
3146
    * now.
3147
    */
3148
    private void checkContainerCollision(Container c)
3149
    {
3150
        /**
3151
        * TODO: Always make sure this holds
3152
        *
3153
        * All objects that implement Container so far
3154
        * are also Entities (hence they have a name)
3155
        */
3156
        Entity containerEntity = cast(Entity)c;
200✔
3157
        assert(containerEntity);
200✔
3158

3159
        /**
3160
        * Get all Entities of the Container with order Clazz, Function, Variable
3161
        */
3162
        Entity[] entities = getContainerMembers(c);
200✔
3163
        DEBUG("checkContainer(C): " ~ to!(string)(entities));
200✔
3164

3165
        foreach (Entity entity; entities)
1,388✔
3166
        {
3167
            // (MODMAN) TODO: We need to loop through each module and make
3168
            // ... sure its name doesn't match with any of them
3169
            foreach(Module curMod; program.getModules())
1,746✔
3170
            {
3171
                if(cmp(entity.getName(), curMod.getName()) == 0)
306✔
3172
                {
3173
                    throw new CollidingNameException(this, curMod, entity, c);
6✔
3174
                }
3175
            }
3176
            
3177

3178
            /**
3179
            * If the current entity's name matches the container then error
3180
            */
3181
            if (cmp(containerEntity.getName(), entity.getName()) == 0)
274✔
3182
            {
3183
                throw new CollidingNameException(this, containerEntity, entity, c);
2✔
3184
            }
3185
            /**
3186
            * If there are conflicting names within the current container
3187
            * (this takes precedence into account based on how `entities`
3188
            * is generated)
3189
            */
3190
            else if (findPrecedence(c, entity.getName()) != entity)
272✔
3191
            {
3192
                throw new CollidingNameException(this, findPrecedence(c,
4✔
3193
                        entity.getName()), entity, c);
3194
            }
3195
            /**
3196
            * Otherwise this Entity is fine
3197
            */
3198
            else
3199
            {
3200
                // (MODMAN) This will need to be fixed (anchored at the Program-level)
3201
                string fullPath = resolver.generateName(this.program, entity);
268✔
3202
                // (MODMAN) This will need to be fixed (anchored at the Program-level)
3203
                string containerNameFullPath = resolver.generateName(this.program, containerEntity);
268✔
3204
                DEBUG("Entity \"" ~ fullPath
268✔
3205
                        ~ "\" is allowed to be defined within container \""
3206
                        ~ containerNameFullPath ~ "\"");
3207

3208
                /**
3209
                * Check if this Entity is a Container, if so, then
3210
                * apply the same round of checks within it
3211
                */
3212
                Container possibleContainerEntity = cast(Container) entity;
268✔
3213
                if (possibleContainerEntity)
268✔
3214
                {
3215
                    checkContainerCollision(possibleContainerEntity);
131✔
3216
                }
3217
            }
3218
        }
3219

3220
    }
3221

3222
    /**
3223
    * TODO: Create a version of the below function that possibly
3224
    * returns the list of Statement[]s ordered like below but
3225
    * via a weighting system rather
3226
    */
3227
    public Statement[] getContainerMembers_W(Container c)
3228
    {
3229
        /* Statements */
3230
        Statement[] statements;
×
3231

3232
        /* TODO: Implement me */
3233

3234
        return statements;
×
3235
    }
3236

3237
    /**
3238
    * Returns container members in order of
3239
    * Clazz, Function, Variable
3240
    */
3241
    private Entity[] getContainerMembers(Container c)
3242
    {
3243
        /* Entities */
3244
        Entity[] entities;
480✔
3245

3246
        /* Get all classes */
3247
        foreach (Statement statement; c.getStatements())
5,136✔
3248
        {
3249
            if (statement !is null && cast(Entity) statement)
2,464✔
3250
            {
3251
                entities ~= cast(Entity) statement;
936✔
3252
            }
3253
        }
3254

3255
        // /* Get all classes */
3256
        // foreach (Statement statement; c.getStatements())
3257
        // {
3258
        //     if (statement !is null && cast(Clazz) statement)
3259
        //     {
3260
        //         entities ~= cast(Clazz) statement;
3261
        //     }
3262
        // }
3263

3264
        // /* Get all functions */
3265
        // foreach (Statement statement; c.getStatements())
3266
        // {
3267
        //     if (statement !is null && cast(Function) statement)
3268
        //     {
3269
        //         entities ~= cast(Function) statement;
3270
        //     }
3271
        // }
3272

3273
        // /* Get all variables */
3274
        // foreach (Statement statement; c.getStatements())
3275
        // {
3276
        //     if (statement !is null && cast(Variable) statement)
3277
        //     {
3278
        //         entities ~= cast(Variable) statement;
3279
        //     }
3280
        // }
3281

3282
        return entities;
480✔
3283

3284
    }
3285

3286
    /**
3287
    * Finds the first occurring Entity with the provided
3288
    * name based on Classes being searched, then Functions
3289
    * and lastly Variables
3290
    */
3291
    public Entity findPrecedence(Container c, string name)
3292
    {
3293
        foreach (Entity entity; getContainerMembers(c))
1,660✔
3294
        {
3295
            /* If we find matching entity names */
3296
            if (cmp(entity.getName(), name) == 0)
460✔
3297
            {
3298
                return entity;
280✔
3299
            }
3300
        }
3301

3302
        return null;
×
3303
    }
3304

3305
    /**
3306
    * Starting from a Container c this makes sure
3307
    * that all classes defined within that container
3308
    * do no clash name wise
3309
    *
3310
    * Make this general, so it checks all Entoties
3311
    * within container, starting first with classes
3312
    * then it should probably mark them, this will
3313
    * be so we can then loop through all entities
3314
    * including classes, of container c and for
3315
    * every entity we come across in c we make
3316
    * sure it doesn't have a name of something that 
3317
    * is marked
3318
    */
3319
    private void checkClassNames(Container c)
3320
    {
3321
        /**
3322
        * TODO: Always make sure this holds
3323
        *
3324
        * All objects that implement Container so far
3325
        * are also Entities (hence they have a name)
3326
        */
3327
        Entity containerEntity = cast(Entity)c;
×
3328
        assert(containerEntity);
×
3329

3330
        /* Get all types (Clazz so far) */
3331
        Clazz[] classTypes;
×
3332

3333
        foreach (Statement statement; c.getStatements())
×
3334
        {
3335
            if (statement !is null && cast(Clazz) statement)
×
3336
            {
3337
                classTypes ~= cast(Clazz) statement;
×
3338
            }
3339
        }
3340

3341
        /* Declare each type */
3342
        foreach (Clazz clazz; classTypes)
×
3343
        {
3344
            // gprintln("Name: "~resolver.generateName(modulle, clazz));
3345
            /**
3346
            * Check if the first class found with my name is the one being
3347
            * processed, if so then it is fine, if not then error, it has
3348
            * been used (that identifier) already
3349
            *
3350
            * TODO: We cann add a check here to not allow containerName == clazz
3351
            * TODO: Call resolveUp as we can then stop class1.class1.class1
3352
            * Okay top would resolve first part but class1.class2.class1
3353
            * would not be caught by that
3354
            *
3355
            * TODO: This will meet inner clazz1 first, we need to do another check
3356
            */
3357
            if (resolver.resolveUp(c, clazz.getName()) != clazz)
×
3358
            {
3359
                expect("Cannot define class \"" ~ resolver.generateName(this.program,
×
3360
                        clazz) ~ "\" as one with same name, \"" ~ resolver.generateName(this.program,
3361
                        resolver.resolveUp(c, clazz.getName())) ~ "\" exists in container \"" ~ resolver.generateName(
3362
                        this.program, containerEntity) ~ "\"");
3363
            }
3364
            else
3365
            {
3366
                /* Get the current container's parent container */
3367
                Container parentContainer = containerEntity.parentOf();
×
3368

3369
                /* Don't allow a class to be named after it's container */
3370
                // if(!parentContainer)
3371
                // {
3372
                if (cmp(containerEntity.getName(), clazz.getName()) == 0)
×
3373
                {
3374
                    expect("Class \"" ~ resolver.generateName(this.program,
×
3375
                            clazz) ~ "\" cannot be defined within container with same name, \"" ~ resolver.generateName(
3376
                            this.program, containerEntity) ~ "\"");
3377
                }
3378

3379
                /* TODO: Loop througn Container ENtitys here */
3380
                /* Make sure that when we call findPrecedence(entity) == current entity */
3381

3382
                // }
3383

3384
                /* TODO: We allow shaddowing so below is disabled */
3385
                /* TODO: We should however use the below for dot-less resolution */
3386
                // /* Find the name starting in upper cotainer */
3387
                // Entity clazzAbove = resolveUp(parentContainer, clazz.getName());
3388

3389
                // if(!clazzAbove)
3390
                // {
3391

3392
                // }
3393
                // else
3394
                // {
3395
                //     Parser.expect("Name in use abpve us, bad"~to!(string)(clazz));
3396
                // }
3397

3398
                /* If the Container's parent container is Module then we can have
3399
                /* TODO: Check that it doesn;t equal any class up the chain */
3400
                /* TODO: Exclude Module from this */
3401

3402
                // /* Still check if there is something with our name above us */
3403
                // Container parentContainer = c.parentOf();
3404

3405
                // /* If at this level container we find duplicate */
3406
                // if(resolveUp(parentContainer, clazz.getName()))
3407
                // {
3408

3409
                //         Parser.expect("Class with name "~clazz.getName()~" defined in class "~c.getName());
3410

3411
                // }
3412

3413
            }
3414
        }
3415

3416
        /**
3417
        * TODO: Now we should loop through each class and do the same
3418
        * so we have all types defined
3419
        */
3420
        //gprintln("Defined classes: "~to!(string)(Program.getAllOf(new Clazz(""), cast(Statement[])marked)));
3421

3422
        /**
3423
        * By now we have confirmed that within the current container
3424
        * there are no classes defined with the same name
3425
        *
3426
        * We now check each Class recursively, once we are done
3427
        * we mark the class entity as "ready" (may be referenced)
3428
        */
3429
        foreach (Clazz clazz; classTypes)
×
3430
        {
3431
            WARN("Check recursive " ~ to!(string)(clazz));
×
3432

3433
            /* Check the current class's types within */
3434
            checkClassNames(clazz);
×
3435

3436
            // checkClassInherit(clazz);
3437
        }
3438

3439
        /*Now we should loop through each class */
3440
        /* Once outerly everything is defined we can then handle class inheritance names */
3441
        /* We can also then handle refereces between classes */
3442

3443
        // gprintln("checkTypes: ")
3444

3445
    }
3446

3447
    /* Test name resolution */
3448
    unittest
3449
    {
3450
        //assert()
3451
    }
3452

3453
    /** 
3454
     * Maps a given `Variable` to its reference
3455
     * count. This includes the declaration
3456
     * thereof.
3457
     */
3458
    private uint[Variable] varRefCounts;
3459

3460
    /** 
3461
     * Increments the given variable's reference
3462
     * count
3463
     *
3464
     * Params:
3465
     *   variable = the variable
3466
     */
3467
    void touch(Variable variable)
3468
    {
3469
        // Create entry if not existing yet
3470
        if(variable !in this.varRefCounts)
336✔
3471
        {
3472
            this.varRefCounts[variable] = 0;    
133✔
3473
        }
3474

3475
        // Increment count
3476
        this.varRefCounts[variable]++;
336✔
3477
    }
3478

3479
    /** 
3480
     * Returns all variables which were declared
3481
     * but not used
3482
     *
3483
     * Returns: the array of variables
3484
     */
3485
    public Variable[] getUnusedVariables()
3486
    {
3487
        Variable[] unused;
47✔
3488
        foreach(Variable variable; this.varRefCounts.keys())
483✔
3489
        {
3490
            if(!(this.varRefCounts[variable] > 1))
114✔
3491
            {
3492
                unused ~= variable;
42✔
3493
            }
3494
        }
3495

3496
        return unused;
47✔
3497
    }
3498
}
3499

3500

3501
version(unittest)
3502
{
3503
    import std.file;
3504
    import std.stdio;
3505
    import tlang.compiler.lexer.core;
3506
    import tlang.compiler.lexer.kinds.basic : BasicLexer;
3507
    import tlang.compiler.parsing.core;
3508
}
3509

3510
/* Test name colliding with container name (1/3) [module] */
3511
unittest
3512
{
3513
    
3514

3515
    string sourceFile = "source/tlang/testing/collide_container_module1.t";
1✔
3516

3517
    File sourceFileFile;
2✔
3518
    sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
1✔
3519
    ulong fileSize = sourceFileFile.size();
1✔
3520
    byte[] fileBytes;
1✔
3521
    fileBytes.length = fileSize;
1✔
3522
    fileBytes = sourceFileFile.rawRead(fileBytes);
1✔
3523
    sourceFileFile.close();
1✔
3524

3525
    string sourceCode = cast(string) fileBytes;
1✔
3526
    File dummyOut;
2✔
3527
    Compiler compiler = new Compiler(sourceCode, sourceFile, dummyOut);
1✔
3528

3529
    compiler.doLex();
1✔
3530
    compiler.doParse();
1✔
3531
    
3532
    try
3533
    {
3534
        /* Perform test */
3535
        compiler.doTypeCheck();
1✔
3536

3537
        /* Shouldn't reach here, collision exception MUST occur */
3538
        assert(false);
3539
    }
3540
    catch (CollidingNameException e)
3541
    {
3542
        Program program = compiler.getProgram();
1✔
3543
        TypeChecker typeChecker = compiler.getTypeChecker();
1✔
3544
        Module modulle = program.getModules()[0];
1✔
3545

3546
        /* Setup testing variables */
3547
        Entity container = typeChecker.getResolver().resolveBest(modulle, "y");
1✔
3548
        Entity colliderMember = typeChecker.getResolver().resolveBest(modulle, "y.y");
1✔
3549

3550
        /* Make sure the member y.y collided with root container (module) y */
3551
        assert(e.defined == container);
1✔
3552
    }
3553
}
3554

3555

3556

3557
/* Test name colliding with container name (2/3) [module, nested collider] */
3558
unittest
3559
{
3560
    string sourceFile = "source/tlang/testing/collide_container_module2.t";
1✔
3561

3562
    File sourceFileFile;
2✔
3563
    sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
1✔
3564
    ulong fileSize = sourceFileFile.size();
1✔
3565
    byte[] fileBytes;
1✔
3566
    fileBytes.length = fileSize;
1✔
3567
    fileBytes = sourceFileFile.rawRead(fileBytes);
1✔
3568
    sourceFileFile.close();
1✔
3569

3570
    string sourceCode = cast(string) fileBytes;
1✔
3571
    File dummyOut;
2✔
3572
    Compiler compiler = new Compiler(sourceCode, sourceFile, dummyOut);
1✔
3573

3574
    compiler.doLex();
1✔
3575
    compiler.doParse();
1✔
3576

3577
    try
3578
    {
3579
        /* Perform test */
3580
        compiler.doTypeCheck();
1✔
3581

3582
        /* Shouldn't reach here, collision exception MUST occur */
3583
        assert(false);
3584
    }
3585
    catch (CollidingNameException e)
3586
    {
3587
        Program program = compiler.getProgram();
1✔
3588
        TypeChecker typeChecker = compiler.getTypeChecker();
1✔
3589
        Module modulle = program.getModules()[0];
1✔
3590

3591
        /* Setup testing variables */
3592
        Entity container = typeChecker.getResolver().resolveBest(modulle, "y");
1✔
3593
        Entity colliderMember = typeChecker.getResolver().resolveBest(modulle, "y.a.b.c.y");
1✔
3594

3595
        /* Make sure the member y.a.b.c.y collided with root container (module) y */
3596
        assert(e.defined == container);
1✔
3597
    }
3598
}
3599

3600
/* Test name colliding with container name (3/3) [container (non-module), nested collider] */
3601
unittest
3602
{
3603
    string sourceFile = "source/tlang/testing/collide_container_non_module.t";
1✔
3604

3605
    File sourceFileFile;
2✔
3606
    sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
1✔
3607
    ulong fileSize = sourceFileFile.size();
1✔
3608
    byte[] fileBytes;
1✔
3609
    fileBytes.length = fileSize;
1✔
3610
    fileBytes = sourceFileFile.rawRead(fileBytes);
1✔
3611
    sourceFileFile.close();
1✔
3612

3613
    string sourceCode = cast(string) fileBytes;
1✔
3614
    File dummyOut;
2✔
3615
    Compiler compiler = new Compiler(sourceCode, sourceFile, dummyOut);
1✔
3616

3617
    compiler.doLex();
1✔
3618
    compiler.doParse();
1✔
3619

3620
    try
3621
    {
3622
        /* Perform test */
3623
        compiler.doTypeCheck();
1✔
3624

3625
        /* Shouldn't reach here, collision exception MUST occur */
3626
        assert(false);
3627
    }
3628
    catch (CollidingNameException e)
3629
    {
3630
        Program program = compiler.getProgram();
1✔
3631
        TypeChecker typeChecker = compiler.getTypeChecker();
1✔
3632
        Module modulle = program.getModules()[0];
1✔
3633

3634
        /* Setup testing variables */
3635
        Entity container = typeChecker.getResolver().resolveBest(modulle, "a.b.c");
1✔
3636
        Entity colliderMember = typeChecker.getResolver().resolveBest(modulle, "a.b.c.c");
1✔
3637

3638
        /* Make sure the member a.b.c.c collided with a.b.c container */
3639
        assert(e.defined == container);
1✔
3640
    }
3641
}
3642

3643
/* Test name colliding with member */
3644
unittest
3645
{
3646
    string sourceFile = "source/tlang/testing/collide_member.t";
1✔
3647

3648
    File sourceFileFile;
2✔
3649
    sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
1✔
3650
    ulong fileSize = sourceFileFile.size();
1✔
3651
    byte[] fileBytes;
1✔
3652
    fileBytes.length = fileSize;
1✔
3653
    fileBytes = sourceFileFile.rawRead(fileBytes);
1✔
3654
    sourceFileFile.close();
1✔
3655

3656
    string sourceCode = cast(string) fileBytes;
1✔
3657
    File dummyOut;
2✔
3658
    Compiler compiler = new Compiler(sourceCode, sourceFile, dummyOut);
1✔
3659

3660
    compiler.doLex();
1✔
3661
    compiler.doParse();
1✔
3662

3663
    try
3664
    {
3665
        /* Perform test */
3666
        compiler.doTypeCheck();
1✔
3667

3668
        /* Shouldn't reach here, collision exception MUST occur */
3669
        assert(false);
3670
    }
3671
    catch (CollidingNameException e)
3672
    {
3673
        Program program = compiler.getProgram();
1✔
3674
        TypeChecker typeChecker = compiler.getTypeChecker();
1✔
3675
        Module modulle = program.getModules()[0];
1✔
3676

3677
        /* Setup testing variables */
3678
        Entity memberFirst = typeChecker.getResolver().resolveBest(modulle, "a.b");
1✔
3679

3680
        /* Make sure the member a.b.c.c collided with a.b.c container */
3681
        assert(e.attempted != memberFirst);
1✔
3682
    }
3683
}
3684

3685
/* Test name colliding with member (check that the member defined is class (precendence test)) */
3686
unittest
3687
{
3688
    string sourceFile = "source/tlang/testing/precedence_collision_test.t";
1✔
3689

3690
    File sourceFileFile;
2✔
3691
    sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
1✔
3692
    ulong fileSize = sourceFileFile.size();
1✔
3693
    byte[] fileBytes;
1✔
3694
    fileBytes.length = fileSize;
1✔
3695
    fileBytes = sourceFileFile.rawRead(fileBytes);
1✔
3696
    sourceFileFile.close();
1✔
3697

3698
    string sourceCode = cast(string) fileBytes;
1✔
3699
    File dummyOut;
2✔
3700
    Compiler compiler = new Compiler(sourceCode, sourceFile, dummyOut);
1✔
3701

3702
    compiler.doLex();
1✔
3703
    compiler.doParse();
1✔
3704

3705
    try
3706
    {
3707
        /* Perform test */
3708
        compiler.doTypeCheck();
1✔
3709

3710
        /* Shouldn't reach here, collision exception MUST occur */
3711
        assert(false);
3712
    }
3713
    catch (CollidingNameException e)
3714
    {
3715
        Program program = compiler.getProgram();
1✔
3716
        TypeChecker typeChecker = compiler.getTypeChecker();
1✔
3717
        Module modulle = program.getModules()[0];
1✔
3718

3719
        /* Setup testing variables */
3720
        Entity ourClassA = typeChecker.getResolver().resolveBest(modulle, "a");
1✔
3721

3722
        /* Make sure the member attempted was Variable and defined was Clazz */
3723
        assert(cast(Variable)e.attempted);
1✔
3724
        assert(cast(Clazz)e.defined);
1✔
3725
    }
3726
}
3727

3728

3729
/* Test name colliding with container name (1/2) */
3730
unittest
3731
{
3732
    string sourceFile = "source/tlang/testing/collide_container.t";
1✔
3733

3734
    File sourceFileFile;
2✔
3735
    sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
1✔
3736
    ulong fileSize = sourceFileFile.size();
1✔
3737
    byte[] fileBytes;
1✔
3738
    fileBytes.length = fileSize;
1✔
3739
    fileBytes = sourceFileFile.rawRead(fileBytes);
1✔
3740
    sourceFileFile.close();
1✔
3741

3742
    string sourceCode = cast(string) fileBytes;
1✔
3743
    File dummyOut;
2✔
3744
    Compiler compiler = new Compiler(sourceCode, sourceFile, dummyOut);
1✔
3745

3746
    compiler.doLex();
1✔
3747
    compiler.doParse();
1✔
3748

3749
    try
3750
    {
3751
        /* Perform test */
3752
        compiler.doTypeCheck();
1✔
3753

3754
        /* Shouldn't reach here, collision exception MUST occur */
3755
        assert(false);
3756
    }
3757
    catch (CollidingNameException e)
3758
    {
3759
        Program program = compiler.getProgram();
1✔
3760
        TypeChecker typeChecker = compiler.getTypeChecker();
1✔
3761
        Module modulle = program.getModules()[0];
1✔
3762

3763
        /* Setup testing variables */
3764
        Entity container = typeChecker.getResolver().resolveBest(modulle, "y");
1✔
3765
        Entity colliderMember = typeChecker.getResolver().resolveBest(modulle, "y.y");
1✔
3766

3767
        /* Make sure the member y.y collided with root container (module) y */
3768
        assert(e.defined == container);
1✔
3769
    }
3770
}
3771

3772
/* Test name colliding with container name (1/2) */
3773
// TODO: Re-enable this when we take a look at the `discards` - for now discards at module level are not allowed
3774
// ... therefore this unittest fails - otherwise it would have normally passed
3775
// unittest
3776
// {
3777
//     import std.file;
3778
//     import std.stdio;
3779
//     import compiler.lexer;
3780
//     import compiler.parsing.core;
3781

3782
//     string sourceFile = "source/tlang/testing/typecheck/simple_dependence_correct7.t";
3783

3784
//     File sourceFileFile;
3785
//     sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
3786
//     ulong fileSize = sourceFileFile.size();
3787
//     byte[] fileBytes;
3788
//     fileBytes.length = fileSize;
3789
//     fileBytes = sourceFileFile.rawRead(fileBytes);
3790
//     sourceFileFile.close();
3791

3792
//     string sourceCode = cast(string) fileBytes;
3793
//     Lexer currentLexer = new Lexer(sourceCode);
3794
//     currentLexer.performLex();
3795

3796
//     Parser parser = new Parser(currentLexer.getTokens());
3797
//     Module modulle = parser.parse();
3798
//     TypeChecker typeChecker = new TypeChecker(modulle);
3799

3800
//     /* Perform test */
3801
//     typeChecker.beginCheck();
3802

3803
//     /* TODO: Insert checks here */
3804
// }
3805

3806

3807

3808
/** 
3809
 * Code generation and typechecking
3810
 *
3811
 * Testing file: `simple_function_call.t`
3812
 */
3813
unittest
3814
{
3815
    string sourceFile = "source/tlang/testing/typecheck/simple_function_call.t";
1✔
3816

3817
    File sourceFileFile;
2✔
3818
    sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
1✔
3819
    ulong fileSize = sourceFileFile.size();
1✔
3820
    byte[] fileBytes;
1✔
3821
    fileBytes.length = fileSize;
1✔
3822
    fileBytes = sourceFileFile.rawRead(fileBytes);
1✔
3823
    sourceFileFile.close();
1✔
3824

3825
    string sourceCode = cast(string) fileBytes;
1✔
3826
    File dummyOut;
2✔
3827
    Compiler compiler = new Compiler(sourceCode, sourceFile, dummyOut);
1✔
3828

3829
    compiler.doLex();
1✔
3830
    compiler.doParse();
1✔
3831

3832
    /* Perform test */
3833
    compiler.doTypeCheck();
1✔
3834

3835
    /* TODO: Actually test generated code queue */
3836
}
3837

3838
/** 
3839
 * Tests the unused variable detection mechanism
3840
 *
3841
 * Case: Positive (unused variables exist)
3842
 * Source file: source/tlang/testing/unused_vars.t
3843
 */
3844
unittest
3845
{
3846
    // Dummy field out
3847
    File fileOutDummy;
2✔
3848
    import tlang.compiler.core;
3849

3850
    string sourceFile = "source/tlang/testing/unused_vars.t";
1✔
3851

3852

3853
    Compiler compiler = new Compiler(gibFileData(sourceFile), sourceFile, fileOutDummy);
1✔
3854
    compiler.doLex();
1✔
3855
    compiler.doParse();
1✔
3856
    compiler.doTypeCheck();
1✔
3857
    TypeChecker tc = compiler.getTypeChecker();
1✔
3858

3859
    /**
3860
     * There should be 1 unused variable and then
3861
     * it should be named `j`
3862
     */
3863
    Variable[] unusedVars = tc.getUnusedVariables();
1✔
3864
    assert(unusedVars.length == 1);
1✔
3865
    Variable unusedVarActual = unusedVars[0];
1✔
3866
    Variable unusedVarExpected = cast(Variable)tc.getResolver().resolveBest(compiler.getProgram().getModules()[0], "j");
1✔
3867
    assert(unusedVarActual is unusedVarExpected);
1✔
3868
}
3869

3870
/** 
3871
 * Tests the unused variable detection mechanism
3872
 *
3873
 * Case: Negative (unused variables do NOT exist)
3874
 * Source file: source/tlang/testing/unused_vars_none.t
3875
 */
3876
unittest
3877
{
3878
    // Dummy field out
3879
    File fileOutDummy;
2✔
3880
    import tlang.compiler.core;
3881

3882
    string sourceFile = "source/tlang/testing/unused_vars_none.t";
1✔
3883

3884

3885
    Compiler compiler = new Compiler(gibFileData(sourceFile), sourceFile, fileOutDummy);
1✔
3886
    compiler.doLex();
1✔
3887
    compiler.doParse();
1✔
3888
    compiler.doTypeCheck();
1✔
3889
    TypeChecker tc = compiler.getTypeChecker();
1✔
3890

3891
    /**
3892
     * There should be 0 unused variables
3893
     */
3894
    Variable[] unusedVars = tc.getUnusedVariables();
1✔
3895
    assert(unusedVars.length == 0);
1✔
3896
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc