source: src/Concurrency/Keywords.cpp @ 048dde4

Last change on this file since 048dde4 was c3d0182a, checked in by kyoung <lseo@…>, 3 months ago

plan9 inline ordering for concurrency keywords.

  • Property mode set to 100644
File size: 49.4 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// Keywords.cpp -- Implement concurrency constructs from their keywords.
8//
9// Author           : Andrew Beach
10// Created On       : Tue Nov 16  9:53:00 2021
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Fri Jan 24 18:33:02 2025
13// Update Count     : 14
14//
15
16#include "Concurrency/Keywords.hpp"
17
18#include <iostream>
19
20#include "AST/Copy.hpp"
21#include "AST/Decl.hpp"
22#include "AST/Expr.hpp"
23#include "AST/Inspect.hpp"
24#include "AST/Pass.hpp"
25#include "AST/Stmt.hpp"
26#include "AST/DeclReplacer.hpp"
27#include "AST/TranslationUnit.hpp"
28#include "CodeGen/OperatorTable.hpp"
29#include "Common/Examine.hpp"
30#include "Common/Utility.hpp"
31#include "Common/UniqueName.hpp"
32#include "ControlStruct/LabelGenerator.hpp"
33#include "InitTweak/InitTweak.hpp"
34#include "Virtual/Tables.hpp"
35
36namespace Concurrency {
37
38namespace {
39
40// --------------------------------------------------------------------------
41// Loose Helper Functions:
42
43/// Detect threads constructed with the keyword thread.
44bool isThread( const ast::DeclWithType * decl ) {
45        auto baseType = decl->get_type()->stripDeclarator();
46        auto instType = dynamic_cast<const ast::StructInstType *>( baseType );
47        if ( nullptr == instType ) { return false; }
48        return instType->base->is_thread();
49}
50
51/// Get the virtual type id if given a type name.
52std::string typeIdType( std::string const & exception_name ) {
53        return exception_name.empty() ? std::string()
54                : Virtual::typeIdType( exception_name );
55}
56
57/// Get the vtable type name if given a type name.
58std::string vtableTypeName( std::string const & exception_name ) {
59        return exception_name.empty() ? std::string()
60                : Virtual::vtableTypeName( exception_name );
61}
62
63static ast::Type * mutate_under_references( ast::ptr<ast::Type>& type ) {
64        ast::Type * mutType = type.get_and_mutate();
65        for ( ast::ReferenceType * mutRef
66                ; (mutRef = dynamic_cast<ast::ReferenceType *>( mutType ))
67                ; mutType = mutRef->base.get_and_mutate() );
68        return mutType;
69}
70
71// Describe that it adds the generic parameters and the uses of the generic parameters on the
72// function and first "this" argument.
73ast::FunctionDecl * fixupGenerics(
74                const ast::FunctionDecl * func, const ast::StructDecl * decl ) {
75        const CodeLocation & location = decl->location;
76        // We have to update both the declaration
77        auto mutFunc = ast::mutate( func );
78        auto mutType = mutFunc->type.get_and_mutate();
79
80        if ( decl->params.empty() ) {
81                return mutFunc;
82        }
83
84        assert( 0 != mutFunc->params.size() );
85        assert( 0 != mutType->params.size() );
86
87        // Add the "forall" clause information.
88        for ( const ast::ptr<ast::TypeDecl> & typeParam : decl->params ) {
89                auto typeDecl = ast::deepCopy( typeParam );
90                mutFunc->type_params.push_back( typeDecl );
91                mutType->forall.push_back( new ast::TypeInstType( typeDecl ) );
92                for ( auto & assertion : typeDecl->assertions ) {
93                        mutFunc->assertions.push_back( assertion );
94                        mutType->assertions.emplace_back(
95                                new ast::VariableExpr( location, assertion ) );
96                }
97                typeDecl->assertions.clear();
98        }
99
100        // Even chain_mutate is not powerful enough for this:
101        ast::ptr<ast::Type>& paramType = strict_dynamic_cast<ast::ObjectDecl *>(
102                mutFunc->params[0].get_and_mutate() )->type;
103        auto paramTypeInst = strict_dynamic_cast<ast::StructInstType *>(
104                mutate_under_references( paramType ) );
105        auto typeParamInst = strict_dynamic_cast<ast::StructInstType *>(
106                mutate_under_references( mutType->params[0] ) );
107
108        for ( const ast::ptr<ast::TypeDecl> & typeDecl : mutFunc->type_params ) {
109                paramTypeInst->params.push_back(
110                        new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
111                typeParamInst->params.push_back(
112                        new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
113        }
114
115        return mutFunc;
116}
117
118// --------------------------------------------------------------------------
119
120// This type describes the general information used to transform a generator, coroutine, monitor, or
121// thread aggregate kind.  A pass is made over the AST in ConcurrentSueKeyword::postvisit looking
122// for each of these kinds. When found, this data structure is filled in with the information from
123// the specific structures below, and handleStruct is called to perform the common changes that
124// augment the aggregate kind with fields and generate appropriate companion routines.  The location
125// of any extra fields is specified in addField.
126
127struct ConcurrentSueKeyword : public ast::WithDeclsToAdd {
128        ConcurrentSueKeyword(
129                std::string&& type_name, std::string&& field_name,
130                std::string&& getter_name, std::string&& context_error,
131                std::string&& exception_name,
132                bool needs_main, ast::AggregateDecl::Aggregate cast_target
133        ) :
134                type_name( type_name ), field_name( field_name ),
135                getter_name( getter_name ), context_error( context_error ),
136                exception_name( exception_name ),
137                typeid_name( typeIdType( exception_name ) ),
138                vtable_name( vtableTypeName( exception_name ) ),
139                needs_main( needs_main ), cast_target( cast_target )
140        {}
141
142        virtual ~ConcurrentSueKeyword() {}
143
144        const ast::Decl * postvisit( const ast::StructDecl * decl );
145        const ast::DeclWithType * postvisit( const ast::FunctionDecl * decl );
146        const ast::Expr * postvisit( const ast::KeywordCastExpr * expr );
147
148        struct StructAndField {
149                const ast::StructDecl * decl;
150                const ast::ObjectDecl * field;
151        };
152
153        const ast::StructDecl * handleStruct( const ast::StructDecl * );
154        void handleMain( const ast::FunctionDecl *, const ast::StructInstType * );
155        void addTypeId( const ast::StructDecl * );
156        void addVtableForward( const ast::StructDecl * );
157        const ast::FunctionDecl * forwardDeclare( const ast::StructDecl * );
158        StructAndField addField( const ast::StructDecl * );
159        void addGetRoutines( const ast::ObjectDecl *, const ast::FunctionDecl * );
160        void addLockUnlockRoutines( const ast::StructDecl * );
161
162private:
163        const std::string type_name;
164        const std::string field_name;
165        const std::string getter_name;
166        const std::string context_error;
167        const std::string exception_name;
168        const std::string typeid_name;
169        const std::string vtable_name;
170        const bool needs_main;
171        const ast::AggregateDecl::Aggregate cast_target;
172
173        const ast::StructDecl   * type_decl = nullptr;
174        const ast::FunctionDecl * dtor_decl = nullptr;
175        const ast::StructDecl * except_decl = nullptr;
176        const ast::StructDecl * typeid_decl = nullptr;
177        const ast::StructDecl * vtable_decl = nullptr;
178
179};
180
181// Handles generator type declarations:
182//
183// generator MyGenerator {                   struct MyGenerator {
184//                                =>             int __generator_state;
185//    int data;                                  int data;
186//    a_struct_t more_data;                      a_struct_t more_data;
187// };                                        };
188//
189struct GeneratorKeyword final : public ConcurrentSueKeyword {
190        GeneratorKeyword() : ConcurrentSueKeyword(
191                "generator$",
192                "__generator_state",
193                "get_generator",
194                "Unable to find builtin type generator$\n",
195                "",
196                true,
197                ast::AggregateDecl::Generator )
198        {}
199
200        virtual ~GeneratorKeyword() {}
201};
202
203// Handles coroutine type declarations:
204//
205// coroutine MyCoroutine {                   struct MyCoroutine {
206//                                =>             coroutine$ __cor_d;
207//    int data;                                  int data;
208//    a_struct_t more_data;                      a_struct_t more_data;
209// };                                        };
210//                                           static inline coroutine$ * get_coroutine( MyCoroutine * this ) { return &this->__cor_d; }
211//
212struct CoroutineKeyword final : public ConcurrentSueKeyword {
213        CoroutineKeyword() : ConcurrentSueKeyword(
214                "coroutine$",
215                "__cor",
216                "get_coroutine",
217                "coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n",
218                "CoroutineCancelled",
219                true,
220                ast::AggregateDecl::Coroutine )
221        {}
222
223        virtual ~CoroutineKeyword() {}
224};
225
226// Handles monitor type declarations:
227//
228// monitor MyMonitor {                       struct MyMonitor {
229//                                =>             monitor$ __mon_d;
230//    int data;                                  int data;
231//    a_struct_t more_data;                      a_struct_t more_data;
232// };                                        };
233//                                           static inline monitor$ * get_coroutine( MyMonitor * this ) {
234//                                               return &this->__cor_d;
235//                                           }
236//                                           void lock(MyMonitor & this) {
237//                                               lock(get_monitor(this));
238//                                           }
239//                                           void unlock(MyMonitor & this) {
240//                                               unlock(get_monitor(this));
241//                                           }
242//
243struct MonitorKeyword final : public ConcurrentSueKeyword {
244        MonitorKeyword() : ConcurrentSueKeyword(
245                "monitor$",
246                "__mon",
247                "get_monitor",
248                "monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n",
249                "",
250                false,
251                ast::AggregateDecl::Monitor )
252        {}
253
254        virtual ~MonitorKeyword() {}
255};
256
257// Handles thread type declarations:
258//
259// thread Mythread {                         struct MyThread {
260//                                =>             thread$ __thrd_d;
261//    int data;                                  int data;
262//    a_struct_t more_data;                      a_struct_t more_data;
263// };                                        };
264//                                           static inline thread$ * get_thread( MyThread * this ) { return &this->__thrd_d; }
265//
266struct ThreadKeyword final : public ConcurrentSueKeyword {
267        ThreadKeyword() : ConcurrentSueKeyword(
268                "thread$",
269                "__thrd",
270                "get_thread",
271                "thread keyword requires threads to be in scope, add #include <thread.hfa>\n",
272                "ThreadCancelled",
273                true,
274                ast::AggregateDecl::Thread )
275        {}
276
277        virtual ~ThreadKeyword() {}
278};
279
280const ast::Decl * ConcurrentSueKeyword::postvisit(
281                const ast::StructDecl * decl ) {
282        if ( !decl->body ) {
283                return decl;
284        } else if ( cast_target == decl->kind ) {
285                return handleStruct( decl );
286        } else if ( type_name == decl->name ) {
287                assert( !type_decl );
288                type_decl = decl;
289        } else if ( exception_name == decl->name ) {
290                assert( !except_decl );
291                except_decl = decl;
292        } else if ( typeid_name == decl->name ) {
293                assert( !typeid_decl );
294                typeid_decl = decl;
295        } else if ( vtable_name == decl->name ) {
296                assert( !vtable_decl );
297                vtable_decl = decl;
298        }
299        return decl;
300}
301
302// Try to get the full definition, but raise an error on conflicts.
303const ast::FunctionDecl * getDefinition(
304                const ast::FunctionDecl * old_decl,
305                const ast::FunctionDecl * new_decl ) {
306        if ( !new_decl->stmts ) {
307                return old_decl;
308        } else if ( !old_decl->stmts ) {
309                return new_decl;
310        } else {
311                assert( !old_decl->stmts || !new_decl->stmts );
312                return nullptr;
313        }
314}
315
316const ast::DeclWithType * ConcurrentSueKeyword::postvisit(
317                const ast::FunctionDecl * decl ) {
318        if ( type_decl && isDestructorFor( decl, type_decl ) ) {
319                // Check for forward declarations, try to get the full definition.
320                dtor_decl = (dtor_decl) ? getDefinition( dtor_decl, decl ) : decl;
321        } else if ( !vtable_name.empty() && decl->has_body() ) {
322                if (const ast::DeclWithType * param = isMainFor( decl, cast_target )) {
323                        if ( !vtable_decl ) {
324                                SemanticError( decl, context_error );
325                        }
326                        // Should be safe because of isMainFor.
327                        const ast::StructInstType * struct_type =
328                                static_cast<const ast::StructInstType *>(
329                                        static_cast<const ast::ReferenceType *>(
330                                                param->get_type() )->base.get() );
331
332                        handleMain( decl, struct_type );
333                }
334        }
335        return decl;
336}
337
338const ast::Expr * ConcurrentSueKeyword::postvisit(
339                const ast::KeywordCastExpr * expr ) {
340        if ( cast_target == expr->target ) {
341                // Convert `(thread &)ex` to `(thread$ &)*get_thread(ex)`, etc.
342                if ( !type_decl || !dtor_decl ) {
343                        SemanticError( expr, context_error );
344                }
345                assert( nullptr == expr->result );
346                auto cast = ast::mutate( expr );
347                cast->result = new ast::ReferenceType( new ast::StructInstType( type_decl ) );
348                cast->concrete_target.field  = field_name;
349                cast->concrete_target.getter = getter_name;
350                return cast;
351        }
352        return expr;
353}
354
355const ast::StructDecl * ConcurrentSueKeyword::handleStruct(
356                const ast::StructDecl * decl ) {
357        assert( decl->body );
358
359        if ( !type_decl || !dtor_decl ) {
360                SemanticError( decl, context_error );
361        }
362
363        if ( !exception_name.empty() ) {
364                if ( !typeid_decl || !vtable_decl ) {
365                        SemanticError( decl, context_error );
366                }
367                addTypeId( decl );
368                addVtableForward( decl );
369        }
370
371        const ast::FunctionDecl * func = forwardDeclare( decl );
372        StructAndField addFieldRet = addField( decl );
373        decl = addFieldRet.decl;
374        const ast::ObjectDecl * field = addFieldRet.field;
375
376        addGetRoutines( field, func );
377        // Add routines to monitors for use by mutex stmt.
378        if ( ast::AggregateDecl::Monitor == cast_target ) {
379                addLockUnlockRoutines( decl );
380        }
381
382        return decl;
383}
384
385void ConcurrentSueKeyword::handleMain(
386                const ast::FunctionDecl * decl, const ast::StructInstType * type ) {
387        assert( vtable_decl );
388        assert( except_decl );
389
390        const CodeLocation & location = decl->location;
391
392        std::vector<ast::ptr<ast::Expr>> poly_args = {
393                new ast::TypeExpr( location, type ),
394        };
395        ast::ObjectDecl * vtable_object = Virtual::makeVtableInstance(
396                location,
397                "_default_vtable_object_declaration",
398                new ast::StructInstType( vtable_decl, copy( poly_args ) ),
399                type,
400                nullptr
401        );
402        declsToAddAfter.push_back( vtable_object );
403        declsToAddAfter.push_back(
404                new ast::ObjectDecl(
405                        location,
406                        Virtual::concurrentDefaultVTableName(),
407                        new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
408                        new ast::SingleInit( location,
409                                new ast::VariableExpr( location, vtable_object ) )
410                )
411        );
412        declsToAddAfter.push_back( Virtual::makeGetExceptionFunction(
413                location,
414                vtable_object,
415                new ast::StructInstType( except_decl, copy( poly_args ) )
416        ) );
417}
418
419void ConcurrentSueKeyword::addTypeId( const ast::StructDecl * decl ) {
420        assert( typeid_decl );
421        const CodeLocation & location = decl->location;
422
423        ast::StructInstType * typeid_type =
424                new ast::StructInstType( typeid_decl, ast::CV::Const );
425        typeid_type->params.push_back(
426                new ast::TypeExpr( location, new ast::StructInstType( decl ) ) );
427        declsToAddBefore.push_back(
428                Virtual::makeTypeIdInstance( location, typeid_type ) );
429        // If the typeid_type is going to be kept, the other reference will have been made by now, but
430        // we also get to avoid extra mutates.
431        ast::ptr<ast::StructInstType> typeid_cleanup = typeid_type;
432}
433
434void ConcurrentSueKeyword::addVtableForward( const ast::StructDecl * decl ) {
435        assert( vtable_decl );
436        const CodeLocation& location = decl->location;
437
438        std::vector<ast::ptr<ast::Expr>> poly_args = {
439                new ast::TypeExpr( location, new ast::StructInstType( decl ) ),
440        };
441        declsToAddBefore.push_back( Virtual::makeGetExceptionForward(
442                location,
443                new ast::StructInstType( vtable_decl, copy( poly_args ) ),
444                new ast::StructInstType( except_decl, copy( poly_args ) )
445        ) );
446        ast::ObjectDecl * vtable_object = Virtual::makeVtableForward(
447                location,
448                "_default_vtable_object_declaration",
449                new ast::StructInstType( vtable_decl, std::move( poly_args ) )
450        );
451        declsToAddBefore.push_back( vtable_object );
452        declsToAddBefore.push_back(
453                new ast::ObjectDecl(
454                        location,
455                        Virtual::concurrentDefaultVTableName(),
456                        new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
457                        nullptr,
458                        ast::Storage::Extern,
459                        ast::Linkage::Cforall
460                )
461        );
462}
463
464const ast::FunctionDecl * ConcurrentSueKeyword::forwardDeclare(
465                const ast::StructDecl * decl ) {
466        const CodeLocation & location = decl->location;
467
468        ast::StructDecl * forward = ast::deepCopy( decl );
469        {
470                // If removing members makes ref-count go to zero, do not free.
471                ast::ptr<ast::StructDecl> forward_ptr = forward;
472                forward->body = false;
473                forward->members.clear();
474                forward_ptr.release();
475        }
476
477        ast::ObjectDecl * this_decl = new ast::ObjectDecl(
478                location,
479                "this",
480                new ast::ReferenceType( new ast::StructInstType( decl ) )
481        );
482
483        ast::ObjectDecl * ret_decl = new ast::ObjectDecl(
484                location,
485                "ret",
486                new ast::PointerType( new ast::StructInstType( type_decl ) )
487        );
488
489        ast::FunctionDecl * get_decl = new ast::FunctionDecl(
490                location,
491                getter_name,
492                { this_decl }, // params
493                { ret_decl }, // returns
494                nullptr, // stmts
495                ast::Storage::Static,
496                ast::Linkage::Cforall,
497                { new ast::Attribute( "const" ) },
498                ast::Function::Inline
499        );
500        get_decl = fixupGenerics( get_decl, decl );
501
502        ast::FunctionDecl * main_decl = nullptr;
503        if ( needs_main ) {
504                // `this_decl` is copied here because the original was used above.
505                main_decl = new ast::FunctionDecl(
506                        location,
507                        "main",
508                        { ast::deepCopy( this_decl ) },
509                        {},
510                        nullptr,
511                        ast::Storage::Classes(),
512                        ast::Linkage::Cforall
513                );
514                main_decl = fixupGenerics( main_decl, decl );
515        }
516
517        declsToAddBefore.push_back( forward );
518        if ( needs_main ) declsToAddBefore.push_back( main_decl );
519        declsToAddBefore.push_back( get_decl );
520
521        return get_decl;
522}
523
524ConcurrentSueKeyword::StructAndField ConcurrentSueKeyword::addField(
525                const ast::StructDecl * decl ) {
526        const CodeLocation & location = decl->location;
527
528        ast::ObjectDecl * field = new ast::ObjectDecl(
529                location,
530                field_name,
531                new ast::StructInstType( type_decl )
532        );
533
534        auto mutDecl = ast::mutate( decl );
535        // Insert at start of special aggregate structures => front of vector
536        mutDecl->members.insert( mutDecl->members.begin(), field );
537
538        return {mutDecl, field};
539}
540
541void ConcurrentSueKeyword::addGetRoutines(
542                const ast::ObjectDecl * field, const ast::FunctionDecl * forward ) {
543        // Clone the signature and then build the body.
544        ast::FunctionDecl * decl = ast::deepCopy( forward );
545
546        // Say it is generated at the "same" places as the forward declaration.
547        const CodeLocation & location = decl->location;
548
549        const ast::DeclWithType * param = decl->params.front();
550        ast::Stmt * stmt = new ast::ReturnStmt( location,
551                new ast::AddressExpr( location,
552                        new ast::MemberExpr( location,
553                                field,
554                                new ast::CastExpr( location,
555                                        new ast::VariableExpr( location, param ),
556                                        ast::deepCopy( param->get_type()->stripReferences() ),
557                                        ast::ExplicitCast
558                                )
559                        )
560                )
561        );
562
563        decl->stmts = new ast::CompoundStmt( location, { stmt } );
564        declsToAddAfter.push_back( decl );
565}
566
567void ConcurrentSueKeyword::addLockUnlockRoutines(
568                const ast::StructDecl * decl ) {
569        // This should only be used on monitors.
570        assert( ast::AggregateDecl::Monitor == cast_target );
571
572        const CodeLocation & location = decl->location;
573
574        // The parameter for both routines.
575        ast::ObjectDecl * this_decl = new ast::ObjectDecl(
576                location,
577                "this",
578                new ast::ReferenceType( new ast::StructInstType( decl ) )
579        );
580
581        ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
582                location,
583                "lock",
584                {
585                        // Copy the declaration of this.
586                        ast::deepCopy( this_decl ),
587                },
588                { /* returns */ },
589                nullptr,
590                ast::Storage::Static,
591                ast::Linkage::Cforall,
592                { /* attributes */ },
593                ast::Function::Inline
594        );
595        lock_decl = fixupGenerics( lock_decl, decl );
596
597        lock_decl->stmts = new ast::CompoundStmt( location, {
598                new ast::ExprStmt( location,
599                        new ast::UntypedExpr( location,
600                                new ast::NameExpr( location, "lock" ),
601                                {
602                                        new ast::UntypedExpr( location,
603                                                new ast::NameExpr( location, "get_monitor" ),
604                                                { new ast::VariableExpr( location,
605                                                        InitTweak::getParamThis( lock_decl ) ) }
606                                        )
607                                }
608                        )
609                )
610        } );
611
612        ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
613                location,
614                "unlock",
615                {
616                        // Last use, consume the declaration of this.
617                        this_decl,
618                },
619                { /* returns */ },
620                nullptr,
621                ast::Storage::Static,
622                ast::Linkage::Cforall,
623                { /* attributes */ },
624                ast::Function::Inline
625        );
626        unlock_decl = fixupGenerics( unlock_decl, decl );
627
628        unlock_decl->stmts = new ast::CompoundStmt( location, {
629                new ast::ExprStmt( location,
630                        new ast::UntypedExpr( location,
631                                new ast::NameExpr( location, "unlock" ),
632                                {
633                                        new ast::UntypedExpr( location,
634                                                new ast::NameExpr( location, "get_monitor" ),
635                                                { new ast::VariableExpr( location,
636                                                        InitTweak::getParamThis( unlock_decl ) ) }
637                                        )
638                                }
639                        )
640                )
641        } );
642
643        declsToAddAfter.push_back( lock_decl );
644        declsToAddAfter.push_back( unlock_decl );
645}
646
647
648// --------------------------------------------------------------------------
649struct SuspendKeyword final :
650                public ast::WithStmtsToAdd, public ast::WithGuards {
651        SuspendKeyword() = default;
652        virtual ~SuspendKeyword() = default;
653
654        void previsit( const ast::FunctionDecl * );
655        const ast::DeclWithType * postvisit( const ast::FunctionDecl * );
656        const ast::Stmt * postvisit( const ast::SuspendStmt * );
657
658private:
659        bool is_real_suspend( const ast::FunctionDecl * );
660
661        const ast::Stmt * make_generator_suspend( const ast::SuspendStmt * );
662        const ast::Stmt * make_coroutine_suspend( const ast::SuspendStmt * );
663
664        struct LabelPair {
665                ast::Label obj;
666                int idx;
667        };
668
669        LabelPair make_label(const ast::Stmt * stmt ) {
670                labels.push_back( ControlStruct::newLabel( "generator", stmt ) );
671                return { labels.back(), int(labels.size()) };
672        }
673
674        const ast::DeclWithType * in_generator = nullptr;
675        const ast::FunctionDecl * decl_suspend = nullptr;
676        std::vector<ast::Label> labels;
677};
678
679void SuspendKeyword::previsit( const ast::FunctionDecl * decl ) {
680        GuardValue( in_generator ); in_generator = nullptr;
681
682        // If it is the real suspend, grab it if we don't have one already.
683        if ( is_real_suspend( decl ) ) {
684                decl_suspend = decl_suspend ? decl_suspend : decl;
685                return;
686        }
687
688        // Otherwise check if this is a generator main and, if so, handle it.
689        auto param = isMainFor( decl, ast::AggregateDecl::Generator );
690        if ( !param ) return;
691
692        if ( 0 != decl->returns.size() ) {
693                SemanticError( decl->location, "Generator main must return void." );
694        }
695
696        in_generator = param;
697        GuardValue( labels ); labels.clear();
698}
699
700const ast::DeclWithType * SuspendKeyword::postvisit(
701                const ast::FunctionDecl * decl ) {
702        // Only modify a full definition of a generator with states.
703        if ( !decl->stmts || !in_generator || labels.empty() ) return decl;
704
705        const CodeLocation & location = decl->location;
706
707        // Create a new function body:
708        // static void * __generator_labels[] = {&&s0, &&s1, ...};
709        // void * __generator_label = __generator_labels[GEN.__generator_state];
710        // goto * __generator_label;
711        // s0: ;
712        // OLD_BODY
713
714        // This is the null statement inserted right before the body.
715        ast::NullStmt * noop = new ast::NullStmt( location );
716        noop->labels.push_back( ControlStruct::newLabel( "generator", noop ) );
717        const ast::Label & first_label = noop->labels.back();
718
719        // Add each label to the init, starting with the first label.
720        std::vector<ast::ptr<ast::Init>> inits = {
721                new ast::SingleInit( location,
722                        new ast::LabelAddressExpr( location, copy( first_label ) ) ) };
723        // Then go through all the stored labels, and clear the store.
724        for ( auto && label : labels ) {
725                inits.push_back( new ast::SingleInit( label.location,
726                        new ast::LabelAddressExpr( label.location, std::move( label )
727                        ) ) );
728        }
729        labels.clear();
730        // Then construct the initializer itself.
731        auto init = new ast::ListInit( location, std::move( inits ) );
732
733        ast::ObjectDecl * generatorLabels = new ast::ObjectDecl(
734                location,
735                "__generator_labels",
736                new ast::ArrayType(
737                        new ast::PointerType( new ast::VoidType() ),
738                        nullptr,
739                        ast::FixedLen,
740                        ast::DynamicDim
741                ),
742                init,
743                ast::Storage::Classes(),
744                ast::Linkage::AutoGen
745        );
746
747        ast::ObjectDecl * generatorLabel = new ast::ObjectDecl(
748                location,
749                "__generator_label",
750                new ast::PointerType( new ast::VoidType() ),
751                new ast::SingleInit( location,
752                        new ast::UntypedExpr( location,
753                                new ast::NameExpr( location, "?[?]" ),
754                                {
755                                        // TODO: Could be a variable expr.
756                                        new ast::NameExpr( location, "__generator_labels" ),
757                                        new ast::UntypedMemberExpr( location,
758                                                new ast::NameExpr( location, "__generator_state" ),
759                                                new ast::VariableExpr( location, in_generator )
760                                        )
761                                }
762                        )
763                ),
764                ast::Storage::Classes(),
765                ast::Linkage::AutoGen
766        );
767
768        ast::BranchStmt * theGoTo = new ast::BranchStmt(
769                location, new ast::VariableExpr( location, generatorLabel )
770        );
771
772        // The noop goes here in order.
773
774        ast::CompoundStmt * body = new ast::CompoundStmt( location, {
775                { new ast::DeclStmt( location, generatorLabels ) },
776                { new ast::DeclStmt( location, generatorLabel ) },
777                { theGoTo },
778                { noop },
779                { decl->stmts },
780        } );
781
782        auto mutDecl = ast::mutate( decl );
783        mutDecl->stmts = body;
784        return mutDecl;
785}
786
787const ast::Stmt * SuspendKeyword::postvisit( const ast::SuspendStmt * stmt ) {
788        switch ( stmt->kind ) {
789        case ast::SuspendStmt::None:
790                // Use the context to determain the implicit target.
791                if ( in_generator ) {
792                        return make_generator_suspend( stmt );
793                } else {
794                        return make_coroutine_suspend( stmt );
795                }
796        case ast::SuspendStmt::Coroutine:
797                return make_coroutine_suspend( stmt );
798        case ast::SuspendStmt::Generator:
799                // Generator suspends must be directly in a generator.
800                if ( !in_generator ) SemanticError( stmt->location, "\"suspend generator\" must be used inside main of generator type." );
801                return make_generator_suspend( stmt );
802        }
803        assert( false );
804        return stmt;
805}
806
807/// Find the real/official suspend declaration.
808bool SuspendKeyword::is_real_suspend( const ast::FunctionDecl * decl ) {
809        return ( !decl->linkage.is_mangled
810                && 0 == decl->params.size()
811                && 0 == decl->returns.size()
812                && "__cfactx_suspend" == decl->name );
813}
814
815const ast::Stmt * SuspendKeyword::make_generator_suspend(
816                const ast::SuspendStmt * stmt ) {
817        assert( in_generator );
818        // Target code is:
819        //   GEN.__generator_state = X;
820        //   THEN
821        //   return;
822        //   __gen_X:;
823
824        const CodeLocation & location = stmt->location;
825
826        LabelPair label = make_label( stmt );
827
828        // This is the context saving statement.
829        stmtsToAddBefore.push_back( new ast::ExprStmt( location,
830                new ast::UntypedExpr( location,
831                        new ast::NameExpr( location, "?=?" ),
832                        {
833                                new ast::UntypedMemberExpr( location,
834                                        new ast::NameExpr( location, "__generator_state" ),
835                                        new ast::VariableExpr( location, in_generator )
836                                ),
837                                ast::ConstantExpr::from_int( location, label.idx ),
838                        }
839                )
840        ) );
841
842        // The THEN component is conditional (return is not).
843        if ( stmt->then ) {
844                stmtsToAddBefore.push_back( stmt->then.get() );
845        }
846        stmtsToAddBefore.push_back( new ast::ReturnStmt( location, nullptr ) );
847
848        // The null statement replaces the old suspend statement.
849        return new ast::NullStmt( location, { label.obj } );
850}
851
852const ast::Stmt * SuspendKeyword::make_coroutine_suspend(
853                const ast::SuspendStmt * stmt ) {
854        // The only thing we need from the old statement is the location.
855        const CodeLocation & location = stmt->location;
856
857        if ( !decl_suspend ) {
858                SemanticError( location, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>." );
859        }
860        if ( stmt->then ) {
861                SemanticError( location, "Compound statement following coroutines is not implemented." );
862        }
863
864        return new ast::ExprStmt( location,
865                new ast::UntypedExpr( location,
866                        ast::VariableExpr::functionPointer( location, decl_suspend ) )
867        );
868}
869
870// --------------------------------------------------------------------------
871struct MutexKeyword final : public ast::WithDeclsToAdd {
872        const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
873        void postvisit( const ast::StructDecl * decl );
874        const ast::Stmt * postvisit( const ast::MutexStmt * stmt );
875
876        static std::vector<const ast::DeclWithType *> findMutexArgs(
877                        const ast::FunctionDecl * decl, bool & first );
878        static void validate( const ast::DeclWithType * decl );
879
880        ast::CompoundStmt * addDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
881        ast::CompoundStmt * addStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
882        ast::CompoundStmt * addStatements( const ast::CompoundStmt * body, const std::vector<ast::ptr<ast::Expr>> & args );
883        ast::CompoundStmt * addThreadDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt * body, const std::vector<const ast::DeclWithType *> & args );
884        ast::ExprStmt * genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param);
885        ast::IfStmt * genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam );
886private:
887        const ast::StructDecl * monitor_decl = nullptr;
888        const ast::StructDecl * guard_decl = nullptr;
889        const ast::StructDecl * dtor_guard_decl = nullptr;
890        const ast::StructDecl * thread_guard_decl = nullptr;
891        const ast::StructDecl * lock_guard_decl = nullptr;
892
893        static ast::ptr<ast::Type> generic_func;
894
895        UniqueName mutex_func_namer = UniqueName("__lock_unlock_curr");
896};
897
898const ast::FunctionDecl * MutexKeyword::postvisit(
899                const ast::FunctionDecl * decl ) {
900        bool is_first_argument_mutex = false;
901        const std::vector<const ast::DeclWithType *> mutexArgs =
902                findMutexArgs( decl, is_first_argument_mutex );
903        bool const isDtor = CodeGen::isDestructor( decl->name );
904
905        // Does this function have any mutex arguments that connect to monitors?
906        if ( mutexArgs.empty() ) {
907                // If this is the destructor for a monitor it must be mutex.
908                if ( isDtor ) {
909                        // This reflects MutexKeyword::validate, but no error messages.
910                        const ast::Type * type = decl->type->params.front();
911
912                        // If it's a copy, it's not a mutex.
913                        const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
914                        if ( nullptr == refType ) {
915                                return decl;
916                        }
917
918                        // If it is not pointing directly to a type, it's not a mutex.
919                        auto base = refType->base;
920                        if ( base.as<ast::ReferenceType>() ) return decl;
921                        if ( base.as<ast::PointerType>() ) return decl;
922
923                        // If it is not a struct, it's not a mutex.
924                        auto baseStruct = base.as<ast::StructInstType>();
925                        if ( nullptr == baseStruct ) return decl;
926
927                        // If it is a monitor, then it is a monitor.
928                        if ( baseStruct->base->is_monitor() || baseStruct->base->is_thread() ) {
929                                SemanticError( decl, "destructors for structures declared as \"monitor\" must use mutex parameters " );
930                        }
931                }
932                return decl;
933        }
934
935        // Monitors can't be constructed with mutual exclusion.
936        if ( CodeGen::isConstructor( decl->name ) && is_first_argument_mutex ) {
937                SemanticError( decl, "constructors cannot have mutex parameters " );
938        }
939
940        // It makes no sense to have multiple mutex parameters for the destructor.
941        if ( isDtor && mutexArgs.size() != 1 ) {
942                SemanticError( decl, "destructors can only have 1 mutex argument " );
943        }
944
945        // Make sure all the mutex arguments are monitors.
946        for ( auto arg : mutexArgs ) {
947                validate( arg );
948        }
949
950        // Check to see if the body needs to be instrument the body.
951        const ast::CompoundStmt * body = decl->stmts;
952        if ( !body ) return decl;
953
954        // Check to if the required headers have been seen.
955        if ( !monitor_decl || !guard_decl || !dtor_guard_decl ) {
956                SemanticError( decl, "mutex keyword requires monitors to be in scope, add #include <monitor.hfa>." );
957        }
958
959        // Instrument the body.
960        ast::CompoundStmt * newBody = nullptr;
961        if ( isDtor && isThread( mutexArgs.front() ) ) {
962                if ( !thread_guard_decl ) {
963                        SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>." );
964                }
965                newBody = addThreadDtorStatements( decl, body, mutexArgs );
966        } else if ( isDtor ) {
967                newBody = addDtorStatements( decl, body, mutexArgs );
968        } else {
969                newBody = addStatements( decl, body, mutexArgs );
970        }
971        assert( newBody );
972        return ast::mutate_field( decl, &ast::FunctionDecl::stmts, newBody );
973}
974
975void MutexKeyword::postvisit( const ast::StructDecl * decl ) {
976        if ( !decl->body ) {
977                return;
978        } else if ( decl->name == "monitor$" ) {
979                assert( !monitor_decl );
980                monitor_decl = decl;
981        } else if ( decl->name == "monitor_guard_t" ) {
982                assert( !guard_decl );
983                guard_decl = decl;
984        } else if ( decl->name == "monitor_dtor_guard_t" ) {
985                assert( !dtor_guard_decl );
986                dtor_guard_decl = decl;
987        } else if ( decl->name == "thread_dtor_guard_t" ) {
988                assert( !thread_guard_decl );
989                thread_guard_decl = decl;
990        } else if ( decl->name == "__mutex_stmt_lock_guard" ) {
991                assert( !lock_guard_decl );
992                lock_guard_decl = decl;
993        }
994}
995
996const ast::Stmt * MutexKeyword::postvisit( const ast::MutexStmt * stmt ) {
997        if ( !lock_guard_decl ) {
998                SemanticError( stmt->location, "mutex stmt requires a header, add #include <mutex_stmt.hfa>." );
999        }
1000        ast::CompoundStmt * body =
1001                        new ast::CompoundStmt( stmt->location, { stmt->stmt } );
1002
1003        return addStatements( body, stmt->mutexObjs );;
1004}
1005
1006std::vector<const ast::DeclWithType *> MutexKeyword::findMutexArgs(
1007                const ast::FunctionDecl * decl, bool & first ) {
1008        std::vector<const ast::DeclWithType *> mutexArgs;
1009
1010        bool once = true;
1011        for ( auto arg : decl->params ) {
1012                const ast::Type * type = arg->get_type();
1013                if ( type->is_mutex() ) {
1014                        if ( once ) first = true;
1015                        mutexArgs.push_back( arg.get() );
1016                }
1017                once = false;
1018        }
1019        return mutexArgs;
1020}
1021
1022void MutexKeyword::validate( const ast::DeclWithType * decl ) {
1023        const ast::Type * type = decl->get_type();
1024
1025        // If it's a copy, it's not a mutex.
1026        const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
1027        if ( nullptr == refType ) {
1028                SemanticError( decl, "Mutex argument must be of reference type " );
1029        }
1030
1031        // If it is not pointing directly to a type, it's not a mutex.
1032        auto base = refType->base;
1033        if ( base.as<ast::ReferenceType>() || base.as<ast::PointerType>() ) {
1034                SemanticError( decl, "Mutex argument have exactly one level of indirection " );
1035        }
1036
1037        // If it is not a struct, it's not a mutex.
1038        auto baseStruct = base.as<ast::StructInstType>();
1039        if ( nullptr == baseStruct ) return;
1040
1041        // Make sure that only the outer reference is mutex.
1042        if ( baseStruct->is_mutex() ) {
1043                SemanticError( decl, "mutex keyword may only appear once per argument " );
1044        }
1045}
1046
1047ast::CompoundStmt * MutexKeyword::addDtorStatements(
1048                const ast::FunctionDecl* func, const ast::CompoundStmt * body,
1049                const std::vector<const ast::DeclWithType *> & args ) {
1050        ast::Type * argType = ast::shallowCopy( args.front()->get_type() );
1051        argType->set_mutex( false );
1052
1053        ast::CompoundStmt * mutBody = ast::mutate( body );
1054
1055        // Generated code goes near the beginning of body:
1056        const CodeLocation & location = mutBody->location;
1057
1058        const ast::ObjectDecl * monitor = new ast::ObjectDecl(
1059                location,
1060                "__monitor",
1061                new ast::PointerType( new ast::StructInstType( monitor_decl ) ),
1062                new ast::SingleInit(
1063                        location,
1064                        new ast::UntypedExpr(
1065                                location,
1066                                new ast::NameExpr( location, "get_monitor" ),
1067                                { new ast::CastExpr(
1068                                        location,
1069                                        new ast::VariableExpr( location, args.front() ),
1070                                        argType, ast::ExplicitCast
1071                                ) }
1072                        )
1073                )
1074        );
1075
1076        assert( generic_func );
1077
1078        // In reverse order:
1079        // monitor_dtor_guard_t __guard = { __monitor, func, false };
1080        mutBody->push_front(
1081                new ast::DeclStmt( location, new ast::ObjectDecl(
1082                        location,
1083                        "__guard",
1084                        new ast::StructInstType( dtor_guard_decl ),
1085                        new ast::ListInit(
1086                                location,
1087                                {
1088                                        new ast::SingleInit( location,
1089                                                new ast::AddressExpr( location,
1090                                                        new ast::VariableExpr( location, monitor ) ) ),
1091                                        new ast::SingleInit( location,
1092                                                new ast::CastExpr( location,
1093                                                        new ast::VariableExpr( location, func ),
1094                                                        generic_func,
1095                                                        ast::ExplicitCast ) ),
1096                                        new ast::SingleInit( location,
1097                                                ast::ConstantExpr::from_bool( location, false ) ),
1098                                },
1099                                {},
1100                                ast::MaybeConstruct
1101                        )
1102                ))
1103        );
1104
1105        // monitor$ * __monitor = get_monitor(a);
1106        mutBody->push_front( new ast::DeclStmt( location, monitor ) );
1107
1108        return mutBody;
1109}
1110
1111ast::CompoundStmt * MutexKeyword::addStatements(
1112                const ast::FunctionDecl* func, const ast::CompoundStmt * body,
1113                const std::vector<const ast::DeclWithType * > & args ) {
1114        ast::CompoundStmt * mutBody = ast::mutate( body );
1115
1116        // Code is generated near the beginning of the compound statement.
1117        const CodeLocation & location = mutBody->location;
1118
1119        // Make pointer to the monitors.
1120        ast::ObjectDecl * monitors = new ast::ObjectDecl(
1121                location,
1122                "__monitors",
1123                new ast::ArrayType(
1124                        new ast::PointerType(
1125                                new ast::StructInstType( monitor_decl )
1126                        ),
1127                        ast::ConstantExpr::from_ulong( location, args.size() ),
1128                        ast::FixedLen,
1129                        ast::DynamicDim
1130                ),
1131                new ast::ListInit(
1132                        location,
1133                        map_range<std::vector<ast::ptr<ast::Init>>>(
1134                                args,
1135                                []( const ast::DeclWithType * decl ) {
1136                                        return new ast::SingleInit(
1137                                                decl->location,
1138                                                new ast::UntypedExpr(
1139                                                        decl->location,
1140                                                        new ast::NameExpr( decl->location, "get_monitor" ),
1141                                                        {
1142                                                                new ast::CastExpr(
1143                                                                        decl->location,
1144                                                                        new ast::VariableExpr( decl->location, decl ),
1145                                                                        decl->get_type(),
1146                                                                        ast::ExplicitCast
1147                                                                )
1148                                                        }
1149                                                )
1150                                        );
1151                                }
1152                        )
1153                )
1154        );
1155
1156        assert( generic_func );
1157
1158        // In Reverse Order:
1159        mutBody->push_front(
1160                new ast::DeclStmt( location, new ast::ObjectDecl(
1161                        location,
1162                        "__guard",
1163                        new ast::StructInstType( guard_decl ),
1164                        new ast::ListInit(
1165                                location,
1166                                {
1167                                        new ast::SingleInit( location,
1168                                                new ast::VariableExpr( location, monitors ) ),
1169                                        new ast::SingleInit( location,
1170                                                ast::ConstantExpr::from_ulong( location, args.size() ) ),
1171                                        new ast::SingleInit( location, new ast::CastExpr(
1172                                                location,
1173                                                new ast::VariableExpr( location, func ),
1174                                                generic_func,
1175                                                ast::ExplicitCast
1176                                        ) ),
1177                                },
1178                                {},
1179                                ast::MaybeConstruct
1180                        )
1181                ))
1182        );
1183
1184        // monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
1185        mutBody->push_front( new ast::DeclStmt( location, monitors ) );
1186
1187        return mutBody;
1188}
1189
1190// Generates a cast to the void ptr to the appropriate lock type and dereferences it before calling
1191// lock or unlock on it used to undo the type erasure done by storing all the lock pointers as void.
1192ast::ExprStmt * MutexKeyword::genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param ) {
1193        return new ast::ExprStmt( location,
1194                new ast::UntypedExpr( location,
1195                        new ast::NameExpr( location, fnName ), {
1196                                ast::UntypedExpr::createDeref(
1197                                        location,
1198                                        new ast::CastExpr( location,
1199                                                param,
1200                                                new ast::PointerType( new ast::TypeofType( new ast::UntypedExpr(
1201                                                        expr->location,
1202                                                        new ast::NameExpr( expr->location, "__get_mutexstmt_lock_type" ),
1203                                                        { expr }
1204                                                ) ) ),
1205                                                ast::GeneratedFlag::ExplicitCast
1206                                        )
1207                                )
1208                        }
1209                )
1210        );
1211}
1212
1213ast::IfStmt * MutexKeyword::genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam ) {
1214        ast::IfStmt * outerLockIf = nullptr;
1215        ast::IfStmt * lastLockIf = nullptr;
1216
1217        //adds an if/elif clause for each lock to assign type from void ptr based on ptr address
1218        for ( long unsigned int i = 0; i < args.size(); i++ ) {
1219
1220                ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
1221                        new ast::NameExpr( location, "?==?" ), {
1222                                ast::deepCopy( thisParam ),
1223                                new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
1224                        }
1225                );
1226
1227                ast::IfStmt * currLockIf = new ast::IfStmt(
1228                        location,
1229                        ifCond,
1230                        genVirtLockUnlockExpr( fnName, args.at(i), location, ast::deepCopy( thisParam ) )
1231                );
1232
1233                if ( i == 0 ) {
1234                        outerLockIf = currLockIf;
1235                } else {
1236                        // add ifstmt to else of previous stmt
1237                        lastLockIf->else_ = currLockIf;
1238                }
1239
1240                lastLockIf = currLockIf;
1241        }
1242        return outerLockIf;
1243}
1244
1245void flattenTuple( const ast::UntypedTupleExpr * tuple, std::vector<ast::ptr<ast::Expr>> & output ) {
1246        for ( auto & expr : tuple->exprs ) {
1247                const ast::UntypedTupleExpr * innerTuple = dynamic_cast<const ast::UntypedTupleExpr *>(expr.get());
1248                if ( innerTuple ) flattenTuple( innerTuple, output );
1249                else output.emplace_back( ast::deepCopy( expr ));
1250        }
1251}
1252
1253ast::CompoundStmt * MutexKeyword::addStatements(
1254                const ast::CompoundStmt * body,
1255                const std::vector<ast::ptr<ast::Expr>> & args ) {
1256
1257        // Code is generated near the beginning of the compound statement.
1258        const CodeLocation & location = body->location;
1259
1260                // final body to return
1261        ast::CompoundStmt * newBody = new ast::CompoundStmt( location );
1262
1263        // std::string lockFnName = mutex_func_namer.newName();
1264        // std::string unlockFnName = mutex_func_namer.newName();
1265
1266        // If any arguments to the mutex stmt are tuples, flatten them
1267        std::vector<ast::ptr<ast::Expr>> flattenedArgs;
1268        for ( auto & arg : args ) {
1269                const ast::UntypedTupleExpr * tuple = dynamic_cast<const ast::UntypedTupleExpr *>(args.at(0).get());
1270                if ( tuple ) flattenTuple( tuple, flattenedArgs );
1271                else flattenedArgs.emplace_back( ast::deepCopy( arg ));
1272        }
1273
1274        // Make pointer to the monitors.
1275        ast::ObjectDecl * monitors = new ast::ObjectDecl(
1276                location,
1277                "__monitors",
1278                new ast::ArrayType(
1279                        new ast::PointerType(
1280                                new ast::VoidType()
1281                        ),
1282                        ast::ConstantExpr::from_ulong( location, flattenedArgs.size() ),
1283                        ast::FixedLen,
1284                        ast::DynamicDim
1285                ),
1286                new ast::ListInit(
1287                        location,
1288                        map_range<std::vector<ast::ptr<ast::Init>>>(
1289                                flattenedArgs, [](const ast::Expr * expr) {
1290                                        return new ast::SingleInit(
1291                                                expr->location,
1292                                                new ast::UntypedExpr(
1293                                                        expr->location,
1294                                                        new ast::NameExpr( expr->location, "__get_mutexstmt_lock_ptr" ),
1295                                                        { expr }
1296                                                )
1297                                        );
1298                                }
1299                        )
1300                )
1301        );
1302
1303        ast::StructInstType * lock_guard_struct =
1304                        new ast::StructInstType( lock_guard_decl );
1305
1306        // use try stmts to lock and finally to unlock
1307        ast::TryStmt * outerTry = nullptr;
1308        ast::TryStmt * currentTry;
1309        ast::CompoundStmt * lastBody = nullptr;
1310
1311        // adds a nested try stmt for each lock we are locking
1312        for ( long unsigned int i = 0; i < flattenedArgs.size(); i++ ) {
1313                ast::UntypedExpr * innerAccess = new ast::UntypedExpr(
1314                        location,
1315                        new ast::NameExpr( location,"?[?]" ), {
1316                                new ast::NameExpr( location, "__monitors" ),
1317                                ast::ConstantExpr::from_int( location, i )
1318                        }
1319                );
1320
1321                // make the try body
1322                ast::CompoundStmt * currTryBody = new ast::CompoundStmt( location );
1323                ast::IfStmt * lockCall = genTypeDiscrimLockUnlock( "lock", flattenedArgs, location, innerAccess );
1324                currTryBody->push_back( lockCall );
1325
1326                // make the finally stmt
1327                ast::CompoundStmt * currFinallyBody = new ast::CompoundStmt( location );
1328                ast::IfStmt * unlockCall = genTypeDiscrimLockUnlock( "unlock", flattenedArgs, location, innerAccess );
1329                currFinallyBody->push_back( unlockCall );
1330
1331                // construct the current try
1332                currentTry = new ast::TryStmt(
1333                        location,
1334                        currTryBody,
1335                        {},
1336                        new ast::FinallyClause( location, currFinallyBody )
1337                );
1338                if ( i == 0 ) outerTry = currentTry;
1339                else {
1340                        // pushback try into the body of the outer try
1341                        lastBody->push_back( currentTry );
1342                }
1343                lastBody = currTryBody;
1344        }
1345
1346        // push body into innermost try body
1347        if ( lastBody != nullptr ) {
1348                lastBody->push_back( body );
1349                newBody->push_front( outerTry );
1350        }
1351
1352        // monitor_guard_t __guard = { __monitors, # };
1353        newBody->push_front(
1354                new ast::DeclStmt(
1355                        location,
1356                        new ast::ObjectDecl(
1357                                location,
1358                                "__guard",
1359                                lock_guard_struct,
1360                                new ast::ListInit(
1361                                        location,
1362                                        {
1363                                                new ast::SingleInit(
1364                                                        location,
1365                                                        new ast::VariableExpr( location, monitors ) ),
1366                                                new ast::SingleInit(
1367                                                        location,
1368                                                        ast::ConstantExpr::from_ulong( location, flattenedArgs.size() ) ),
1369                                        },
1370                                        {},
1371                                        ast::MaybeConstruct
1372                                )
1373                        )
1374                )
1375        );
1376
1377        // monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
1378        newBody->push_front( new ast::DeclStmt( location, monitors ) );
1379
1380        // // The parameter for both __lock_curr/__unlock_curr routines.
1381        // ast::ObjectDecl * this_decl = new ast::ObjectDecl(
1382        //      location,
1383        //      "this",
1384        //      new ast::PointerType( new ast::VoidType() ),
1385        //      nullptr,
1386        //      {},
1387        //      ast::Linkage::Cforall
1388        // );
1389
1390        // ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
1391        //      location,
1392        //      lockFnName,
1393        //      { /* forall */ },
1394        //      {
1395        //              // Copy the declaration of this.
1396        //              this_decl,
1397        //      },
1398        //      { /* returns */ },
1399        //      nullptr,
1400        //      0,
1401        //      ast::Linkage::Cforall,
1402        //      { /* attributes */ },
1403        //      ast::Function::Inline
1404        // );
1405
1406        // ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
1407        //      location,
1408        //      unlockFnName,
1409        //      { /* forall */ },
1410        //      {
1411        //              // Copy the declaration of this.
1412        //              ast::deepCopy( this_decl ),
1413        //      },
1414        //      { /* returns */ },
1415        //      nullptr,
1416        //      0,
1417        //      ast::Linkage::Cforall,
1418        //      { /* attributes */ },
1419        //      ast::Function::Inline
1420        // );
1421
1422        // ast::IfStmt * outerLockIf = nullptr;
1423        // ast::IfStmt * outerUnlockIf = nullptr;
1424        // ast::IfStmt * lastLockIf = nullptr;
1425        // ast::IfStmt * lastUnlockIf = nullptr;
1426
1427        // //adds an if/elif clause for each lock to assign type from void ptr based on ptr address
1428        // for ( long unsigned int i = 0; i < args.size(); i++ ) {
1429        //      ast::VariableExpr * thisParam = new ast::VariableExpr( location, InitTweak::getParamThis( lock_decl ) );
1430        //      ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
1431        //              new ast::NameExpr( location, "?==?" ), {
1432        //                      thisParam,
1433        //                      new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
1434        //              }
1435        //      );
1436
1437        //      ast::IfStmt * currLockIf = new ast::IfStmt(
1438        //              location,
1439        //              ast::deepCopy( ifCond ),
1440        //              genVirtLockUnlockExpr( "lock", args.at(i), location, ast::deepCopy( thisParam ) )
1441        //      );
1442
1443        //      ast::IfStmt * currUnlockIf = new ast::IfStmt(
1444        //              location,
1445        //              ifCond,
1446        //              genVirtLockUnlockExpr( "unlock", args.at(i), location, ast::deepCopy( thisParam ) )
1447        //      );
1448
1449        //      if ( i == 0 ) {
1450        //              outerLockIf = currLockIf;
1451        //              outerUnlockIf = currUnlockIf;
1452        //      } else {
1453        //              // add ifstmt to else of previous stmt
1454        //              lastLockIf->else_ = currLockIf;
1455        //              lastUnlockIf->else_ = currUnlockIf;
1456        //      }
1457
1458        //      lastLockIf = currLockIf;
1459        //      lastUnlockIf = currUnlockIf;
1460        // }
1461
1462        // // add pointer typing if/elifs to body of routines
1463        // lock_decl->stmts = new ast::CompoundStmt( location, { outerLockIf } );
1464        // unlock_decl->stmts = new ast::CompoundStmt( location, { outerUnlockIf } );
1465
1466        // // add routines to scope
1467        // declsToAddBefore.push_back( lock_decl );
1468        // declsToAddBefore.push_back( unlock_decl );
1469
1470        // newBody->push_front(new ast::DeclStmt( location, lock_decl ));
1471        // newBody->push_front(new ast::DeclStmt( location, unlock_decl ));
1472
1473        return newBody;
1474}
1475
1476ast::CompoundStmt * MutexKeyword::addThreadDtorStatements(
1477                const ast::FunctionDecl*, const ast::CompoundStmt * body,
1478                const std::vector<const ast::DeclWithType * > & args ) {
1479        assert( args.size() == 1 );
1480        const ast::DeclWithType * arg = args.front();
1481        const ast::Type * argType = arg->get_type();
1482        assert( argType->is_mutex() );
1483
1484        ast::CompoundStmt * mutBody = ast::mutate( body );
1485
1486        // The code is generated near the front of the body.
1487        const CodeLocation & location = mutBody->location;
1488
1489        // thread_dtor_guard_t __guard = { this, intptr( 0 ) };
1490        mutBody->push_front( new ast::DeclStmt(
1491                location,
1492                new ast::ObjectDecl(
1493                        location,
1494                        "__guard",
1495                        new ast::StructInstType( thread_guard_decl ),
1496                        new ast::ListInit(
1497                                location,
1498                                {
1499                                        new ast::SingleInit( location,
1500                                                new ast::CastExpr( location,
1501                                                        new ast::VariableExpr( location, arg ), argType ) ),
1502                                        new ast::SingleInit(
1503                                                location,
1504                                                new ast::UntypedExpr(
1505                                                        location,
1506                                                        new ast::NameExpr( location, "intptr" ), {
1507                                                                ast::ConstantExpr::from_int( location, 0 ),
1508                                                        }
1509                                                ) ),
1510                                },
1511                                {},
1512                                ast::MaybeConstruct
1513                        )
1514                )
1515        ));
1516
1517        return mutBody;
1518}
1519
1520ast::ptr<ast::Type> MutexKeyword::generic_func =
1521        new ast::FunctionType( ast::FixedArgs );
1522
1523// --------------------------------------------------------------------------
1524struct ThreadStarter final {
1525        void previsit( const ast::StructDecl * decl );
1526        const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
1527
1528private:
1529        bool thread_ctor_seen = false;
1530        const ast::StructDecl * thread_decl = nullptr;
1531};
1532
1533void ThreadStarter::previsit( const ast::StructDecl * decl ) {
1534        if ( decl->body && decl->name == "thread$" ) {
1535                assert( !thread_decl );
1536                thread_decl = decl;
1537        }
1538}
1539
1540const ast::FunctionDecl * ThreadStarter::postvisit( const ast::FunctionDecl * decl ) {
1541        if ( !CodeGen::isConstructor( decl->name ) ) return decl;
1542
1543        // Seach for the thread constructor.
1544        // (Are the "prefixes" of these to blocks the same?)
1545        const ast::Type * typeof_this = InitTweak::getTypeofThis( decl->type );
1546        auto ctored_type = dynamic_cast<const ast::StructInstType *>( typeof_this );
1547        if ( ctored_type && ctored_type->base == thread_decl ) {
1548                thread_ctor_seen = true;
1549        }
1550
1551        // Modify this declaration, the extra checks to see if we will are first.
1552        const ast::ptr<ast::DeclWithType> & param = decl->params.front();
1553        auto type = dynamic_cast<const ast::StructInstType *>(
1554                ast::getPointerBase( param->get_type() ) );
1555        if ( nullptr == type ) return decl;
1556        if ( !type->base->is_thread() ) return decl;
1557        if ( !thread_decl || !thread_ctor_seen ) {
1558                SemanticError( type->base->location, "thread keyword requires threads to be in scope, add #include <thread.hfa>." );
1559        }
1560        const ast::CompoundStmt * stmt = decl->stmts;
1561        if ( nullptr == stmt ) return decl;
1562
1563        // Now do the actual modification:
1564        ast::CompoundStmt * mutStmt = ast::mutate( stmt );
1565        const CodeLocation & location = mutStmt->location;
1566        mutStmt->push_back(
1567                new ast::ExprStmt(
1568                        location,
1569                        new ast::UntypedExpr(
1570                                location,
1571                                new ast::NameExpr( location, "__thrd_start" ),
1572                                {
1573                                        new ast::VariableExpr( location, param ),
1574                                        new ast::NameExpr( location, "main" ),
1575                                }
1576                        )
1577                )
1578        );
1579
1580        return ast::mutate_field( decl, &ast::FunctionDecl::stmts, mutStmt );
1581}
1582
1583} // namespace
1584
1585// --------------------------------------------------------------------------
1586// Interface Functions:
1587
1588void implementKeywords( ast::TranslationUnit & translationUnit ) {
1589        ast::Pass<ThreadKeyword>::run( translationUnit );
1590        ast::Pass<CoroutineKeyword>::run( translationUnit );
1591        ast::Pass<MonitorKeyword>::run( translationUnit );
1592        ast::Pass<GeneratorKeyword>::run( translationUnit );
1593        ast::Pass<SuspendKeyword>::run( translationUnit );
1594}
1595
1596void implementMutex( ast::TranslationUnit & translationUnit ) {
1597        ast::Pass<MutexKeyword>::run( translationUnit );
1598}
1599
1600void implementThreadStarter( ast::TranslationUnit & translationUnit ) {
1601        ast::Pass<ThreadStarter>::run( translationUnit );
1602}
1603
1604}
1605
1606// Local Variables: //
1607// tab-width: 4 //
1608// mode: c++ //
1609// compile-command: "make install" //
1610// End: //
Note: See TracBrowser for help on using the repository browser.