Index: src/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/InitTweak/FixInit.cc	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -65,5 +65,9 @@
 			static void resolveImplicitCalls( std::list< Declaration * > & translationUnit );
 
+			typedef SymTab::Indexer Parent;
+			using Parent::visit;
+
 			virtual void visit( ImplicitCopyCtorExpr * impCpCtorExpr );
+			virtual void visit( UniqueExpr * unqExpr );
 
 			/// create and resolve ctor/dtor expression: fname(var, [cpArg])
@@ -178,4 +182,5 @@
 
 			virtual Expression * mutate( ImplicitCopyCtorExpr * impCpCtorExpr );
+			virtual Expression * mutate( UniqueExpr * unqExpr );
 		};
 
@@ -421,5 +426,5 @@
 		void ResolveCopyCtors::visit( ImplicitCopyCtorExpr *impCpCtorExpr ) {
 			CP_CTOR_PRINT( std::cerr << "ResolveCopyCtors: " << impCpCtorExpr << std::endl; )
-			Visitor::visit( impCpCtorExpr );
+			Parent::visit( impCpCtorExpr );
 			env = impCpCtorExpr->get_env(); // xxx - maybe we really should just have a PolyIndexer...
 
@@ -455,4 +460,26 @@
 		}
 
+		void ResolveCopyCtors::visit( UniqueExpr * unqExpr ) {
+			static std::unordered_set< int > vars;
+			if ( vars.count( unqExpr->get_id() ) ) {
+				// xxx - hack to prevent double-handling of unique exprs, otherwise too many temporary variables and destructors are generated
+				return;
+			}
+
+			Parent::visit( unqExpr );
+			// it should never be necessary to wrap a void-returning expression in a UniqueExpr - if this assumption changes, this needs to be rethought
+			assert( unqExpr->get_result() );
+			if ( ImplicitCopyCtorExpr * impCpCtorExpr = dynamic_cast<ImplicitCopyCtorExpr*>( unqExpr->get_expr() ) ) {
+				// note the variable used as the result from the call
+				assert( impCpCtorExpr->get_result() && impCpCtorExpr->get_returnDecls().size() == 1 );
+				unqExpr->set_var( new VariableExpr( impCpCtorExpr->get_returnDecls().front() ) );
+			} else {
+				// expr isn't a call expr, so create a new temporary variable to use to hold the value of the unique expression
+				unqExpr->set_object( new ObjectDecl( toString("_unq_expr_", unqExpr->get_id()), DeclarationNode::NoStorageClass, LinkageSpec::C, nullptr, unqExpr->get_result()->clone(), nullptr ) );
+				unqExpr->set_var( new VariableExpr( unqExpr->get_object() ) );
+			}
+			vars.insert( unqExpr->get_id() );
+		}
+
 
 		Expression * FixCopyCtors::mutate( ImplicitCopyCtorExpr * impCpCtorExpr ) {
@@ -526,4 +553,35 @@
 				return callExpr;
 			} // if
+		}
+
+		Expression * FixCopyCtors::mutate( UniqueExpr * unqExpr ) {
+			static std::unordered_map< int, UniqueExpr * > unqMap;
+			static std::unordered_set< int > addDeref;
+			// has to be done to clean up ImplicitCopyCtorExpr nodes, even when this node was skipped in previous passes
+			unqExpr = safe_dynamic_cast< UniqueExpr * >( Parent::mutate( unqExpr ) );
+			if ( unqMap.count( unqExpr->get_id() ) ) {
+				// take data from other UniqueExpr to ensure consistency
+				delete unqExpr->get_expr();
+				unqExpr->set_expr( unqMap[unqExpr->get_id()]->get_expr()->clone() );
+				delete unqExpr->get_result();
+				unqExpr->set_result( maybeClone( unqExpr->get_expr()->get_result() ) );
+				if ( addDeref.count( unqExpr->get_id() ) ) {
+					// other UniqueExpr was dereferenced because it was an lvalue return, so this one should be too
+					return UntypedExpr::createDeref( unqExpr );
+				}
+				return unqExpr;
+			}
+			unqMap[unqExpr->get_id()] = unqExpr;
+			if ( UntypedExpr * deref = dynamic_cast< UntypedExpr * >( unqExpr->get_expr() ) ) {
+				// unique expression is now a dereference, because the inner expression is an lvalue returning function call.
+				// Normalize the expression by dereferencing the unique expression, rather than the inner expression
+				// (i.e. move the dereference out a level)
+				assert( getFunctionName( deref ) == "*?" );
+				unqExpr->set_expr( getCallArg( deref, 0 ) );
+				getCallArg( deref, 0 ) = unqExpr;
+				addDeref.insert( unqExpr->get_id() );
+				return deref;
+			}
+			return unqExpr;
 		}
 
Index: src/Makefile.in
===================================================================
--- src/Makefile.in	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/Makefile.in	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -189,6 +189,8 @@
 	SynTree/driver_cfa_cpp-TypeSubstitution.$(OBJEXT) \
 	SynTree/driver_cfa_cpp-Attribute.$(OBJEXT) \
+	SynTree/driver_cfa_cpp-VarExprReplacer.$(OBJEXT) \
 	Tuples/driver_cfa_cpp-TupleAssignment.$(OBJEXT) \
-	Tuples/driver_cfa_cpp-TupleExpansion.$(OBJEXT)
+	Tuples/driver_cfa_cpp-TupleExpansion.$(OBJEXT) \
+	Tuples/driver_cfa_cpp-Explode.$(OBJEXT)
 am_driver_cfa_cpp_OBJECTS = $(am__objects_1)
 driver_cfa_cpp_OBJECTS = $(am_driver_cfa_cpp_OBJECTS)
@@ -403,6 +405,7 @@
 	SynTree/Initializer.cc SynTree/Visitor.cc SynTree/Mutator.cc \
 	SynTree/AddStmtVisitor.cc SynTree/TypeSubstitution.cc \
-	SynTree/Attribute.cc Tuples/TupleAssignment.cc \
-	Tuples/TupleExpansion.cc
+	SynTree/Attribute.cc SynTree/VarExprReplacer.cc \
+	Tuples/TupleAssignment.cc Tuples/TupleExpansion.cc \
+	Tuples/Explode.cc
 MAINTAINERCLEANFILES = Parser/parser.output ${libdir}/${notdir \
 	${cfa_cpplib_PROGRAMS}}
@@ -764,4 +767,6 @@
 SynTree/driver_cfa_cpp-Attribute.$(OBJEXT): SynTree/$(am__dirstamp) \
 	SynTree/$(DEPDIR)/$(am__dirstamp)
+SynTree/driver_cfa_cpp-VarExprReplacer.$(OBJEXT):  \
+	SynTree/$(am__dirstamp) SynTree/$(DEPDIR)/$(am__dirstamp)
 Tuples/$(am__dirstamp):
 	@$(MKDIR_P) Tuples
@@ -774,4 +779,6 @@
 Tuples/driver_cfa_cpp-TupleExpansion.$(OBJEXT):  \
 	Tuples/$(am__dirstamp) Tuples/$(DEPDIR)/$(am__dirstamp)
+Tuples/driver_cfa_cpp-Explode.$(OBJEXT): Tuples/$(am__dirstamp) \
+	Tuples/$(DEPDIR)/$(am__dirstamp)
 driver/$(am__dirstamp):
 	@$(MKDIR_P) driver
@@ -878,7 +885,9 @@
 	-rm -f SynTree/driver_cfa_cpp-TypeofType.$(OBJEXT)
 	-rm -f SynTree/driver_cfa_cpp-VarArgsType.$(OBJEXT)
+	-rm -f SynTree/driver_cfa_cpp-VarExprReplacer.$(OBJEXT)
 	-rm -f SynTree/driver_cfa_cpp-Visitor.$(OBJEXT)
 	-rm -f SynTree/driver_cfa_cpp-VoidType.$(OBJEXT)
 	-rm -f SynTree/driver_cfa_cpp-ZeroOneType.$(OBJEXT)
+	-rm -f Tuples/driver_cfa_cpp-Explode.$(OBJEXT)
 	-rm -f Tuples/driver_cfa_cpp-TupleAssignment.$(OBJEXT)
 	-rm -f Tuples/driver_cfa_cpp-TupleExpansion.$(OBJEXT)
@@ -984,7 +993,9 @@
 @AMDEP_TRUE@@am__include@ @am__quote@SynTree/$(DEPDIR)/driver_cfa_cpp-TypeofType.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@SynTree/$(DEPDIR)/driver_cfa_cpp-VarArgsType.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@SynTree/$(DEPDIR)/driver_cfa_cpp-VarExprReplacer.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@SynTree/$(DEPDIR)/driver_cfa_cpp-Visitor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@SynTree/$(DEPDIR)/driver_cfa_cpp-VoidType.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@SynTree/$(DEPDIR)/driver_cfa_cpp-ZeroOneType.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@Tuples/$(DEPDIR)/driver_cfa_cpp-Explode.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Tuples/$(DEPDIR)/driver_cfa_cpp-TupleAssignment.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Tuples/$(DEPDIR)/driver_cfa_cpp-TupleExpansion.Po@am__quote@
@@ -2406,4 +2417,18 @@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o SynTree/driver_cfa_cpp-Attribute.obj `if test -f 'SynTree/Attribute.cc'; then $(CYGPATH_W) 'SynTree/Attribute.cc'; else $(CYGPATH_W) '$(srcdir)/SynTree/Attribute.cc'; fi`
 
+SynTree/driver_cfa_cpp-VarExprReplacer.o: SynTree/VarExprReplacer.cc
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -MT SynTree/driver_cfa_cpp-VarExprReplacer.o -MD -MP -MF SynTree/$(DEPDIR)/driver_cfa_cpp-VarExprReplacer.Tpo -c -o SynTree/driver_cfa_cpp-VarExprReplacer.o `test -f 'SynTree/VarExprReplacer.cc' || echo '$(srcdir)/'`SynTree/VarExprReplacer.cc
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) SynTree/$(DEPDIR)/driver_cfa_cpp-VarExprReplacer.Tpo SynTree/$(DEPDIR)/driver_cfa_cpp-VarExprReplacer.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='SynTree/VarExprReplacer.cc' object='SynTree/driver_cfa_cpp-VarExprReplacer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o SynTree/driver_cfa_cpp-VarExprReplacer.o `test -f 'SynTree/VarExprReplacer.cc' || echo '$(srcdir)/'`SynTree/VarExprReplacer.cc
+
+SynTree/driver_cfa_cpp-VarExprReplacer.obj: SynTree/VarExprReplacer.cc
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -MT SynTree/driver_cfa_cpp-VarExprReplacer.obj -MD -MP -MF SynTree/$(DEPDIR)/driver_cfa_cpp-VarExprReplacer.Tpo -c -o SynTree/driver_cfa_cpp-VarExprReplacer.obj `if test -f 'SynTree/VarExprReplacer.cc'; then $(CYGPATH_W) 'SynTree/VarExprReplacer.cc'; else $(CYGPATH_W) '$(srcdir)/SynTree/VarExprReplacer.cc'; fi`
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) SynTree/$(DEPDIR)/driver_cfa_cpp-VarExprReplacer.Tpo SynTree/$(DEPDIR)/driver_cfa_cpp-VarExprReplacer.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='SynTree/VarExprReplacer.cc' object='SynTree/driver_cfa_cpp-VarExprReplacer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o SynTree/driver_cfa_cpp-VarExprReplacer.obj `if test -f 'SynTree/VarExprReplacer.cc'; then $(CYGPATH_W) 'SynTree/VarExprReplacer.cc'; else $(CYGPATH_W) '$(srcdir)/SynTree/VarExprReplacer.cc'; fi`
+
 Tuples/driver_cfa_cpp-TupleAssignment.o: Tuples/TupleAssignment.cc
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -MT Tuples/driver_cfa_cpp-TupleAssignment.o -MD -MP -MF Tuples/$(DEPDIR)/driver_cfa_cpp-TupleAssignment.Tpo -c -o Tuples/driver_cfa_cpp-TupleAssignment.o `test -f 'Tuples/TupleAssignment.cc' || echo '$(srcdir)/'`Tuples/TupleAssignment.cc
@@ -2433,4 +2458,18 @@
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o Tuples/driver_cfa_cpp-TupleExpansion.obj `if test -f 'Tuples/TupleExpansion.cc'; then $(CYGPATH_W) 'Tuples/TupleExpansion.cc'; else $(CYGPATH_W) '$(srcdir)/Tuples/TupleExpansion.cc'; fi`
+
+Tuples/driver_cfa_cpp-Explode.o: Tuples/Explode.cc
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -MT Tuples/driver_cfa_cpp-Explode.o -MD -MP -MF Tuples/$(DEPDIR)/driver_cfa_cpp-Explode.Tpo -c -o Tuples/driver_cfa_cpp-Explode.o `test -f 'Tuples/Explode.cc' || echo '$(srcdir)/'`Tuples/Explode.cc
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) Tuples/$(DEPDIR)/driver_cfa_cpp-Explode.Tpo Tuples/$(DEPDIR)/driver_cfa_cpp-Explode.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='Tuples/Explode.cc' object='Tuples/driver_cfa_cpp-Explode.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o Tuples/driver_cfa_cpp-Explode.o `test -f 'Tuples/Explode.cc' || echo '$(srcdir)/'`Tuples/Explode.cc
+
+Tuples/driver_cfa_cpp-Explode.obj: Tuples/Explode.cc
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -MT Tuples/driver_cfa_cpp-Explode.obj -MD -MP -MF Tuples/$(DEPDIR)/driver_cfa_cpp-Explode.Tpo -c -o Tuples/driver_cfa_cpp-Explode.obj `if test -f 'Tuples/Explode.cc'; then $(CYGPATH_W) 'Tuples/Explode.cc'; else $(CYGPATH_W) '$(srcdir)/Tuples/Explode.cc'; fi`
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) Tuples/$(DEPDIR)/driver_cfa_cpp-Explode.Tpo Tuples/$(DEPDIR)/driver_cfa_cpp-Explode.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='Tuples/Explode.cc' object='Tuples/driver_cfa_cpp-Explode.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o Tuples/driver_cfa_cpp-Explode.obj `if test -f 'Tuples/Explode.cc'; then $(CYGPATH_W) 'Tuples/Explode.cc'; else $(CYGPATH_W) '$(srcdir)/Tuples/Explode.cc'; fi`
 
 .ll.cc:
Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -39,4 +39,5 @@
 #include "SymTab/Validate.h"
 #include "Tuples/Tuples.h"
+#include "Tuples/Explode.h"
 #include "Common/utility.h"
 #include "InitTweak/InitTweak.h"
@@ -240,4 +241,12 @@
 				} // if
 			} // if
+		} else if ( NameExpr * nameExpr = dynamic_cast< NameExpr * >( member ) ) {
+			// xxx - temporary hack until 0/1 are int constants
+			if ( nameExpr->get_name() == "0" || nameExpr->get_name() == "1" ) {
+				std::stringstream ss( nameExpr->get_name() );
+				int val;
+				ss >> val;
+				alternatives.push_back( Alternative( new TupleIndexExpr( expr->clone(), val ), env, newCost ) );
+			}
 		} // if
 	}
@@ -1057,21 +1066,10 @@
 
 	void AlternativeFinder::visit( UniqueExpr *unqExpr ) {
-		// this won't work because the unqExprs wont share an expression anymore...
 		AlternativeFinder finder( indexer, env );
 		finder.findWithAdjustment( unqExpr->get_expr() );
 		for ( Alternative & alt : finder.alternatives ) {
-			// xxx - attach a resolved ConstructorInit node?
-			// xxx - is it possible to make the objDecl's type const?
-			static UniqueName tempNamer( "_unq_expr_" );
-			ObjectDecl * objDecl = new ObjectDecl( tempNamer.newName(), DeclarationNode::NoStorageClass, LinkageSpec::Cforall, nullptr, alt.expr->get_result()->clone(), nullptr );
-			// must be done on two lines because genCtorInit accesses objDecl's fields
-			objDecl->set_init( InitTweak::genCtorInit( objDecl ) );
-
+			// ensure that the id is passed on to the UniqueExpr alternative so that the expressions are "linked"
 			UniqueExpr * newUnqExpr = new UniqueExpr( alt.expr->clone(), unqExpr->get_id() );
-			newUnqExpr->set_object( objDecl );
-
-			resolveObject( indexer, objDecl );
-
-			alternatives.push_back( Alternative( newUnqExpr, env, Cost::zero ) );
+			alternatives.push_back( Alternative( newUnqExpr, alt.env, alt.cost ) );
 		}
 	}
Index: src/SynTree/Expression.cc
===================================================================
--- src/SynTree/Expression.cc	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/SynTree/Expression.cc	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -609,23 +609,18 @@
 
 long long UniqueExpr::count = 0;
-UniqueExpr::UniqueExpr( Expression *expr, long long idVal ) : expr( new Expression* ), object( new ObjectDecl* ), id( idVal ) {
+UniqueExpr::UniqueExpr( Expression *expr, long long idVal ) : expr( expr ), object( nullptr ), var( nullptr ), id( idVal ) {
+	assert( expr );
 	assert( count != -1 );
 	if ( id == -1 ) id = count++;
-	set_expr( expr );
-	assert( expr );
 	if ( expr->get_result() ) {
 		set_result( expr->get_result()->clone() );
 	}
-	set_object( nullptr );
-}
-UniqueExpr::UniqueExpr( const UniqueExpr &other ) : Expression( other ), expr( other.expr ), object( other.object ), id( other.id ) {
+}
+UniqueExpr::UniqueExpr( const UniqueExpr &other ) : Expression( other ), expr( maybeClone( other.expr ) ), object( maybeClone( other.object ) ), var( maybeClone( other.var ) ), id( other.id ) {
 }
 UniqueExpr::~UniqueExpr() {
-	if ( expr.unique() ) {
-		delete *expr;
-	}
-	if ( object.unique() ) {
-		delete *object;
-	}
+	delete expr;
+	delete object;
+	delete var;
 }
 void UniqueExpr::print( std::ostream &os, int indent ) const {
Index: src/SynTree/Expression.h
===================================================================
--- src/SynTree/Expression.h	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/SynTree/Expression.h	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -742,9 +742,12 @@
 	~UniqueExpr();
 
-	Expression * get_expr() const { return *expr; }
-	UniqueExpr * set_expr( Expression * newValue ) { *expr = newValue; return this; }
-
-	ObjectDecl * get_object() const { return *object; }
-	UniqueExpr * set_object( ObjectDecl * newValue ) { *object = newValue; return this; }
+	Expression * get_expr() const { return expr; }
+	UniqueExpr * set_expr( Expression * newValue ) { expr = newValue; return this; }
+
+	ObjectDecl * get_object() const { return object; }
+	UniqueExpr * set_object( ObjectDecl * newValue ) { object = newValue; return this; }
+
+	VariableExpr * get_var() const { return var; }
+	UniqueExpr * set_var( VariableExpr * newValue ) { var = newValue; return this; }
 
 	int get_id() const { return id; }
@@ -755,6 +758,7 @@
 	virtual void print( std::ostream &os, int indent = 0 ) const;
 private:
-	std::shared_ptr< Expression * > expr;
-	std::shared_ptr< ObjectDecl * > object;
+	Expression * expr;
+	ObjectDecl * object;
+	VariableExpr * var;
 	int id;
 	static long long count;
Index: src/Tuples/Explode.cc
===================================================================
--- src/Tuples/Explode.cc	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
+++ src/Tuples/Explode.cc	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -0,0 +1,82 @@
+//
+// 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 ) ){
+					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.
+				foundUniqueExpr = true;
+				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: //
Index: src/Tuples/Explode.h
===================================================================
--- src/Tuples/Explode.h	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
+++ src/Tuples/Explode.h	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -0,0 +1,97 @@
+//
+// 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 : Rob Schluntz
+// Last Modified On : Wed Nov 9 13:20:24 2016
+// Update Count     : 2
+//
+
+#ifndef _EXPLODE_H_
+#define _EXPLODE_H_
+
+#include "ResolvExpr/AlternativeFinder.h"
+#include "ResolvExpr/Resolver.h"
+
+#include "SynTree/Expression.h"
+#include "SynTree/Declaration.h"
+#include "SynTree/Type.h"
+
+#include "Tuples.h"
+
+namespace Tuples {
+	/// helper function used by explode to properly distribute
+	/// '&' across a tuple expression
+	Expression * distributeAddr( Expression * expr );
+
+	/// helper function used by explode
+	template< typename OutputIterator >
+	void explodeUnique( Expression * expr, const ResolvExpr::Alternative & alt, const SymTab::Indexer & indexer, OutputIterator out ) {
+		Type * res = expr->get_result();
+		if ( AddressExpr * addrExpr = dynamic_cast< AddressExpr * >( expr ) ) {
+			ResolvExpr::AltList alts;
+			explodeUnique( addrExpr->get_arg(), alt, indexer, back_inserter( alts ) );
+			for ( ResolvExpr::Alternative & alt : alts ) {
+				// distribute '&' over all components
+				alt.expr = distributeAddr( alt.expr );
+				*out++ = alt;
+			}
+		} else if ( TupleType * tupleType = dynamic_cast< TupleType * > ( res ) ) {
+			if ( TupleExpr * tupleExpr = dynamic_cast< TupleExpr * >( expr ) ) {
+				// can open tuple expr and dump its exploded components
+				for ( Expression * expr : tupleExpr->get_exprs() ) {
+					explodeUnique( expr, alt, indexer, out );
+				}
+			} else {
+				// tuple type, but not tuple expr - recursively index into its components
+				Expression * arg = expr->clone();
+				if ( Tuples::maybeImpure( arg ) && ! dynamic_cast< UniqueExpr * >( arg ) ) {
+					// expressions which may contain side effects require a single unique instance of the expression.
+					arg = new UniqueExpr( arg );
+				}
+				for ( unsigned int i = 0; i < tupleType->size(); i++ ) {
+					TupleIndexExpr * idx = new TupleIndexExpr( arg->clone(), i );
+					explodeUnique( idx, alt, indexer, out );
+					delete idx;
+				}
+				delete arg;
+			}
+		} else {
+			// atomic (non-tuple) type - output a clone of the expression in a new alternative
+			*out++ = ResolvExpr::Alternative( expr->clone(), alt.env, alt.cost, alt.cvtCost );
+		}
+	}
+
+	/// expands a tuple-valued alternative into multiple alternatives, each with a non-tuple-type
+	template< typename OutputIterator >
+	void explode( const ResolvExpr::Alternative &alt, const SymTab::Indexer & indexer, OutputIterator out ) {
+		explodeUnique( alt.expr, alt, indexer, out );
+	}
+
+	// explode list of alternatives
+	template< typename AltIterator, typename OutputIterator >
+	void explode( AltIterator altBegin, AltIterator altEnd, const SymTab::Indexer & indexer, OutputIterator out ) {
+		for ( ; altBegin != altEnd; ++altBegin ) {
+			explode( *altBegin, indexer, out );
+		}
+	}
+
+	template< typename OutputIterator >
+	void explode( const ResolvExpr::AltList & alts, const SymTab::Indexer & indexer, OutputIterator out ) {
+		explode( alts.begin(), alts.end(), indexer, out );
+	}
+} // namespace Tuples
+
+#endif // _TUPLE_ASSIGNMENT_H_
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/TupleAssignment.cc
===================================================================
--- src/Tuples/TupleAssignment.cc	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/Tuples/TupleAssignment.cc	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -20,4 +20,5 @@
 #include "SynTree/Initializer.h"
 #include "Tuples.h"
+#include "Explode.h"
 #include "Common/SemanticError.h"
 #include "InitTweak/InitTweak.h"
Index: src/Tuples/TupleExpansion.cc
===================================================================
--- src/Tuples/TupleExpansion.cc	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/Tuples/TupleExpansion.cc	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -41,6 +41,14 @@
 		public:
 			typedef GenPoly::DeclMutator Parent;
+
 			virtual Expression * mutate( UniqueExpr * unqExpr );
-			std::map< int, ObjectDecl * > decls; // not vector, because order added may not be increasing order
+
+			std::map< int, Expression * > decls; // not vector, because order added may not be increasing order
+
+			~UniqueExprExpander() {
+				for ( std::pair<const int, Expression *> & p : decls ) {
+					delete p.second;
+				}
+			}
 		};
 
@@ -107,5 +115,5 @@
 		/// given a expression representing the member and an expression representing the aggregate,
 		/// reconstructs a flattened UntypedMemberExpr with the right precedence
-		Expression * reconstructMemberExpr( Expression * member, UniqueExpr * aggr ) {
+		Expression * reconstructMemberExpr( Expression * member, Expression * aggr ) {
 			if ( UntypedMemberExpr * memberExpr = dynamic_cast< UntypedMemberExpr * >( member ) ) {
 				// construct a new UntypedMemberExpr with the correct structure , and recursively
@@ -127,9 +135,13 @@
 	Expression * MemberTupleExpander::mutate( UntypedMemberExpr * memberExpr ) {
 		if ( TupleExpr * tupleExpr = dynamic_cast< TupleExpr * > ( memberExpr->get_member() ) ) {
-			UniqueExpr * unqExpr = new UniqueExpr( memberExpr->get_aggregate()->clone() );
+			Expression * aggr = memberExpr->get_aggregate()->clone()->acceptMutator( *this );
+			// aggregate expressions which might be impure must be wrapped in unique expressions
+			// xxx - if there's a member-tuple expression nested in the aggregate, this currently generates the wrong code if a UniqueExpr is not used, and it's purely an optimization to remove the UniqueExpr
+			// if ( Tuples::maybeImpure( memberExpr->get_aggregate() ) ) aggr = new UniqueExpr( aggr );
+			aggr = new UniqueExpr( aggr );
 			for ( Expression *& expr : tupleExpr->get_exprs() ) {
-				expr = reconstructMemberExpr( expr, unqExpr );
-			}
-			delete unqExpr;
+				expr = reconstructMemberExpr( expr, aggr );
+			}
+			delete aggr;
 			return tupleExpr;
 		} else {
@@ -142,31 +154,42 @@
 	Expression * UniqueExprExpander::mutate( UniqueExpr * unqExpr ) {
 		unqExpr = safe_dynamic_cast< UniqueExpr * > ( Parent::mutate( unqExpr ) );
-		if ( ! decls.count( unqExpr->get_id() ) ) {
-			// xxx - it's possible (likely?) that expressions can appear in the wrong order because of this. Need to ensure they're placed in the correct location.
-
-			// xxx - this doesn't work, because it would need to be placed after fixInit, but fixInit doesn't know (and shouldn't have to know) about the existance of UniqueExprs - i.e. it will visit them twice
-			// need to construct/destruct unique exprs in general - maybe it's not worth it and fixInit should handle UniqueExpr explicitly?
-			// currently, tmp is being destructed before unqExpr is used, which suggests there should be a separate lifetime for unqExpr from the tmp_ret
-
-			// if ( CommaExpr * commaExpr = dynamic_cast< CommaExpr * >( unqExpr->get_expr() ) ) {
-			// 	if ( VariableExpr * varExpr = dynamic_cast< VariableExpr * >( commaExpr->get_arg2() ) ) {
-			// 		// steal existing decl from expr
-			// 		if ( ObjectDecl * decl = dynamic_cast< ObjectDecl * >( varExpr->get_var() ) ) {
-			// 			decls[unqExpr->get_id()] = decl;
-			// 			return unqExpr->get_expr()->clone();
-			// 		}
-			// 	}
-			// }
-
-			ObjectDecl * objDecl = unqExpr->get_object();
-			unqExpr->set_object( nullptr );
-			decls[unqExpr->get_id()] = objDecl;
-			addDeclaration( objDecl );
-		}
-		return new VariableExpr( decls[unqExpr->get_id()] );
+		const int id = unqExpr->get_id();
+
+		// on first time visiting a unique expr with a particular ID, generate the expression that replaces all UniqueExprs with that ID,
+		// and lookup on subsequent hits. This ensures that all unique exprs with the same ID reference the same variable.
+		if ( ! decls.count( id ) ) {
+			Expression * assignUnq;
+			Expression * var = unqExpr->get_var();
+			if ( unqExpr->get_object() ) {
+				// an object was generated to represent this unique expression -- it should be added to the list of declarations now
+				addDeclaration( unqExpr->get_object() );
+				unqExpr->set_object( nullptr );
+				// steal the expr from the unqExpr
+				assignUnq = UntypedExpr::createAssign( unqExpr->get_var()->clone(), unqExpr->get_expr() );
+				unqExpr->set_expr( nullptr );
+			} else {
+				// steal the already generated assignment to var from the unqExpr - this has been generated by FixInit
+				Expression * expr = unqExpr->get_expr();
+				CommaExpr * commaExpr = safe_dynamic_cast< CommaExpr * >( expr );
+				assignUnq = commaExpr->get_arg1();
+				commaExpr->set_arg1( nullptr );
+			}
+			BasicType * boolType = new BasicType( Type::Qualifiers(), BasicType::Bool );
+			ObjectDecl * finished = new ObjectDecl( toString( "_unq_expr_finished_", id ), DeclarationNode::NoStorageClass, LinkageSpec::Cforall, nullptr, new BasicType( Type::Qualifiers(), BasicType::Bool ), new SingleInit( new ConstantExpr( Constant( boolType->clone(), "0" ) ), noDesignators ) );
+			addDeclaration( finished );
+			// (finished ? _unq_expr_N : (_unq_expr_N = <unqExpr->get_expr()>, finished = 1, _unq_expr_N))
+			// This pattern ensures that each unique expression is evaluated once, regardless of evaluation order of the generated C code.
+			Expression * assignFinished = UntypedExpr::createAssign( new VariableExpr(finished), new ConstantExpr( Constant( boolType->clone(), "1" ) ) );
+			ConditionalExpr * condExpr = new ConditionalExpr( new VariableExpr( finished ), var->clone(),
+				new CommaExpr( new CommaExpr( assignUnq, assignFinished ), var->clone() ) );
+			condExpr->set_result( var->get_result()->clone() );
+			decls[id] = condExpr;
+		}
+		delete unqExpr;
+		return decls[id]->clone();
 	}
 
 	Expression * TupleAssignExpander::mutate( TupleAssignExpr * assnExpr ) {
-		// xxx - Parent::mutate?
+		assnExpr = safe_dynamic_cast< TupleAssignExpr * >( Parent::mutate( assnExpr ) );
 		CompoundStmt * compoundStmt = new CompoundStmt( noLabels );
 		std::list< Statement * > & stmts = compoundStmt->get_kids();
@@ -192,5 +215,5 @@
 			int cnt = 0;
 			for ( Type * t : *newType ) {
-				decl->get_members().push_back( new ObjectDecl( "field_"+std::to_string(++cnt), DeclarationNode::NoStorageClass, LinkageSpec::C, nullptr, t->clone(), nullptr ) );
+				decl->get_members().push_back( new ObjectDecl( toString("field_", cnt++), DeclarationNode::NoStorageClass, LinkageSpec::C, nullptr, t->clone(), nullptr ) );
 			}
 			typeMap[mangleName] = decl;
Index: src/Tuples/Tuples.h
===================================================================
--- src/Tuples/Tuples.h	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/Tuples/Tuples.h	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Mon May 18 15:04:02 2015
-// Update Count     : 2
+// Last Modified By : Rob Schluntz
+// Last Modified On : Wed Nov 9 13:17:58 2016
+// Update Count     : 15
 //
 
@@ -18,11 +18,10 @@
 
 #include <string>
-#include <vector>
-#include "ResolvExpr/AlternativeFinder.h"
-#include "ResolvExpr/Resolver.h"
 
 #include "SynTree/Expression.h"
 #include "SynTree/Declaration.h"
 #include "SynTree/Type.h"
+
+#include "ResolvExpr/AlternativeFinder.h"
 
 namespace Tuples {
@@ -45,65 +44,4 @@
 	/// returns true if the expression may contain side-effects.
 	bool maybeImpure( Expression * expr );
-
-
-	/// helper function used by explode
-	template< typename OutputIterator >
-	void explodeUnique( Expression * expr, const ResolvExpr::Alternative & alt, const SymTab::Indexer & indexer, OutputIterator out ) {
-		Type * res = expr->get_result();
-		if ( AddressExpr * addrExpr = dynamic_cast< AddressExpr * >( expr ) ) {
-			ResolvExpr::AltList alts;
-			explodeUnique( addrExpr->get_arg(), alt, indexer, back_inserter( alts ) );
-			for ( ResolvExpr::Alternative & alt : alts ) {
-				// distribute '&' over all components
-				alt.expr = new AddressExpr( alt.expr );
-				*out++ = alt;
-			}
-		} else if ( TupleType * tupleType = dynamic_cast< TupleType * > ( res ) ) {
-			if ( TupleExpr * tupleExpr = dynamic_cast< TupleExpr * >( expr ) ) {
-				// can open tuple expr and dump its exploded components
-				for ( Expression * expr : tupleExpr->get_exprs() ) {
-					explodeUnique( expr, alt, indexer, out );
-				}
-			} else {
-				// tuple type, but not tuple expr - recursively index into its components
-				Expression * arg = expr->clone();
-				if ( Tuples::maybeImpure( arg ) ) {
-					// expressions which may contain side effects require a single unique instance of the expression.
-					// resolve the UniqueExpr (which should be relatively cheap, since the argument is already resolved)
-					// so that its accompanying object is properly constructed and destructed.
-					arg = new UniqueExpr( arg );
-					arg = ResolvExpr::resolveInVoidContext( arg, indexer );
-				}
-				for ( unsigned int i = 0; i < tupleType->size(); i++ ) {
-					TupleIndexExpr * idx = new TupleIndexExpr( arg->clone(), i );
-					explodeUnique( idx, alt, indexer, out );
-					delete idx;
-				}
-				delete arg;
-			}
-		} else {
-			// atomic (non-tuple) type - output a clone of the expression in a new alternative
-			*out++ = ResolvExpr::Alternative( expr->clone(), alt.env, alt.cost, alt.cvtCost );
-		}
-	}
-
-	/// expands a tuple-valued alternative into multiple alternatives, each with a non-tuple-type
-	template< typename OutputIterator >
-	void explode( const ResolvExpr::Alternative &alt, const SymTab::Indexer & indexer, OutputIterator out ) {
-		explodeUnique( alt.expr, alt, indexer, out );
-	}
-
-	// explode list of alternatives
-	template< typename AltIterator, typename OutputIterator >
-	void explode( AltIterator altBegin, AltIterator altEnd, const SymTab::Indexer & indexer, OutputIterator out ) {
-		for ( ; altBegin != altEnd; ++altBegin ) {
-			explode( *altBegin, indexer, out );
-		}
-	}
-
-	template< typename OutputIterator >
-	void explode( const ResolvExpr::AltList & alts, const SymTab::Indexer & indexer, OutputIterator out ) {
-		explode( alts.begin(), alts.end(), indexer, out );
-	}
 } // namespace Tuples
 
Index: src/Tuples/module.mk
===================================================================
--- src/Tuples/module.mk	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/Tuples/module.mk	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -16,3 +16,4 @@
 
 SRC += 	Tuples/TupleAssignment.cc \
-	Tuples/TupleExpansion.cc
+	Tuples/TupleExpansion.cc \
+	Tuples/Explode.cc
Index: src/main.cc
===================================================================
--- src/main.cc	(revision 23bb1b935afeee154439a3a5065ccd588e7c946e)
+++ src/main.cc	(revision 141b7868c72c7947affae41e61e4bfcd7f9c6652)
@@ -251,7 +251,4 @@
 		} // if
 
-		OPTPRINT( "expandUniqueExpr" ); // xxx - is this the right place for this? want to expand ASAP so that subsequent passes don't need to worry about double-visiting a unique expr
-		Tuples::expandUniqueExpr( translationUnit );
-
 		// fix ObjectDecl - replaces ConstructorInit nodes
 		OPTPRINT( "fixInit" )
@@ -261,4 +258,7 @@
 			return 0;
 		} // if
+
+		OPTPRINT( "expandUniqueExpr" ); // xxx - is this the right place for this? want to expand ASAP so that subsequent passes don't need to worry about double-visiting a unique expr - needs to go after InitTweak::fix so that copy constructed return declarations are reused
+		Tuples::expandUniqueExpr( translationUnit );
 
 		OPTPRINT("instantiateGenerics")
