#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