Goose  Artifact [461d1cf363]

Artifact 461d1cf3635980242abc380e6e8a1a5810df45ef9b5a802bc057d6a99956edf6:

  • File bs/builtins/types/reference/typecheck.cpp — part of check-in [55994a469a] at 2021-11-03 22:55:10 on branch trunk —
    • References: ref now creates a template type accepting any kind of reference, and mut/const/temp are prefix operators turning that into a ref (or template ref) type of the corresponding kind (this also changes the syntax from ref mut XXX to mut ref XXX which is more natural)
    • Fixed the bizarely broken implicit referencing typechecking rule that performed a superfluous intermediate TypeCheck that crashed on non mutable template reference types
    • Fixed builtin function wrapper that omitted a cast of the return value to the specified type, which allowed builtin functions to silently return values of a different type than advertised, resulting in fun type checking errors
    • Fixed a bunch of fucked up return types that worked by chance (because of the above) in several g0 api functions
    • Implemented non mutable reference passing verification + related tests (it doesn't work yet but too many fixes need to be committed first)
    (user: zlodo size: 8321)

#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 )
    {
        if( !tcc.context().codeBuilder() )
            co_return;

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

        auto lRefType = *FromValue< ReferenceType >( *EIRToValue( ValuePatternFromEIR( lhs )->type() ) );

        if( lRefType.behavior() == TSID( mut ) )
            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(), TSID( temp ) );

                auto rhsVal = *EIRToValue( rhs );

                auto tempIndex = tcc.context().codeBuilder()->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 ), TSID( const ), ANYTERM( _ ) ) ) );

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

        auto refTypePatternTemporary = ValueToEIR(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( temp ), 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,
            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()->addTypeCheckingRule( TCRINFOS,
            TSID( const ),
            TSID( temp ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                co_yield { TSID( const ), 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
        );
    }
}