Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision a5853962bf23b5d7a28c561f2c6749df02ac9144)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision c43c171cf3155ec51da1dfbcf876f5d441caef5b)
@@ -887,6 +887,6 @@
 		// take care of possible tuple assignments
 		// if not tuple assignment, assignment is taken care of as a normal function call
-/*FIX	Tuples::handleTupleAssignment( *this, untypedExpr, possibilities );
-*/
+		Tuples::handleTupleAssignment( *this, untypedExpr, argAlternatives );
+
 		// find function operators
 		AlternativeFinder funcOpFinder( indexer, env );
Index: src/Tuples/Explode.h
===================================================================
--- src/Tuples/Explode.h	(revision a5853962bf23b5d7a28c561f2c6749df02ac9144)
+++ src/Tuples/Explode.h	(revision c43c171cf3155ec51da1dfbcf876f5d441caef5b)
@@ -30,4 +30,13 @@
 	Expression * distributeReference( Expression * );
 
+	static inline CastExpr * isReferenceCast( Expression * expr ) {
+		if ( CastExpr * castExpr = dynamic_cast< CastExpr * >( expr ) ) {
+			if ( dynamic_cast< ReferenceType * >( castExpr->result ) ) {
+				return castExpr;
+			}
+		}
+		return nullptr;
+	}
+
 	/// helper function used by explode
 	template< typename OutputIterator >
@@ -35,5 +44,5 @@
 		if ( isTupleAssign ) {
 			// tuple assignment needs CastExprs to be recursively exploded to easily get at all of the components
-			if ( CastExpr * castExpr = dynamic_cast< CastExpr * >( expr ) ) {
+			if ( CastExpr * castExpr = isReferenceCast( expr ) ) {
 				ResolvExpr::AltList alts;
 				explodeUnique( castExpr->get_arg(), alt, indexer, back_inserter( alts ), isTupleAssign );
Index: src/Tuples/TupleAssignment.cc
===================================================================
--- src/Tuples/TupleAssignment.cc	(revision a5853962bf23b5d7a28c561f2c6749df02ac9144)
+++ src/Tuples/TupleAssignment.cc	(revision c43c171cf3155ec51da1dfbcf876f5d441caef5b)
@@ -20,4 +20,5 @@
 #include <memory>                          // for unique_ptr, allocator_trai...
 #include <string>                          // for string
+#include <vector>
 
 #include "CodeGen/OperatorTable.h"
@@ -33,4 +34,5 @@
 #include "ResolvExpr/Resolver.h"           // for resolveCtorInit
 #include "ResolvExpr/TypeEnvironment.h"    // for TypeEnvironment
+#include "ResolvExpr/typeops.h"            // for combos
 #include "SynTree/Declaration.h"           // for ObjectDecl
 #include "SynTree/Expression.h"            // for Expression, CastExpr, Name...
@@ -46,5 +48,5 @@
 		// dispatcher for Tuple (multiple and mass) assignment operations
 		TupleAssignSpotter( ResolvExpr::AlternativeFinder & );
-		void spot( UntypedExpr * expr, const std::list<ResolvExpr::AltList> &possibilities );
+		void spot( UntypedExpr * expr, std::vector<ResolvExpr::AlternativeFinder> &args );
 
 	  private:
@@ -53,5 +55,6 @@
 		struct Matcher {
 		  public:
-			Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList & alts );
+			Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, const 
+				ResolvExpr::AltList& rhs );
 			virtual ~Matcher() {}
 			virtual void match( std::list< Expression * > &out ) = 0;
@@ -66,5 +69,6 @@
 		struct MassAssignMatcher : public Matcher {
 		  public:
-			MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList & alts );
+			MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, 
+				const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {}
 			virtual void match( std::list< Expression * > &out );
 		};
@@ -72,5 +76,6 @@
 		struct MultipleAssignMatcher : public Matcher {
 		  public:
-			MultipleAssignMatcher( TupleAssignSpotter &spot, const ResolvExpr::AltList & alts );
+			MultipleAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, 
+				const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {}
 			virtual void match( std::list< Expression * > &out );
 		};
@@ -108,7 +113,8 @@
 	}
 
-	void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * expr, const std::list<ResolvExpr::AltList> &possibilities ) {
+	void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * expr, 
+				std::vector<ResolvExpr::AlternativeFinder> &args ) {
 		TupleAssignSpotter spotter( currentFinder );
-		spotter.spot( expr, possibilities );
+		spotter.spot( expr, args );
 	}
 
@@ -116,27 +122,74 @@
 		: currentFinder(f) {}
 
-	void TupleAssignSpotter::spot( UntypedExpr * expr, const std::list<ResolvExpr::AltList> &possibilities ) {
+	void TupleAssignSpotter::spot( UntypedExpr * expr, 
+			std::vector<ResolvExpr::AlternativeFinder> &args ) {
 		if (  NameExpr *op = dynamic_cast< NameExpr * >(expr->get_function()) ) {
 			if ( CodeGen::isCtorDtorAssign( op->get_name() ) ) {
 				fname = op->get_name();
-				for ( std::list<ResolvExpr::AltList>::const_iterator ali = possibilities.begin(); ali != possibilities.end(); ++ali ) {
-					if ( ali->size() == 0 ) continue; // AlternativeFinder will natrually handle this case, if it's legal
-					if ( ali->size() <= 1 && CodeGen::isAssignment( op->get_name() ) ) {
-						// what does it mean if an assignment takes 1 argument? maybe someone defined such a function, in which case AlternativeFinder will naturally handle it
-						continue;
+
+				// AlternativeFinder will naturally handle this case case, if it's legal
+				if ( args.size() == 0 ) return;
+
+				// if an assignment only takes 1 argument, that's odd, but maybe someone wrote 
+				// the function, in which case AlternativeFinder will handle it normally
+				if ( args.size() == 1 && CodeGen::isAssignment( fname ) ) return;
+
+				// look over all possible left-hand-sides
+				for ( ResolvExpr::Alternative& lhsAlt : args[0] ) {
+					// skip non-tuple LHS
+					if ( ! refToTuple(lhsAlt.expr) ) continue;
+
+					// explode is aware of casts - ensure every LHS expression is sent into explode 
+					// with a reference cast
+					// xxx - this seems to change the alternatives before the normal 
+					//  AlternativeFinder flow; maybe this is desired?
+					if ( ! dynamic_cast<CastExpr*>( lhsAlt.expr ) ) {
+						lhsAlt.expr = new CastExpr( lhsAlt.expr, 
+								new ReferenceType( Type::Qualifiers(), 
+									lhsAlt.expr->get_result()->clone() ) );
 					}
 
-					assert( ! ali->empty() );
-					// grab args 2-N and group into a TupleExpr
-					const ResolvExpr::Alternative & alt1 = ali->front();
-					auto begin = std::next(ali->begin(), 1), end = ali->end();
-					if ( refToTuple(alt1.expr) ) {
-						if ( isMultAssign( begin, end ) ) {
-							matcher.reset( new MultipleAssignMatcher( *this, *ali ) );
-						} else {
-							// mass assignment
-							matcher.reset( new MassAssignMatcher( *this,  *ali ) );
+					// explode the LHS so that each field of a tuple-valued-expr is assigned
+					ResolvExpr::AltList lhs;
+					explode( lhsAlt, currentFinder.get_indexer(), back_inserter(lhs), true );
+					for ( ResolvExpr::Alternative& alt : lhs ) {
+						// each LHS value must be a reference - some come in with a cast expression, 
+						// if not just cast to reference here
+						if ( ! dynamic_cast<ReferenceType*>( alt.expr->get_result() ) ) {
+							alt.expr = new CastExpr( alt.expr, 
+								new ReferenceType( Type::Qualifiers(), 
+									alt.expr->get_result()->clone() ) );
 						}
-						match();
+					}
+
+					if ( args.size() > 2 ) {
+						// expand all possible RHS possibilities
+						// TODO build iterative version of this instead of using combos
+						std::vector< ResolvExpr::AltList > rhsAlts;
+						combos( std::next(args.begin(), 1), args.end(), 
+							std::back_inserter( rhsAlts ) );
+						for ( const ResolvExpr::AltList& rhsAlt : rhsAlts ) {
+							// multiple assignment
+							ResolvExpr::AltList rhs;
+							explode( rhsAlt, currentFinder.get_indexer(), 
+								std::back_inserter(rhs), true );
+							matcher.reset( new MultipleAssignMatcher( *this, lhs, rhs ) );
+							match();
+						}
+					} else {
+						for ( const ResolvExpr::Alternative& rhsAlt : args[1] ) {
+							ResolvExpr::AltList rhs;
+							if ( isTuple(rhsAlt.expr) ) {
+								// multiple assignment
+								explode( rhsAlt, currentFinder.get_indexer(),  
+									std::back_inserter(rhs), true );
+								matcher.reset( new MultipleAssignMatcher( *this, lhs, rhs ) );
+							} else {
+								// mass assignment
+								rhs.push_back( rhsAlt );
+								matcher.reset( new MassAssignMatcher( *this, lhs, rhs ) );
+							}
+							match();
+						}
 					}
 				}
@@ -183,37 +236,8 @@
 	}
 
-	TupleAssignSpotter::Matcher::Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList &alts ) : spotter(spotter), baseCost( ResolvExpr::sumCost( alts ) ) {
-		assert( ! alts.empty() );
-		// combine argument environments into combined expression environment
-		simpleCombineEnvironments( alts.begin(), alts.end(), compositeEnv );
-
-		ResolvExpr::Alternative lhsAlt = alts.front();
-		// explode is aware of casts - ensure every LHS expression is sent into explode with a reference cast
-		if ( ! dynamic_cast< CastExpr * >( lhsAlt.expr ) ) {
-			lhsAlt.expr = new CastExpr( lhsAlt.expr, new ReferenceType( Type::Qualifiers(), lhsAlt.expr->get_result()->clone() ) );
-		}
-
-		// explode the lhs so that each field of the tuple-valued-expr is assigned.
-		explode( lhsAlt, spotter.currentFinder.get_indexer(), back_inserter(lhs), true );
-
-		for ( ResolvExpr::Alternative & alt : lhs ) {
-			// every LHS value must be a reference - some come in with a cast expression, if it doesn't just cast to reference here.
-			if ( ! dynamic_cast< ReferenceType * >( alt.expr->get_result() ) ) {
-				alt.expr = new CastExpr( alt.expr, new ReferenceType( Type::Qualifiers(), alt.expr->get_result()->clone() ) );
-			}
-		}
-	}
-
-	TupleAssignSpotter::MassAssignMatcher::MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList & alts ) : Matcher( spotter, alts ) {
-		assert( alts.size() == 1 || alts.size() == 2 );
-		if ( alts.size() == 2 ) {
-			rhs.push_back( alts.back() );
-		}
-	}
-
-	TupleAssignSpotter::MultipleAssignMatcher::MultipleAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList & alts ) : Matcher( spotter, alts ) {
-		// explode the rhs so that each field of the tuple-valued-expr is assigned.
-		explode( std::next(alts.begin(), 1), alts.end(), spotter.currentFinder.get_indexer(), back_inserter(rhs), true );
-	}
+	TupleAssignSpotter::Matcher::Matcher( TupleAssignSpotter &spotter, 
+		const ResolvExpr::AltList &lhs, const ResolvExpr::AltList &rhs ) 
+	: lhs(lhs), rhs(rhs), spotter(spotter), 
+	  baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ) {}
 
 	UntypedExpr * createFunc( const std::string &fname, ObjectDecl *left, ObjectDecl *right ) {
Index: src/Tuples/Tuples.h
===================================================================
--- src/Tuples/Tuples.h	(revision a5853962bf23b5d7a28c561f2c6749df02ac9144)
+++ src/Tuples/Tuples.h	(revision c43c171cf3155ec51da1dfbcf876f5d441caef5b)
@@ -17,4 +17,5 @@
 
 #include <string>
+#include <vector>
 
 #include "SynTree/Expression.h"
@@ -26,6 +27,7 @@
 namespace Tuples {
 	// TupleAssignment.cc
-	void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * assign, const std::list<ResolvExpr::AltList> & possibilities );
-
+	void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * assign, 
+		std::vector< ResolvExpr::AlternativeFinder >& 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
