/*
 * This file is part of the Cforall project
 *
 * $Id: Lvalue.cc,v 1.5 2005/08/29 20:14:13 rcbilson Exp $
 *
 */

#include <cassert>

#include "Lvalue.h"

#include "SynTree/Declaration.h"
#include "SynTree/Type.h"
#include "SynTree/Expression.h"
#include "SynTree/Statement.h"
#include "SynTree/Visitor.h"
#include "SynTree/Mutator.h"
#include "SymTab/Indexer.h"
#include "ResolvExpr/Resolver.h"
#include "ResolvExpr/typeops.h"

#include "UniqueName.h"
#include "utility.h"


namespace GenPoly {

namespace {

const std::list<Label> noLabels;

class Pass1 : public Mutator
{
public:
  Pass1();
  
  virtual Expression *mutate( ApplicationExpr *appExpr );
  virtual Statement *mutate( ReturnStmt *appExpr );
  virtual DeclarationWithType *mutate( FunctionDecl *funDecl );

private:
  DeclarationWithType* retval;
};

class Pass2 : public Visitor
{
public:
  virtual void visit( FunctionType *funType );

private:
};

} // namespace

void
convertLvalue( std::list< Declaration* >& translationUnit )
{
  Pass1 p1;
  Pass2 p2;
  mutateAll( translationUnit, p1 );
  acceptAll( translationUnit, p2 );
}

namespace {

bool
isLvalueRet( FunctionType *function )
{
  if( !function->get_returnVals().empty() ) {
    return function->get_returnVals().front()->get_type()->get_isLvalue();
  } else {
    return false;
  }
}

bool
isIntrinsicApp( ApplicationExpr *appExpr )
{
  if( VariableExpr *varExpr = dynamic_cast< VariableExpr* >( appExpr->get_function() ) ) {
    return varExpr->get_var()->get_linkage() == LinkageSpec::Intrinsic;
  } else {
    return false;
  }
}

Pass1::Pass1()
{
}

DeclarationWithType* 
Pass1::mutate( FunctionDecl *funcDecl )
{
  if( funcDecl->get_statements() ) {
    DeclarationWithType* oldRetval = retval;
    retval = 0;
    if( !LinkageSpec::isBuiltin( funcDecl->get_linkage() ) && isLvalueRet( funcDecl->get_functionType() ) ) {
      retval = funcDecl->get_functionType()->get_returnVals().front();
    }
    // fix expressions and return statements in this function
    funcDecl->set_statements( funcDecl->get_statements()->acceptMutator( *this ) );
    retval = oldRetval;
  }
  return funcDecl;
}

Expression* 
Pass1::mutate( ApplicationExpr *appExpr )
{
  appExpr->get_function()->acceptMutator( *this );
  mutateAll( appExpr->get_args(), *this );
  
  assert( !appExpr->get_function()->get_results().empty() );

  PointerType *pointer = dynamic_cast< PointerType* >( appExpr->get_function()->get_results().front() );
  assert( pointer );
  FunctionType *function = dynamic_cast< FunctionType* >( pointer->get_base() );
  assert( function );

  std::string typeName;
  if( isLvalueRet( function ) && !isIntrinsicApp( appExpr ) ) {
    UntypedExpr *deref = new UntypedExpr( new NameExpr( "*?" ) );
    deref->get_results().push_back( appExpr->get_results().front() );
    appExpr->get_results().front() = new PointerType( Type::Qualifiers(), deref->get_results().front()->clone() );
    deref->get_args().push_back( appExpr );
    return deref;
  } else {
    return appExpr;
  }
}

Statement* 
Pass1::mutate(ReturnStmt *retStmt)
{
  if( retval && retStmt->get_expr() ) {
    assert( !retStmt->get_expr()->get_results().empty() );
    while( CastExpr *castExpr = dynamic_cast< CastExpr* >( retStmt->get_expr() ) ) {
      retStmt->set_expr( castExpr->get_arg() );
      retStmt->get_expr()->set_env( castExpr->get_env() );
      castExpr->set_env( 0 );
      castExpr->set_arg( 0 );
      delete castExpr;
    }
    if( retStmt->get_expr()->get_results().front()->get_isLvalue() ) {
      retStmt->set_expr( new AddressExpr( retStmt->get_expr()->acceptMutator( *this ) ) );
    } else {
      throw SemanticError( "Attempt to return non-lvalue from an lvalue-qualified function" );
    }
  }
  return retStmt;
}

void 
Pass2::visit( FunctionType *funType )
{
  std::string typeName;
  if( isLvalueRet( funType ) ) {
    DeclarationWithType *retParm = funType->get_returnVals().front();
    
    // make a new parameter that is a pointer to the type of the old return value
    retParm->set_type( new PointerType( Type::Qualifiers(), retParm->get_type() ) );
  }
  
  Visitor::visit( funType );
}

} // namespace

} // namespace GenPoly
