#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class FunctionInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const final
{
optional< TypeCheckingContext > bestTCC;
optional< Term > bestSol;
auto sig = GetFuncSig( callee );
auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid ) );
auto us = FindBestTyping( sig, callPat, c );
if( holds_alternative< NoUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"function arguments mismatch." );
return PoisonValue();
}
if( holds_alternative< AmbiguousTypeCheck >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ambiguous function call." );
return PoisonValue();
}
auto&& [s,tcc] = get< TCSol >( us );
return invoke( c, loc, callee, args, s, tcc );
}
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
if( preparedCallee.isPoison() )
return PoisonValue();
auto callDecomp = Decompose( typeCheckedCallPat,
Val< pvec >()
);
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
preparedCallee.setLocationId( loc );
if( IsBuiltinFunc( preparedCallee ) )
return BuildComputedValue( typeCheckedRType, cir::Call( preparedCallee, move( typeCheckedArgs ) ) );
if( IsBuiltinIntrinsicFunc( preparedCallee ) )
return GetBuiltinIntrinsicFuncWrapper( preparedCallee )( c, move( typeCheckedArgs ) );
auto ft = *FromValue< FuncType >( *EIRToValue( preparedCallee.type() ) );
if( ft.kind() == FuncType::Kind::Intrinsic )
{
// Intrinsic call: we insert the context wrapper as first param,
// wrap all args with TypeWrapper< Value >, and execute the function directly.
auto argList = BuildArgListForIntrinsicCall( c, ft, typeCheckedArgs );
if( !argList )
return PoisonValue();
execute::VM vm;
auto result = vm.execute( cir::Call( preparedCallee, move( *argList ) ) );
if( ft.returnType() == GetValueType< void >() )
return Value( GetValueType< void >(), 0U );
if( !result )
return PoisonValue();
// Unwrap the returned value
auto unwrapped = FromValue< TypeWrapper< Value > >( *result );
return unwrapped ? *unwrapped : PoisonValue();
}
auto argList = BuildArgListForCall( c, ft, typeCheckedArgs );
if( !argList )
return PoisonValue();
if( ft.kind() == FuncType::Kind::Ghost )
{
if( !CanInvokeGhostFuncs( c ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ghost functions can't be called in this context." );
return PoisonValue();
}
// A ghost call is a mutable reference to a ghost closure, using the special "GhostCall"
// as the instruction to compute its "address"
auto rt = ValueToEIR( ToValue( ReferenceType{ typeCheckedRType, MutAccessSpecifer() } ) );
return BuildComputedValue( typeCheckedRType, cir::GhostCall( preparedCallee, move( *argList ) ) );
}
else
{
auto result = BuildComputedValue( typeCheckedRType, cir::Call( preparedCallee, move( *argList ) ) );
// If the result is non-void, register it for destruction.
if( result.type() != GetValueType< void >() )
{
if( auto cfg = GetCFG( c ) )
DeclareValue( c, result, cfg->getNewTemporaryIndex() );
}
return result;
}
}
optional< Term > getSignature( const Value& callee ) const final
{
return GetFuncSig( callee );
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
if( IsBuiltinFunc( callee ) || IsBuiltinIntrinsicFunc( callee ) || IsGhostFunc( callee ) )
return callee;
// TODO better description with the function's name if possible (we may need to explicitely store it in the func)
DiagnosticsContext dc( 0, true );
VerbosityContext vc( Verbosity::Normal, true );
return CompileFunc( c, callee );
}
};
ptr< InvocationRule >& GetFuncInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< FunctionInvocationRule >();
return pRule;
}
void SetupFunctionInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(), VEC( TSID( func ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
GetFuncInvocationRule() );
}
}