Goose  Artifact [847ab1e033]

Artifact 847ab1e033865546046deb22565c784c73e82be987f6e3f178adff759e5bfe16:

  • File bs/builtins/types/runtime/typecheck.cpp — part of check-in [3bf30e74ac] at 2021-01-13 11:46:18 on branch trunk — Sema: simplification: the "half-unification" rules don't need to be generators, they can only output one result. (user: achavasse size: 7366)

#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;

namespace goose::builtins
{
    void SetupRuntimeTypesUnification( Env& e )
    {
        auto rtIntTypePattern = Value( TypeType(), MkStdType( TSID( integer ),
            VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // ct_int type against a IntegerType type:
        // return the IntegerType type. We don't care if the
        // ct_int fits at this point, this will be dealt with by
        // the ct_int value type checking rule below.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            ValueToEIR( rtIntTypePattern ),
            GetValueType< BigInt >(),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext c ) -> TCGen
        {
            if( auto s = HalfUnify( lhs, c ) )
                co_yield { *s, c };
        } );

        // Reject the ct_int param and IntegerType arg pattern,
        // so that it doesn't fall into the rule above which would be incorrect
        // in that case.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( GetValueType< BigInt >() ),

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                ValueToEIR( rtIntTypePattern ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
        {
            co_return;
        } );

        // ct_integer constant type checking against a IntegerType:
        // Check if the IntegerType is big enough for the constant,
        // and emit a LoadConstantInt cir instruction if so.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                ValueToEIR( rtIntTypePattern ),
                ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern(
                TSID( constant ),
                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 = *ValueFromEIR( rhs );
            if( !rhsVal.isConstant() )
                co_return;

            auto lhsVal = ValuePatternFromIRExpr( lhs );
            if( !lhsVal )
                co_return;

            auto s = HalfUnify( lhsVal->type(), tcc );
            if( !s )
                co_return;

            // We need to defer the calculation of the init value until after the rest of the type checking is complete,
            // because we need to make sure that any hole present in the integer type is resolved before we can do
            // the sign/bitsize check. However, since we still need to be able to manipulate the result as a value in
            // the mean time, we construct a value with the correct (but possibly not complete yet type), and a payload
            // that contains the type + the ct_int value wrapped with a post process callback that finalizes the job.
            auto wrapped = WrapWithPostprocFunc( VEC( lhsVal->type(), rhs ),
            []( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
            {
                auto result = Decompose( t,
                    Vec(
                        SubTerm(),
                        SubTerm()
                    )
                );

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

                auto ct = *FromValue< BigInt >( *ValueFromEIR( rhs ) );

                auto rttypeVal = ValueFromEIR( type );
                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 ) ) ), tcc };
        } );

        auto rtInt8TypePattern = Value( TypeType(), MkStdType( TSID( integer ),
            VEC( TERM( 8U ), ANYTERM( _ ) ) ) );

        auto rtInt8PtrTypePattern = Value( TypeType(), MkStdType( TSID( pointer ),
            ValueToEIR( rtInt8TypePattern ) ) );

        // ct_string type against a char*:
        // return the char* type.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            ValueToEIR( rtInt8PtrTypePattern ),
            GetValueType< string >(),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext c ) -> TCGen
        {
            if( auto s = HalfUnify( lhs, c ) )
                co_yield { *s, c };
        } );

        // ct_string constant type checking against a pointer to a integer( 8 ):
        // Emit a LoadConstantStr cir instruction.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                ValueToEIR( rtInt8PtrTypePattern ),
                ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                GetValueType< string >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
        {
            auto str = *FromValue< string >( *ValueFromEIR( rhs ) );
            auto lhsVal = *ValuePatternFromIRExpr( lhs );

            co_yield { ValueToEIR(
                BuildComputedValue( lhsVal.type(), cir::LoadConstStr( str ) ) ), c };
        } );

        auto ptrTypePattern = Value( TypeType(), MkStdType( 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( TCRINFOS,

            ParamPat( ValueToEIR( ptrTypePattern ) ),

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                GetValueType< NullPointer >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
        {
            auto lVal = *ValuePatternFromIRExpr( lhs );
            co_yield { ValueToEIR( Value( lVal.type(), 0U ) ), c };
        } );
    }
}