Index: src/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision a15b72c7f3615301d805eaa9e83fdaa108a40b36)
+++ src/InitTweak/FixInit.cc	(revision 6840e7c444f894b4717b6e1799f5624666ca025c)
@@ -54,4 +54,5 @@
 #include "SynTree/Type.h"              // for Type, Type::StorageClasses
 #include "SynTree/TypeSubstitution.h"  // for TypeSubstitution, operator<<
+#include "SynTree/VarExprReplacer.h"   // for VarExprReplacer
 #include "SynTree/Visitor.h"           // for acceptAll, maybeAccept
 
@@ -158,10 +159,6 @@
 			using Parent::previsit;
 
-			void previsit( ObjectDecl * objDecl );
 			void previsit( FunctionDecl * funcDecl );
 
-			void previsit( CompoundStmt * compoundStmt );
-			void postvisit( CompoundStmt * compoundStmt );
-			void previsit( ReturnStmt * returnStmt );
 			void previsit( BranchStmt * stmt );
 		private:
@@ -179,4 +176,6 @@
 
 			DeclarationWithType * postmutate( ObjectDecl *objDecl );
+
+			DeclarationWithType * getDtorFunc( ObjectDecl * objDecl, Statement * dtor );
 
 			std::list< Declaration * > staticDtorDecls;
@@ -636,4 +635,33 @@
 		}
 
+		DeclarationWithType * FixInit::getDtorFunc( ObjectDecl * objDecl, Statement * dtor ) {
+			if ( dynamic_cast< ExprStmt * >( dtor ) ) {
+				if ( DeclarationWithType * func = getFunction( getCtorDtorCall( dtor ) ) ) {
+					// 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 ), new CompoundStmt( noLabels ) );
+			stmtsToAddBefore.push_back( new DeclStmt( noLabels, dtorFunc ) );
+
+			// the original code contains uses of objDecl - replace them with the newly generated 'this' parameter.
+			ObjectDecl * thisParam = getThisParam( dtorFunc->type );
+			VarExprReplacer::replace( dtor, { std::make_pair( objDecl, thisParam ) } );
+			dtorFunc->statements->push_back( 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)
@@ -748,4 +776,19 @@
 							ctorInit->ctor = nullptr;
 						}
+
+						Statement * dtor = ctorInit->dtor;
+						if ( dtor ) {
+							ImplicitCtorDtorStmt * implicit = strict_dynamic_cast< ImplicitCtorDtorStmt * >( dtor );
+							Statement * dtorStmt = implicit->callStmt;
+							// don't need to call intrinsic dtor, because it does nothing, but
+							// non-intrinsic dtors must be called
+							if ( ! isIntrinsicSingleArgCallStmt( dtorStmt ) ) {
+								// set dtor location to the object's location for error messages
+								DeclarationWithType * dtorFunc = getDtorFunc( objDecl, dtorStmt );
+								objDecl->attributes.push_back( new Attribute( "cleanup", { new VariableExpr( dtorFunc ) } ) );
+								// objDecl->attributes.push_back( new Attribute( "cleanup", { new NameExpr( dtorFunc->name ) } ) );
+								ctorInit->dtor = nullptr;
+							} // if
+						}
 					} // if
 				} else if ( Initializer * init = ctorInit->init ) {
@@ -790,36 +833,4 @@
 
 
-		template<typename Iterator, typename OutputIterator>
-		void insertDtors( Iterator begin, Iterator end, OutputIterator out ) {
-			for ( Iterator it = begin ; it != end ; ++it ) {
-				// extract destructor statement from the object decl and insert it into the output. Note that this is
-				// only called on lists of non-static objects with implicit non-intrinsic dtors, so if the user manually
-				// calls an intrinsic dtor then the call must (and will) still be generated since the argument may
-				// contain side effects.
-				ObjectDecl * objDecl = *it;
-				ConstructorInit * ctorInit = dynamic_cast< ConstructorInit * >( objDecl->get_init() );
-				assert( ctorInit && ctorInit->get_dtor() );
-				*out++ = ctorInit->get_dtor()->clone();
-			} // for
-		}
-
-		void InsertDtors::previsit( ObjectDecl * objDecl ) {
-			// remember non-static destructed objects so that their destructors can be inserted later
-			if ( ! objDecl->get_storageClasses().is_static ) {
-				if ( ConstructorInit * ctorInit = dynamic_cast< ConstructorInit * >( objDecl->get_init() ) ) {
-					// a decision should have been made by the resolver, so ctor and init are not both non-NULL
-					assert( ! ctorInit->get_ctor() || ! ctorInit->get_init() );
-					Statement * dtor = ctorInit->get_dtor();
-					// don't need to call intrinsic dtor, because it does nothing, but
-					// non-intrinsic dtors must be called
-					if ( dtor && ! isIntrinsicSingleArgCallStmt( dtor ) ) {
-						// set dtor location to the object's location for error messages
-						ctorInit->dtor->location = objDecl->location;
-						reverseDeclOrder.front().push_front( objDecl );
-					} // if
-				} // if
-			} // if
-		}
-
 		void InsertDtors::previsit( FunctionDecl * funcDecl ) {
 			// each function needs to have its own set of labels
@@ -834,29 +845,4 @@
 		}
 
-		void InsertDtors::previsit( CompoundStmt * compoundStmt ) {
-			// visit statements - this will also populate reverseDeclOrder list.  don't want to dump all destructors
-			// when block is left, just the destructors associated with variables defined in this block, so push a new
-			// list to the top of the stack so that we can differentiate scopes
-			reverseDeclOrder.push_front( OrderedDecls() );
-			Parent::previsit( compoundStmt );
-		}
-
-		void InsertDtors::postvisit( CompoundStmt * compoundStmt ) {
-			// add destructors for the current scope that we're exiting, unless the last statement is a return, which
-			// causes unreachable code warnings
-			std::list< Statement * > & statements = compoundStmt->get_kids();
-			if ( ! statements.empty() && ! dynamic_cast< ReturnStmt * >( statements.back() ) ) {
-				insertDtors( reverseDeclOrder.front().begin(), reverseDeclOrder.front().end(), back_inserter( statements ) );
-			}
-			reverseDeclOrder.pop_front();
-		}
-
-		void InsertDtors::previsit( ReturnStmt * ) {
-			// return exits all scopes, so dump destructors for all scopes
-			for ( OrderedDecls & od : reverseDeclOrder ) {
-				insertDtors( od.begin(), od.end(), back_inserter( stmtsToAddBefore ) );
-			} // for
-		}
-
 		// Handle break/continue/goto in the same manner as C++.  Basic idea: any objects that are in scope at the
 		// BranchStmt but not at the labelled (target) statement must be destructed.  If there are any objects in scope
@@ -886,22 +872,4 @@
 			if ( ! diff.empty() ) {
 				throw SemanticError( std::string("jump to label '") + stmt->get_target().get_name() + "' crosses initialization of " + (*diff.begin())->get_name() + " ", stmt );
-			} // if
-			// S_G-S_L results in set of objects that must be destructed
-			diff.clear();
-			std::set_difference( curVars.begin(), curVars.end(), lvars.begin(), lvars.end(), std::inserter( diff, diff.end() ) );
-			DTOR_PRINT(
-				std::cerr << "S_G-S_L = " << printSet( diff ) << std::endl;
-			)
-			if ( ! diff.empty() ) {
-				// create an auxilliary set for fast lookup -- can't make diff a set, because diff ordering should be consistent for error messages.
-				std::unordered_set<ObjectDecl *> needsDestructor( diff.begin(), diff.end() );
-
-				// go through decl ordered list of objectdecl. for each element that occurs in diff, output destructor
-				OrderedDecls ordered;
-				for ( OrderedDecls & rdo : reverseDeclOrder ) {
-					// add elements from reverseDeclOrder into ordered if they occur in diff - it is key that this happens in reverse declaration order.
-					copy_if( rdo.begin(), rdo.end(), back_inserter( ordered ), [&]( ObjectDecl * objDecl ) { return needsDestructor.count( objDecl ); } );
-				} // for
-				insertDtors( ordered.begin(), ordered.end(), back_inserter( stmtsToAddBefore ) );
 			} // if
 		}
Index: src/SynTree/Mutator.h
===================================================================
--- src/SynTree/Mutator.h	(revision a15b72c7f3615301d805eaa9e83fdaa108a40b36)
+++ src/SynTree/Mutator.h	(revision 6840e7c444f894b4717b6e1799f5624666ca025c)
@@ -119,4 +119,5 @@
 
 	virtual TypeSubstitution * mutate( TypeSubstitution * sub );
+
   private:
 	virtual Declaration * handleAggregateDecl(AggregateDecl * aggregateDecl );
