#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
namespace goose::builtins
{
void SetupReturnStmt( Env& e )
{
auto handleReturn = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto cfg = GetCFG( p.context() );
if( p.isInParenExpr() || !cfg )
{
dm.emitSyntaxErrorMessage( locationId, "the return statement is not allowed here.", 0 );
return false;
}
if( !cfg->currentBB() || cfg->currentBB()->terminator() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "unreachable code.", 0 );
PoisonBuilder( p.context() );
}
p.flushValue();
// Emit cleanups (destructor calls) for all currently live values in the function.
DestroyAllLiveValues( p.context() );
const auto& context = p.context();
if( context.returnType() == GetValueType< void >() )
{
cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret() );
return true;
}
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
{
dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
return false;
}
auto retVal = np.popValue();
if( !retVal )
{
dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
return false;
}
auto converted = ConvertValueToType( context, *retVal, *context.returnType() );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
case ValUnifyError::NoSolution:
dm.emitErrorMessage( retVal->locationId(), "return value type mismatch." );
break;
case ValUnifyError::Ambiguous:
dm.emitErrorMessage( retVal->locationId(), "ambiguous return value conversion." );
break;
}
// Emit a terminator with a poison value to avoid the function compilation
// code to complain about a missing return.
cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
PoisonBuilder( p.context() );
return true;
}
cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( get< Value >( converted ) ) );
return true;
};
RegisterRule( e, "return"_sid, Rule( handleReturn ) );
}
}