Goose  Artifact [71fcc79bf9]

Artifact 71fcc79bf9d20366726dd667283088a977c9d6da79922f39337b9322f9590e76:

  • File bs/builtins/builders/codebuilder.h — part of check-in [9a57f2204f] at 2023-07-15 19:25:56 on branch trunk — Fixed values not being destroyed when a lifetime scope ends (user: zlodo size: 5001)

#ifndef GOOSE_BUILTINS_BUILDER_CODEBUILDER_H
#define GOOSE_BUILTINS_BUILDER_CODEBUILDER_H

namespace goose::builtins
{
    class CodeBuilder
    {
        public:
            template< typename C >
            CodeBuilder( C&& cfg ) :
                m_cfg( forward< C >( cfg ) )
            {}

            const auto& cfg() const { return m_cfg; }

            void poison()
            {
                if( m_cfg )
                    m_cfg->poison();
            }

            void beginVisibilityScope( Context& c );

            void beginLifetimeScope() { ++m_lifetimeScopeLevels; }
            void endLifetimeScope( Context& c );

            void beginBreakableScope() { ++m_breakableScopeLevels; }
            void endBreakableScope() { --m_breakableScopeLevels; }

            void beginContinuableScope() { ++m_continuableScopeLevels; }
            void endContinuableScope() { --m_continuableScopeLevels; }

            uint32_t lifetimeScopeLevels() const { return m_lifetimeScopeLevels; }
            uint32_t breakableScopeLevels() const { return m_breakableScopeLevels; }
            uint32_t continuableScopeLevels() const { return m_continuableScopeLevels; }

            template< typename T >
            void declareValue( T&& v, uint32_t index )
            {
                m_liveValuesList.emplace_front(
                    forward< T >( v ), index,
                    m_lifetimeScopeLevels,
                    m_breakableScopeLevels,
                    m_continuableScopeLevels
                );
            }

            // Extend the life duration of a live value to the current lifetime scope.
            void extendValueLifetime( uint32_t index );

            // Destruction
            // Destroys an individual value by invoking _DestroyValue, appends any resulting cir
            // to the current basic block if necessary.
            bool destroyLiveValue( const Context& c, const Value& v,
                source_location sloc = source_location::current() );

            // Invoke the destroy extension point for every live variable, but leave them on the stack.
            // To be called before emitting a return terminator.
            bool destroyAllLiveValues( const Context& c );

            // Invoke the destroy extension point for every live variable that belong in the specified
            // break scope and every child scope, but leave them on the stack.
            // To be called before emitting a break terminator.
            bool destroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel );

            // Invoke the destroy extension point for every live variable that belong in the specified
            // continue scope and every child scope, but leave them on the stack.
            // To be called before emitting a continue terminator.
            bool destroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel );

            Value emitTypePredicatesCheck( const Context& c, const Value& val, const ptr< Propositions >& props );

        private:
            // Invoke the destroy extension point for every live variable from the current
            // lifetime scope, and pops off the stack. To be called when exiting a lifetime scope.
            // Returns false if something goes wrong, in which case the cfg will also be marked
            // as poisoned.
            // Note: it is called automatically by LifetimeScopeGuard.
            void destroyCurrentLifetimeScopeValues( const Context& c );

            ptr< cir::CFG > m_cfg;

            // The number of nested lifetime scopes.
            uint32_t m_lifetimeScopeLevels = 0;

            // The number of nested breakable scopes.
            uint32_t m_breakableScopeLevels = 0;

            // The number of nested continuable scopes.
            uint32_t m_continuableScopeLevels = 0;

            // The live values stack, used to track when
            // to destroy values (such as local variables going out of scope)
            struct LiveValue
            {
                LiveValue() {}

                template< typename T >
                LiveValue( T&& v, uint32_t index, uint32_t lt, uint32_t b, uint32_t c ) :
                    m_value( forward< T >( v ) ),
                    m_index( index ),
                    m_lifetimeScopeLevel( lt ),
                    m_breakableScopeLevel( b ),
                    m_continuableScopeLevel( c )
                {}

                Value m_value;
                uint32_t m_index = 0;

                // We keep track of those to determine which values to
                // destroy when exiting a scope, emitting a break terminator,
                // and emitting a continue terminator, respectively.
                uint32_t m_lifetimeScopeLevel = 0;
                uint32_t m_breakableScopeLevel = 0;
                uint32_t m_continuableScopeLevel = 0;
            };

            list< LiveValue > m_liveValuesList;
    };
}

#endif