#include "builtins/builtins.h"
#include "builtins/helpers.h"
using namespace goose;
using namespace goose::eir;
namespace goose::builtins
{
void SetupRuntimeTypesUnification( Env& e )
{
auto rtIntTypePattern = Value( TypeType(), MkStdType( TSID( integer ),
VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );
// ct_int type against a IntegerType type:
// return the IntegerType type. We don't care if the
// ct_int fits at this point, this will be dealt with by
// the ct_int value type checking rule below.
e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
ValueToEIR( rtIntTypePattern ),
GetValueType< BigInt >(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext c ) -> TCGen
{
if( auto s = HalfUnify( lhs, c ) )
co_yield { *s, c };
} );
// Reject the ct_int param and IntegerType arg pattern,
// so that it doesn't fall into the rule above which would be incorrect
// in that case.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( GetValueType< BigInt >() ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
ValueToEIR( rtIntTypePattern ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
{
co_return;
} );
// ct_integer constant type checking against a IntegerType:
// Check if the IntegerType is big enough for the constant,
// and emit a LoadConstantInt cir instruction if so.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToEIR( ValuePattern(
ANYTERM( _ ),
ValueToEIR( rtIntTypePattern ),
ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern(
TSID( constant ),
GetValueType< BigInt >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
// The rhs might not be constant. It can happen even with ct_int if the rhs is a
// fake argument pattern generated to unify a higher-order function param.
// It means that we are unifying a function parameter against any ct_int value,
// which we can't (because we have to know the value to safely convert it),
// so we reject it.
auto rhsVal = *ValueFromEIR( rhs );
if( !rhsVal.isConstant() )
co_return;
auto lhsVal = ValuePatternFromIRExpr( lhs );
if( !lhsVal )
co_return;
auto s = HalfUnify( lhsVal->type(), tcc );
if( !s )
co_return;
// We need to defer the calculation of the init value until after the rest of the type checking is complete,
// because we need to make sure that any hole present in the integer type is resolved before we can do
// the sign/bitsize check. However, since we still need to be able to manipulate the result as a value in
// the mean time, we construct a value with the correct (but possibly not complete yet type), and a payload
// that contains the type + the ct_int value wrapped with a post process callback that finalizes the job.
auto wrapped = WrapWithPostprocFunc( VEC( lhsVal->type(), rhs ),
[]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
auto result = Decompose( t,
Vec(
SubTerm(),
SubTerm()
)
);
assert( result );
auto&& [type, rhs] = *result;
auto ct = *FromValue< BigInt >( *ValueFromEIR( rhs ) );
auto rttypeVal = ValueFromEIR( type );
assert( rttypeVal );
auto rttype = FromValue< IntegerType >( *rttypeVal );
assert( rttype );
APSInt valToLoad;
if( rttype->m_signed )
{
if( ct.getMinSignedBits() > rttype->m_numBits )
return nullopt;
valToLoad = ct.sext( rttype->m_numBits );
valToLoad.setIsSigned( true );
}
else
{
if( ct.isNegative() )
return nullopt;
if( ct.getActiveBits() > rttype->m_numBits )
return nullopt;
valToLoad = ct.zext( rttype->m_numBits );
valToLoad.setIsSigned( false );
}
return TERM( valToLoad );
} );
co_yield { ValueToEIR( Value( *s, move( wrapped ) ) ), tcc };
} );
auto rtInt8TypePattern = Value( TypeType(), MkStdType( TSID( integer ),
VEC( TERM( 8U ), ANYTERM( _ ) ) ) );
auto rtInt8PtrTypePattern = Value( TypeType(), MkStdType( TSID( pointer ),
ValueToEIR( rtInt8TypePattern ) ) );
// ct_string type against a char*:
// return the char* type.
e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
ValueToEIR( rtInt8PtrTypePattern ),
GetValueType< string >(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext c ) -> TCGen
{
if( auto s = HalfUnify( lhs, c ) )
co_yield { *s, c };
} );
// ct_string constant type checking against a pointer to a integer( 8 ):
// Emit a LoadConstantStr cir instruction.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToEIR( ValuePattern(
ANYTERM( _ ),
ValueToEIR( rtInt8PtrTypePattern ),
ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
GetValueType< string >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
{
auto str = *FromValue< string >( *ValueFromEIR( rhs ) );
auto lhsVal = *ValuePatternFromIRExpr( lhs );
co_yield { ValueToEIR(
BuildComputedValue( lhsVal.type(), cir::LoadConstStr( str ) ) ), c };
} );
auto ptrTypePattern = Value( TypeType(), MkStdType( TSID( pointer ),
ANYTERM( _ ) ) );
// nullptr constant type checking against a pointer of any type;
// Yield a value of the given pointer type, with a 0 integer as its content.
// This'll be recognized by codegen to emit a null pointer value of the right type.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( ValueToEIR( ptrTypePattern ) ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
GetValueType< NullPointer >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
{
auto lVal = *ValuePatternFromIRExpr( lhs );
co_yield { ValueToEIR( Value( lVal.type(), 0U ) ), c };
} );
}
}