#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::builtins;
using namespace goose::cir;
using namespace goose::parse;
namespace goose::builtins
{
const Term& LocalVar::PatternAny::GetPattern()
{
static auto pattern = GetValueType< LocalVar >( HOLE( "_"_sid ) );
return pattern;
}
const Term& LocalVar::PatternAnyOfTypeT::GetPattern()
{
static auto pattern = GetValueType< LocalVar >( HOLE( "T"_sid ) );
return pattern;
}
Value DeclareLocalVar( const Context& c, const Term& type, StringId name, const optional< Value >& initializer )
{
const auto& cb = c.codeBuilder();
assert( cb );
const auto& cfg = cb->cfg();
auto index = cfg->getNewTemporaryIndex();
auto bb = cfg->currentBB();
if( !bb )
return PoisonValue();
// If the type is a tuple of type, transform it into the type of a tuple.
// I'm hoping that I'm just a huge idiot and not seeing a more obvious and
// simpler way that would avoid this confusing distinction...
Value typeVal = *ValueFromEIR( type );
if( IsTuple( typeVal ) )
typeVal = TupleOfTypesToTupleType( *ValueFromEIR( type ) );
LocalVar lv( name, ValueToEIR( typeVal ), index );
bb->emplace_back( AllocVar( typeVal, index ) );
Value initResult;
{
DiagnosticsContext dc( 0, "When invoking Initialize." );
if( initializer )
{
initResult = InvokeOverloadSet( c,
c.env()->extInitialize(),
MakeTuple( ToValue( lv ), *initializer ) );
}
else
{
initResult = InvokeOverloadSet( c,
c.env()->extInitialize(),
MakeTuple( ToValue( lv ) ) );
}
}
if( !initResult.isPoison() )
{
DiagnosticsContext dc2( initResult.locationId(), "When invoking DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(),
MakeTuple( move( initResult ) ) );
}
auto locVar = ToValue( lv );
auto identity = AppendToVectorTerm( c.identity(), name );
c.env()->storeValue( identity, ANYTERM( _ ),
ValueToEIR( locVar ) );
cb->pushLiveValue( locVar, lv.index() );
return locVar;
}
Value DeclareLocalVarWithTypeInference( Context& c, const Term& typeTExpr, StringId name, const Value& initVal )
{
const auto& cb = c.codeBuilder();
assert( cb );
const auto& cfg = cb->cfg();
auto bb = cfg->currentBB();
if( !bb )
return PoisonValue();
// To infer the type and obtain a suitable initialization function,
// we want the declaration to behave as if it were the following function call:
// Params: ( $T, _ ( MutRef[$T], initValType ) initFunc )
// Args: ( pat, Initialize )
//
// Where pat is the TNamedDecl's type pattern and initValType is the initialization
// expression's type.
// We construct the above expressions and unify them. The best solution (if any)
// will give us the wanted type and the function to invoke to initialize it.
// Create the _ texpr.
static auto anyTVar = ValueToEIR( ToValue( TVar( "_"_sid ) ) );
// The $T texpr.
static auto TTVar = ValueToEIR( ToValue( TVar( "T"_sid ) ) );
// Create the MutRef[$T] param.
static auto mutRefParamPattern = ValueToEIR( ToValue( TNamedDecl(
ValueToEIR( ToValue( ReferenceType{ TTVar, TSID( mut ) } ) ), "ref"_sid ) ) );
auto initValParamPattern = ValueToEIR( ToValue( Decl( initVal.type(), "initVal"_sid ) ) );
// Create the _ ( MutRef[pat], initType ) initFunc param.
auto initFuncTFTParam = ValueToEIR( ToValue(
TNamedDecl( ValueToEIR( ToValue( TFuncType( anyTVar, VEC( move( mutRefParamPattern ), move( initValParamPattern ) ),
make_shared< vector< TermLoc > >(), make_shared< vector< TermLoc > >() ) ) ), "initFunc"_sid ) ) );
// Create our parameter list pattern.
auto paramPat = VEC(
*BuildTemplateSignature( c, TTVar ),
*BuildTemplateSignature( c, initFuncTFTParam )
);
// Create our arg list pattern.
auto args = VEC(
*BuildTemplateArgPattern( c, typeTExpr ),
ValueToEIR( ToValue( c.env()->extInitialize() ) )
);
auto us = FindBestTyping( paramPat, args, c );
if( holds_alternative< NoUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( 0,
"variable initialization type mismatch." );
return PoisonValue();
}
if( holds_alternative< AmbiguousTypeCheck >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( 0,
"ambiguous variable type inference." );
return PoisonValue();
}
auto&& [s,tcc] = get< TCSol >( us );
// Perform the setup of the type template expression. This will create local bindings
// for tvars ($whatever), if any, making them available for further use.
// We need to create a temporary context and adjust the identity so that the tvar
// bindings will be injected to our parent scope, rather than in our local statement scope.
auto parentIdentity = make_shared< Vector >( *get< pvec >( c.identity() ) );
parentIdentity->terms().resize( parentIdentity->terms().size() - 1 );
Context ctxt( c.env(), parentIdentity );
TemplateSetup( ctxt, tcc, typeTExpr );
auto callDecomp = Decompose( s,
Vec(
SubTerm(), // locvar
SubTerm() // initializer
)
);
auto&& [type, initializer] = *callDecomp;
auto initializerVal = *ValueFromEIR( initializer );
auto index = cfg->getNewTemporaryIndex();
// Retrieve the texpr's location and set it on the inferred type. This way if an
// error occurs later with it, for instance when calling LowerTypeForRuntime on it during codegen,
// it will have a meaningful location for the error message to attach itself on.
uint32_t typeLoc = ValueFromEIR( typeTExpr )->locationId();
LocalVar lv( name, type, index );
bb->emplace_back( AllocVar( ValueFromEIR( lv.type() )->setLocationId( typeLoc ), index ) );
DiagnosticsContext dc( 0, "When invoking Initialize." );
auto initResult = ResolveInvocation( c, GetInvocationRule( *c.env(), initializerVal ), initializerVal,
MakeTuple( ToValue( lv ), initVal ) );
if( !initResult.isConstant() && cir::CanValueBeEagerlyEvaluated( initResult ) )
{
execute::VM vm;
initResult = execute::Evaluate( initResult, vm );
}
if( !initResult.isPoison() )
{
DiagnosticsContext dc2( initResult.locationId(), "When invoking DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(),
MakeTuple( move( initResult ) ) );
}
auto locVar = ToValue( lv );
if( name != ""_sid )
{
auto identity = AppendToVectorTerm( c.identity(), name );
c.env()->storeValue( identity, ANYTERM( _ ),
ValueToEIR( locVar ) );
}
cb->pushLiveValue( locVar, lv.index() );
return locVar;
}
}
namespace goose::eir
{
const Term& Bridge< LocalVarType >::Type()
{
return TypeType();
}
Value Bridge< LocalVarType >::ToValue( const LocalVarType& t )
{
return Value( Type(), TVEC( TSID( ct_type ), TSID( local_var ), t.type() ) );
}
Value Bridge< LocalVarType >::ToValue( const Term& type )
{
return Value( Type(), TVEC( TSID( ct_type ), TSID( local_var ), type ) );
}
optional< LocalVarType > Bridge< LocalVarType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(),
Vec(
Lit( "ct_type"_sid ),
Lit( "local_var"_sid ),
SubTerm()
)
);
if( !result )
return nullopt;
auto&& [type] = *result;
return LocalVarType( move( type ) );
}
Term Bridge< LocalVar >::Type( const Term& type )
{
return ValueToEIR( ToValue< LocalVarType >( type ) );
}
Value Bridge< LocalVar >::ToValue( const LocalVar& lv )
{
return Value( Type( lv.type() ), VEC( TERM( lv.name() ), TERM( lv.index() ) ) );
}
optional< LocalVar > Bridge< LocalVar >::FromValue( const Value& v )
{
if( !v.isConstant() )
return nullopt;
auto t = FromValue< LocalVarType >( *ValueFromEIR( v.type() ) );
if( !t )
return nullopt;
auto result = Decompose( v.val(),
Vec(
Val< StringId >(),
Val< uint32_t >()
)
);
if( !result )
return nullopt;
auto&& [name,index] = *result;
return LocalVar( name, move( t->type() ), index );
}
}