Goose  Artifact [1cc633281d]

Artifact 1cc633281d4ee2f9e7bf7eac5c7f6ca0cf59952d30806e55f738283665206a4a:

  • File bs/builtins/types/reference/typecheck.cpp — part of check-in [7d2def7b75] at 2020-12-27 14:40:24 on branch trunk — Renamed "ir" to "eir" (expression intermediate representation) and "llr" to "cir" (code intermediate representation) for clarity. (user: achavasse size: 7879)

#include "builtins/builtins.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::cir;

namespace goose::builtins
{
    void SetupReferenceTypeChecking( Env& e )
    {
        auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );

        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( temp ), ANYTERM( _ ) ) ) );

        // Reference type checking rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( refTypePattern ),

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

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto lRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( ValuePatternFromIRExpr( lhs )->type() ) );

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

                // Unify the behaviors
                for( auto&& [b, tcc] : Unify( lRefType.behavior(), rRefType.behavior(), tcc ) )
                {
                    // Unify the types
                    for( auto&& [t, tcc] : Unify( lRefType.type(), rRefType.type(), tcc ) )
                    {
                        co_yield { ValueToIRExpr( ValuePattern( rhsVal.sort(), ValueToIRExpr( ToValue( ReferenceType( t, b ) ) ),
                            rhsVal.val() ) ), tcc };
                    }
                }
            }
        );

        // mut -> const reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            TSID( const ),
            TSID( mut ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                co_yield { TSID( const ), tcc };
            }
        );

        // temp -> const reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            TSID( const ),
            TSID( temp ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                co_yield { TSID( const ), tcc };
            }
        );

        // temp -> mut reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            TSID( mut ),
            TSID( temp ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                co_yield { TSID( mut ), tcc };
            }
        );

        // Reference type checking against a param (implicit dereferencing):
        // Unify the referenced value with the param.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ANYTERM( _ ),

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

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

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

                // TypeCheck the param with the ref's content
                co_yield TypeCheck( lhs, content, tcc );
            } );

        // LocalVar type checking against a param (implicit referencing):
        // Build a mutable ref value, or just unwrap the locVar if it
        // already contains a ref
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

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

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto ltype = ValuePatternFromIRExpr( lhs )->type();

            auto lvval = *ValueFromIRExpr( rhs );
            auto locvar = FromValue< LocalVar >( lvval );
            if( !locvar )
                co_return;

            auto ref = ValueToIRExpr( ToValue( BuildLocalVarMutRef( *locvar ) )
                .setLocationId( lvval.locationId() ) );

            co_yield TypeCheck( lhs, ref, tcc );
        } );

        // Implicit referencing of non-variables: build a tempref
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

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

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

        [refTypePattern]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            if( !tcc.context().codeBuilder() )
                co_return;

            if( !tcc.context().codeBuilder()->cfg() )
                co_return;

            auto lRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( ValuePatternFromIRExpr( lhs )->type() ) );
            auto lhsPat = ValueToIRExpr( ValuePattern( TSID( param ), lRefType.type(), HOLE( "_"_sid ) ) );

            auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();

            for( auto&& [s,tcc] : TypeCheck( lhsPat, rhs, tcc ) )
            {
                auto valPat = *ValuePatternFromIRExpr( s );
                ReferenceType rt( valPat.type(), TSID( temp ) );

                auto refPat = ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), ValueToIRExpr( ToValue( rt ) ), HOLE( "_"_sid ) ) );

                // TypeCheck the param with the ref
                for( auto&& [s,tcc] : TypeCheck( lhs, refPat, tcc ) )
                {
                    assert( tcc.complexity() >= GetComplexity( ParamPat( refTypePattern ) ) );
                    tcc.subComplexity( GetComplexity( ParamPat( refTypePattern ) ) );

                    auto wrapped = WrapWithPostprocFunc( VEC( s, rhs ),
                    [tempIndex]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
                    {
                        auto result = Decompose( t,
                            Vec(
                                SubTerm(),
                                SubTerm()
                            )
                        );

                        assert( result );
                        auto&& [ref, rhs] = *result;

                        auto rhsVal = *ValueFromIRExpr( rhs );

                        auto refPat = *ValuePatternFromIRExpr( ref );
                        auto rt = *FromValue< ReferenceType >( *ValueFromIRExpr( refPat.type() ) );

                        return ValueToIRExpr( ToValue( Reference{ move( rt ), TemporaryBaseAddr( tempIndex, rhsVal ) } ) );
                    } );

                    co_yield { move( wrapped ), tcc };
                }
            }
        } );
    }
}