Goose  tupass.cpp at [0db147f117]

File bs/builtins/operators/tupass.cpp artifact 4be035fb00 part of check-in 0db147f117


#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;

namespace goose::builtins
{
    Generator< Value > GenerateRHSValuesFromTupleRef(
        const ptr< OverloadSet >& assOp, const Context& c, const Value& tup )
    {
        auto cfg = GetCFG( c );
        if( !cfg )
            co_yield Value( PoisonValue() );

        auto tupRefType = *FromValue< ReferenceType >( *EIRToValue( tup.type() ) );

        // Store the source tuple as a temporary, otherwise we're going to recompute rhs
        // for each member assigned (which is bad)
        // TODO_SSA rewrite this (since CreateTemporary makes no sense anymore)
        co_return;
        /*    auto index = util::GenerateNewUID();
            cfg->currentBB()->append( tup, CreateTemporary( index, false, tup.locationId() ) );
            auto srcTup = BuildComputedValue( tupRefType.type(), GetTemporary( tupRefType.type(),
           index, tup.locationId() ) );

            auto anonVarDecl = ToValue( TNamedDecl( ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid
           ) ); co_yield GenerateValuesFromComputedTuple( srcTup );*/
    }

    // Assignment of a compile time tuple or tuple reference to another compile time tuple,
    // directly. Most tuple assignment will go through one of the other overload that takes a mutref
    // to a tuple on the lhs, but sometimes we have compilation time tuples of objects that can
    // directly be assigned such as decls or refs.
    //
    // So this overload is for all these cases, which encompass initializing multiple comma
    // separated decls from a tuple, as well as variable swapping/shuffling. To make sure that the
    // later works properly, we store all the rhs values into temporary variables first, before
    // assigning them to the lhs values.
    Value NonRefTupleAssignment(
        const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs )
    {
        auto tupSize = TupleSize( lhs );

        // Load the values from the rhs tuple into temporary vars.
        vector< Value > tempVars;
        tempVars.reserve( tupSize );

        Generator< Value > gen;
        if( rhs.isConstant() )
            gen = GenerateValuesFromConstantTuple( rhs );
        else
            gen = GenerateRHSValuesFromTupleRef( assOp, c, rhs );

        for( auto&& srcVal : gen )
        {
            // We go at very high level to construct the temporary variables by resolving an
            // invocation of the assignment of the rhs value to a TNamedDecl with an empty name. The
            // reason we need to work at such high level is so that the initializer value gets
            // resolved exactly like if it was a regular assignment. In particular, if the value in
            // question is a locvar, we want it to go through the type checking process to be
            // replaced with its content. This is the simplest and most robust way to achieve this,
            // which should honor every relevant extension point.
            auto tmpVar = DeclareLocalVarWithTypeInference(
                c, ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid, srcVal, srcVal.locationId() );

            if( tmpVar.isPoison() )
                return PoisonValue();

            tmpVar.setLocationId( srcVal.locationId() );
            tempVars.emplace_back( move( tmpVar ) );
        }

        if( tupSize != tempVars.size() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
            return PoisonValue();
        }

        auto result = EmptyOpenTuple();

        size_t i = 0;
        bool success = true;
        ForEachInTuple( lhs,
            [&]( auto&& destVal )
            {
                auto r = InvokeOverloadSet( c, assOp, MakeClosedTuple( destVal, tempVars[i++] ) );

                if( r.isPoison() )
                {
                    success = false;
                    return false;
                }

                result = AppendToTuple( result, move( r ) );
                return true;
            } );

        if( !success )
            return PoisonValue();

        return result;
    }

    Value MutRefTupleAssignment(
        const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs )
    {
        auto lhsRefType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
        auto lhsTupType = *EIRToValue( lhsRefType.type() );

        auto rhsTupTypeEIR = rhs.type();
        if( !rhs.isConstant() )
        {
            auto rhsRefType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
            rhsTupTypeEIR = rhsRefType.type();
        }

        auto rhsTupType = *EIRToValue( rhsTupTypeEIR );

        if( IsTrivialTupleAssignment( c, lhsTupType, rhsTupType ) )
        {
            auto cfg = GetCFG( c );
            if( !cfg )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    0, "assignments are not allowed here." );
                return PoisonValue();
            }
            else if( auto bb = cfg->currentBB() )
            {
                bb->append( rhs );
                if( !rhs.isConstant() )
                    bb->append( Load( rhsTupTypeEIR, rhs.locationId() ) );
                bb->append( lhs, Store( lhsRefType.type(), rhs.locationId(), lhs.locationId() ) );
            }
            return lhs;
        }

        auto result = EmptyOpenTuple();

        Generator< Value > gen;
        if( rhs.isConstant() )
            gen = GenerateValuesFromConstantTuple( rhs );
        else
            gen = GenerateRHSValuesFromTupleRef( assOp, c, rhs );

        uint32_t index = 0;
        bool success = true;
        ForEachInTupleType( lhsTupType,
            [&]( auto&& t )
            {
                if( gen.finished() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        0, "incompatible tuple sizes." );
                    success = false;
                    return false;
                }

                auto srcVal = gen.consume();
                auto rt = ValueToEIR( ToValue( ReferenceType( t, MutAccessSpecifier() ) ) );
                auto destVal =
                    BuildComputedValue( rt, lhs, cir::Select( index++, lhs.locationId() ) );

                auto r = InvokeOverloadSet( c, assOp, MakeClosedTuple( destVal, srcVal ) );

                success = !r.isPoison();
                return success;
            } );

        if( !success )
            return PoisonValue();

        if( !gen.finished() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
            return PoisonValue();
        }

        return lhs;
    }
} // namespace goose::builtins