Goose  Artifact [0ee0424ee8]

Artifact 0ee0424ee8273ee18c6eb25289259509c3a3a511a4f7f23d10260b6ce4a9276b:

  • File bs/builtins/operators/assignment.cpp — part of check-in [2d2defb442] at 2021-08-29 21:49:39 on branch trunk —
    • Type predicates' identity and placeholder are now created immediately, rather than when parsing their content
    • Added missing type predicates lazy parsing during typechecking
    • Fixed a type checking rule falling back to unification, which is no longer needed
    (user: achavasse size: 7680)

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

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

namespace goose::builtins
{
    void SetupAssignmentOps( Env& e )
    {
        auto assOp = GetOrCreateOverloadSet( e, "operator_assign"_sid );

        // Generic function to perform the assignation of a builtin runtime type
        // to a mutable reference of that same type.
        auto BuiltinTypeAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            G_VAL_ASSERT( lhs, !lhs.isConstant() );

            auto refType = *FromValue< ReferenceType >( *ValueFromEIR( lhs.type() ) );

            if( !ParseTypePredicates( c, *ValueFromEIR( refType.type() ) ) )
                return PoisonValue();

            const auto& cb = c.codeBuilder();
            if( !cb )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." );
                return PoisonValue();
            }

            if( auto bb = cb->cfg()->currentBB() )
                bb->emplace_back( Store( lhs.cir(), refType.type(), rhs, lhs.locationId() ) );

            // Return the ref to support chained assignments, like in c++.
            return Value( lhs );
        };

        // Generic function to assign a value to a decl (local variable declaration)
        auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            if( !c.codeBuilder() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );
                return PoisonValue();
            }

            auto decl = *FromValue< Decl >( lhs );
            return DeclareLocalVar( c, decl.type(), decl.name(), rhs, lhs.locationId() );
        };

        // Generic function to assign a value to a template named decl (local variable declaration with local type inference)
        auto TNamedDeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            if( !c.codeBuilder() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );
                return PoisonValue();
            }

            auto tndecl = *FromValue< TNamedDecl >( lhs );
            return DeclareLocalVarWithTypeInference( c, tndecl.type(), tndecl.name(), rhs, lhs.locationId() );
        };

        // Tuple assignment.
        //
        // We create anonymous variables with type inference, and initialize them with
        // the values form the rhs tuple. Then we assign each of them to members of the lhs tuple.
        //
        // The reason we do all this is so that cases such as
        // a,b = b,a work as expected.
        //
        // We count on llvm to elide the temp vars in case they are not needed, but then again
        // we do already count on it to elide all those load and stores and allocas anyway.
        //
        // The real performance overhead danger here is for complex type whose InitializeVar()
        // and Destroy() implementations emit code that can't be elided and that may generate object
        // copies.
        //
        // TODO: Ideally, we should restrict tuple assignation only to types that can be moved.
        // For types that can only be copied, it may be preferable to write it the old fashioned
        // way with a temp var to make the copy more apparent.
        // But complex types and the notion of moving them don't even exist yet.
        auto TupleAssignment = [assOp]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            auto tupSize = TupleSize( lhs );
            if( tupSize != TupleSize( rhs ) )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
                return PoisonValue();
            }

            // Load the values from the rhs tuple into temporary vars.
            vector< Value > tempVars;
            tempVars.reserve( tupSize );

            bool success = true;
            ForEachInTuple( rhs, [&]( auto&& srcVal )
            {
                // We go at very high level to construct the temporary variables by resolving an invocation
                // of the assignment of the rhs value to a TNamedDecl with an empty name.
                // The reason we need to work at such high level is so that the initializer value gets
                // resolved exactly like if it was a regular assignment. In particular, if the value in question
                // is a locvar, we want it to go through the type checking process to be replaced with its content.
                // This is the simplest and most robust way to achieve this, which should honor every relevant
                // extension point.
                auto anonVarDecl = ToValue( TNamedDecl( ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid ) );

                auto tmpVar = InvokeOverloadSet( c,
                    assOp, MakeTuple( anonVarDecl, srcVal ) );

                if( tmpVar.isPoison() )
                {
                    success = false;
                    return false;
                }

                tmpVar.setLocationId( srcVal.locationId() );
                tempVars.emplace_back( move( tmpVar ) );
                return true;
            } );

            if( !success )
                return PoisonValue();

            auto result = EmptyTuple();

            size_t i = 0;
            ForEachInTuple( lhs, [&]( auto&& destVal )
            {
                auto r = InvokeOverloadSet( c,
                    assOp, MakeTuple( destVal, tempVars[i++] ) );

                if( r.isPoison() )
                {
                    success = false;
                    return false;
                }

                result = AppendToTuple( result, move( r ) );
                return true;
            } );

            if( !success )
                return PoisonValue();

            return result;
        };

        BuildParseRule( e, "="_sid,
            RightAssInfixOp( "operator_assign"_sid, precedence::AssignmentOp,
                // Assignment with a decl of type $T to the left and a value of type $T to the right:
                // Local variable declaration and initialization.
                ForTypes< CustomPattern< Decl, Decl::Pattern >, CustomPattern< Value, ForwarderPattern > >( DeclAssignment ),

                // Assignment with a tnameddecl to the left and a value to the right:
                // Local variable declaration and initialization with type inference.
                ForTypes< CustomPattern< TNamedDecl, TNamedDecl::Pattern >, CustomPattern< Value, ForwarderPattern > >( TNamedDeclAssignment ),

                ForType< CustomPattern< Value, TuplePattern > >( TupleAssignment ),

                // Mutable ref assignment
                ForTypes< CustomPattern< Value, ReferenceType::PatternAnyMutableOfTypeT >,
                    CustomPattern< Value, ValuePatternT > >( BuiltinTypeAssignment ),

                // Explicit overload for assigning an rt_int to an rt_int reference:
                // We need this to be able to assign a ct_int to a rt_int: the generic version
                // above can't perform the necessary implicit conversion.
                ForTypes< CustomPattern< Value, ReferenceType::PatternMutable< IntegerType::Pattern > >,
                    CustomPattern< Value, IntegerType::Pattern > >(
                    BuiltinTypeAssignment )
            )
        );
    }
}