Index: src/InitTweak/GenInit.cc
===================================================================
--- src/InitTweak/GenInit.cc	(revision 62e5546cd62ceedcb0b33b24775b308bc35ad734)
+++ src/InitTweak/GenInit.cc	(revision 9facf3b0994f0b1e51acecc9de03cae8628dc5fa)
@@ -29,4 +29,5 @@
 #include "GenPoly/DeclMutator.h"
 #include "GenPoly/ScopedSet.h"
+#include "ResolvExpr/typeops.h"
 
 namespace InitTweak {
@@ -45,11 +46,11 @@
 		ReturnFixer();
 
-		using GenPoly::PolyMutator::mutate;
+		typedef GenPoly::PolyMutator Parent;
+		using Parent::mutate;
 		virtual DeclarationWithType * mutate( FunctionDecl *functionDecl ) override;
 		virtual Statement * mutate( ReturnStmt * returnStmt ) override;
 
 	  protected:
-		std::list<DeclarationWithType*> returnVals;
-		UniqueName tempNamer;
+		FunctionType * ftype;
 		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;
@@ -133,24 +135,22 @@
 	}
 
-	ReturnFixer::ReturnFixer() : tempNamer( "_retVal" ) {}
+	ReturnFixer::ReturnFixer() {}
 
 	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
 		// is being returned
+		// Note: under the assumption that assignments return *this, checking for ?=? here is an optimization, since it shouldn't be necessary to copy construct `this`. This is a temporary optimization until reference types are added, at which point this should be removed, along with the analogous optimization in copy constructor generation.
 		if ( returnStmt->get_expr() && returnVals.size() == 1 && funcName != "?=?" && ! returnVals.front()->get_type()->get_isLvalue() ) {
-			// ensure return value is not destructed by explicitly creating
-			// an empty SingleInit node wherein maybeConstruct is false
-			ObjectDecl *newObj = new ObjectDecl( tempNamer.newName(), DeclarationNode::NoStorageClass, LinkageSpec::C, 0, returnVals.front()->get_type()->clone(), new ListInit( std::list<Initializer*>(), noDesignators, false ) );
-			stmtsToAdd.push_back( new DeclStmt( noLabels, newObj ) );
-
-			// and explicitly create the constructor expression separately
+			// explicitly construct the return value using the return expression and the retVal object
+			assertf( returnVals.front()->get_name() != "", "Function %s has unnamed return value\n", funcName.c_str() );
 			UntypedExpr *construct = new UntypedExpr( new NameExpr( "?{}" ) );
-			construct->get_args().push_back( new AddressExpr( new VariableExpr( newObj ) ) );
+			construct->get_args().push_back( new AddressExpr( new VariableExpr( returnVals.front() ) ) );
 			construct->get_args().push_back( returnStmt->get_expr() );
 			stmtsToAdd.push_back(new ExprStmt(noLabels, construct));
 
-			returnStmt->set_expr( new VariableExpr( newObj ) );
+			// return the retVal object
+			returnStmt->set_expr( new VariableExpr( returnVals.front() ) );
 		} // if
 		return returnStmt;
@@ -158,12 +158,10 @@
 
 	DeclarationWithType* ReturnFixer::mutate( FunctionDecl *functionDecl ) {
-		ValueGuard< std::list<DeclarationWithType*> > oldReturnVals( returnVals );
+		ValueGuard< FunctionType * > oldFtype( ftype );
 		ValueGuard< std::string > oldFuncName( funcName );
 
-		FunctionType * type = functionDecl->get_functionType();
-		returnVals = type->get_returnVals();
+		ftype = functionDecl->get_functionType();
 		funcName = functionDecl->get_name();
-		DeclarationWithType * decl = Mutator::mutate( functionDecl );
-		return decl;
+		return Parent::mutate( functionDecl );
 	}
 
@@ -222,4 +220,15 @@
 	}
 
+	bool CtorDtor::isManaged( Type * type ) const {
+		if ( TupleType * tupleType = dynamic_cast< TupleType * > ( type ) ) {
+			// tuple is also managed if any of its components are managed
+			if ( std::any_of( tupleType->get_types().begin(), tupleType->get_types().end(), [&](Type * type) { return isManaged( type ); }) ) {
+				return true;
+			}
+		}
+		// a type is managed if it appears in the map of known managed types, or if it contains any polymorphism (is a type variable or generic type containing a type variable)
+		return managedTypes.find( SymTab::Mangler::mangle( type ) ) != managedTypes.end() || GenPoly::isPolyType( type );
+	}
+
 	bool CtorDtor::isManaged( ObjectDecl * objDecl ) const {
 		Type * type = objDecl->get_type();
@@ -227,5 +236,5 @@
 			type = at->get_base();
 		}
-		return managedTypes.find( SymTab::Mangler::mangle( type ) ) != managedTypes.end();
+		return isManaged( type );
 	}
 
@@ -238,4 +247,36 @@
 			managedTypes.insert( SymTab::Mangler::mangle( type->get_base() ) );
 		}
+	}
+
+	ConstructorInit * genCtorInit( ObjectDecl * objDecl ) {
+		// call into genImplicitCall from Autogen.h to generate calls to ctor/dtor
+		// for each constructable object
+		std::list< Statement * > ctor;
+		std::list< Statement * > dtor;
+
+		InitExpander srcParam( objDecl->get_init() );
+		InitExpander nullParam( (Initializer *)NULL );
+		SymTab::genImplicitCall( srcParam, new VariableExpr( objDecl ), "?{}", back_inserter( ctor ), objDecl );
+		SymTab::genImplicitCall( nullParam, new VariableExpr( objDecl ), "^?{}", front_inserter( dtor ), objDecl, false );
+
+		// Currently genImplicitCall produces a single Statement - a CompoundStmt
+		// which  wraps everything that needs to happen. As such, it's technically
+		// possible to use a Statement ** in the above calls, but this is inherently
+		// unsafe, so instead we take the slightly less efficient route, but will be
+		// immediately informed if somehow the above assumption is broken. In this case,
+		// we could always wrap the list of statements at this point with a CompoundStmt,
+		// but it seems reasonable at the moment for this to be done by genImplicitCall
+		// itself. It is possible that genImplicitCall produces no statements (e.g. if
+		// an array type does not have a dimension). In this case, it's fine to ignore
+		// the object for the purposes of construction.
+		assert( ctor.size() == dtor.size() && ctor.size() <= 1 );
+		if ( ctor.size() == 1 ) {
+			// need to remember init expression, in case no ctors exist
+			// if ctor does exist, want to use ctor expression instead of init
+			// push this decision to the resolver
+			assert( dynamic_cast< ImplicitCtorDtorStmt * > ( ctor.front() ) && dynamic_cast< ImplicitCtorDtorStmt * > ( dtor.front() ) );
+			return new ConstructorInit( ctor.front(), dtor.front(), objDecl->get_init() );
+		}
+		return nullptr;
 	}
 
@@ -250,32 +291,5 @@
 			if ( ! checkInitDepth( objDecl ) ) throw SemanticError( "Managed object's initializer is too deep ", objDecl );
 
-			// call into genImplicitCall from Autogen.h to generate calls to ctor/dtor
-			// for each constructable object
-			std::list< Statement * > ctor;
-			std::list< Statement * > dtor;
-
-			InitExpander srcParam( objDecl->get_init() );
-			InitExpander nullParam( (Initializer *)NULL );
-			SymTab::genImplicitCall( srcParam, new VariableExpr( objDecl ), "?{}", back_inserter( ctor ), objDecl );
-			SymTab::genImplicitCall( nullParam, new VariableExpr( objDecl ), "^?{}", front_inserter( dtor ), objDecl, false );
-
-			// Currently genImplicitCall produces a single Statement - a CompoundStmt
-			// which  wraps everything that needs to happen. As such, it's technically
-			// possible to use a Statement ** in the above calls, but this is inherently
-			// unsafe, so instead we take the slightly less efficient route, but will be
-			// immediately informed if somehow the above assumption is broken. In this case,
-			// we could always wrap the list of statements at this point with a CompoundStmt,
-			// but it seems reasonable at the moment for this to be done by genImplicitCall
-			// itself. It is possible that genImplicitCall produces no statements (e.g. if
-			// an array type does not have a dimension). In this case, it's fine to ignore
-			// the object for the purposes of construction.
-			assert( ctor.size() == dtor.size() && ctor.size() <= 1 );
-			if ( ctor.size() == 1 ) {
-				// need to remember init expression, in case no ctors exist
-				// if ctor does exist, want to use ctor expression instead of init
-				// push this decision to the resolver
-				assert( dynamic_cast< ImplicitCtorDtorStmt * > ( ctor.front() ) && dynamic_cast< ImplicitCtorDtorStmt * > ( dtor.front() ) );
-				objDecl->set_init( new ConstructorInit( ctor.front(), dtor.front(), objDecl->get_init() ) );
-			}
+			objDecl->set_init( genCtorInit( objDecl ) );
 		}
 		return Parent::mutate( objDecl );
@@ -290,5 +304,5 @@
 		managedTypes.beginScope();
 		// go through assertions and recursively add seen ctor/dtors
-		for ( TypeDecl * tyDecl : functionDecl->get_functionType()->get_forall() ) {
+		for ( auto & tyDecl : functionDecl->get_functionType()->get_forall() ) {
 			for ( DeclarationWithType *& assertion : tyDecl->get_assertions() ) {
 				assertion = assertion->acceptMutator( *this );
