#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
namespace goose::builtins
{
// TODO: converting a computed ct_int to a rt int is legit in compile time code.
// Consider this:
// var someTuple = (123,456)
// void someCompileTimeFunc( ( uint(32), uint(32) tup )
// Can't perform a compile time to someCompileTimeFunc with someTuple as the later contains computed ct_ints
// So we need to allow this conversion, but only at compile time. Perhaps emit the conversion as a call to a compile time conversion
// func, which therefore would fail when compiling because the ct_int wouldn't be able to be lowered to a runtime type?
// (we'd also need to emit asserton checks for the ct_int bounds)
// Probably something to do in the prelude.
void SetupRuntimeTypesChecking( Env& e )
{
auto rtIntTypePattern = Value( TypeType(), MkStdType( TSID( rt_type ), TSID( integer ),
VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );
// Conversion rule for ct_int to runtime int:
// we verify that the ct_int fits in the bitsize/signage
// of the runtime int
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern(
ANYTERM( _ ),
ValueToEIR( rtIntTypePattern ),
ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
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 = *EIRToValue( rhs );
if( !rhsVal.isConstant() )
co_return;
auto lhsVal = EIRToValuePattern( lhs );
if( !lhsVal )
co_return;
auto s = HalfUnify( lhsVal->type(), tcc );
if( !s )
co_return;
auto wrapped = WrapWithPostprocFunc( *s,
[rhs]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
auto argVal = EIRToValue( rhs );
assert( argVal );
auto ct = *FromValue< BigInt >( *argVal );
auto rttypeVal = EIRToValue( t );
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 ) )
.setLocationId( rhsVal.locationId() ) ), tcc };
} );
auto rtInt8TypePattern = Value( TypeType(), MkStdType( TSID( rt_type ), TSID( integer ),
VEC( TERM( 8U ), ANYTERM( _ ) ) ) );
auto rtInt8PtrTypePattern = Value( TypeType(), MkStdType( TSID( rt_type ), TSID( pointer ),
ValueToEIR( rtInt8TypePattern ) ) );
// ct_string type against a char*:
// return the char* type.
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( rtInt8PtrTypePattern ),
GetValueType< string >(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext c ) -> TCGen
{
if( auto s = HalfUnify( lhs, c ) )
co_yield { *s, c };
} );
auto ptrTypePattern = Value( TypeType(), MkStdType( TSID( rt_type ), 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(
ParamPat( ValueToEIR( ptrTypePattern ) ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
GetValueType< NullPointer >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
{
auto lVal = *EIRToValuePattern( lhs );
co_yield { ValueToEIR( Value( lVal.type(), 0U ) ), c };
} );
}
}