Goose  Artifact [5d1180010b]

Artifact 5d1180010b91f457bfb97dd6f5a82cce92b48832ee3882ec5ea5bfa180fdda56:

  • File bs/builtins/statements/if.cpp — part of check-in [3d8b581261] at 2019-08-15 22:37:33 on branch trunk —
    • Fixed some bugs related to dropping values.
    • Implemented local variable declarations with default initialization.
    • codegen: Fixed allocas not properly grouped up at the start of the first basic block of functions.
    (user: achavasse size: 4725)

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

using namespace empathy;
using namespace empathy::ir;
using namespace empathy::parse;

namespace empathy::builtins
{
    void SetupIfStmt( Env& e )
    {
        auto handleIf = []( Parser& p, uint32_t locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            if( p.isInParenExpr() )
            {
                dm.emitSyntaxErrorMessage( locationId, "the if statement is not allowed here.", 0 );
                return false;
            }

            auto pPrecBB = p.currentBB();

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::IfStmt ) )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the if statement.", 0 );
                return false;
            }

            auto condVal = np.popValue();
            if( !condVal )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the if statement.", 0 );
                return false;
            }

            const auto& context = p.resolver()->context();
            auto converted = ConvertValueToType( context, *condVal, GetValueType< bool >() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    // If the condition is invalid, bail out and mark the current
                    // diagnostics context as bust to avoid spamming cascading errors.
                    case ValUnifyError::NoSolution:
                        dm.emitSyntaxErrorMessage( condVal->locationId(), "the if condition can't be converted to a bool." );
                        break;

                    case ValUnifyError::Ambiguous:
                        dm.emitSyntaxErrorMessage( condVal->locationId(), "ambiguous if condition bool conversion." );
                        break;
                }

                return false;
            }

            auto pThenBB = ParseSubStatement( p, precedence::IfStmt );
            if( !pThenBB )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->getCurrentLocation(), "expected a statement after the if condition.", 0 );
                return false;
            }

            // Retrieve the successor block of the then branch, if any
            auto pThenSuccBB = p.currentBB();

            ptr< llr::BasicBlock > pElseBB, pElseSuccBB;

            auto next = p.resolver()->lookAheadUnresolved();
            if( next )
            {
                const auto* nextSid = get_if< StringId >( &next->first );
                if( nextSid && *nextSid == "else"_sid )
                {
                    p.resolver()->consumeUnresolved();
                    pElseBB = ParseSubStatement( p, precedence::IfStmt );
                    if( !pElseBB )
                    {
                        dm.emitSyntaxErrorMessage( p.resolver()->getCurrentLocation(), "expected a statement after 'else'.", 0 );
                        return false;
                    }

                    // Retrieve the successor block of the else branch, if any
                    pElseSuccBB = p.currentBB();
                }
            }

            ptr< llr::BasicBlock > pSuccBB;

            // If both the then and the else blocks successors exist and are terminated,
            // we don't need a successor block.
            if( !pElseBB || ( pThenSuccBB && !pThenSuccBB->terminator() )
                || ( pElseSuccBB && !pElseSuccBB->terminator() ) )
                pSuccBB = p.cfg()->createBB();

            pPrecBB->setTerminator( llr::CondBranch(
                get< Value >( converted ),
                pThenBB,
                pElseBB ? pElseBB : pSuccBB ) );

            if( pThenSuccBB && !pThenSuccBB->terminator() )
                pThenSuccBB->setTerminator( llr::Branch( pSuccBB ) );

            if( pElseSuccBB && !pElseSuccBB->terminator() )
                pElseSuccBB->setTerminator( llr::Branch( pSuccBB ) );

            // Intentionally set the current BB to null if no
            // successor block was created: it means all our code paths
            // are already terminated and there is no point in emitting any more
            // instructions.
            p.setCurrentBB( pSuccBB );
            return true;
        };

        Rule r( handleIf );
        auto ruleVal = ToValue( move( r ) );
        auto ruleTerm = ValueToIRExpr( ruleVal );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( if ) ), ANYTERM( _ ), ruleTerm );
    }
}