Goose  unify.cpp at [6099081bdd]

File bs/builtins/types/reference/unify.cpp artifact e58a6c4a77 part of check-in 6099081bdd


#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;
using namespace goose::llr;

namespace goose::builtins
{
    void SetupReferenceUnification( Env& e )
    {
        auto refTypePattern = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        auto refTypePatternConstant = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( const ), ANYTERM( _ ) ) ) );

        auto refTypePatternMutable = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( mut ), ANYTERM( _ ) ) ) );

        auto refTypePatternTemporary = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( temporary ), ANYTERM( _ ) ) ) );

        // Common function to process references unification: unify the type and create a reference
        // with the unified type and the param reference behavior.
        // We define it separatelt because we need to register rules for all the allowed behavior conversion rules
        // (any->constant, temporary->mutable), as well as a behavior preserving rule.
        auto unifyRefs = []( const Term& lhs, const Term& rhs, UnificationContext& uc ) -> UniGen
            {
                auto lRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( ValuePatternFromIRExpr( lhs )->type() ) );

                auto rhsVal = *ValueFromIRExpr( rhs );
                auto rRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( rhsVal.type() ) );

                for( auto&& [s, uc] : Unify( lRefType.type(), rRefType.type(), uc ) )
                {
                    co_yield { ValueToIRExpr( Value( ValueToIRExpr( ToValue( ReferenceType( s, lRefType.behavior() ) ) ),
                        rhsVal.llr() ) ), uc };
                }
            };

        // Behavior preserving reference unification rule.
        e.unificationRuleSet()->addSymRule(

            ParamPat( refTypePattern ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

            unifyRefs
        );

        // Any behavior -> constant reference unification rule.
        e.unificationRuleSet()->addSymRule(

            ParamPat( refTypePatternConstant ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

            unifyRefs
        );

        // temporary -> mutable reference unification rule.
        e.unificationRuleSet()->addSymRule(

            ParamPat( refTypePatternMutable ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                refTypePatternTemporary,
                ANYTERM( _ ) ) ),

            unifyRefs
        );

        // Reference unification against a param:
        // Unify the referenced value with the param.
        e.unificationRuleSet()->addSymRule(

            ParamPat( ANYTERM( _ ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

            []( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
            {
                auto refval = *ValueFromIRExpr( rhs );
                auto ref = FromValue< Reference >( refval );
                if( !ref )
                    co_return;

                auto content = ValueToIRExpr( BuildComputedValue( ref->type().type(),
                    llr::Load( ref->address(), ref->type().type() ) )
                    .setLocationId( refval.locationId() ) );

                // Unify the param with the ref's content
                co_yield Unify( lhs, content, c );
            } );
    }
}