//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// Explode.cc --
//
// Author           : Rob Schluntz
// Created On       : Wed Nov 9 13:12:24 2016
// Last Modified By : Rob Schluntz
// Last Modified On : Wed Nov 9 13:20:24 2016
// Update Count     : 2
//

#include "Explode.h"
#include "SynTree/Mutator.h"

namespace Tuples {
	namespace {
		struct AddrExploder : public Mutator {
			bool foundUniqueExpr = false;
			Expression * applyAddr( Expression * expr, bool first = true ) {
				if ( TupleExpr * tupleExpr = dynamic_cast< TupleExpr * >( expr ) ){
					foundUniqueExpr = true;
					std::list< Expression * > exprs;
					for ( Expression *& expr : tupleExpr->get_exprs() ) {
						// move & into tuple exprs
						exprs.push_back( applyAddr( expr, false ) );
					}
					// want the top-level expression to be address-taken, but not nested
					// tuple expressions
					if ( first ) {
						return new AddressExpr( new TupleExpr( exprs ) );
					} else {
						return new TupleExpr( exprs );
					}
				}
				// anything else should be address-taken as normal
				return new AddressExpr( expr->clone() );
			}

			virtual Expression * mutate( UniqueExpr * uniqueExpr ) {
				// move & into unique expr so that the unique expr has type T* rather than
				// type T. In particular, this transformation helps with generating the
				// correct code for address-taken member tuple expressions, since the result
				// should now be a tuple of addresses rather than the address of a tuple.
				// Still, this code is a bit awkward, and could use some improvement.
				if ( dynamic_cast< AddressExpr * > ( uniqueExpr->get_expr() ) ) {
					// this unique expression has already been mutated or otherwise shouldn't be (can't take the address-of an address-of expression)
					return uniqueExpr;
				}
				UniqueExpr * newUniqueExpr = new UniqueExpr( applyAddr( uniqueExpr->get_expr() ), uniqueExpr->get_id() );
				delete uniqueExpr;
				UntypedExpr * deref = UntypedExpr::createDeref( Mutator::mutate( newUniqueExpr ) );
				return deref;
			}

			virtual Expression * mutate( TupleIndexExpr * tupleExpr ) {
				// tuple index expr needs to be rebuilt to ensure that the type of the
				// field is consistent with the type of the tuple expr, since the field
				// may have changed from type T to T*.
				Expression * expr = tupleExpr->get_tuple()->acceptMutator( *this );
				tupleExpr->set_tuple( nullptr );
				TupleIndexExpr * ret = new TupleIndexExpr( expr, tupleExpr->get_index() );
				delete tupleExpr;
				return ret;
			}
		};
	} // namespace

	Expression * distributeAddr( Expression * expr ) {
		AddrExploder addrExploder;
		expr = expr->acceptMutator( addrExploder );
		if ( ! addrExploder.foundUniqueExpr ) {
			expr = new AddressExpr( expr );
		}
		return expr;
	}
} // namespace Tuples

// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
