Index: src/Tuples/Explode.cc
===================================================================
--- src/Tuples/Explode.cc	(revision 7a780ad23506507b6a1feccc46d18e12da183ceb)
+++ 	(revision )
@@ -1,106 +1,0 @@
-//
-// 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 : Andrew Beach
-// Last Modified On : Wed Jun 12 16:40:00 2016
-// Update Count     : 3
-//
-
-#include "Explode.h"
-
-#include "AST/Pass.hpp"          // for Pass
-
-namespace Tuples {
-
-namespace {
-
-// Remove one level of reference from a reference type.
-const ast::Type * getReferenceBase( const ast::Type * t ) {
-	if ( const ast::ReferenceType * ref = dynamic_cast< const ast::ReferenceType * >( t ) ) {
-		return ref->base;
-	} else {
-		assertf( false, "getReferenceBase for non-ref: %s", toString( t ).c_str() );
-		return nullptr;
-	}
-}
-
-struct CastExploderCore {
-	bool castAdded = false;
-	bool foundUniqueExpr = false;
-	const ast::Expr * applyCast( const ast::Expr * expr, bool first = true ) {
-		// On tuple push the cast down.
-		if ( const ast::TupleExpr * tupleExpr = dynamic_cast< const ast::TupleExpr * >( expr ) ) {
-			foundUniqueExpr = true;
-			std::vector< ast::ptr< ast::Expr > > exprs;
-			for ( const ast::Expr * expr : tupleExpr->exprs ) {
-				exprs.emplace_back( applyCast( expr, false ) );
-			}
-			if ( first ) {
-				castAdded = true;
-				const ast::Expr * tuple = new ast::TupleExpr{
-					tupleExpr->location, std::move( exprs ) };
-				return new ast::CastExpr{ tuple, new ast::ReferenceType{ tuple->result } };
-			} else {
-				return new ast::TupleExpr( tupleExpr->location, std::move( exprs ) );
-			}
-		}
-		if ( dynamic_cast< const ast::ReferenceType * >( expr->result.get() ) ) {
-			return expr;
-		} else {
-			castAdded = true;
-			return new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } };
-		}
-	}
-
-	const ast::Expr * postvisit( const ast::UniqueExpr * node ) {
-		// move cast 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 reference-cast member tuple expressions, since the result
-		// should now be a tuple of references rather than a reference to a tuple.
-		// Still, this code is a bit awkward, and could use some improvement.
-		const ast::UniqueExpr * newNode = new ast::UniqueExpr( node->location,
-				applyCast( node->expr ), node->id );
-		if ( castAdded ) {
-			// if a cast was added by applyCast, then unique expr now has one more layer of reference
-			// than it had coming into this function. To ensure types still match correctly, need to cast
-			//  to reference base so that outer expressions are still correct.
-			castAdded = false;
-			const ast::Type * newType = getReferenceBase( newNode->result );
-			return new ast::CastExpr{ newNode->location, newNode, newType };
-		}
-		return newNode;
-	}
-
-	const ast::Expr * postvisit( const ast::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&.
-		return new ast::TupleIndexExpr( tupleExpr->location, tupleExpr->tuple, tupleExpr->index );
-	}
-};
-
-} // namespace
-
-const ast::Expr * distributeReference( const ast::Expr * expr ) {
-	ast::Pass<CastExploderCore> exploder;
-	expr = expr->accept( exploder );
-	if ( ! exploder.core.foundUniqueExpr ) {
-		expr = new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } };
-	}
-	return expr;
-}
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/Explode.cpp
===================================================================
--- src/Tuples/Explode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/Explode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,106 @@
+//
+// 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.cpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Wed Nov 9 13:12:24 2016
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Jun 12 16:40:00 2016
+// Update Count     : 3
+//
+
+#include "Explode.hpp"
+
+#include "AST/Pass.hpp"          // for Pass
+
+namespace Tuples {
+
+namespace {
+
+// Remove one level of reference from a reference type.
+const ast::Type * getReferenceBase( const ast::Type * t ) {
+	if ( const ast::ReferenceType * ref = dynamic_cast< const ast::ReferenceType * >( t ) ) {
+		return ref->base;
+	} else {
+		assertf( false, "getReferenceBase for non-ref: %s", toString( t ).c_str() );
+		return nullptr;
+	}
+}
+
+struct CastExploderCore {
+	bool castAdded = false;
+	bool foundUniqueExpr = false;
+	const ast::Expr * applyCast( const ast::Expr * expr, bool first = true ) {
+		// On tuple push the cast down.
+		if ( const ast::TupleExpr * tupleExpr = dynamic_cast< const ast::TupleExpr * >( expr ) ) {
+			foundUniqueExpr = true;
+			std::vector< ast::ptr< ast::Expr > > exprs;
+			for ( const ast::Expr * expr : tupleExpr->exprs ) {
+				exprs.emplace_back( applyCast( expr, false ) );
+			}
+			if ( first ) {
+				castAdded = true;
+				const ast::Expr * tuple = new ast::TupleExpr{
+					tupleExpr->location, std::move( exprs ) };
+				return new ast::CastExpr{ tuple, new ast::ReferenceType{ tuple->result } };
+			} else {
+				return new ast::TupleExpr( tupleExpr->location, std::move( exprs ) );
+			}
+		}
+		if ( dynamic_cast< const ast::ReferenceType * >( expr->result.get() ) ) {
+			return expr;
+		} else {
+			castAdded = true;
+			return new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } };
+		}
+	}
+
+	const ast::Expr * postvisit( const ast::UniqueExpr * node ) {
+		// move cast 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 reference-cast member tuple expressions, since the result
+		// should now be a tuple of references rather than a reference to a tuple.
+		// Still, this code is a bit awkward, and could use some improvement.
+		const ast::UniqueExpr * newNode = new ast::UniqueExpr( node->location,
+				applyCast( node->expr ), node->id );
+		if ( castAdded ) {
+			// if a cast was added by applyCast, then unique expr now has one more layer of reference
+			// than it had coming into this function. To ensure types still match correctly, need to cast
+			//  to reference base so that outer expressions are still correct.
+			castAdded = false;
+			const ast::Type * newType = getReferenceBase( newNode->result );
+			return new ast::CastExpr{ newNode->location, newNode, newType };
+		}
+		return newNode;
+	}
+
+	const ast::Expr * postvisit( const ast::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&.
+		return new ast::TupleIndexExpr( tupleExpr->location, tupleExpr->tuple, tupleExpr->index );
+	}
+};
+
+} // namespace
+
+const ast::Expr * distributeReference( const ast::Expr * expr ) {
+	ast::Pass<CastExploderCore> exploder;
+	expr = expr->accept( exploder );
+	if ( ! exploder.core.foundUniqueExpr ) {
+		expr = new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } };
+	}
+	return expr;
+}
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/Explode.h
===================================================================
--- src/Tuples/Explode.h	(revision 7a780ad23506507b6a1feccc46d18e12da183ceb)
+++ 	(revision )
@@ -1,148 +1,0 @@
-//
-// 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.h --
-//
-// Author           : Rob Schluntz
-// Created On       : Wed Nov 9 13:12:24 2016
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Jun 17 14:36:00 2019
-// Update Count     : 4
-//
-
-#pragma once
-
-#include <iterator>                     // for back_inserter, back_insert_iterator
-#include <utility>                      // for forward
-
-#include "AST/Expr.hpp"
-#include "ResolvExpr/Candidate.hpp"     // for Candidate, CandidateList
-#include "ResolvExpr/ExplodedArg.hpp"   // for ExplodedArg
-#include "Tuples.h"                     // for maybeImpure
-
-namespace ast {
-	class SymbolTable;
-}
-
-namespace Tuples {
-
-const ast::Expr * distributeReference( const ast::Expr * );
-
-/// Append candidate to an OutputIterator of Candidates.
-template<typename OutputIterator>
-void append( OutputIterator out, const ast::Expr * expr, const ast::TypeEnvironment & env,
-		const ast::OpenVarSet & open, const ast::AssertionList & need,
-		const ResolvExpr::Cost & cost, const ResolvExpr::Cost & cvtCost ) {
-	ast::TypeEnvironment copyEnv = env;
-	ast::OpenVarSet copyOpen = open;
-	ast::AssertionSet set;
-	mergeAssertionSet( set, need );
-	*out++ = std::make_shared<ResolvExpr::Candidate>( expr, std::move( copyEnv ),
-		std::move( copyOpen ), std::move( set ), cost, cvtCost );
-}
-
-/// Append candidate to an ExplodedArg.
-static inline void append( ResolvExpr::ExplodedArg& ea, const ast::Expr * expr,
-		const ast::TypeEnvironment&, const ast::OpenVarSet&,
-		const ast::AssertionList&, const ResolvExpr::Cost&, const ResolvExpr::Cost& ) {
-	// I'm not sure why most of the arguments are unused. But they were in the old version.
-	ea.exprs.emplace_back( expr );
-}
-
-/// Check if the expression is a cast to a reference type, return it if it is.
-static inline const ast::CastExpr * isReferenceCast( const ast::Expr * expr ) {
-	if ( const ast::CastExpr * cast = dynamic_cast< const ast::CastExpr * >( expr ) ) {
-		if ( dynamic_cast< const ast::ReferenceType * >( cast->result.get() ) ) {
-			return cast;
-		}
-	}
-	return nullptr;
-}
-
-/// helper function (indirectely) used by explode
-template< typename Output >
-void explodeRecursive(
-	const ast::CastExpr *, const ResolvExpr::Candidate &,
-	const ast::SymbolTable &, Output &&
-) {
-}
-
-/// helper function used by explode
-template< typename Output >
-void explodeUnique(
-	const ast::ptr< ast::Expr > & expr, const ResolvExpr::Candidate & arg,
-	const ast::SymbolTable & symtab, Output && out, bool isTupleAssign
-) {
-	// Tuple assignment can use a faster method if it is cast. Uses recursive exploding.
-	if ( isTupleAssign ) if ( const ast::CastExpr * castExpr = isReferenceCast( expr ) ) {
-		ResolvExpr::CandidateList candidates;
-		explodeUnique( castExpr->arg, arg, symtab, back_inserter( candidates ), true );
-		for ( ResolvExpr::CandidateRef & cand : candidates ) {
-			// Distribute the reference cast over all components of the candidate.
-			append( std::forward<Output>(out), distributeReference( cand->expr ), cand->env,
-				cand->open, cand->need, cand->cost, cand->cvtCost );
-		}
-		return;
-	}
-	const ast::Type * res = expr->result->stripReferences();
-	if ( const ast::TupleType * tupleType = dynamic_cast< const ast::TupleType * >( res ) ) {
-		if ( const ast::ptr< ast::TupleExpr > & tupleExpr = expr.as< ast::TupleExpr >() ) {
-			// Open the tuple expr and continue on its components.
-			for ( const ast::Expr * expr : tupleExpr->exprs ) {
-				explodeUnique( expr, arg, symtab, std::forward<Output>(out), isTupleAssign );
-			}
-		} else {
-			ast::ptr< ast::Expr > local = expr;
-			// Expressions which may have side effects require a single unique instance.
-			if ( Tuples::maybeImpureIgnoreUnique( local ) ) {
-				local = new ast::UniqueExpr( local->location, local );
-			}
-			// Cast a reference away to a value-type to allow further explosion.
-			if ( local->result.as< ast::ReferenceType >() ) {
-				local = new ast::CastExpr{ local, tupleType };
-			}
-			// Now we have to go across the tuple via indexing.
-			for ( unsigned int i = 0 ; i < tupleType->size() ; ++i ) {
-				ast::TupleIndexExpr * idx = new ast::TupleIndexExpr( local->location, local, i );
-				explodeUnique( idx, arg, symtab, std::forward<Output>(out), isTupleAssign );
-				// TODO: We need more input to figure out the exact lifetimes of these types.
-				// delete idx;
-			}
-		}
-	} else {
-		// For atomic/non-tuple types, no explosion is used.
-		append( std::forward<Output>(out), expr, arg.env, arg.open, arg.need, arg.cost,
-			arg.cvtCost );
-	}
-}
-
-/// expands a tuple-valued candidate into multiple candidates, each with a non-tuple type
-template< typename Output >
-void explode(
-	const ResolvExpr::Candidate & arg, const ast::SymbolTable & symtab, Output && out,
-	bool isTupleAssign = false
-) {
-	explodeUnique( arg.expr, arg, symtab, std::forward< Output >( out ), isTupleAssign );
-}
-
-/// explode list of candidates into flattened list of candidates
-template< typename Output >
-void explode(
-	const ResolvExpr::CandidateList & cands, const ast::SymbolTable & symtab, Output && out,
-	bool isTupleAssign = false
-) {
-	for ( const ResolvExpr::CandidateRef & cand : cands ) {
-		explode( *cand, symtab, std::forward< Output >( out ), isTupleAssign );
-	}
-}
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/Explode.hpp
===================================================================
--- src/Tuples/Explode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/Explode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,148 @@
+//
+// 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.hpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Wed Nov 9 13:12:24 2016
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Jun 17 14:36:00 2019
+// Update Count     : 4
+//
+
+#pragma once
+
+#include <iterator>                     // for back_inserter, back_insert_iterator
+#include <utility>                      // for forward
+
+#include "AST/Expr.hpp"
+#include "ResolvExpr/Candidate.hpp"     // for Candidate, CandidateList
+#include "ResolvExpr/ExplodedArg.hpp"   // for ExplodedArg
+#include "Tuples.hpp"                   // for maybeImpure
+
+namespace ast {
+	class SymbolTable;
+}
+
+namespace Tuples {
+
+const ast::Expr * distributeReference( const ast::Expr * );
+
+/// Append candidate to an OutputIterator of Candidates.
+template<typename OutputIterator>
+void append( OutputIterator out, const ast::Expr * expr, const ast::TypeEnvironment & env,
+		const ast::OpenVarSet & open, const ast::AssertionList & need,
+		const ResolvExpr::Cost & cost, const ResolvExpr::Cost & cvtCost ) {
+	ast::TypeEnvironment copyEnv = env;
+	ast::OpenVarSet copyOpen = open;
+	ast::AssertionSet set;
+	mergeAssertionSet( set, need );
+	*out++ = std::make_shared<ResolvExpr::Candidate>( expr, std::move( copyEnv ),
+		std::move( copyOpen ), std::move( set ), cost, cvtCost );
+}
+
+/// Append candidate to an ExplodedArg.
+static inline void append( ResolvExpr::ExplodedArg& ea, const ast::Expr * expr,
+		const ast::TypeEnvironment&, const ast::OpenVarSet&,
+		const ast::AssertionList&, const ResolvExpr::Cost&, const ResolvExpr::Cost& ) {
+	// I'm not sure why most of the arguments are unused. But they were in the old version.
+	ea.exprs.emplace_back( expr );
+}
+
+/// Check if the expression is a cast to a reference type, return it if it is.
+static inline const ast::CastExpr * isReferenceCast( const ast::Expr * expr ) {
+	if ( const ast::CastExpr * cast = dynamic_cast< const ast::CastExpr * >( expr ) ) {
+		if ( dynamic_cast< const ast::ReferenceType * >( cast->result.get() ) ) {
+			return cast;
+		}
+	}
+	return nullptr;
+}
+
+/// helper function (indirectely) used by explode
+template< typename Output >
+void explodeRecursive(
+	const ast::CastExpr *, const ResolvExpr::Candidate &,
+	const ast::SymbolTable &, Output &&
+) {
+}
+
+/// helper function used by explode
+template< typename Output >
+void explodeUnique(
+	const ast::ptr< ast::Expr > & expr, const ResolvExpr::Candidate & arg,
+	const ast::SymbolTable & symtab, Output && out, bool isTupleAssign
+) {
+	// Tuple assignment can use a faster method if it is cast. Uses recursive exploding.
+	if ( isTupleAssign ) if ( const ast::CastExpr * castExpr = isReferenceCast( expr ) ) {
+		ResolvExpr::CandidateList candidates;
+		explodeUnique( castExpr->arg, arg, symtab, back_inserter( candidates ), true );
+		for ( ResolvExpr::CandidateRef & cand : candidates ) {
+			// Distribute the reference cast over all components of the candidate.
+			append( std::forward<Output>(out), distributeReference( cand->expr ), cand->env,
+				cand->open, cand->need, cand->cost, cand->cvtCost );
+		}
+		return;
+	}
+	const ast::Type * res = expr->result->stripReferences();
+	if ( const ast::TupleType * tupleType = dynamic_cast< const ast::TupleType * >( res ) ) {
+		if ( const ast::ptr< ast::TupleExpr > & tupleExpr = expr.as< ast::TupleExpr >() ) {
+			// Open the tuple expr and continue on its components.
+			for ( const ast::Expr * expr : tupleExpr->exprs ) {
+				explodeUnique( expr, arg, symtab, std::forward<Output>(out), isTupleAssign );
+			}
+		} else {
+			ast::ptr< ast::Expr > local = expr;
+			// Expressions which may have side effects require a single unique instance.
+			if ( Tuples::maybeImpureIgnoreUnique( local ) ) {
+				local = new ast::UniqueExpr( local->location, local );
+			}
+			// Cast a reference away to a value-type to allow further explosion.
+			if ( local->result.as< ast::ReferenceType >() ) {
+				local = new ast::CastExpr{ local, tupleType };
+			}
+			// Now we have to go across the tuple via indexing.
+			for ( unsigned int i = 0 ; i < tupleType->size() ; ++i ) {
+				ast::TupleIndexExpr * idx = new ast::TupleIndexExpr( local->location, local, i );
+				explodeUnique( idx, arg, symtab, std::forward<Output>(out), isTupleAssign );
+				// TODO: We need more input to figure out the exact lifetimes of these types.
+				// delete idx;
+			}
+		}
+	} else {
+		// For atomic/non-tuple types, no explosion is used.
+		append( std::forward<Output>(out), expr, arg.env, arg.open, arg.need, arg.cost,
+			arg.cvtCost );
+	}
+}
+
+/// expands a tuple-valued candidate into multiple candidates, each with a non-tuple type
+template< typename Output >
+void explode(
+	const ResolvExpr::Candidate & arg, const ast::SymbolTable & symtab, Output && out,
+	bool isTupleAssign = false
+) {
+	explodeUnique( arg.expr, arg, symtab, std::forward< Output >( out ), isTupleAssign );
+}
+
+/// explode list of candidates into flattened list of candidates
+template< typename Output >
+void explode(
+	const ResolvExpr::CandidateList & cands, const ast::SymbolTable & symtab, Output && out,
+	bool isTupleAssign = false
+) {
+	for ( const ResolvExpr::CandidateRef & cand : cands ) {
+		explode( *cand, symtab, std::forward< Output >( out ), isTupleAssign );
+	}
+}
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/TupleAssignment.cc
===================================================================
--- src/Tuples/TupleAssignment.cc	(revision 7a780ad23506507b6a1feccc46d18e12da183ceb)
+++ 	(revision )
@@ -1,394 +1,0 @@
-//
-// 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.
-//
-// TupleAssignment.cc --
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Mar 16 14:06:00 2022
-// Update Count     : 10
-//
-
-#include "Tuples.h"
-
-#include <algorithm>                       // for transform
-#include <cassert>                         // for assert
-#include <iterator>                        // for back_insert_iterator, back...
-#include <list>                            // for _List_const_iterator, _Lis...
-#include <memory>                          // for unique_ptr, allocator_trai...
-#include <string>                          // for string
-#include <vector>
-
-#include "AST/Decl.hpp"
-#include "AST/Init.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Stmt.hpp"
-#include "AST/TypeEnvironment.hpp"
-#include "CodeGen/OperatorTable.h"
-#include "Common/UniqueName.h"             // for UniqueName
-#include "Common/utility.h"                // for splice, zipWith
-#include "Explode.h"                       // for explode
-#include "InitTweak/GenInit.h"             // for genCtorInit
-#include "InitTweak/InitTweak.h"           // for getPointerBase, isAssignment
-#include "ResolvExpr/CandidateFinder.hpp"  // for CandidateFinder
-#include "ResolvExpr/Cost.h"               // for Cost
-#include "ResolvExpr/Resolver.h"           // for resolveCtorInit
-#include "ResolvExpr/typeops.h"            // for combos
-
-#if 0
-#define PRINT(x) x
-#else
-#define PRINT(x)
-#endif
-
-namespace Tuples {
-
-namespace {
-
-/// Checks if `expr` is of tuple type.
-bool isTuple( const ast::Expr * expr ) {
-	if ( !expr ) return false;
-	assert( expr->result );
-	return dynamic_cast< const ast::TupleType * >( expr->result->stripReferences() );
-}
-
-/// Checks if `expr` is of tuple type or a cast to one.
-bool refToTuple( const ast::Expr * expr ) {
-	assert( expr->result );
-	// Check for function returning tuple of reference types.
-	if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
-		return refToTuple( castExpr->arg );
-	} else {
-		return isTuple( expr );
-	}
-}
-
-/// Dispatcher for tuple (multiple and mass) assignment operations.
-class TupleAssignSpotter final {
-	/// Actually finds tuple assignment operations, by subclass.
-	struct Matcher {
-		ResolvExpr::CandidateList lhs, rhs;
-		TupleAssignSpotter & spotter;
-		CodeLocation location;
-		ResolvExpr::Cost baseCost;
-		std::vector< ast::ptr< ast::ObjectDecl > > tmpDecls;
-		ast::TypeEnvironment env;
-		ast::OpenVarSet open;
-		ast::AssertionSet need;
-
-		void combineState( const ResolvExpr::Candidate & cand ) {
-			env.simpleCombine( cand.env );
-			ast::mergeOpenVars( open, cand.open );
-			need.insert( cand.need.begin(), cand.need.end() );
-		}
-
-		Matcher(
-			TupleAssignSpotter & s, const CodeLocation & loc,
-			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
-		: lhs( l ), rhs( r ), spotter( s ), location( loc ),
-		  baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ), tmpDecls(),
-		  env(), open(), need() {
-			for ( auto & cand : lhs ) combineState( *cand );
-			for ( auto & cand : rhs ) combineState( *cand );
-		}
-		virtual ~Matcher() = default;
-
-		virtual std::vector< ast::ptr< ast::Expr > > match() = 0;
-
-		/// Removes environments from subexpressions within statement expressions, which could
-		/// throw off later passes like those in Box which rely on PolyMutator, and adds the
-		/// bindings to the env.
-		struct EnvRemover {
-			/// Environment to hoist ExprStmt environments to.
-			ast::TypeEnvironment & tenv;
-
-			EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {}
-
-			const ast::ExprStmt * previsit( const ast::ExprStmt * stmt ) {
-				if ( stmt->expr->env ) {
-					tenv.add( *stmt->expr->env );
-					ast::ExprStmt * mut = mutate( stmt );
-					mut->expr.get_and_mutate()->env = nullptr;
-					return mut;
-				}
-				return stmt;
-			}
-		};
-
-		ast::ObjectDecl * newObject( UniqueName & namer, const ast::Expr * expr ) {
-			assert( expr->result && !expr->result->isVoid() );
-
-			ast::ObjectDecl * ret = new ast::ObjectDecl(
-				location, namer.newName(), expr->result, new ast::SingleInit( location, expr ),
-				ast::Storage::Classes{}, ast::Linkage::Cforall );
-
-			// If expression type is a reference, just need an initializer, otherwise construct.
-			if ( ! expr->result.as< ast::ReferenceType >() ) {
-				// Resolve ctor/dtor for the new object.
-				ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
-						InitTweak::genCtorInit( location, ret ), spotter.crntFinder.context );
-				// Remove environments from subexpressions of stmtExpr.
-				ast::Pass< EnvRemover > rm( env );
-				ret->init = ctorInit->accept( rm );
-			}
-
-			PRINT( std::cerr << "new object: " << ret << std::endl; )
-			return ret;
-		}
-
-		ast::UntypedExpr * createFunc(
-			const std::string & fname, const ast::ObjectDecl * left,
-			const ast::ObjectDecl * right
-		) {
-			assert( left );
-			std::vector< ast::ptr< ast::Expr > > args;
-			args.emplace_back( new ast::VariableExpr( location, left ) );
-			if ( right ) { args.emplace_back( new ast::VariableExpr( location, right ) ); }
-
-			if ( left->type->referenceDepth() > 1 && CodeGen::isConstructor( fname ) ) {
-				args.front() = new ast::AddressExpr( location, args.front() );
-				if ( right ) { args.back() = new ast::AddressExpr( location, args.back() ); }
-				return new ast::UntypedExpr(
-					location, new ast::NameExpr( location, "?=?" ), std::move( args ) );
-			} else {
-				return new ast::UntypedExpr(
-					location, new ast::NameExpr( location, fname ), std::move( args ) );
-			}
-		}
-	};
-
-	/// Finds mass-assignment operations.
-	struct MassAssignMatcher final : public Matcher {
-		MassAssignMatcher(
-			TupleAssignSpotter & s, const CodeLocation & loc,
-			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
-		: Matcher( s, loc, l, r ) {}
-
-		std::vector< ast::ptr< ast::Expr > > match() override {
-			static UniqueName lhsNamer( "__massassign_L" );
-			static UniqueName rhsNamer( "__massassign_R" );
-			// Empty tuple case falls into this matcher.
-			assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 );
-
-			ast::ptr< ast::ObjectDecl > rtmp =
-				1 == rhs.size() ? newObject( rhsNamer, rhs.front()->expr ) : nullptr;
-
-			std::vector< ast::ptr< ast::Expr > > out;
-			for ( ResolvExpr::CandidateRef & lhsCand : lhs ) {
-				// Create a temporary object for each value in
-				// the LHS and create a call involving the RHS.
-				ast::ptr< ast::ObjectDecl > ltmp = newObject( lhsNamer, lhsCand->expr );
-				out.emplace_back( createFunc( spotter.fname, ltmp, rtmp ) );
-				tmpDecls.emplace_back( std::move( ltmp ) );
-			}
-			if ( rtmp ) tmpDecls.emplace_back( std::move( rtmp ) );
-
-			return out;
-		}
-	};
-
-	/// Finds multiple-assignment operations.
-	struct MultipleAssignMatcher final : public Matcher {
-		MultipleAssignMatcher(
-			TupleAssignSpotter & s, const CodeLocation & loc,
-			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
-		: Matcher( s, loc, l, r ) {}
-
-		std::vector< ast::ptr< ast::Expr > > match() override {
-			static UniqueName lhsNamer( "__multassign_L" );
-			static UniqueName rhsNamer( "__multassign_R" );
-
-			if ( lhs.size() != rhs.size() ) return {};
-
-			// Produce a new temporary object for each value in
-			// the LHS and RHS and pairwise create the calls.
-			std::vector< ast::ptr< ast::ObjectDecl > > ltmp, rtmp;
-
-			std::vector< ast::ptr< ast::Expr > > out;
-			for ( unsigned i = 0; i < lhs.size(); ++i ) {
-				ResolvExpr::CandidateRef & lhsCand = lhs[i];
-				ResolvExpr::CandidateRef & rhsCand = rhs[i];
-
-				// Convert RHS to LHS type minus one reference --
-				// important for case where LHS is && and RHS is lvalue.
-				auto lhsType = lhsCand->expr->result.strict_as< ast::ReferenceType >();
-				rhsCand->expr = new ast::CastExpr( rhsCand->expr, lhsType->base );
-				ast::ptr< ast::ObjectDecl > lobj = newObject( lhsNamer, lhsCand->expr );
-				ast::ptr< ast::ObjectDecl > robj = newObject( rhsNamer, rhsCand->expr );
-				out.emplace_back( createFunc( spotter.fname, lobj, robj ) );
-				ltmp.emplace_back( std::move( lobj ) );
-				rtmp.emplace_back( std::move( robj ) );
-
-				// Resolve the cast expression so that rhsCand return type is bound
-				// by the cast type as needed, and transfer the resulting environment.
-				ResolvExpr::CandidateFinder finder( spotter.crntFinder.context, env );
-				finder.find( rhsCand->expr, ResolvExpr::ResolveMode::withAdjustment() );
-				assert( 1 == finder.candidates.size() );
-				env = std::move( finder.candidates.front()->env );
-			}
-
-			splice( tmpDecls, ltmp );
-			splice( tmpDecls, rtmp );
-
-			return out;
-		}
-	};
-
-	ResolvExpr::CandidateFinder & crntFinder;
-	std::string fname;
-	std::unique_ptr< Matcher > matcher;
-
-public:
-	TupleAssignSpotter( ResolvExpr::CandidateFinder & f )
-	: crntFinder( f ), fname(), matcher() {}
-
-	// Find left- and right-hand-sides for mass or multiple assignment.
-	void spot(
-		const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args
-	) {
-		if ( auto op = expr->func.as< ast::NameExpr >() ) {
-			// Skip non-assignment functions.
-			if ( !CodeGen::isCtorDtorAssign( op->name ) ) return;
-			fname = op->name;
-
-			// Handled by CandidateFinder if applicable (both odd cases).
-			if ( args.empty() || ( 1 == args.size() && CodeGen::isAssignment( fname ) ) ) {
-				return;
-			}
-
-			// Look over all possible left-hand-side.
-			for ( ResolvExpr::CandidateRef & lhsCand : args[0] ) {
-				// Skip non-tuple LHS.
-				if ( !refToTuple( lhsCand->expr ) ) continue;
-
-				// Explode is aware of casts - ensure every LHS
-				// is sent into explode with a reference cast.
-				if ( !lhsCand->expr.as< ast::CastExpr >() ) {
-					lhsCand->expr = new ast::CastExpr(
-						lhsCand->expr, new ast::ReferenceType( lhsCand->expr->result ) );
-				}
-
-				// Explode the LHS so that each field of a tuple-valued expr is assigned.
-				ResolvExpr::CandidateList lhs;
-				explode( *lhsCand, crntFinder.context.symtab, back_inserter(lhs), true );
-				for ( ResolvExpr::CandidateRef & cand : lhs ) {
-					// Each LHS value must be a reference - some come in
-					// with a cast, if not just cast to reference here.
-					if ( !cand->expr->result.as< ast::ReferenceType >() ) {
-						cand->expr = new ast::CastExpr(
-							cand->expr, new ast::ReferenceType( cand->expr->result ) );
-					}
-				}
-
-				if ( 1 == args.size() ) {
-					// Mass default-initialization/destruction.
-					ResolvExpr::CandidateList rhs{};
-					matcher.reset( new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
-					match();
-				} else if ( 2 == args.size() ) {
-					for ( const ResolvExpr::CandidateRef & rhsCand : args[1] ) {
-						ResolvExpr::CandidateList rhs;
-						if ( isTuple( rhsCand->expr ) ) {
-							// Multiple assignment:
-							explode( *rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
-							matcher.reset(
-								new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
-						} else {
-							// Mass assignment:
-							rhs.emplace_back( rhsCand );
-							matcher.reset(
-								new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
-						}
-						match();
-					}
-				} else {
-					// Expand all possible RHS possibilities.
-					std::vector< ResolvExpr::CandidateList > rhsCands;
-					combos(
-						std::next( args.begin(), 1 ), args.end(), back_inserter( rhsCands ) );
-					for ( const ResolvExpr::CandidateList & rhsCand : rhsCands ) {
-						// Multiple assignment:
-						ResolvExpr::CandidateList rhs;
-						explode( rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
-						matcher.reset(
-							new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
-						match();
-					}
-				}
-			}
-		}
-	}
-
-	void match() {
-		assert( matcher );
-
-		std::vector< ast::ptr< ast::Expr > > newAssigns = matcher->match();
-
-		if ( !( matcher->lhs.empty() && matcher->rhs.empty() ) ) {
-			// If both LHS and RHS are empty than this is the empty tuple
-			// case, wherein it's okay for newAssigns to be empty. Otherwise,
-			// return early so that no new candidates are generated.
-			if ( newAssigns.empty() ) return;
-		}
-
-		ResolvExpr::CandidateList crnt;
-		// Now resolve new assignments.
-		for ( const ast::Expr * expr : newAssigns ) {
-			PRINT(
-				std::cerr << "== resolving tuple assign ==" << std::endl;
-				std::cerr << expr << std::endl;
-			)
-
-			ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env );
-			finder.allowVoid = true;
-
-			try {
-				finder.find( expr, ResolvExpr::ResolveMode::withAdjustment() );
-			} catch (...) {
-				// No match is not failure, just that this tuple assignment is invalid.
-				return;
-			}
-
-			ResolvExpr::CandidateList & cands = finder.candidates;
-			assert( 1 == cands.size() );
-			assert( cands.front()->expr );
-			crnt.emplace_back( std::move( cands.front() ) );
-		}
-
-		// extract expressions from the assignment candidates to produce a list of assignments
-		// that together form a sigle candidate
-		std::vector< ast::ptr< ast::Expr > > solved;
-		for ( ResolvExpr::CandidateRef & cand : crnt ) {
-			solved.emplace_back( cand->expr );
-			matcher->combineState( *cand );
-		}
-
-		crntFinder.candidates.emplace_back( std::make_shared< ResolvExpr::Candidate >(
-			new ast::TupleAssignExpr(
-				matcher->location, std::move( solved ), std::move( matcher->tmpDecls ) ),
-			std::move( matcher->env ), std::move( matcher->open ), std::move( matcher->need ),
-			ResolvExpr::sumCost( crnt ) + matcher->baseCost ) );
-	}
-};
-
-} // anonymous namespace
-
-void handleTupleAssignment(
-	ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
-	std::vector< ResolvExpr::CandidateFinder > & args
-) {
-	TupleAssignSpotter spotter( finder );
-	spotter.spot( assign, args );
-}
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/TupleAssignment.cpp
===================================================================
--- src/Tuples/TupleAssignment.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/TupleAssignment.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,394 @@
+//
+// 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.
+//
+// TupleAssignment.cpp --
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Mar 16 14:06:00 2022
+// Update Count     : 10
+//
+
+#include "Tuples.hpp"
+
+#include <algorithm>                       // for transform
+#include <cassert>                         // for assert
+#include <iterator>                        // for back_insert_iterator, back...
+#include <list>                            // for _List_const_iterator, _Lis...
+#include <memory>                          // for unique_ptr, allocator_trai...
+#include <string>                          // for string
+#include <vector>
+
+#include "AST/Decl.hpp"
+#include "AST/Init.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/TypeEnvironment.hpp"
+#include "CodeGen/OperatorTable.hpp"
+#include "Common/UniqueName.hpp"           // for UniqueName
+#include "Common/Utility.hpp"              // for splice, zipWith
+#include "Explode.hpp"                     // for explode
+#include "InitTweak/GenInit.hpp"           // for genCtorInit
+#include "InitTweak/InitTweak.hpp"         // for getPointerBase, isAssignment
+#include "ResolvExpr/CandidateFinder.hpp"  // for CandidateFinder
+#include "ResolvExpr/Cost.hpp"             // for Cost
+#include "ResolvExpr/Resolver.hpp"         // for resolveCtorInit
+#include "ResolvExpr/Typeops.hpp"          // for combos
+
+#if 0
+#define PRINT(x) x
+#else
+#define PRINT(x)
+#endif
+
+namespace Tuples {
+
+namespace {
+
+/// Checks if `expr` is of tuple type.
+bool isTuple( const ast::Expr * expr ) {
+	if ( !expr ) return false;
+	assert( expr->result );
+	return dynamic_cast< const ast::TupleType * >( expr->result->stripReferences() );
+}
+
+/// Checks if `expr` is of tuple type or a cast to one.
+bool refToTuple( const ast::Expr * expr ) {
+	assert( expr->result );
+	// Check for function returning tuple of reference types.
+	if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
+		return refToTuple( castExpr->arg );
+	} else {
+		return isTuple( expr );
+	}
+}
+
+/// Dispatcher for tuple (multiple and mass) assignment operations.
+class TupleAssignSpotter final {
+	/// Actually finds tuple assignment operations, by subclass.
+	struct Matcher {
+		ResolvExpr::CandidateList lhs, rhs;
+		TupleAssignSpotter & spotter;
+		CodeLocation location;
+		ResolvExpr::Cost baseCost;
+		std::vector< ast::ptr< ast::ObjectDecl > > tmpDecls;
+		ast::TypeEnvironment env;
+		ast::OpenVarSet open;
+		ast::AssertionSet need;
+
+		void combineState( const ResolvExpr::Candidate & cand ) {
+			env.simpleCombine( cand.env );
+			ast::mergeOpenVars( open, cand.open );
+			need.insert( cand.need.begin(), cand.need.end() );
+		}
+
+		Matcher(
+			TupleAssignSpotter & s, const CodeLocation & loc,
+			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
+		: lhs( l ), rhs( r ), spotter( s ), location( loc ),
+		  baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ), tmpDecls(),
+		  env(), open(), need() {
+			for ( auto & cand : lhs ) combineState( *cand );
+			for ( auto & cand : rhs ) combineState( *cand );
+		}
+		virtual ~Matcher() = default;
+
+		virtual std::vector< ast::ptr< ast::Expr > > match() = 0;
+
+		/// Removes environments from subexpressions within statement expressions, which could
+		/// throw off later passes like those in Box which rely on PolyMutator, and adds the
+		/// bindings to the env.
+		struct EnvRemover {
+			/// Environment to hoist ExprStmt environments to.
+			ast::TypeEnvironment & tenv;
+
+			EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {}
+
+			const ast::ExprStmt * previsit( const ast::ExprStmt * stmt ) {
+				if ( stmt->expr->env ) {
+					tenv.add( *stmt->expr->env );
+					ast::ExprStmt * mut = mutate( stmt );
+					mut->expr.get_and_mutate()->env = nullptr;
+					return mut;
+				}
+				return stmt;
+			}
+		};
+
+		ast::ObjectDecl * newObject( UniqueName & namer, const ast::Expr * expr ) {
+			assert( expr->result && !expr->result->isVoid() );
+
+			ast::ObjectDecl * ret = new ast::ObjectDecl(
+				location, namer.newName(), expr->result, new ast::SingleInit( location, expr ),
+				ast::Storage::Classes{}, ast::Linkage::Cforall );
+
+			// If expression type is a reference, just need an initializer, otherwise construct.
+			if ( ! expr->result.as< ast::ReferenceType >() ) {
+				// Resolve ctor/dtor for the new object.
+				ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
+						InitTweak::genCtorInit( location, ret ), spotter.crntFinder.context );
+				// Remove environments from subexpressions of stmtExpr.
+				ast::Pass< EnvRemover > rm( env );
+				ret->init = ctorInit->accept( rm );
+			}
+
+			PRINT( std::cerr << "new object: " << ret << std::endl; )
+			return ret;
+		}
+
+		ast::UntypedExpr * createFunc(
+			const std::string & fname, const ast::ObjectDecl * left,
+			const ast::ObjectDecl * right
+		) {
+			assert( left );
+			std::vector< ast::ptr< ast::Expr > > args;
+			args.emplace_back( new ast::VariableExpr( location, left ) );
+			if ( right ) { args.emplace_back( new ast::VariableExpr( location, right ) ); }
+
+			if ( left->type->referenceDepth() > 1 && CodeGen::isConstructor( fname ) ) {
+				args.front() = new ast::AddressExpr( location, args.front() );
+				if ( right ) { args.back() = new ast::AddressExpr( location, args.back() ); }
+				return new ast::UntypedExpr(
+					location, new ast::NameExpr( location, "?=?" ), std::move( args ) );
+			} else {
+				return new ast::UntypedExpr(
+					location, new ast::NameExpr( location, fname ), std::move( args ) );
+			}
+		}
+	};
+
+	/// Finds mass-assignment operations.
+	struct MassAssignMatcher final : public Matcher {
+		MassAssignMatcher(
+			TupleAssignSpotter & s, const CodeLocation & loc,
+			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
+		: Matcher( s, loc, l, r ) {}
+
+		std::vector< ast::ptr< ast::Expr > > match() override {
+			static UniqueName lhsNamer( "__massassign_L" );
+			static UniqueName rhsNamer( "__massassign_R" );
+			// Empty tuple case falls into this matcher.
+			assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 );
+
+			ast::ptr< ast::ObjectDecl > rtmp =
+				1 == rhs.size() ? newObject( rhsNamer, rhs.front()->expr ) : nullptr;
+
+			std::vector< ast::ptr< ast::Expr > > out;
+			for ( ResolvExpr::CandidateRef & lhsCand : lhs ) {
+				// Create a temporary object for each value in
+				// the LHS and create a call involving the RHS.
+				ast::ptr< ast::ObjectDecl > ltmp = newObject( lhsNamer, lhsCand->expr );
+				out.emplace_back( createFunc( spotter.fname, ltmp, rtmp ) );
+				tmpDecls.emplace_back( std::move( ltmp ) );
+			}
+			if ( rtmp ) tmpDecls.emplace_back( std::move( rtmp ) );
+
+			return out;
+		}
+	};
+
+	/// Finds multiple-assignment operations.
+	struct MultipleAssignMatcher final : public Matcher {
+		MultipleAssignMatcher(
+			TupleAssignSpotter & s, const CodeLocation & loc,
+			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
+		: Matcher( s, loc, l, r ) {}
+
+		std::vector< ast::ptr< ast::Expr > > match() override {
+			static UniqueName lhsNamer( "__multassign_L" );
+			static UniqueName rhsNamer( "__multassign_R" );
+
+			if ( lhs.size() != rhs.size() ) return {};
+
+			// Produce a new temporary object for each value in
+			// the LHS and RHS and pairwise create the calls.
+			std::vector< ast::ptr< ast::ObjectDecl > > ltmp, rtmp;
+
+			std::vector< ast::ptr< ast::Expr > > out;
+			for ( unsigned i = 0; i < lhs.size(); ++i ) {
+				ResolvExpr::CandidateRef & lhsCand = lhs[i];
+				ResolvExpr::CandidateRef & rhsCand = rhs[i];
+
+				// Convert RHS to LHS type minus one reference --
+				// important for case where LHS is && and RHS is lvalue.
+				auto lhsType = lhsCand->expr->result.strict_as< ast::ReferenceType >();
+				rhsCand->expr = new ast::CastExpr( rhsCand->expr, lhsType->base );
+				ast::ptr< ast::ObjectDecl > lobj = newObject( lhsNamer, lhsCand->expr );
+				ast::ptr< ast::ObjectDecl > robj = newObject( rhsNamer, rhsCand->expr );
+				out.emplace_back( createFunc( spotter.fname, lobj, robj ) );
+				ltmp.emplace_back( std::move( lobj ) );
+				rtmp.emplace_back( std::move( robj ) );
+
+				// Resolve the cast expression so that rhsCand return type is bound
+				// by the cast type as needed, and transfer the resulting environment.
+				ResolvExpr::CandidateFinder finder( spotter.crntFinder.context, env );
+				finder.find( rhsCand->expr, ResolvExpr::ResolveMode::withAdjustment() );
+				assert( 1 == finder.candidates.size() );
+				env = std::move( finder.candidates.front()->env );
+			}
+
+			splice( tmpDecls, ltmp );
+			splice( tmpDecls, rtmp );
+
+			return out;
+		}
+	};
+
+	ResolvExpr::CandidateFinder & crntFinder;
+	std::string fname;
+	std::unique_ptr< Matcher > matcher;
+
+public:
+	TupleAssignSpotter( ResolvExpr::CandidateFinder & f )
+	: crntFinder( f ), fname(), matcher() {}
+
+	// Find left- and right-hand-sides for mass or multiple assignment.
+	void spot(
+		const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args
+	) {
+		if ( auto op = expr->func.as< ast::NameExpr >() ) {
+			// Skip non-assignment functions.
+			if ( !CodeGen::isCtorDtorAssign( op->name ) ) return;
+			fname = op->name;
+
+			// Handled by CandidateFinder if applicable (both odd cases).
+			if ( args.empty() || ( 1 == args.size() && CodeGen::isAssignment( fname ) ) ) {
+				return;
+			}
+
+			// Look over all possible left-hand-side.
+			for ( ResolvExpr::CandidateRef & lhsCand : args[0] ) {
+				// Skip non-tuple LHS.
+				if ( !refToTuple( lhsCand->expr ) ) continue;
+
+				// Explode is aware of casts - ensure every LHS
+				// is sent into explode with a reference cast.
+				if ( !lhsCand->expr.as< ast::CastExpr >() ) {
+					lhsCand->expr = new ast::CastExpr(
+						lhsCand->expr, new ast::ReferenceType( lhsCand->expr->result ) );
+				}
+
+				// Explode the LHS so that each field of a tuple-valued expr is assigned.
+				ResolvExpr::CandidateList lhs;
+				explode( *lhsCand, crntFinder.context.symtab, back_inserter(lhs), true );
+				for ( ResolvExpr::CandidateRef & cand : lhs ) {
+					// Each LHS value must be a reference - some come in
+					// with a cast, if not just cast to reference here.
+					if ( !cand->expr->result.as< ast::ReferenceType >() ) {
+						cand->expr = new ast::CastExpr(
+							cand->expr, new ast::ReferenceType( cand->expr->result ) );
+					}
+				}
+
+				if ( 1 == args.size() ) {
+					// Mass default-initialization/destruction.
+					ResolvExpr::CandidateList rhs{};
+					matcher.reset( new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
+					match();
+				} else if ( 2 == args.size() ) {
+					for ( const ResolvExpr::CandidateRef & rhsCand : args[1] ) {
+						ResolvExpr::CandidateList rhs;
+						if ( isTuple( rhsCand->expr ) ) {
+							// Multiple assignment:
+							explode( *rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
+							matcher.reset(
+								new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
+						} else {
+							// Mass assignment:
+							rhs.emplace_back( rhsCand );
+							matcher.reset(
+								new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
+						}
+						match();
+					}
+				} else {
+					// Expand all possible RHS possibilities.
+					std::vector< ResolvExpr::CandidateList > rhsCands;
+					combos(
+						std::next( args.begin(), 1 ), args.end(), back_inserter( rhsCands ) );
+					for ( const ResolvExpr::CandidateList & rhsCand : rhsCands ) {
+						// Multiple assignment:
+						ResolvExpr::CandidateList rhs;
+						explode( rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
+						matcher.reset(
+							new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
+						match();
+					}
+				}
+			}
+		}
+	}
+
+	void match() {
+		assert( matcher );
+
+		std::vector< ast::ptr< ast::Expr > > newAssigns = matcher->match();
+
+		if ( !( matcher->lhs.empty() && matcher->rhs.empty() ) ) {
+			// If both LHS and RHS are empty than this is the empty tuple
+			// case, wherein it's okay for newAssigns to be empty. Otherwise,
+			// return early so that no new candidates are generated.
+			if ( newAssigns.empty() ) return;
+		}
+
+		ResolvExpr::CandidateList crnt;
+		// Now resolve new assignments.
+		for ( const ast::Expr * expr : newAssigns ) {
+			PRINT(
+				std::cerr << "== resolving tuple assign ==" << std::endl;
+				std::cerr << expr << std::endl;
+			)
+
+			ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env );
+			finder.allowVoid = true;
+
+			try {
+				finder.find( expr, ResolvExpr::ResolveMode::withAdjustment() );
+			} catch (...) {
+				// No match is not failure, just that this tuple assignment is invalid.
+				return;
+			}
+
+			ResolvExpr::CandidateList & cands = finder.candidates;
+			assert( 1 == cands.size() );
+			assert( cands.front()->expr );
+			crnt.emplace_back( std::move( cands.front() ) );
+		}
+
+		// extract expressions from the assignment candidates to produce a list of assignments
+		// that together form a sigle candidate
+		std::vector< ast::ptr< ast::Expr > > solved;
+		for ( ResolvExpr::CandidateRef & cand : crnt ) {
+			solved.emplace_back( cand->expr );
+			matcher->combineState( *cand );
+		}
+
+		crntFinder.candidates.emplace_back( std::make_shared< ResolvExpr::Candidate >(
+			new ast::TupleAssignExpr(
+				matcher->location, std::move( solved ), std::move( matcher->tmpDecls ) ),
+			std::move( matcher->env ), std::move( matcher->open ), std::move( matcher->need ),
+			ResolvExpr::sumCost( crnt ) + matcher->baseCost ) );
+	}
+};
+
+} // anonymous namespace
+
+void handleTupleAssignment(
+	ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
+	std::vector< ResolvExpr::CandidateFinder > & args
+) {
+	TupleAssignSpotter spotter( finder );
+	spotter.spot( assign, args );
+}
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/TupleExpansion.cpp
===================================================================
--- src/Tuples/TupleExpansion.cpp	(revision 7a780ad23506507b6a1feccc46d18e12da183ceb)
+++ src/Tuples/TupleExpansion.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,8 +14,8 @@
 //
 
-#include "Tuples.h"
+#include "Tuples.hpp"
 
 #include "AST/Pass.hpp"
-#include "Common/ScopedMap.h"
+#include "Common/ScopedMap.hpp"
 
 namespace Tuples {
Index: src/Tuples/Tuples.cc
===================================================================
--- src/Tuples/Tuples.cc	(revision 7a780ad23506507b6a1feccc46d18e12da183ceb)
+++ 	(revision )
@@ -1,70 +1,0 @@
-//
-// 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.
-//
-// Tuples.cc -- A collection of tuple operations.
-//
-// Author           : Andrew Beach
-// Created On       : Mon Jun 17 14:41:00 2019
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon May 16 16:15:00 2022
-// Update Count     : 2
-//
-
-#include "Tuples.h"
-
-#include "AST/Pass.hpp"
-#include "AST/Inspect.hpp"
-#include "AST/LinkageSpec.hpp"
-#include "InitTweak/InitTweak.h"
-
-namespace Tuples {
-
-namespace {
-
-/// Determines if impurity (read: side-effects) may exist in a piece of code.
-/// Currently gives a very crude approximation, wherein almost any function
-/// call expression means the code may be impure.
-struct ImpurityDetector : public ast::WithShortCircuiting {
-	bool result = false;
-
-	void previsit( ast::ApplicationExpr const * appExpr ) {
-		if ( ast::DeclWithType const * function = ast::getFunction( appExpr ) ) {
-			if ( function->linkage == ast::Linkage::Intrinsic
-					&& ( function->name == "*?" || function->name == "?[?]" ) ) {
-				return;
-			}
-		}
-		result = true; visit_children = false;
-	}
-	void previsit( ast::UntypedExpr const * ) {
-		result = true; visit_children = false;
-	}
-};
-
-struct ImpurityDetectorIgnoreUnique : public ImpurityDetector {
-	using ImpurityDetector::previsit;
-	void previsit( ast::UniqueExpr const * ) {
-		visit_children = false;
-	}
-};
-
-} // namespace
-
-bool maybeImpure( const ast::Expr * expr ) {
-	return ast::Pass<ImpurityDetector>::read( expr );
-}
-
-bool maybeImpureIgnoreUnique( const ast::Expr * expr ) {
-	return ast::Pass<ImpurityDetectorIgnoreUnique>::read( expr );
-}
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/Tuples.cpp
===================================================================
--- src/Tuples/Tuples.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/Tuples.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,70 @@
+//
+// 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.
+//
+// Tuples.cpp -- A collection of tuple operations.
+//
+// Author           : Andrew Beach
+// Created On       : Mon Jun 17 14:41:00 2019
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon May 16 16:15:00 2022
+// Update Count     : 2
+//
+
+#include "Tuples.hpp"
+
+#include "AST/Pass.hpp"
+#include "AST/Inspect.hpp"
+#include "AST/LinkageSpec.hpp"
+#include "InitTweak/InitTweak.hpp"
+
+namespace Tuples {
+
+namespace {
+
+/// Determines if impurity (read: side-effects) may exist in a piece of code.
+/// Currently gives a very crude approximation, wherein almost any function
+/// call expression means the code may be impure.
+struct ImpurityDetector : public ast::WithShortCircuiting {
+	bool result = false;
+
+	void previsit( ast::ApplicationExpr const * appExpr ) {
+		if ( ast::DeclWithType const * function = ast::getFunction( appExpr ) ) {
+			if ( function->linkage == ast::Linkage::Intrinsic
+					&& ( function->name == "*?" || function->name == "?[?]" ) ) {
+				return;
+			}
+		}
+		result = true; visit_children = false;
+	}
+	void previsit( ast::UntypedExpr const * ) {
+		result = true; visit_children = false;
+	}
+};
+
+struct ImpurityDetectorIgnoreUnique : public ImpurityDetector {
+	using ImpurityDetector::previsit;
+	void previsit( ast::UniqueExpr const * ) {
+		visit_children = false;
+	}
+};
+
+} // namespace
+
+bool maybeImpure( const ast::Expr * expr ) {
+	return ast::Pass<ImpurityDetector>::read( expr );
+}
+
+bool maybeImpureIgnoreUnique( const ast::Expr * expr ) {
+	return ast::Pass<ImpurityDetectorIgnoreUnique>::read( expr );
+}
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/Tuples.h
===================================================================
--- src/Tuples/Tuples.h	(revision 7a780ad23506507b6a1feccc46d18e12da183ceb)
+++ 	(revision )
@@ -1,63 +1,0 @@
-//
-// 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.
-//
-// Tuples.h -- A collection of tuple operations.
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Henry Xue
-// Last Modified On : Mon Aug 23 15:36:09 2021
-// Update Count     : 19
-//
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include "AST/Fwd.hpp"
-#include "AST/Node.hpp"
-namespace ResolvExpr {
-	class CandidateFinder;
-}
-
-namespace Tuples {
-
-// TupleAssignment.cc
-void handleTupleAssignment(
-	ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
-	std::vector< ResolvExpr::CandidateFinder > & args );
-
-// TupleExpansion.cc
-/// Expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate.
-void expandMemberTuples( ast::TranslationUnit & translationUnit );
-
-/// Replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc.
-void expandTuples( ast::TranslationUnit & translaionUnit );
-
-/// Replaces UniqueExprs with a temporary variable and one call.
-void expandUniqueExpr( ast::TranslationUnit & translationUnit );
-
-/// Returns VoidType if any of the expressions have Voidtype, otherwise TupleType of the Expression result types.
-const ast::Type * makeTupleType( const std::vector<ast::ptr<ast::Expr>> & exprs );
-
-/// Returns a TypeInstType if `type` is a ttype, nullptr otherwise
-const ast::TypeInstType * isTtype( const ast::Type * type );
-
-/// Returns true if the expression may contain side-effects.
-bool maybeImpure( const ast::Expr * expr );
-
-/// Returns true if the expression may contain side-effect,
-/// ignoring the presence of unique expressions.
-bool maybeImpureIgnoreUnique( const ast::Expr * expr );
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/Tuples.hpp
===================================================================
--- src/Tuples/Tuples.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/Tuples.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,63 @@
+//
+// 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.
+//
+// Tuples.hpp -- A collection of tuple operations.
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Henry Xue
+// Last Modified On : Mon Aug 23 15:36:09 2021
+// Update Count     : 19
+//
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "AST/Fwd.hpp"
+#include "AST/Node.hpp"
+namespace ResolvExpr {
+	class CandidateFinder;
+}
+
+namespace Tuples {
+
+// TupleAssignment.cc
+void handleTupleAssignment(
+	ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
+	std::vector< ResolvExpr::CandidateFinder > & args );
+
+// TupleExpansion.cc
+/// Expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate.
+void expandMemberTuples( ast::TranslationUnit & translationUnit );
+
+/// Replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc.
+void expandTuples( ast::TranslationUnit & translaionUnit );
+
+/// Replaces UniqueExprs with a temporary variable and one call.
+void expandUniqueExpr( ast::TranslationUnit & translationUnit );
+
+/// Returns VoidType if any of the expressions have Voidtype, otherwise TupleType of the Expression result types.
+const ast::Type * makeTupleType( const std::vector<ast::ptr<ast::Expr>> & exprs );
+
+/// Returns a TypeInstType if `type` is a ttype, nullptr otherwise
+const ast::TypeInstType * isTtype( const ast::Type * type );
+
+/// Returns true if the expression may contain side-effects.
+bool maybeImpure( const ast::Expr * expr );
+
+/// Returns true if the expression may contain side-effect,
+/// ignoring the presence of unique expressions.
+bool maybeImpureIgnoreUnique( const ast::Expr * expr );
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/module.mk
===================================================================
--- src/Tuples/module.mk	(revision 7a780ad23506507b6a1feccc46d18e12da183ceb)
+++ src/Tuples/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,10 +16,10 @@
 
 SRC_TUPLES = \
-	Tuples/Explode.cc \
-	Tuples/Explode.h \
-	Tuples/TupleAssignment.cc \
+	Tuples/Explode.cpp \
+	Tuples/Explode.hpp \
+	Tuples/TupleAssignment.cpp \
 	Tuples/TupleExpansion.cpp \
-	Tuples/Tuples.cc \
-	Tuples/Tuples.h
+	Tuples/Tuples.cpp \
+	Tuples/Tuples.hpp
 
 SRC += $(SRC_TUPLES)
