Index: src/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision 1132b62939ee72e624dd65d3bacb0e67cb1a0347)
+++ src/InitTweak/FixInit.cc	(revision 65660bd065cbc0b68c92db2425b5984d675dff58)
@@ -69,6 +69,6 @@
 
 			/// create and resolve ctor/dtor expression: fname(var, [cpArg])
-			ApplicationExpr * makeCtorDtor( const std::string & fname, ObjectDecl * var, Expression * cpArg = NULL );
-			ApplicationExpr * makeCtorDtor( const std::string & fname, Expression * thisArg, Expression * cpArg = NULL );
+			Expression * makeCtorDtor( const std::string & fname, ObjectDecl * var, Expression * cpArg = NULL );
+			Expression * makeCtorDtor( const std::string & fname, Expression * thisArg, Expression * cpArg = NULL );
 			/// true if type does not need to be copy constructed to ensure correctness
 			bool skipCopyConstruct( Type * type );
@@ -360,10 +360,10 @@
 		}
 
-		ApplicationExpr * ResolveCopyCtors::makeCtorDtor( const std::string & fname, ObjectDecl * var, Expression * cpArg ) {
+		Expression * ResolveCopyCtors::makeCtorDtor( const std::string & fname, ObjectDecl * var, Expression * cpArg ) {
 			assert( var );
 			return makeCtorDtor( fname, new AddressExpr( new VariableExpr( var ) ), cpArg );
 		}
 
-		ApplicationExpr * ResolveCopyCtors::makeCtorDtor( const std::string & fname, Expression * thisArg, Expression * cpArg ) {
+		Expression * ResolveCopyCtors::makeCtorDtor( const std::string & fname, Expression * thisArg, Expression * cpArg ) {
 			assert( thisArg );
 			UntypedExpr * untyped = new UntypedExpr( new NameExpr( fname ) );
@@ -375,10 +375,10 @@
 			// (VariableExpr and already resolved expression)
 			CP_CTOR_PRINT( std::cerr << "ResolvingCtorDtor " << untyped << std::endl; )
-			ApplicationExpr * resolved = dynamic_cast< ApplicationExpr * >( ResolvExpr::findVoidExpression( untyped, *this ) );
+			Expression * resolved = ResolvExpr::findVoidExpression( untyped, *this );
+			assert( resolved );
 			if ( resolved->get_env() ) {
 				env->add( *resolved->get_env() );
 			} // if
 
-			assert( resolved );
 			delete untyped;
 			return resolved;
@@ -392,11 +392,4 @@
 			if ( skipCopyConstruct( result ) ) return; // skip certain non-copyable types
 
-			if ( TupleExpr * tupleExpr = dynamic_cast< TupleExpr * >( arg ) ) {
-				for ( Expression * & expr : tupleExpr->get_exprs() ) {
-					copyConstructArg( expr, impCpCtorExpr );
-				}
-				return;
-			}
-
 			// type may involve type variables, so apply type substitution to get temporary variable's actual type
 			result = result->clone();
@@ -407,27 +400,21 @@
 			// create and resolve copy constructor
 			CP_CTOR_PRINT( std::cerr << "makeCtorDtor for an argument" << std::endl; )
-			ApplicationExpr * cpCtor = makeCtorDtor( "?{}", tmp, arg );
-
-			// if the chosen constructor is intrinsic, the copy is unnecessary, so
-			// don't create the temporary and don't call the copy constructor
-			VariableExpr * function = dynamic_cast< VariableExpr * >( cpCtor->get_function() );
-			assert( function );
-			if ( function->get_var()->get_linkage() != LinkageSpec::Intrinsic ) {
-				// replace argument to function call with temporary
-				arg = new CommaExpr( cpCtor, new VariableExpr( tmp ) );
-				impCpCtorExpr->get_tempDecls().push_back( tmp );
-				impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", tmp ) );
-			} // if
+			Expression * cpCtor = makeCtorDtor( "?{}", tmp, arg );
+
+			if ( ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * >( cpCtor ) ) {
+				// if the chosen constructor is intrinsic, the copy is unnecessary, so
+				// don't create the temporary and don't call the copy constructor
+				VariableExpr * function = dynamic_cast< VariableExpr * >( appExpr->get_function() );
+				assert( function );
+				if ( function->get_var()->get_linkage() == LinkageSpec::Intrinsic ) return;
+			}
+
+			// replace argument to function call with temporary
+			arg = new CommaExpr( cpCtor, new VariableExpr( tmp ) );
+			impCpCtorExpr->get_tempDecls().push_back( tmp );
+			impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", tmp ) );
 		}
 
 		void ResolveCopyCtors::destructRet( Expression * ret, ImplicitCopyCtorExpr * impCpCtorExpr ) {
-			if ( TupleType * tupleType = dynamic_cast< TupleType * > ( ret->get_result() ) ) {
-				int idx = 0;
-				for ( Type *& t : tupleType->get_types() ) {
-					(void)t;
-					destructRet( new TupleIndexExpr( ret->clone(), idx++ ), impCpCtorExpr );
-				}
-				return;
-			}
 			impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", new AddressExpr( ret ) ) );
 		}
Index: src/InitTweak/GenInit.cc
===================================================================
--- src/InitTweak/GenInit.cc	(revision 1132b62939ee72e624dd65d3bacb0e67cb1a0347)
+++ src/InitTweak/GenInit.cc	(revision 65660bd065cbc0b68c92db2425b5984d675dff58)
@@ -29,4 +29,5 @@
 #include "GenPoly/DeclMutator.h"
 #include "GenPoly/ScopedSet.h"
+#include "ResolvExpr/typeops.h"
 
 namespace InitTweak {
@@ -50,5 +51,5 @@
 
 	  protected:
-		std::list<DeclarationWithType*> returnVals;
+		FunctionType * ftype;
 		UniqueName tempNamer;
 		std::string funcName;
@@ -86,4 +87,5 @@
 
 		bool isManaged( ObjectDecl * objDecl ) const ; // determine if object is managed
+		bool isManaged( Type * type ) const; // determine if type is managed
 		void handleDWT( DeclarationWithType * dwt ); // add type to managed if ctor/dtor
 		GenPoly::ScopedSet< std::string > managedTypes;
@@ -134,5 +136,5 @@
 
 	Statement *ReturnFixer::mutate( ReturnStmt *returnStmt ) {
-		// update for multiple return values
+		std::list< DeclarationWithType * > & returnVals = ftype->get_returnVals();
 		assert( returnVals.size() == 0 || returnVals.size() == 1 );
 		// hands off if the function returns an lvalue - we don't want to allocate a temporary if a variable's address
@@ -156,9 +158,24 @@
 
 	DeclarationWithType* ReturnFixer::mutate( FunctionDecl *functionDecl ) {
-		ValueGuard< std::list<DeclarationWithType*> > oldReturnVals( returnVals );
+		// xxx - need to handle named return values - this pass may need to happen
+		// after resolution? the ordering is tricky because return statements must be
+		// constructed - the simplest way to do that (while also handling multiple
+		// returns) is to structure the returnVals into a tuple, as done here.
+		// however, if the tuple return value is structured before resolution,
+		// it's difficult to resolve named return values, since the name is lost
+		// in conversion to a tuple. this might be easiest to deal with
+		// after reference types are added, as it may then be possible to
+		// uniformly move named return values to the parameter list directly
+		ValueGuard< FunctionType * > oldFtype( ftype );
 		ValueGuard< std::string > oldFuncName( funcName );
 
-		FunctionType * type = functionDecl->get_functionType();
-		returnVals = type->get_returnVals();
+		ftype = functionDecl->get_functionType();
+		std::list< DeclarationWithType * > & retVals = ftype->get_returnVals();
+		if ( retVals.size() > 1 ) {
+			TupleType * tupleType = safe_dynamic_cast< TupleType * >( ResolvExpr::extractResultType( ftype ) );
+			ObjectDecl * newRet = new ObjectDecl( tempNamer.newName(), DeclarationNode::NoStorageClass, LinkageSpec::C, 0, tupleType, new ListInit( std::list<Initializer*>(), noDesignators, false ) );
+			retVals.clear();
+			retVals.push_back( newRet );
+		}
 		funcName = functionDecl->get_name();
 		DeclarationWithType * decl = Mutator::mutate( functionDecl );
@@ -220,4 +237,8 @@
 	}
 
+	bool CtorDtor::isManaged( Type * type ) const {
+		return managedTypes.find( SymTab::Mangler::mangle( type ) ) != managedTypes.end();
+	}
+
 	bool CtorDtor::isManaged( ObjectDecl * objDecl ) const {
 		Type * type = objDecl->get_type();
@@ -225,5 +246,8 @@
 			type = at->get_base();
 		}
-		return managedTypes.find( SymTab::Mangler::mangle( type ) ) != managedTypes.end();
+		if ( TupleType * tupleType = dynamic_cast< TupleType * > ( type ) ) {
+			return std::any_of( tupleType->get_types().begin(), tupleType->get_types().end(), [&](Type * type) { return isManaged( type ); });
+		}
+		return isManaged( type );
 	}
 
