#include "builtins/builtins.h"
namespace goose::builtins
{
Term BuildParamPat( const Context& c, Value type, LocationId loc )
{
if( !type.isType() )
type = ToType( c, type );
return ValueToEIR( ValuePattern( TSID( param ), ValueToEIR( type ), HOLE( "_"_sid ) ).setLocationId( loc ) );
}
optional< FuncType > BuildFuncType( const Context& c, const Value& returnType, const Value& params )
{
auto verificationIdentity = VEC( Env::NewUniqueId() );
c.env()->addVisibilityRule( c.identity(), verificationIdentity );
auto tv = make_shared< Vector >();
tv->reserve( TupleSize( params ) );
bool failed = false;
uint32_t varId = 0;
ForEachInTuple( params, [&]( auto&& param )
{
if( IsDecl( param ) )
{
auto decl = *FromValue< Decl >( param );
auto type = InvokeOverloadSet( c, c.env()->extConvertFuncParam(),
MakeTuple( *EIRToValue( decl.type() ) ) );
if( type.isPoison() )
{
failed = true;
return false;
}
tv->append( BuildParamPat( c, type, param.locationId() ) );
// Bind a stand-in value with the parameters name to be used inside of verification expressions.
auto paramVerificationIdentity = AppendToVectorTerm( verificationIdentity,
TERM( decl.name() ) );
c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
ValueToEIR( BuildComputedValue( decl.type(),
cir::VarAddr( varId++, decl.type(), param.locationId() ),
cir::Load( decl.type(), param.locationId() ) ) ) );
}
else if( param.isConstant() )
tv->append( ValueToEIR( param ) );
return true;
} );
if( failed )
return nullopt;
// If the return type is non-void, expose @result under the verification identity as a computed value whose type
// is the function's return type, and the value is a placeholder cir instruction. This will allow verification
// expressions to refer to the current function's return value as a value of the correct type.
auto rtTerm = ValueToEIR( returnType );
if( rtTerm != GetValueType< void >() )
{
auto name = "@result"_sid;
auto retValVerificationIdentity = AppendToVectorTerm( verificationIdentity, TERM( name ) );
c.env()->storeValue( retValVerificationIdentity, ANYTERM( _ ),
ValueToEIR( BuildComputedValue( rtTerm, cir::Placeholder( rtTerm, name, returnType.locationId() ) ) ) );
}
auto pVerifInfos = make_shared< FuncVerificationInfos >( move( verificationIdentity ) );
return FuncType( rtTerm, tv, move( pVerifInfos ) );
}
Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg )
{
if( varArg )
funcType.setKind( FuncType::Kind::VarArg );
return Func( funcType, symbol );
}
optional< Func > BuildFunc( const Context& c,
const Term& parentIdentity, const Term& funcIdentity, const Value& returnType,
const Value& paramsDecl, const ptr< void >& unparsedBody, Context& out_bodyContext )
{
auto funcType = BuildFuncType( c, returnType, paramsDecl );
if( !funcType )
return nullopt;
return BuildFunc( c, *funcType, parentIdentity, funcIdentity, paramsDecl, unparsedBody, out_bodyContext );
}
Func BuildFunc( const Context& c,
const FuncType& funcType, const Term& parentIdentity, const Term& funcIdentity,
const Value& paramsDecl, const ptr< void >& unparsedBody, Context& out_bodyContext )
{
// TODO: instead of a normal import rule from the parent scope, we should use
// a custom visibility rule that deals with variables from the parent context
// in a special way:
// If the function body tries to access variables from the parent function,
// we should invoke an overridable global function that can transform both the
// variable object and the function type. This way, we can implement/customize
// smart lexical captures from inside the language itself (and at a minimum, have
// a default implementation that denies access to outside variables with an error)
auto funcTypeTerm = ValueToEIR( ToValue( funcType ) );
auto identity = VEC( funcIdentity, funcTypeTerm );
assert( parentIdentity != identity );
c.env()->addVisibilityRule( parentIdentity, identity );
out_bodyContext = Context( c.env(), identity, funcType.returnType() );
auto pFuncCIR = c.env()->createCIRFunc( identity );
return Func( funcType, unparsedBody, get< pvec >( paramsDecl.val() ), pFuncCIR.get() );
}
Term BuildCallPatternFromFuncType( const Value& funcType )
{
auto ftype = *FromValue< FuncType >( funcType );
auto apv = make_shared< Vector >();
apv->reserve( VecSize( ftype.params() ) + 1 );
apv->append( ftype.returnType() );
ForEachInVectorTerm( ftype.params(), [&]( auto&& param )
{
auto result = Decompose( param,
Vec(
Lit( "value"_sid ),
SubTerm(),
SubTerm(),
SubTerm(),
Val< LocationId >()
)
);
assert( result );
auto&& [sort, type, val, locId] = *result;
if( sort == TSID( constant ) )
apv->append( param );
else
apv->append( ValueToEIR( ValuePattern( TSID( computed ), type, TERM( ptr< void >() ) ) ) );
return true;
} );
return TERM( apv );
}
}