Index: src/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision 2bfc6b281e65c5e8bdf4d021589a170c2cb15418)
+++ src/InitTweak/FixInit.cc	(revision bcc09464085fdb6198737f2486429509a330950f)
@@ -255,15 +255,63 @@
 		GenStructMemberCalls::generate( translationUnit );
 
-		// xxx - ctor expansion currently has to be after FixCopyCtors, because there is currently a
-		// hack in the way untyped assignments are generated, where the first argument cannot have
-		// its address taken because of the way codegeneration handles UntypedExpr vs. ApplicationExpr.
-		// Thus such assignment exprs must never pushed through expression resolution (and thus should
-		// not go through the FixCopyCtors pass), otherwise they will fail -- guaranteed.
-		// Also needs to happen after GenStructMemberCalls, since otherwise member constructors exprs
-		// don't look right, and a member can be constructed more than once.
+		// Needs to happen after GenStructMemberCalls, since otherwise member constructors exprs
+		// don't have the correct form, and a member can be constructed more than once.
 		FixCtorExprs::fix( translationUnit );
 	}
 
 	namespace {
+		/// find and return the destructor used in `input`. If `input` is not a simple destructor call, generate a thunk
+		/// that wraps the destructor, insert it into `stmtsToAdd` and return the new function declaration
+		DeclarationWithType * getDtorFunc( ObjectDecl * objDecl, Statement * input, std::list< Statement * > & stmtsToAdd ) {
+			// unwrap implicit statement wrapper
+			Statement * dtor = input;
+			if ( ImplicitCtorDtorStmt * implicit = dynamic_cast< ImplicitCtorDtorStmt * >( input ) ) {
+				// dtor = implicit->callStmt;
+				// implicit->callStmt = nullptr;
+			}
+			assert( dtor );
+			std::list< Expression * > matches;
+			collectCtorDtorCalls( dtor, matches );
+
+			if ( dynamic_cast< ExprStmt * >( dtor ) ) {
+				// only one destructor call in the expression
+				if ( matches.size() == 1 ) {
+					DeclarationWithType * func = getFunction( matches.front() );
+					assertf( func, "getFunction failed to find function in %s", toString( matches.front() ).c_str() );
+
+					// cleanup argument must be a function, not an object (including function pointer)
+					if ( FunctionDecl * dtorFunc = dynamic_cast< FunctionDecl * > ( func ) ) {
+						if ( dtorFunc->type->forall.empty() ) {
+							// simple case where the destructor is a monomorphic function call - can simply
+							// use that function as the cleanup function.
+							delete dtor;
+							return func;
+						}
+					}
+				}
+			}
+
+			// otherwise the cleanup is more complicated - need to build a single argument cleanup function that
+			// wraps the more complicated code.
+			static UniqueName dtorNamer( "__cleanup_dtor" );
+			FunctionDecl * dtorFunc = FunctionDecl::newFunction( dtorNamer.newName(), SymTab::genDefaultType( objDecl->type->stripReferences(), false ), new CompoundStmt() );
+			stmtsToAdd.push_back( new DeclStmt( dtorFunc ) );
+
+			// the original code contains uses of objDecl - replace them with the newly generated 'this' parameter.
+			ObjectDecl * thisParam = getParamThis( dtorFunc->type );
+			Expression * replacement = new VariableExpr( thisParam );
+
+			Type * base = replacement->result->stripReferences();
+			if ( dynamic_cast< ArrayType * >( base ) || dynamic_cast< TupleType * > ( base ) ) {
+				// need to cast away reference for array types, since the destructor is generated without the reference type,
+				// and for tuple types since tuple indexing does not work directly on a reference
+				replacement = new CastExpr( replacement, base->clone() );
+			}
+			DeclReplacer::replace( dtor, { std::make_pair( objDecl, replacement ) } );
+			dtorFunc->statements->push_back( strict_dynamic_cast<Statement *>( dtor ) );
+
+			return dtorFunc;
+		}
+
 		void InsertImplicitCalls::insert( std::list< Declaration * > & translationUnit ) {
 			PassVisitor<InsertImplicitCalls> inserter;
@@ -602,13 +650,13 @@
 
 			// 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() ) ) {
+			assert( unqExpr->result );
+			if ( ImplicitCopyCtorExpr * impCpCtorExpr = dynamic_cast<ImplicitCopyCtorExpr*>( unqExpr->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() ) );
+				assert( impCpCtorExpr->result && impCpCtorExpr->returnDecls.size() == 1 );
+				unqExpr->set_var( new VariableExpr( impCpCtorExpr->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( ObjectDecl::newObject( toString("_unq", unqExpr->get_id()), unqExpr->get_result()->clone(), makeInit( unqExpr->get_result() ) ) );
-				unqExpr->set_var( new VariableExpr( unqExpr->get_object() ) );
+				unqExpr->object = ObjectDecl::newObject( toString("_unq", unqExpr->get_id()), unqExpr->result->clone(), makeInit( unqExpr->result ) );
+				unqExpr->var = new VariableExpr( unqExpr->object );
 			}
 			vars.insert( unqExpr->get_id() );
@@ -618,7 +666,7 @@
 			CP_CTOR_PRINT( std::cerr << "FixCopyCtors: " << impCpCtorExpr << std::endl; )
 
-			std::list< ObjectDecl * > & tempDecls = impCpCtorExpr->get_tempDecls();
-			std::list< ObjectDecl * > & returnDecls = impCpCtorExpr->get_returnDecls();
-			std::list< Expression * > & dtors = impCpCtorExpr->get_dtors();
+			std::list< ObjectDecl * > & tempDecls = impCpCtorExpr->tempDecls;
+			std::list< ObjectDecl * > & returnDecls = impCpCtorExpr->returnDecls;
+			std::list< Expression * > & dtors = impCpCtorExpr->dtors;
 
 			// add all temporary declarations and their constructors
@@ -647,5 +695,5 @@
 			tempDecls.clear();
 			returnDecls.clear();
-			impCpCtorExpr->set_callExpr( nullptr );
+			impCpCtorExpr->callExpr = nullptr;
 			std::swap( impCpCtorExpr->env, callExpr->env );
 			assert( impCpCtorExpr->env == nullptr );
@@ -730,55 +778,4 @@
 		}
 
-		DeclarationWithType * getDtorFunc( ObjectDecl * objDecl, Statement * input, std::list< Statement * > & stmtsToAdd ) {
-			// unwrap implicit statement wrapper
-			Statement * dtor = input;
-			if ( ImplicitCtorDtorStmt * implicit = dynamic_cast< ImplicitCtorDtorStmt * >( input ) ) {
-				// dtor = implicit->callStmt;
-				// implicit->callStmt = nullptr;
-			}
-			assert( dtor );
-			std::list< Expression * > matches;
-			collectCtorDtorCalls( dtor, matches );
-
-			if ( dynamic_cast< ExprStmt * >( dtor ) ) {
-				// only one destructor call in the expression
-				if ( matches.size() == 1 ) {
-					DeclarationWithType * func = getFunction( matches.front() );
-					assertf( func, "getFunction failed to find function in %s", toString( matches.front() ).c_str() );
-
-					// cleanup argument must be a function, not an object (including function pointer)
-					if ( FunctionDecl * dtorFunc = dynamic_cast< FunctionDecl * > ( func ) ) {
-						if ( dtorFunc->type->forall.empty() ) {
-							// simple case where the destructor is a monomorphic function call - can simply
-							// use that function as the cleanup function.
-							delete dtor;
-							return func;
-						}
-					}
-				}
-			}
-
-			// otherwise the cleanup is more complicated - need to build a single argument cleanup function that
-			// wraps the more complicated code.
-			static UniqueName dtorNamer( "__cleanup_dtor" );
-			FunctionDecl * dtorFunc = FunctionDecl::newFunction( dtorNamer.newName(), SymTab::genDefaultType( objDecl->type->stripReferences(), false ), new CompoundStmt() );
-			stmtsToAdd.push_back( new DeclStmt( dtorFunc ) );
-
-			// the original code contains uses of objDecl - replace them with the newly generated 'this' parameter.
-			ObjectDecl * thisParam = getParamThis( dtorFunc->type );
-			Expression * replacement = new VariableExpr( thisParam );
-
-			Type * base = replacement->result->stripReferences();
-			if ( dynamic_cast< ArrayType * >( base ) || dynamic_cast< TupleType * > ( base ) ) {
-				// need to cast away reference for array types, since the destructor is generated without the reference type,
-				// and for tuple types since tuple indexing does not work directly on a reference
-				replacement = new CastExpr( replacement, base->clone() );
-			}
-			DeclReplacer::replace( dtor, { std::make_pair( objDecl, replacement ) } );
-			dtorFunc->statements->push_back( strict_dynamic_cast<Statement *>( dtor ) );
-
-			return dtorFunc;
-		}
-
 		DeclarationWithType * FixInit::postmutate( ObjectDecl *objDecl ) {
 			// since this removes the init field from objDecl, it must occur after children are mutated (i.e. postmutate)
