#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
namespace
{
using namespace goose::builtins;
auto refTypePattern = ValueToEIR(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
TCGen TypeCheckingDereference( const Term& lhs, const Term& rhs, TypeCheckingContext tcc )
{
auto refval = EIRToValue( rhs );
if( !refval )
co_return;
auto refType = FromValue< ReferenceType >( *EIRToValue( refval->type() ) );
if( !refType )
co_return;
auto content = ValueToEIR( BuildComputedValue( refType->type(),
cir::Load( refval->cir(), refType->type() ) )
.setLocationId( refval->locationId() ) );
// TypeCheck the param with the ref's content
co_yield TypeCheck( lhs, content, tcc );
}
TCGen TypeCheckingBuildTempRef( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc )
{
auto lRefType = *FromValue< ReferenceType >( *EIRToValue( ValuePatternFromEIR( lhs )->type() ) );
if( lRefType.behavior() == MutAccessSpecifer() )
co_return;
auto lhsPat = ValueToEIR( ValuePattern( TSID( param ), lRefType.type(), HOLE( "_"_sid ) ) );
for( auto&& [s,tcc] : TypeCheck( lhsPat, rhs, tcc ) )
{
auto wrapped = WrapWithPostprocFunc( VEC( s, rhs ),
[lhs]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
auto result = Decompose( t,
Vec(
SubTerm(),
SubTerm()
)
);
assert( result );
auto&& [arg, rhs] = *result;
auto val = *EIRToValue( arg );
ReferenceType rt( val.type(), TempAccessSpecifer() );
auto rhsVal = *EIRToValue( rhs );
auto cfg = GetCFG( tcc.context() );
if( !cfg )
return nullopt;
// TODO create an ext point for this
auto tempIndex = cfg->getNewTemporaryIndex();
return ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ), TempAddr( tempIndex, rhsVal ) )
.setLocationId( rhsVal.locationId() ) );
} );
// Override the weight because we don't want
// this solution to count more than directly using
// the value without wrapping it into a tempref
SetWeight( wrapped, GetWeight( rhs ) - 1 );
co_yield { move( wrapped ), tcc };
}
}
}
namespace goose::builtins
{
void SetupReferenceTypeChecking( Env& e )
{
auto refRefTypePattern = ValueToEIR(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ANYTERM( _ ), refTypePattern ) ) );
auto refTypePatternConstant = ValueToEIR(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ConstAccessSpecifer(), ANYTERM( _ ) ) ) );
auto refTypePatternMutable = ValueToEIR(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), MutAccessSpecifer(), ANYTERM( _ ) ) ) );
auto refTypePatternTemporary = ValueToEIR(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TempAccessSpecifer(), ANYTERM( _ ) ) ) );
auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );
// Reference type checking rule.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( refTypePattern ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
refTypePattern,
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lRefType = *FromValue< ReferenceType >( *EIRToValue( ValuePatternFromEIR( lhs )->type() ) );
auto rhsVal = *ValuePatternFromEIR( rhs );
auto rRefType = *FromValue< ReferenceType >( *EIRToValue( rhsVal.type() ) );
// Type check the behaviors
for( auto&& [b, tcc] : TypeCheck( lRefType.behavior(), rRefType.behavior(), tcc ) )
{
// Check the types
for( auto&& [t, tcc] : TypeCheck( lRefType.type(), rRefType.type(), tcc ) )
{
co_yield { ValueToEIR( ValuePattern( rhsVal.sort(), ValueToEIR( ToValue( ReferenceType( t, b ) ) ),
rhsVal.val() ).setLocationId( rhsVal.locationId() ) ), tcc };
}
}
}
);
// mut -> const reference type checking rule.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ConstAccessSpecifer(),
MutAccessSpecifer(),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
co_yield { lhs, tcc };
}
);
// temp -> const reference unification rule.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ConstAccessSpecifer(),
TempAccessSpecifer(),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
co_yield { lhs, tcc };
}
);
// Reference type checking against a param (implicit dereferencing):
// Unify the referenced value with the param.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( ANYTERM( _ ) ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
refTypePattern,
ANYTERM( _ ) ) ),
TypeCheckingDereference
);
// References versus TVars: a tvar should never directly bind to a reference.
// Therefore, when unifying a TVar against a reference, we strip the reference.
// And when typechecking a TVar typed parameter against a reference, we dereference.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( HOLE( ""_sid, TSID( tvar ) ) ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
refTypePattern,
ANYTERM( _ ) ) ),
TypeCheckingDereference
);
// Unifying a TVar with a reference type isn't allowed.
// A reference whose type is a TVar, on the other hand,
// can be unified with another reference type.
e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
HOLE( ""_sid, TSID( tvar ) ),
refTypePattern,
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
co_return;
}
);
// LocalVar type checking against a param (implicit referencing)
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToEIR( ValuePattern(
ANYTERM( _ ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
localVarPattern,
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ltype = ValuePatternFromEIR( lhs )->type();
auto lvval = *EIRToValue( rhs );
auto locvar = FromValue< LocalVar >( lvval );
if( !locvar )
co_return;
auto ref = ValueToEIR( ToValue( BuildLocalVarMutRef( *locvar ) )
.setLocationId( lvval.locationId() ) );
co_yield TypeCheck( lhs, ref, tcc );
} );
// Implicit referencing of non-variables against a non mutable ref: build a tempref
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToEIR( ValuePattern(
ANYTERM( _ ),
refTypePattern,
ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern(
ANYTERM( _ ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
TypeCheckingBuildTempRef
);
}
}