Changeset 4f6dda0 for src/Concurrency


Ignore:
Timestamp:
Mar 11, 2022, 11:08:19 AM (3 years ago)
Author:
Andrew Beach <ajbeach@…>
Branches:
ADT, ast-experimental, enum, master, pthread-emulation, qualifiedEnum
Children:
630c4bb
Parents:
b053083
Message:

Converted Implement Concurrent Keywords to the new AST. Includes updates to various helpers, including the virtual table and a lot of examine helpers.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Concurrency/KeywordsNew.cpp

    rb053083 r4f6dda0  
    1010// Created On       : Tue Nov 16  9:53:00 2021
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Dec  1 11:24:00 2021
    13 // Update Count     : 1
     12// Last Modified On : Fri Mar 11 10:40:00 2022
     13// Update Count     : 2
    1414//
    1515
     
    1818#include "AST/Copy.hpp"
    1919#include "AST/Decl.hpp"
     20#include "AST/Expr.hpp"
    2021#include "AST/Pass.hpp"
    2122#include "AST/Stmt.hpp"
     23#include "AST/DeclReplacer.hpp"
    2224#include "AST/TranslationUnit.hpp"
    2325#include "CodeGen/OperatorTable.h"
     26#include "Common/Examine.h"
    2427#include "Common/utility.h"
     28#include "ControlStruct/LabelGeneratorNew.hpp"
    2529#include "InitTweak/InitTweak.h"
     30#include "Virtual/Tables.h"
    2631
    2732namespace Concurrency {
     
    2934namespace {
    3035
    31 inline static bool isThread( const ast::DeclWithType * decl ) {
     36// --------------------------------------------------------------------------
     37// Loose Helper Functions:
     38
     39/// Detect threads constructed with the keyword thread.
     40bool isThread( const ast::DeclWithType * decl ) {
    3241        auto baseType = decl->get_type()->stripDeclarator();
    3342        auto instType = dynamic_cast<const ast::StructInstType *>( baseType );
    3443        if ( nullptr == instType ) { return false; }
    3544        return instType->base->is_thread();
     45}
     46
     47/// Get the virtual type id if given a type name.
     48std::string typeIdType( std::string const & exception_name ) {
     49        return exception_name.empty() ? std::string()
     50                : Virtual::typeIdType( exception_name );
     51}
     52
     53/// Get the vtable type name if given a type name.
     54std::string vtableTypeName( std::string const & exception_name ) {
     55        return exception_name.empty() ? std::string()
     56                : Virtual::vtableTypeName( exception_name );
     57}
     58
     59static ast::Type * mutate_under_references( ast::ptr<ast::Type>& type ) {
     60        ast::Type * mutType = type.get_and_mutate();
     61        for ( ast::ReferenceType * mutRef
     62                ; (mutRef = dynamic_cast<ast::ReferenceType *>( mutType ))
     63                ; mutType = mutRef->base.get_and_mutate() );
     64        return mutType;
     65}
     66
     67// Describe that it adds the generic parameters and the uses of the generic
     68// parameters on the function and first "this" argument.
     69ast::FunctionDecl * fixupGenerics(
     70                const ast::FunctionDecl * func, const ast::StructDecl * decl ) {
     71        const CodeLocation & location = decl->location;
     72        // We have to update both the declaration
     73        auto mutFunc = ast::mutate( func );
     74        auto mutType = mutFunc->type.get_and_mutate();
     75
     76        if ( decl->params.empty() ) {
     77                return mutFunc;
     78        }
     79
     80        assert( 0 != mutFunc->params.size() );
     81        assert( 0 != mutType->params.size() );
     82
     83        // Add the "forall" clause information.
     84        for ( const ast::ptr<ast::TypeDecl> & typeParam : decl->params ) {
     85                auto typeDecl = ast::deepCopy( typeParam );
     86                mutFunc->type_params.push_back( typeDecl );
     87                mutType->forall.push_back(
     88                        new ast::TypeInstType( typeDecl->name, typeDecl ) );
     89                for ( auto & assertion : typeDecl->assertions ) {
     90                        mutFunc->assertions.push_back( assertion );
     91                        mutType->assertions.emplace_back(
     92                                new ast::VariableExpr( location, assertion ) );
     93                }
     94                typeDecl->assertions.clear();
     95        }
     96
     97        // Even chain_mutate is not powerful enough for this:
     98        ast::ptr<ast::Type>& paramType = strict_dynamic_cast<ast::ObjectDecl *>(
     99                mutFunc->params[0].get_and_mutate() )->type;
     100        auto paramTypeInst = strict_dynamic_cast<ast::StructInstType *>(
     101                mutate_under_references( paramType ) );
     102        auto typeParamInst = strict_dynamic_cast<ast::StructInstType *>(
     103                mutate_under_references( mutType->params[0] ) );
     104
     105        for ( const ast::ptr<ast::TypeDecl> & typeDecl : mutFunc->type_params ) {
     106                paramTypeInst->params.push_back(
     107                        new ast::TypeExpr( location,
     108                                new ast::TypeInstType( typeDecl->name, typeDecl ) ) );
     109                typeParamInst->params.push_back(
     110                        new ast::TypeExpr( location,
     111                                new ast::TypeInstType( typeDecl->name, typeDecl ) ) );
     112        }
     113
     114        return mutFunc;
     115}
     116
     117// --------------------------------------------------------------------------
     118struct ConcurrentSueKeyword : public ast::WithDeclsToAdd<> {
     119        ConcurrentSueKeyword(
     120                std::string&& type_name, std::string&& field_name,
     121                std::string&& getter_name, std::string&& context_error,
     122                std::string&& exception_name,
     123                bool needs_main, ast::AggregateDecl::Aggregate cast_target
     124        ) :
     125                type_name( type_name ), field_name( field_name ),
     126                getter_name( getter_name ), context_error( context_error ),
     127                exception_name( exception_name ),
     128                typeid_name( typeIdType( exception_name ) ),
     129                vtable_name( vtableTypeName( exception_name ) ),
     130                needs_main( needs_main ), cast_target( cast_target )
     131        {}
     132
     133        virtual ~ConcurrentSueKeyword() {}
     134
     135        const ast::Decl * postvisit( const ast::StructDecl * decl );
     136        const ast::DeclWithType * postvisit( const ast::FunctionDecl * decl );
     137        const ast::Expr * postvisit( const ast::KeywordCastExpr * expr );
     138
     139        struct StructAndField {
     140                const ast::StructDecl * decl;
     141                const ast::ObjectDecl * field;
     142        };
     143
     144        const ast::StructDecl * handleStruct( const ast::StructDecl * );
     145        void handleMain( const ast::FunctionDecl *, const ast::StructInstType * );
     146        void addTypeId( const ast::StructDecl * );
     147        void addVtableForward( const ast::StructDecl * );
     148        const ast::FunctionDecl * forwardDeclare( const ast::StructDecl * );
     149        StructAndField addField( const ast::StructDecl * );
     150        void addGetRoutines( const ast::ObjectDecl *, const ast::FunctionDecl * );
     151        void addLockUnlockRoutines( const ast::StructDecl * );
     152
     153private:
     154        const std::string type_name;
     155        const std::string field_name;
     156        const std::string getter_name;
     157        const std::string context_error;
     158        const std::string exception_name;
     159        const std::string typeid_name;
     160        const std::string vtable_name;
     161        const bool needs_main;
     162        const ast::AggregateDecl::Aggregate cast_target;
     163
     164        const ast::StructDecl   * type_decl = nullptr;
     165        const ast::FunctionDecl * dtor_decl = nullptr;
     166        const ast::StructDecl * except_decl = nullptr;
     167        const ast::StructDecl * typeid_decl = nullptr;
     168        const ast::StructDecl * vtable_decl = nullptr;
     169};
     170
     171// Handles thread type declarations:
     172//
     173// thread Mythread {                         struct MyThread {
     174//  int data;                                  int data;
     175//  a_struct_t more_data;                      a_struct_t more_data;
     176//                                =>             thread$ __thrd_d;
     177// };                                        };
     178//                                           static inline thread$ * get_thread( MyThread * this ) { return &this->__thrd_d; }
     179//
     180struct ThreadKeyword final : public ConcurrentSueKeyword {
     181        ThreadKeyword() : ConcurrentSueKeyword(
     182                "thread$",
     183                "__thrd",
     184                "get_thread",
     185                "thread keyword requires threads to be in scope, add #include <thread.hfa>\n",
     186                "ThreadCancelled",
     187                true,
     188                ast::AggregateDecl::Thread )
     189        {}
     190
     191        virtual ~ThreadKeyword() {}
     192};
     193
     194// Handles coroutine type declarations:
     195//
     196// coroutine MyCoroutine {                   struct MyCoroutine {
     197//  int data;                                  int data;
     198//  a_struct_t more_data;                      a_struct_t more_data;
     199//                                =>             coroutine$ __cor_d;
     200// };                                        };
     201//                                           static inline coroutine$ * get_coroutine( MyCoroutine * this ) { return &this->__cor_d; }
     202//
     203struct CoroutineKeyword final : public ConcurrentSueKeyword {
     204        CoroutineKeyword() : ConcurrentSueKeyword(
     205                "coroutine$",
     206                "__cor",
     207                "get_coroutine",
     208                "coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n",
     209                "CoroutineCancelled",
     210                true,
     211                ast::AggregateDecl::Coroutine )
     212        {}
     213
     214        virtual ~CoroutineKeyword() {}
     215};
     216
     217// Handles monitor type declarations:
     218//
     219// monitor MyMonitor {                       struct MyMonitor {
     220//  int data;                                  int data;
     221//  a_struct_t more_data;                      a_struct_t more_data;
     222//                                =>             monitor$ __mon_d;
     223// };                                        };
     224//                                           static inline monitor$ * get_coroutine( MyMonitor * this ) {
     225//                                               return &this->__cor_d;
     226//                                           }
     227//                                           void lock(MyMonitor & this) {
     228//                                               lock(get_monitor(this));
     229//                                           }
     230//                                           void unlock(MyMonitor & this) {
     231//                                               unlock(get_monitor(this));
     232//                                           }
     233//
     234struct MonitorKeyword final : public ConcurrentSueKeyword {
     235        MonitorKeyword() : ConcurrentSueKeyword(
     236                "monitor$",
     237                "__mon",
     238                "get_monitor",
     239                "monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n",
     240                "",
     241                false,
     242                ast::AggregateDecl::Monitor )
     243        {}
     244
     245        virtual ~MonitorKeyword() {}
     246};
     247
     248// Handles generator type declarations:
     249//
     250// generator MyGenerator {                   struct MyGenerator {
     251//  int data;                                  int data;
     252//  a_struct_t more_data;                      a_struct_t more_data;
     253//                                =>             int __generator_state;
     254// };                                        };
     255//
     256struct GeneratorKeyword final : public ConcurrentSueKeyword {
     257        GeneratorKeyword() : ConcurrentSueKeyword(
     258                "generator$",
     259                "__generator_state",
     260                "get_generator",
     261                "Unable to find builtin type generator$\n",
     262                "",
     263                true,
     264                ast::AggregateDecl::Generator )
     265        {}
     266
     267        virtual ~GeneratorKeyword() {}
     268};
     269
     270const ast::Decl * ConcurrentSueKeyword::postvisit(
     271                const ast::StructDecl * decl ) {
     272        if ( !decl->body ) {
     273                return decl;
     274        } else if ( cast_target == decl->kind ) {
     275                return handleStruct( decl );
     276        } else if ( type_name == decl->name ) {
     277                assert( !type_decl );
     278                type_decl = decl;
     279        } else if ( exception_name == decl->name ) {
     280                assert( !except_decl );
     281                except_decl = decl;
     282        } else if ( typeid_name == decl->name ) {
     283                assert( !typeid_decl );
     284                typeid_decl = decl;
     285        } else if ( vtable_name == decl->name ) {
     286                assert( !vtable_decl );
     287                vtable_decl = decl;
     288        }
     289        return decl;
     290}
     291
     292// Try to get the full definition, but raise an error on conflicts.
     293const ast::FunctionDecl * getDefinition(
     294                const ast::FunctionDecl * old_decl,
     295                const ast::FunctionDecl * new_decl ) {
     296        if ( !new_decl->stmts ) {
     297                return old_decl;
     298        } else if ( !old_decl->stmts ) {
     299                return new_decl;
     300        } else {
     301                assert( !old_decl->stmts || !new_decl->stmts );
     302                return nullptr;
     303        }
     304}
     305
     306const ast::DeclWithType * ConcurrentSueKeyword::postvisit(
     307                const ast::FunctionDecl * decl ) {
     308        if ( type_decl && isDestructorFor( decl, type_decl ) ) {
     309                // Check for forward declarations, try to get the full definition.
     310                dtor_decl = (dtor_decl) ? getDefinition( dtor_decl, decl ) : decl;
     311        } else if ( !vtable_name.empty() && decl->has_body() ) {
     312                if (const ast::DeclWithType * param = isMainFor( decl, cast_target )) {
     313                        if ( !vtable_decl ) {
     314                                SemanticError( decl, context_error );
     315                        }
     316                        // Should be safe because of isMainFor.
     317                        const ast::StructInstType * struct_type =
     318                                static_cast<const ast::StructInstType *>(
     319                                        static_cast<const ast::ReferenceType *>(
     320                                                param->get_type() )->base.get() );
     321
     322                        handleMain( decl, struct_type );
     323                }
     324        }
     325        return decl;
     326}
     327
     328const ast::Expr * ConcurrentSueKeyword::postvisit(
     329                const ast::KeywordCastExpr * expr ) {
     330        if ( cast_target == expr->target ) {
     331                // Convert `(thread &)ex` to `(thread$ &)*get_thread(ex)`, etc.
     332                if ( !type_decl || !dtor_decl ) {
     333                        SemanticError( expr, context_error );
     334                }
     335                assert( nullptr == expr->result );
     336                auto cast = ast::mutate( expr );
     337                cast->result = new ast::ReferenceType( new ast::StructInstType( type_decl ) );
     338                cast->concrete_target.field  = field_name;
     339                cast->concrete_target.getter = getter_name;
     340                return cast;
     341        }
     342        return expr;
     343}
     344
     345const ast::StructDecl * ConcurrentSueKeyword::handleStruct(
     346                const ast::StructDecl * decl ) {
     347        assert( decl->body );
     348
     349        if ( !type_decl || !dtor_decl ) {
     350                SemanticError( decl, context_error );
     351        }
     352
     353        if ( !exception_name.empty() ) {
     354                if( !typeid_decl || !vtable_decl ) {
     355                        SemanticError( decl, context_error );
     356                }
     357                addTypeId( decl );
     358                addVtableForward( decl );
     359        }
     360
     361        const ast::FunctionDecl * func = forwardDeclare( decl );
     362        StructAndField addFieldRet = addField( decl );
     363        decl = addFieldRet.decl;
     364        const ast::ObjectDecl * field = addFieldRet.field;
     365
     366        addGetRoutines( field, func );
     367        // Add routines to monitors for use by mutex stmt.
     368        if ( ast::AggregateDecl::Monitor == cast_target ) {
     369                addLockUnlockRoutines( decl );
     370        }
     371
     372        return decl;
     373}
     374
     375void ConcurrentSueKeyword::handleMain(
     376                const ast::FunctionDecl * decl, const ast::StructInstType * type ) {
     377        assert( vtable_decl );
     378        assert( except_decl );
     379
     380        const CodeLocation & location = decl->location;
     381
     382        std::vector<ast::ptr<ast::Expr>> poly_args = {
     383                new ast::TypeExpr( location, type ),
     384        };
     385        ast::ObjectDecl * vtable_object = Virtual::makeVtableInstance(
     386                location,
     387                "_default_vtable_object_declaration",
     388                new ast::StructInstType( vtable_decl, copy( poly_args ) ),
     389                type,
     390                nullptr
     391        );
     392        declsToAddAfter.push_back( vtable_object );
     393        declsToAddAfter.push_back(
     394                new ast::ObjectDecl(
     395                        location,
     396                        Virtual::concurrentDefaultVTableName(),
     397                        new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
     398                        new ast::SingleInit( location,
     399                                new ast::VariableExpr( location, vtable_object ) ),
     400                        ast::Storage::Classes(),
     401                        ast::Linkage::Cforall
     402                )
     403        );
     404        declsToAddAfter.push_back( Virtual::makeGetExceptionFunction(
     405                location,
     406                vtable_object,
     407                new ast::StructInstType( except_decl, copy( poly_args ) )
     408        ) );
     409}
     410
     411void ConcurrentSueKeyword::addTypeId( const ast::StructDecl * decl ) {
     412        assert( typeid_decl );
     413        const CodeLocation & location = decl->location;
     414
     415        ast::StructInstType * typeid_type =
     416                new ast::StructInstType( typeid_decl, ast::CV::Const );
     417        typeid_type->params.push_back(
     418                new ast::TypeExpr( location, new ast::StructInstType( decl ) ) );
     419        declsToAddBefore.push_back(
     420                Virtual::makeTypeIdInstance( location, typeid_type ) );
     421        // If the typeid_type is going to be kept, the other reference will have
     422        // been made by now, but we also get to avoid extra mutates.
     423        ast::ptr<ast::StructInstType> typeid_cleanup = typeid_type;
     424}
     425
     426void ConcurrentSueKeyword::addVtableForward( const ast::StructDecl * decl ) {
     427        assert( vtable_decl );
     428        const CodeLocation& location = decl->location;
     429
     430        std::vector<ast::ptr<ast::Expr>> poly_args = {
     431                new ast::TypeExpr( location, new ast::StructInstType( decl ) ),
     432        };
     433        declsToAddBefore.push_back( Virtual::makeGetExceptionForward(
     434                location,
     435                new ast::StructInstType( vtable_decl, copy( poly_args ) ),
     436                new ast::StructInstType( except_decl, copy( poly_args ) )
     437        ) );
     438        ast::ObjectDecl * vtable_object = Virtual::makeVtableForward(
     439                location,
     440                "_default_vtable_object_declaration",
     441                new ast::StructInstType( vtable_decl, std::move( poly_args ) )
     442        );
     443        declsToAddBefore.push_back( vtable_object );
     444        declsToAddBefore.push_back(
     445                new ast::ObjectDecl(
     446                        location,
     447                        Virtual::concurrentDefaultVTableName(),
     448                        new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
     449                        nullptr,
     450                        ast::Storage::Extern,
     451                        ast::Linkage::Cforall
     452                )
     453        );
     454}
     455
     456const ast::FunctionDecl * ConcurrentSueKeyword::forwardDeclare(
     457                const ast::StructDecl * decl ) {
     458        const CodeLocation & location = decl->location;
     459
     460        ast::StructDecl * forward = ast::deepCopy( decl );
     461        {
     462                // If removing members makes ref-count go to zero, do not free.
     463                ast::ptr<ast::StructDecl> forward_ptr = forward;
     464                forward->body = false;
     465                forward->members.clear();
     466                forward_ptr.release();
     467        }
     468
     469        ast::ObjectDecl * this_decl = new ast::ObjectDecl(
     470                location,
     471                "this",
     472                new ast::ReferenceType( new ast::StructInstType( decl ) ),
     473                nullptr,
     474                ast::Storage::Classes(),
     475                ast::Linkage::Cforall
     476        );
     477
     478        ast::ObjectDecl * ret_decl = new ast::ObjectDecl(
     479                location,
     480                "ret",
     481                new ast::PointerType( new ast::StructInstType( type_decl ) ),
     482                nullptr,
     483                ast::Storage::Classes(),
     484                ast::Linkage::Cforall
     485        );
     486
     487        ast::FunctionDecl * get_decl = new ast::FunctionDecl(
     488                location,
     489                getter_name,
     490                {}, // forall
     491                { this_decl }, // params
     492                { ret_decl }, // returns
     493                nullptr, // stmts
     494                ast::Storage::Static,
     495                ast::Linkage::Cforall,
     496                { new ast::Attribute( "const" ) },
     497                ast::Function::Inline
     498        );
     499        get_decl = fixupGenerics( get_decl, decl );
     500
     501        ast::FunctionDecl * main_decl = nullptr;
     502        if ( needs_main ) {
     503                // `this_decl` is copied here because the original was used above.
     504                main_decl = new ast::FunctionDecl(
     505                        location,
     506                        "main",
     507                        {},
     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                nullptr,
     533                ast::Storage::Classes(),
     534                ast::Linkage::Cforall
     535        );
     536
     537        auto mutDecl = ast::mutate( decl );
     538        mutDecl->members.push_back( field );
     539
     540        return {mutDecl, field};
     541}
     542
     543void ConcurrentSueKeyword::addGetRoutines(
     544                const ast::ObjectDecl * field, const ast::FunctionDecl * forward ) {
     545        // Say it is generated at the "same" places as the forward declaration.
     546        const CodeLocation & location = forward->location;
     547
     548        const ast::DeclWithType * param = forward->params.front();
     549        ast::Stmt * stmt = new ast::ReturnStmt( location,
     550                new ast::AddressExpr( location,
     551                        new ast::MemberExpr( location,
     552                                field,
     553                                new ast::CastExpr( location,
     554                                        new ast::VariableExpr( location, param ),
     555                                        ast::deepCopy( param->get_type()->stripReferences() ),
     556                                        ast::ExplicitCast
     557                                )
     558                        )
     559                )
     560        );
     561
     562        ast::FunctionDecl * decl = ast::deepCopy( forward );
     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                nullptr,
     580                ast::Storage::Classes(),
     581                ast::Linkage::Cforall
     582        );
     583
     584        ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
     585                location,
     586                "lock",
     587                { /* forall */ },
     588                {
     589                        // Copy the declaration of this.
     590                        ast::deepCopy( this_decl ),
     591                },
     592                { /* returns */ },
     593                nullptr,
     594                ast::Storage::Static,
     595                ast::Linkage::Cforall,
     596                { /* attributes */ },
     597                ast::Function::Inline
     598        );
     599        lock_decl = fixupGenerics( lock_decl, decl );
     600
     601        lock_decl->stmts = new ast::CompoundStmt( location, {
     602                new ast::ExprStmt( location,
     603                        new ast::UntypedExpr( location,
     604                                new ast::NameExpr( location, "lock" ),
     605                                {
     606                                        new ast::UntypedExpr( location,
     607                                                new ast::NameExpr( location, "get_monitor" ),
     608                                                { new ast::VariableExpr( location,
     609                                                        InitTweak::getParamThis( lock_decl ) ) }
     610                                        )
     611                                }
     612                        )
     613                )
     614        } );
     615
     616        ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
     617                location,
     618                "unlock",
     619                { /* forall */ },
     620                {
     621                        // Last use, consume the declaration of this.
     622                        this_decl,
     623                },
     624                { /* returns */ },
     625                nullptr,
     626                ast::Storage::Static,
     627                ast::Linkage::Cforall,
     628                { /* attributes */ },
     629                ast::Function::Inline
     630        );
     631        unlock_decl = fixupGenerics( unlock_decl, decl );
     632
     633        unlock_decl->stmts = new ast::CompoundStmt( location, {
     634                new ast::ExprStmt( location,
     635                        new ast::UntypedExpr( location,
     636                                new ast::NameExpr( location, "unlock" ),
     637                                {
     638                                        new ast::UntypedExpr( location,
     639                                                new ast::NameExpr( location, "get_monitor" ),
     640                                                { new ast::VariableExpr( location,
     641                                                        InitTweak::getParamThis( unlock_decl ) ) }
     642                                        )
     643                                }
     644                        )
     645                )
     646        } );
     647
     648        declsToAddAfter.push_back( lock_decl );
     649        declsToAddAfter.push_back( unlock_decl );
     650}
     651
     652
     653// --------------------------------------------------------------------------
     654struct SuspendKeyword final :
     655                public ast::WithStmtsToAdd<>, public ast::WithGuards {
     656        SuspendKeyword() = default;
     657        virtual ~SuspendKeyword() = default;
     658
     659        void previsit( const ast::FunctionDecl * );
     660        const ast::DeclWithType * postvisit( const ast::FunctionDecl * );
     661        const ast::Stmt * postvisit( const ast::SuspendStmt * );
     662
     663private:
     664        bool is_real_suspend( const ast::FunctionDecl * );
     665
     666        const ast::Stmt * make_generator_suspend( const ast::SuspendStmt * );
     667        const ast::Stmt * make_coroutine_suspend( const ast::SuspendStmt * );
     668
     669        struct LabelPair {
     670                ast::Label obj;
     671                int idx;
     672        };
     673
     674        LabelPair make_label(const ast::Stmt * stmt ) {
     675                labels.push_back( ControlStruct::newLabel( "generator", stmt ) );
     676                return { labels.back(), int(labels.size()) };
     677        }
     678
     679        const ast::DeclWithType * in_generator = nullptr;
     680        const ast::FunctionDecl * decl_suspend = nullptr;
     681        std::vector<ast::Label> labels;
     682};
     683
     684void SuspendKeyword::previsit( const ast::FunctionDecl * decl ) {
     685        GuardValue( in_generator ); in_generator = nullptr;
     686
     687        // If it is the real suspend, grab it if we don't have one already.
     688        if ( is_real_suspend( decl ) ) {
     689                decl_suspend = decl_suspend ? decl_suspend : decl;
     690                return;
     691        }
     692
     693        // Otherwise check if this is a generator main and, if so, handle it.
     694        auto param = isMainFor( decl, ast::AggregateDecl::Generator );
     695        if ( !param ) return;
     696
     697        if ( 0 != decl->returns.size() ) {
     698                SemanticError( decl->location, "Generator main must return void" );
     699        }
     700
     701        in_generator = param;
     702        GuardValue( labels ); labels.clear();
     703}
     704
     705const ast::DeclWithType * SuspendKeyword::postvisit(
     706                const ast::FunctionDecl * decl ) {
     707        // Only modify a full definition of a generator with states.
     708        if ( !decl->stmts || !in_generator || labels.empty() ) return decl;
     709
     710        const CodeLocation & location = decl->location;
     711
     712        // Create a new function body:
     713        // static void * __generator_labels[] = {&&s0, &&s1, ...};
     714        // void * __generator_label = __generator_labels[GEN.__generator_state];
     715        // goto * __generator_label;
     716        // s0: ;
     717        // OLD_BODY
     718
     719        // This is the null statement inserted right before the body.
     720        ast::NullStmt * noop = new ast::NullStmt( location );
     721        noop->labels.push_back( ControlStruct::newLabel( "generator", noop ) );
     722        const ast::Label & first_label = noop->labels.back();
     723
     724        // Add each label to the init, starting with the first label.
     725        std::vector<ast::ptr<ast::Init>> inits = {
     726                new ast::SingleInit( location,
     727                        new ast::LabelAddressExpr( location, copy( first_label ) ) ) };
     728        // Then go through all the stored labels, and clear the store.
     729        for ( auto && label : labels ) {
     730                inits.push_back( new ast::SingleInit( label.location,
     731                        new ast::LabelAddressExpr( label.location, std::move( label )
     732                        ) ) );
     733        }
     734        labels.clear();
     735        // Then construct the initializer itself.
     736        auto init = new ast::ListInit( location, std::move( inits ) );
     737
     738        ast::ObjectDecl * generatorLabels = new ast::ObjectDecl(
     739                location,
     740                "__generator_labels",
     741                new ast::ArrayType(
     742                        new ast::PointerType( new ast::VoidType() ),
     743                        nullptr,
     744                        ast::FixedLen,
     745                        ast::DynamicDim
     746                ),
     747                init,
     748                ast::Storage::Classes(),
     749                ast::Linkage::AutoGen
     750        );
     751
     752        ast::ObjectDecl * generatorLabel = new ast::ObjectDecl(
     753                location,
     754                "__generator_label",
     755                new ast::PointerType( new ast::VoidType() ),
     756                new ast::SingleInit( location,
     757                        new ast::UntypedExpr( location,
     758                                new ast::NameExpr( location, "?[?]" ),
     759                                {
     760                                        // TODO: Could be a variable expr.
     761                                        new ast::NameExpr( location, "__generator_labels" ),
     762                                        new ast::UntypedMemberExpr( location,
     763                                                new ast::NameExpr( location, "__generator_state" ),
     764                                                new ast::VariableExpr( location, in_generator )
     765                                        )
     766                                }
     767                        )
     768                ),
     769                ast::Storage::Classes(),
     770                ast::Linkage::AutoGen
     771        );
     772
     773        ast::BranchStmt * theGoTo = new ast::BranchStmt(
     774                location, new ast::VariableExpr( location, generatorLabel )
     775        );
     776
     777        // The noop goes here in order.
     778
     779        ast::CompoundStmt * body = new ast::CompoundStmt( location, {
     780                { new ast::DeclStmt( location, generatorLabels ) },
     781                { new ast::DeclStmt( location, generatorLabel ) },
     782                { theGoTo },
     783                { noop },
     784                { decl->stmts },
     785        } );
     786
     787        auto mutDecl = ast::mutate( decl );
     788        mutDecl->stmts = body;
     789        return mutDecl;
     790}
     791
     792const ast::Stmt * SuspendKeyword::postvisit( const ast::SuspendStmt * stmt ) {
     793        switch ( stmt->type ) {
     794        case ast::SuspendStmt::None:
     795                // Use the context to determain the implicit target.
     796                if ( in_generator ) {
     797                        return make_generator_suspend( stmt );
     798                } else {
     799                        return make_coroutine_suspend( stmt );
     800                }
     801        case ast::SuspendStmt::Coroutine:
     802                return make_coroutine_suspend( stmt );
     803        case ast::SuspendStmt::Generator:
     804                // Generator suspends must be directly in a generator.
     805                if ( !in_generator ) SemanticError( stmt->location, "'suspend generator' must be used inside main of generator type." );
     806                return make_generator_suspend( stmt );
     807        }
     808        assert( false );
     809        return stmt;
     810}
     811
     812/// Find the real/official suspend declaration.
     813bool SuspendKeyword::is_real_suspend( const ast::FunctionDecl * decl ) {
     814        return ( !decl->linkage.is_mangled
     815                && 0 == decl->params.size()
     816                && 0 == decl->returns.size()
     817                && "__cfactx_suspend" == decl->name );
     818}
     819
     820const ast::Stmt * SuspendKeyword::make_generator_suspend(
     821                const ast::SuspendStmt * stmt ) {
     822        assert( in_generator );
     823        // Target code is:
     824        //   GEN.__generator_state = X;
     825        //   THEN
     826        //   return;
     827        //   __gen_X:;
     828
     829        const CodeLocation & location = stmt->location;
     830
     831        LabelPair label = make_label( stmt );
     832
     833        // This is the context saving statement.
     834        stmtsToAddBefore.push_back( new ast::ExprStmt( location,
     835                new ast::UntypedExpr( location,
     836                        new ast::NameExpr( location, "?=?" ),
     837                        {
     838                                new ast::UntypedMemberExpr( location,
     839                                        new ast::NameExpr( location, "__generator_state" ),
     840                                        new ast::VariableExpr( location, in_generator )
     841                                ),
     842                                ast::ConstantExpr::from_int( location, label.idx ),
     843                        }
     844                )
     845        ) );
     846
     847        // The THEN component is conditional (return is not).
     848        if ( stmt->then ) {
     849                stmtsToAddBefore.push_back( stmt->then.get() );
     850        }
     851        stmtsToAddBefore.push_back( new ast::ReturnStmt( location, nullptr ) );
     852
     853        // The null statement replaces the old suspend statement.
     854        return new ast::NullStmt( location, { label.obj } );
     855}
     856
     857const ast::Stmt * SuspendKeyword::make_coroutine_suspend(
     858                const ast::SuspendStmt * stmt ) {
     859        // The only thing we need from the old statement is the location.
     860        const CodeLocation & location = stmt->location;
     861
     862        if ( !decl_suspend ) {
     863                SemanticError( location, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>\n" );
     864        }
     865        if ( stmt->then ) {
     866                SemanticError( location, "Compound statement following coroutines is not implemented." );
     867        }
     868
     869        return new ast::ExprStmt( location,
     870                new ast::UntypedExpr( location,
     871                        ast::VariableExpr::functionPointer( location, decl_suspend ) )
     872        );
    36873}
    37874
     
    2511088                                {
    2521089                                        new ast::SingleInit( location,
    253                                                 new ast::AddressExpr(
     1090                                                new ast::AddressExpr( location,
    2541091                                                        new ast::VariableExpr( location, monitor ) ) ),
    2551092                                        new ast::SingleInit( location,
     
    5641401
    5651402// --------------------------------------------------------------------------
     1403// Interface Functions:
    5661404
    5671405void implementKeywords( ast::TranslationUnit & translationUnit ) {
    568         (void)translationUnit;
    569         assertf(false, "Apply Keywords not implemented." );
     1406        ast::Pass<ThreadKeyword>::run( translationUnit );
     1407        ast::Pass<CoroutineKeyword>::run( translationUnit );
     1408        ast::Pass<MonitorKeyword>::run( translationUnit );
     1409        ast::Pass<GeneratorKeyword>::run( translationUnit );
     1410        ast::Pass<SuspendKeyword>::run( translationUnit );
    5701411}
    5711412
Note: See TracChangeset for help on using the changeset viewer.