#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
namespace goose::builtins
{
void SetupIfStmt( Env& e )
{
auto handleIf = []( Parser& p, uint32_t locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
const auto& cb = p.context().codeBuilder();
if( p.isInParenExpr() || !cb )
{
dm.emitSyntaxErrorMessage( locationId, "the if statement is not allowed here.", 0 );
return false;
}
const auto& cfg = cb->cfg();
auto pPrecBB = cfg->currentBB();
if( !pPrecBB || pPrecBB->terminator() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "unreachable code.", 0 );
cb->poison();
}
// Create a scope for the entire if, so that any var declared
// inside of the condition is alive and visible throughout the if.
Scope s( p );
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.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;
}
if( get< Value >( converted ).isPoison() )
cb->poison();
// The condition may have emitted additional basic blocks, so get the current block again.
pPrecBB = cfg->currentBB();
auto pThenBB = ParseSubStatement( p, precedence::IfStmt );
if( !pThenBB )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected a statement after the if condition.", 0 );
return false;
}
// Retrieve the successor block of the then branch, if any
auto pThenSuccBB = cfg->currentBB();
ptr< cir::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()->currentLocation(), "expected a statement after 'else'.", 0 );
return false;
}
// Retrieve the successor block of the else branch, if any
pElseSuccBB = cfg->currentBB();
}
}
if( !pPrecBB )
return true;
ptr< cir::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 = cb->cfg()->createBB();
pPrecBB->setTerminator( cir::CondBranch(
get< Value >( converted ),
pThenBB,
pElseBB ? pElseBB : pSuccBB ) );
if( pThenSuccBB && !pThenSuccBB->terminator() )
pThenSuccBB->setTerminator( cir::Branch( pSuccBB ) );
if( pElseSuccBB && !pElseSuccBB->terminator() )
pElseSuccBB->setTerminator( cir::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.
cfg->setCurrentBB( pSuccBB );
return true;
};
Rule r( handleIf );
auto ruleVal = ToValue( move( r ) );
auto ruleTerm = ValueToEIR( ruleVal );
e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( if ) ), ANYTERM( _ ), ruleTerm );
}
}