Goose  Artifact [9d56617df3]

Artifact 9d56617df3ddad18a0e8330bc442666e9f68814272558d7fd8971e31c61f7ade:

  • File bs/builtins/statements/return.cpp — part of check-in [1ad61a2717] at 2021-11-11 20:05:58 on branch trunk — Refactored the code builder: it is now carried around as a Value, and accessed through a bunch of extension points, so we can have different builders (and even user defined ones) later to make classes etc. (user: zlodo size: 3271)

#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"

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

namespace goose::builtins
{
    void SetupReturnStmt( Env& e )
    {
        auto handleReturn = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            auto cfg = GetCFG( p.context() );

            if( p.isInParenExpr() || !cfg )
            {
                dm.emitSyntaxErrorMessage( locationId, "the return statement is not allowed here.", 0 );
                return false;
            }

            if( !cfg->currentBB() || cfg->currentBB()->terminator() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locationId, "unreachable code.", 0 );
                PoisonBuilder( p.context() );
            }

            p.flushValue();

            // Emit cleanups (destructor calls) for all currently live values in the function.
            DestroyAllLiveValues( p.context() );

            const auto& context = p.context();

            if( context.returnType() == GetValueType< void >() )
            {
                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret() );
                return true;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                return false;
            }

            auto retVal = np.popValue();
            if( !retVal )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                return false;
            }

            auto converted = ConvertValueToType( context, *retVal, *context.returnType() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    case ValUnifyError::NoSolution:
                        dm.emitErrorMessage( retVal->locationId(), "return value type mismatch." );
                        break;

                    case ValUnifyError::Ambiguous:
                        dm.emitErrorMessage( retVal->locationId(), "ambiguous return value conversion." );
                        break;
                }

                // Emit a terminator with a poison value to avoid the function compilation
                // code to complain about a missing return.
                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                PoisonBuilder( p.context() );
                return true;
            }

            cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( get< Value >( converted ) ) );
            return true;
        };

        RegisterRule( e, "return"_sid, Rule( handleReturn ) );
    }
}