Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/Common/PassVisitor.impl.h	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -1778,9 +1778,6 @@
 	VISIT_START( node );
 
-	indexerScopedAccept( node->result     , *this );
-	maybeAccept_impl   ( node->callExpr   , *this );
-	maybeAccept_impl   ( node->tempDecls  , *this );
-	maybeAccept_impl   ( node->returnDecls, *this );
-	maybeAccept_impl   ( node->dtors      , *this );
+	indexerScopedAccept( node->result    , *this );
+	maybeAccept_impl   ( node->callExpr  , *this );
 
 	VISIT_END( node );
@@ -1791,10 +1788,7 @@
 	MUTATE_START( node );
 
-	indexerScopedMutate( node->env        , *this );
-	indexerScopedMutate( node->result     , *this );
-	maybeMutate_impl   ( node->callExpr   , *this );
-	maybeMutate_impl   ( node->tempDecls  , *this );
-	maybeMutate_impl   ( node->returnDecls, *this );
-	maybeMutate_impl   ( node->dtors      , *this );
+	indexerScopedMutate( node->env       , *this );
+	indexerScopedMutate( node->result    , *this );
+	maybeMutate_impl   ( node->callExpr  , *this );
 
 	MUTATE_END( Expression, node );
Index: src/ControlStruct/ExceptTranslate.cc
===================================================================
--- src/ControlStruct/ExceptTranslate.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/ControlStruct/ExceptTranslate.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -319,6 +319,6 @@
 			}
 
-			block->push_back( handler->get_body() );
-			handler->set_body( nullptr );
+			block->push_back( handler->body );
+			handler->body = nullptr;
 
 			std::list<Statement *> caseBody
Index: src/GenPoly/Box.cc
===================================================================
--- src/GenPoly/Box.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/GenPoly/Box.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -657,10 +657,10 @@
 				paramExpr = new AddressExpr( paramExpr );
 			} // if
-			arg = appExpr->get_args().insert( arg, paramExpr ); // add argument to function call
+			arg = appExpr->args.insert( arg, paramExpr ); // add argument to function call
 			arg++;
 			// Build a comma expression to call the function and emulate a normal return.
 			CommaExpr *commaExpr = new CommaExpr( appExpr, retExpr );
-			commaExpr->set_env( appExpr->get_env() );
-			appExpr->set_env( 0 );
+			commaExpr->env = appExpr->env;
+			appExpr->env = nullptr;
 			return commaExpr;
 		}
@@ -708,5 +708,5 @@
 //			if ( ! function->get_returnVals().empty() && isPolyType( function->get_returnVals().front()->get_type(), tyVars ) ) {
 			if ( isDynRet( function, tyVars ) ) {
-				ret = addRetParam( appExpr, function->get_returnVals().front()->get_type(), arg );
+				ret = addRetParam( appExpr, function->returnVals.front()->get_type(), arg );
 			} // if
 			std::string mangleName = mangleAdapterName( function, tyVars );
@@ -715,5 +715,5 @@
 			// cast adaptee to void (*)(), since it may have any type inside a polymorphic function
 			Type * adapteeType = new PointerType( Type::Qualifiers(), new FunctionType( Type::Qualifiers(), true ) );
-			appExpr->get_args().push_front( new CastExpr( appExpr->get_function(), adapteeType ) );
+			appExpr->get_args().push_front( new CastExpr( appExpr->function, adapteeType ) );
 			appExpr->set_function( new NameExpr( adapterName ) ); // xxx - result is never set on NameExpr
 
@@ -1764,7 +1764,7 @@
 
 		Expression *PolyGenericCalculator::postmutate( SizeofExpr *sizeofExpr ) {
-			Type *ty = sizeofExpr->get_isType() ? 
+			Type *ty = sizeofExpr->get_isType() ?
 				sizeofExpr->get_type() : sizeofExpr->get_expr()->get_result();
-			
+
 			Expression * gen = genSizeof( ty );
 			if ( gen ) {
Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/GenPoly/GenPoly.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -459,6 +459,5 @@
 
 	void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap ) {
-		// xxx - should this actually be insert?
-		tyVarMap[ tyVar->get_name() ] = TypeDecl::Data{ tyVar };
+		tyVarMap.insert( tyVar->name, TypeDecl::Data{ tyVar } );
 	}
 
Index: src/GenPoly/Lvalue.cc
===================================================================
--- src/GenPoly/Lvalue.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/GenPoly/Lvalue.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -21,9 +21,9 @@
 #include "Lvalue.h"
 
+#include "InitTweak/InitTweak.h"
 #include "Parser/LinkageSpec.h"          // for Spec, isBuiltin, Intrinsic
 #include "ResolvExpr/TypeEnvironment.h"  // for AssertionSet, OpenVarSet
 #include "ResolvExpr/Unify.h"            // for unify
 #include "ResolvExpr/typeops.h"
-#include "SymTab/Autogen.h"
 #include "SymTab/Indexer.h"              // for Indexer
 #include "SynTree/Declaration.h"         // for Declaration, FunctionDecl
@@ -33,4 +33,5 @@
 #include "SynTree/Type.h"                // for PointerType, Type, FunctionType
 #include "SynTree/Visitor.h"             // for Visitor, acceptAll
+#include "Validate/FindSpecialDecls.h"   // for dereferenceOperator
 
 #if 0
@@ -44,7 +45,7 @@
 		// TODO: fold this into the general createDeref function??
 		Expression * mkDeref( Expression * arg ) {
-			if ( SymTab::dereferenceOperator ) {
+			if ( Validate::dereferenceOperator ) {
 				// note: reference depth can be arbitrarily deep here, so peel off the outermost pointer/reference, not just pointer because they are effecitvely equivalent in this pass
-				VariableExpr * deref = new VariableExpr( SymTab::dereferenceOperator );
+				VariableExpr * deref = new VariableExpr( Validate::dereferenceOperator );
 				deref->result = new PointerType( Type::Qualifiers(), deref->result );
 				Type * base = InitTweak::getPointerBase( arg->result );
@@ -353,4 +354,6 @@
 			Type * destType = castExpr->result;
 			Type * srcType = castExpr->arg->result;
+			assertf( destType, "Cast to no type in: %s", toCString( castExpr ) );
+			assertf( srcType, "Cast from no type in: %s", toCString( castExpr ) );
 			int depth1 = destType->referenceDepth();
 			int depth2 = srcType->referenceDepth();
Index: src/GenPoly/ScopedSet.h
===================================================================
--- src/GenPoly/ScopedSet.h	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/GenPoly/ScopedSet.h	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -38,5 +38,5 @@
 		typedef typename Scope::pointer pointer;
 		typedef typename Scope::const_pointer const_pointer;
-		
+
 		class iterator : public std::iterator< std::bidirectional_iterator_tag,
 		                                       value_type > {
@@ -72,5 +72,5 @@
 				return *this;
 			}
-			
+
 			reference operator* () { return *it; }
 			pointer operator-> () { return it.operator->(); }
@@ -104,4 +104,6 @@
 			bool operator!= (const iterator &that) { return !( *this == that ); }
 
+			size_type get_level() const { return i; }
+
 		private:
 			scope_list const *scopes;
@@ -180,4 +182,6 @@
 			bool operator!= (const const_iterator &that) { return !( *this == that ); }
 
+			size_type get_level() const { return i; }
+
 		private:
 			scope_list const *scopes;
@@ -185,5 +189,5 @@
 			size_type i;
 		};
-		
+
 		/// Starts a new scope
 		void beginScope() {
@@ -222,5 +226,5 @@
 			return const_iterator( const_cast< ScopedSet< Value >* >(this)->find( key ) );
 		}
-		
+
 		/// Finds the given key in the outermost scope inside the given scope where it occurs
 		iterator findNext( const_iterator &it, const Value &key ) {
@@ -242,5 +246,5 @@
 			return std::make_pair( iterator(scopes, res.first, scopes.size()-1), res.second );
 		}
-		
+
 	};
 } // namespace GenPoly
Index: src/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/InitTweak/FixInit.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -54,5 +54,7 @@
 #include "SynTree/Type.h"              // for Type, Type::StorageClasses
 #include "SynTree/TypeSubstitution.h"  // for TypeSubstitution, operator<<
+#include "SynTree/DeclReplacer.h"      // for DeclReplacer
 #include "SynTree/Visitor.h"           // for acceptAll, maybeAccept
+#include "Validate/FindSpecialDecls.h" // for dtorStmt, dtorStructDestroy
 
 bool ctordtorp = false; // print all debug
@@ -66,6 +68,4 @@
 namespace InitTweak {
 	namespace {
-		typedef std::unordered_map< int, int > UnqCount;
-
 		struct SelfAssignChecker {
 			void previsit( ApplicationExpr * appExpr );
@@ -80,16 +80,13 @@
 		};
 
-		struct ResolveCopyCtors final : public WithIndexer, public WithShortCircuiting, public WithTypeSubstitution {
+		struct ResolveCopyCtors final : public WithStmtsToAdd, public WithIndexer, public WithShortCircuiting, public WithTypeSubstitution, public WithVisitorRef<ResolveCopyCtors> {
 			/// generate temporary ObjectDecls for each argument and return value of each ImplicitCopyCtorExpr,
 			/// generate/resolve copy construction expressions for each, and generate/resolve destructors for both
 			/// arguments and return value temporaries
-			static void resolveImplicitCalls( std::list< Declaration * > & translationUnit, UnqCount & unqCount );
-
-			ResolveCopyCtors( UnqCount & unqCount ) : unqCount( unqCount ) {}
-
-			void postvisit( ImplicitCopyCtorExpr * impCpCtorExpr );
-			void postvisit( StmtExpr * stmtExpr );
-			void previsit( UniqueExpr * unqExpr );
-			void postvisit( UniqueExpr * unqExpr );
+			static void resolveImplicitCalls( std::list< Declaration * > & translationUnit );
+
+			Expression * postmutate( ImplicitCopyCtorExpr * impCpCtorExpr );
+			void premutate( StmtExpr * stmtExpr );
+			void premutate( UniqueExpr * unqExpr );
 
 			/// create and resolve ctor/dtor expression: fname(var, [cpArg])
@@ -98,8 +95,5 @@
 			bool skipCopyConstruct( Type * type );
 			void copyConstructArg( Expression *& arg, ImplicitCopyCtorExpr * impCpCtorExpr, Type * formal );
-			void destructRet( ObjectDecl * ret, ImplicitCopyCtorExpr * impCpCtorExpr );
-
-			UnqCount & unqCount; // count the number of times each unique expr ID appears
-			std::unordered_set< int > vars;
+			void destructRet( ObjectDecl * ret, ImplicitCopyCtorExpr * impCpCtorExpr, Expression *& arg );
 		};
 
@@ -162,10 +156,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:
@@ -185,18 +175,4 @@
 
 			std::list< Declaration * > staticDtorDecls;
-		};
-
-		class FixCopyCtors final : public WithStmtsToAdd, public WithShortCircuiting, public WithVisitorRef<FixCopyCtors>, public WithTypeSubstitution {
-		  public:
-			FixCopyCtors( UnqCount & unqCount ) : unqCount( unqCount ){}
-			/// expand ImplicitCopyCtorExpr nodes into the temporary declarations, copy constructors, call expression,
-			/// and destructors
-			static void fixCopyCtors( std::list< Declaration * > &translationUnit, UnqCount & unqCount );
-
-			Expression * postmutate( ImplicitCopyCtorExpr * impCpCtorExpr );
-			void premutate( StmtExpr * stmtExpr );
-			void premutate( UniqueExpr * unqExpr );
-
-			UnqCount & unqCount;
 		};
 
@@ -236,4 +212,12 @@
 			Expression * postmutate( ConstructorExpr * ctorExpr );
 		};
+
+		struct SplitExpressions : public WithShortCircuiting, public WithTypeSubstitution, public WithStmtsToAdd {
+			/// add CompoundStmts around top-level expressions so that temporaries are destroyed in the correct places.
+			static void split( std::list< Declaration * > &translationUnit );
+
+			Statement * postmutate( ExprStmt * stmt );
+			void premutate( TupleAssignExpr * expr );
+		};
 	} // namespace
 
@@ -245,27 +229,81 @@
 		InitTweak::fixGlobalInit( translationUnit, inLibrary );
 
-		UnqCount unqCount;
+		// must happen before ResolveCopyCtors because temporaries have to be inserted into the correct scope
+		SplitExpressions::split( translationUnit );
 
 		InsertImplicitCalls::insert( translationUnit );
-		ResolveCopyCtors::resolveImplicitCalls( translationUnit, unqCount );
+
+		// Needs to happen before ResolveCopyCtors, because argument/return temporaries should not be considered in
+		// error checking branch statements
 		InsertDtors::insert( translationUnit );
+
+		ResolveCopyCtors::resolveImplicitCalls( translationUnit );
 		FixInit::fixInitializers( translationUnit );
-
-		// FixCopyCtors must happen after FixInit, so that destructors are placed correctly
-		FixCopyCtors::fixCopyCtors( translationUnit, unqCount );
-
 		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 SplitExpressions::split( std::list< Declaration * > & translationUnit ) {
+			PassVisitor<SplitExpressions> splitter;
+			mutateAll( translationUnit, splitter );
+		}
+
 		void InsertImplicitCalls::insert( std::list< Declaration * > & translationUnit ) {
 			PassVisitor<InsertImplicitCalls> inserter;
@@ -273,7 +311,7 @@
 		}
 
-		void ResolveCopyCtors::resolveImplicitCalls( std::list< Declaration * > & translationUnit, UnqCount & unqCount ) {
-			PassVisitor<ResolveCopyCtors> resolver( unqCount );
-			acceptAll( translationUnit, resolver );
+		void ResolveCopyCtors::resolveImplicitCalls( std::list< Declaration * > & translationUnit ) {
+			PassVisitor<ResolveCopyCtors> resolver;
+			mutateAll( translationUnit, resolver );
 		}
 
@@ -303,9 +341,4 @@
 		}
 
-		void FixCopyCtors::fixCopyCtors( std::list< Declaration * > & translationUnit, UnqCount & unqCount ) {
-			PassVisitor<FixCopyCtors> fixer( unqCount );
-			mutateAll( translationUnit, fixer );
-		}
-
 		void GenStructMemberCalls::generate( std::list< Declaration * > & translationUnit ) {
 			PassVisitor<GenStructMemberCalls> warner;
@@ -318,67 +351,77 @@
 		}
 
-		namespace {
-			// Relatively simple structural comparison for expressions, needed to determine
-			// if two expressions are "the same" (used to determine if self assignment occurs)
-			struct StructuralChecker {
-				Expression * stripCasts( Expression * expr ) {
-					// this might be too permissive. It's possible that only particular casts are relevant.
-					while ( CastExpr * cast = dynamic_cast< CastExpr * >( expr ) ) {
-						expr = cast->arg;
-					}
-					return expr;
-				}
-
-				void previsit( Expression * ) {
-					// anything else does not qualify
-					isSimilar = false;
-				}
-
-				template<typename T>
-				T * cast( Expression * node ) {
-					// all expressions need to ignore casts, so this bit has been factored out
-					return dynamic_cast< T * >( stripCasts( node ) );
-				}
-
-				// ignore casts
-				void previsit( CastExpr * ) {}
-
-				void previsit( MemberExpr * memExpr ) {
-					if ( MemberExpr * otherMember = cast< MemberExpr >( other ) ) {
-						if ( otherMember->member == memExpr->member ) {
-							other = otherMember->aggregate;
-							return;
-						}
-					}
-					isSimilar = false;
-				}
-
-				void previsit( VariableExpr * varExpr ) {
-					if ( VariableExpr * otherVar = cast< VariableExpr >( other ) ) {
-						if ( otherVar->var == varExpr->var ) {
-							return;
-						}
-					}
-					isSimilar = false;
-				}
-
-				void previsit( AddressExpr * ) {
-					if ( AddressExpr * addrExpr = cast< AddressExpr >( other ) ) {
-						other = addrExpr->arg;
+		Statement * SplitExpressions::postmutate( ExprStmt * stmt ) {
+			// wrap each top-level ExprStmt in a block so that destructors for argument and return temporaries are destroyed
+			// in the correct places
+			CompoundStmt * ret = new CompoundStmt( { stmt } );
+			return ret;
+		}
+
+		void SplitExpressions::premutate( TupleAssignExpr * ) {
+			// don't do this within TupleAssignExpr, since it is already broken up into multiple expressions
+			visit_children = false;
+		}
+
+		// Relatively simple structural comparison for expressions, needed to determine
+		// if two expressions are "the same" (used to determine if self assignment occurs)
+		struct StructuralChecker {
+			Expression * stripCasts( Expression * expr ) {
+				// this might be too permissive. It's possible that only particular casts are relevant.
+				while ( CastExpr * cast = dynamic_cast< CastExpr * >( expr ) ) {
+					expr = cast->arg;
+				}
+				return expr;
+			}
+
+			void previsit( Expression * ) {
+				// anything else does not qualify
+				isSimilar = false;
+			}
+
+			template<typename T>
+			T * cast( Expression * node ) {
+				// all expressions need to ignore casts, so this bit has been factored out
+				return dynamic_cast< T * >( stripCasts( node ) );
+			}
+
+			// ignore casts
+			void previsit( CastExpr * ) {}
+
+			void previsit( MemberExpr * memExpr ) {
+				if ( MemberExpr * otherMember = cast< MemberExpr >( other ) ) {
+					if ( otherMember->member == memExpr->member ) {
+						other = otherMember->aggregate;
 						return;
 					}
-					isSimilar = false;
-				}
-
-				Expression * other = nullptr;
-				bool isSimilar = true;
-			};
-
-			bool structurallySimilar( Expression * e1, Expression * e2 ) {
-				PassVisitor<StructuralChecker> checker;
-				checker.pass.other = e2;
-				e1->accept( checker );
-				return checker.pass.isSimilar;
-			}
+				}
+				isSimilar = false;
+			}
+
+			void previsit( VariableExpr * varExpr ) {
+				if ( VariableExpr * otherVar = cast< VariableExpr >( other ) ) {
+					if ( otherVar->var == varExpr->var ) {
+						return;
+					}
+				}
+				isSimilar = false;
+			}
+
+			void previsit( AddressExpr * ) {
+				if ( AddressExpr * addrExpr = cast< AddressExpr >( other ) ) {
+					other = addrExpr->arg;
+					return;
+				}
+				isSimilar = false;
+			}
+
+			Expression * other = nullptr;
+			bool isSimilar = true;
+		};
+
+		bool structurallySimilar( Expression * e1, Expression * e2 ) {
+			PassVisitor<StructuralChecker> checker;
+			checker.pass.other = e2;
+			e1->accept( checker );
+			return checker.pass.isSimilar;
 		}
 
@@ -457,5 +500,5 @@
 			if ( TupleAssignExpr * assign = dynamic_cast< TupleAssignExpr * >( resolved ) ) {
 				// fix newly generated StmtExpr
-				postvisit( assign->stmtExpr );
+				premutate( assign->stmtExpr );
 			}
 			return resolved;
@@ -489,4 +532,5 @@
 					// so that the object isn't changed inside of the polymorphic function
 					if ( ! GenPoly::needsBoxing( formal, result, impCpCtorExpr->callExpr, env ) ) return;
+					// xxx - leaking tmp
 				}
 			}
@@ -496,17 +540,59 @@
 
 			// replace argument to function call with temporary
-			arg = new CommaExpr( cpCtor, new VariableExpr( tmp ) );
-			impCpCtorExpr->tempDecls.push_back( tmp );
-			impCpCtorExpr->dtors.push_front( makeCtorDtor( "^?{}", tmp ) );
-		}
-
-		void ResolveCopyCtors::destructRet( ObjectDecl * ret, ImplicitCopyCtorExpr * impCpCtorExpr ) {
-			impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", ret ) );
-		}
-
-		void ResolveCopyCtors::postvisit( ImplicitCopyCtorExpr *impCpCtorExpr ) {
+			stmtsToAddBefore.push_back( new DeclStmt( tmp ) );
+			arg = cpCtor;
+			destructRet( tmp, impCpCtorExpr, arg );
+
+			// impCpCtorExpr->dtors.push_front( makeCtorDtor( "^?{}", tmp ) );
+		}
+
+		void ResolveCopyCtors::destructRet( ObjectDecl * ret, ImplicitCopyCtorExpr * impCpCtorExpr, Expression *& arg ) {
+			// TODO: refactor code for generating cleanup attribute, since it's common and reused in ~3-4 places
+			// check for existing cleanup attribute before adding another(?)
+			// need to add __Destructor for _tmp_cp variables as well
+
+			assertf( Validate::dtorStruct && Validate::dtorStruct->members.size() == 2, "Destructor generation requires __Destructor definition." );
+			assertf( Validate::dtorStructDestroy, "Destructor generation requires __destroy_Destructor." );
+
+			// generate a __Destructor for ret that calls the destructor
+			Expression * dtor = makeCtorDtor( "^?{}", ret );
+
+			// if the chosen destructor is intrinsic, elide the generated dtor handler
+			if ( arg && isIntrinsicCallExpr( dtor ) ) {
+				arg = new CommaExpr( arg, new VariableExpr( ret ) );
+				return;
+			}
+
+			if ( ! dtor->env ) dtor->env = maybeClone( env );
+			DeclarationWithType * dtorFunc = getDtorFunc( ret, new ExprStmt( dtor ), stmtsToAddBefore );
+
+			StructInstType * dtorStructType = new StructInstType( Type::Qualifiers(), Validate::dtorStruct );
+			dtorStructType->parameters.push_back( new TypeExpr( new VoidType( Type::Qualifiers() ) ) );
+
+			// cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
+			FunctionType * dtorFtype = new FunctionType( Type::Qualifiers(), false );
+			dtorFtype->parameters.push_back( ObjectDecl::newObject( "", new PointerType( Type::Qualifiers(), new VoidType( Type::Qualifiers() ) ), nullptr ) );
+			Type * dtorType = new PointerType( Type::Qualifiers(), dtorFtype );
+
+			static UniqueName namer( "_ret_dtor" );
+			ObjectDecl * retDtor = ObjectDecl::newObject( namer.newName(), dtorStructType, new ListInit( { new SingleInit( new ConstantExpr( Constant::null() ) ), new SingleInit( new CastExpr( new VariableExpr( dtorFunc ), dtorType ) ) } ) );
+			retDtor->attributes.push_back( new Attribute( "cleanup", { new VariableExpr( Validate::dtorStructDestroy ) } ) );
+			stmtsToAddBefore.push_back( new DeclStmt( retDtor ) );
+
+			if ( arg ) {
+				Expression * member = new MemberExpr( strict_dynamic_cast<DeclarationWithType *>( Validate::dtorStruct->members.front() ), new VariableExpr( retDtor ) );
+				Expression * object = new CastExpr( new AddressExpr( new VariableExpr( ret ) ), new PointerType( Type::Qualifiers(), new VoidType( Type::Qualifiers() ) ) );
+				Expression * assign = createBitwiseAssignment( member, object );
+				arg = new CommaExpr( new CommaExpr( arg, assign ), new VariableExpr( ret ) );
+			}
+
+			// impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", ret ) );
+		}
+
+		Expression * ResolveCopyCtors::postmutate( ImplicitCopyCtorExpr *impCpCtorExpr ) {
 			CP_CTOR_PRINT( std::cerr << "ResolveCopyCtors: " << impCpCtorExpr << std::endl; )
 
 			ApplicationExpr * appExpr = impCpCtorExpr->callExpr;
+			ObjectDecl * returnDecl = nullptr;
 
 			// take each argument and attempt to copy construct it.
@@ -517,5 +603,5 @@
 			for ( Expression * & arg : appExpr->args ) {
 				Type * formal = nullptr;
-				if ( iter != params.end() ) {
+				if ( iter != params.end() ) { // does not copy construct C-style variadic arguments
 					DeclarationWithType * param = *iter++;
 					formal = param->get_type();
@@ -535,18 +621,56 @@
 				ObjectDecl * ret = ObjectDecl::newObject( retNamer.newName(), result, nullptr );
 				ret->type->set_const( false );
-				impCpCtorExpr->returnDecls.push_back( ret );
+				returnDecl = ret;
+				stmtsToAddBefore.push_back( new DeclStmt( ret ) );
 				CP_CTOR_PRINT( std::cerr << "makeCtorDtor for a return" << std::endl; )
+			} // for
+			CP_CTOR_PRINT( std::cerr << "after Resolving: " << impCpCtorExpr << std::endl; )
+			// ------------------------------------------------------
+
+			CP_CTOR_PRINT( std::cerr << "Coming out the back..." << impCpCtorExpr << std::endl; )
+
+			// detach fields from wrapper node so that it can be deleted without deleting too much
+			impCpCtorExpr->callExpr = nullptr;
+			std::swap( impCpCtorExpr->env, appExpr->env );
+			assert( impCpCtorExpr->env == nullptr );
+			delete impCpCtorExpr;
+
+			if ( returnDecl ) {
+				Expression * assign = createBitwiseAssignment( new VariableExpr( returnDecl ), appExpr );
 				if ( ! dynamic_cast< ReferenceType * >( result ) ) {
 					// destructing reference returns is bad because it can cause multiple destructor calls to the same object - the returned object is not a temporary
-					destructRet( ret, impCpCtorExpr );
-				}
+					destructRet( returnDecl, impCpCtorExpr, assign );
+				} else {
+					assign = new CommaExpr( assign, new VariableExpr( returnDecl ) );
+				}
+				// move env from appExpr to retExpr
+				std::swap( assign->env, appExpr->env );
+				return assign;
+			} else {
+				return appExpr;
+			} // if
+		}
+
+		void ResolveCopyCtors::premutate( StmtExpr * stmtExpr ) {
+			// function call temporaries should be placed at statement-level, rather than nested inside of a new statement expression,
+			// since temporaries can be shared across sub-expressions, e.g.
+			//   [A, A] f();
+			//   g([A] x, [A] y);
+			//   g(f());
+			// f is executed once, so the return temporary is shared across the tuple constructors for x and y.
+			// Explicitly mutating children instead of mutating the inner compound statement forces the temporaries to be added
+			// to the outer context, rather than inside of the statement expression.
+			visit_children = false;
+
+			assert( env );
+
+			// visit all statements
+			std::list< Statement * > & stmts = stmtExpr->statements->get_kids();
+			for ( Statement *& stmt : stmts ) {
+				stmt = stmt->acceptMutator( *visitor );
 			} // for
-			CP_CTOR_PRINT( std::cerr << "after Resolving: " << impCpCtorExpr << std::endl; )
-		}
-
-		void ResolveCopyCtors::postvisit( StmtExpr * stmtExpr ) {
-			assert( env );
-			assert( stmtExpr->get_result() );
-			Type * result = stmtExpr->get_result();
+
+			assert( stmtExpr->result );
+			Type * result = stmtExpr->result;
 			if ( ! result->isVoid() ) {
 				static UniqueName retNamer("_tmp_stmtexpr_ret");
@@ -562,23 +686,25 @@
 				ObjectDecl * ret = ObjectDecl::newObject( retNamer.newName(), result, nullptr );
 				ret->type->set_const( false );
-				stmtExpr->returnDecls.push_front( ret );
+				stmtsToAddBefore.push_back( new DeclStmt( ret ) );
 
 				// must have a non-empty body, otherwise it wouldn't have a result
 				CompoundStmt * body = stmtExpr->statements;
-				assert( ! body->get_kids().empty() );
+				assert( ! body->kids.empty() );
 				// must be an ExprStmt, otherwise it wouldn't have a result
-				ExprStmt * last = strict_dynamic_cast< ExprStmt * >( body->get_kids().back() );
-				last->expr = makeCtorDtor( "?{}", ret, last->get_expr() );
-
-				stmtExpr->dtors.push_front( makeCtorDtor( "^?{}", ret ) );
+				ExprStmt * last = strict_dynamic_cast< ExprStmt * >( body->kids.back() );
+				last->expr = makeCtorDtor( "?{}", ret, last->expr );
+
+				// add destructors after current statement
+				stmtsToAddAfter.push_back( new ExprStmt( makeCtorDtor( "^?{}", ret ) ) );
+
+				// must have a non-empty body, otherwise it wouldn't have a result
+				assert( ! stmts.empty() );
+
+				// if there is a return decl, add a use as the last statement; will not have return decl on non-constructable returns
+				stmts.push_back( new ExprStmt( new VariableExpr( ret ) ) );
 			} // if
-		}
-
-		void ResolveCopyCtors::previsit( UniqueExpr * unqExpr ) {
-			unqCount[ unqExpr->get_id() ]++;  // count the number of unique expressions for each ID
-			if ( vars.count( unqExpr->get_id() ) ) {
-				// xxx - hack to prevent double-handling of unique exprs, otherwise too many temporary variables and destructors are generated
-				visit_children = false;
-			}
+
+			assert( stmtExpr->returnDecls.empty() );
+			assert( stmtExpr->dtors.empty() );
 		}
 
@@ -597,137 +723,38 @@
 		}
 
-		void ResolveCopyCtors::postvisit( UniqueExpr * unqExpr ) {
-			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;
-			}
-
-			// 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() ) );
+		void ResolveCopyCtors::premutate( UniqueExpr * unqExpr ) {
+			visit_children = false;
+			// xxx - hack to prevent double-handling of unique exprs, otherwise too many temporary variables and destructors are generated
+			static std::unordered_map< int, UniqueExpr * > unqMap;
+			if ( ! unqMap.count( unqExpr->get_id() ) ) {
+				// resolve expr and find its
+
+				ImplicitCopyCtorExpr * impCpCtorExpr = dynamic_cast< ImplicitCopyCtorExpr * >( unqExpr->expr );
+				// PassVisitor<ResolveCopyCtors> fixer;
+				unqExpr->expr = unqExpr->expr->acceptMutator( *visitor );
+
+				// 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->result );
+				if ( impCpCtorExpr ) {
+					CommaExpr * comma = strict_dynamic_cast< CommaExpr * >( unqExpr->expr );
+					VariableExpr * var = strict_dynamic_cast<VariableExpr *>( comma->arg2 );
+					// note the variable used as the result from the call
+					unqExpr->var = var->clone();
+				} else {
+					// expr isn't a call expr, so create a new temporary variable to use to hold the value of the unique expression
+					unqExpr->object = ObjectDecl::newObject( toString("_unq", unqExpr->get_id()), unqExpr->result->clone(), makeInit( unqExpr->result ) );
+					unqExpr->var = new VariableExpr( unqExpr->object );
+				}
+
+				// stmtsToAddBefore.splice( stmtsToAddBefore.end(), fixer.pass.stmtsToAddBefore );
+				// stmtsToAddAfter.splice( stmtsToAddAfter.end(), fixer.pass.stmtsToAddAfter );
+				unqMap[unqExpr->get_id()] = unqExpr;
 			} 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() ) );
-			}
-			vars.insert( unqExpr->get_id() );
-		}
-
-		Expression * FixCopyCtors::postmutate( ImplicitCopyCtorExpr * impCpCtorExpr ) {
-			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();
-
-			// add all temporary declarations and their constructors
-			for ( ObjectDecl * obj : tempDecls ) {
-				stmtsToAddBefore.push_back( new DeclStmt( obj ) );
-			} // for
-			for ( ObjectDecl * obj : returnDecls ) {
-				stmtsToAddBefore.push_back( new DeclStmt( obj ) );
-			} // for
-
-			// add destructors after current statement
-			for ( Expression * dtor : dtors ) {
-				// take relevant bindings from environment
-				assert( ! dtor->env );
-				dtor->env =  maybeClone( env );
-				stmtsToAddAfter.push_back( new ExprStmt( dtor ) );
-			} // for
-
-			ObjectDecl * returnDecl = returnDecls.empty() ? nullptr : returnDecls.front();
-			Expression * callExpr = impCpCtorExpr->get_callExpr();
-
-			CP_CTOR_PRINT( std::cerr << "Coming out the back..." << impCpCtorExpr << std::endl; )
-
-			// detach fields from wrapper node so that it can be deleted without deleting too much
-			dtors.clear();
-			tempDecls.clear();
-			returnDecls.clear();
-			impCpCtorExpr->set_callExpr( nullptr );
-			std::swap( impCpCtorExpr->env, callExpr->env );
-			assert( impCpCtorExpr->env == nullptr );
-			delete impCpCtorExpr;
-
-			if ( returnDecl ) {
-				ApplicationExpr * assign = createBitwiseAssignment( new VariableExpr( returnDecl ), callExpr );
-				Expression * retExpr = new CommaExpr( assign, new VariableExpr( returnDecl ) );
-				// move env from callExpr to retExpr
-				std::swap( retExpr->env, callExpr->env );
-				return retExpr;
-			} else {
-				return callExpr;
-			} // if
-		}
-
-		void FixCopyCtors::premutate( StmtExpr * stmtExpr ) {
-			// function call temporaries should be placed at statement-level, rather than nested inside of a new statement expression,
-			// since temporaries can be shared across sub-expressions, e.g.
-			//   [A, A] f();
-			//   g([A] x, [A] y);
-			//   g(f());
-			// f is executed once, so the return temporary is shared across the tuple constructors for x and y.
-			// Explicitly mutating children instead of mutating the inner compound statment forces the temporaries to be added
-			// to the outer context, rather than inside of the statement expression.
-			visit_children = false;
-			std::list< Statement * > & stmts = stmtExpr->statements->get_kids();
-			for ( Statement *& stmt : stmts ) {
-				stmt = stmt->acceptMutator( *visitor );
-			} // for
-			assert( stmtExpr->result );
-			Type * result = stmtExpr->result;
-			if ( ! result->isVoid() ) {
-				for ( ObjectDecl * obj : stmtExpr->returnDecls ) {
-					stmtsToAddBefore.push_back( new DeclStmt( obj ) );
-				} // for
-				// add destructors after current statement
-				for ( Expression * dtor : stmtExpr->dtors ) {
-					stmtsToAddAfter.push_back( new ExprStmt( dtor ) );
-				} // for
-				// must have a non-empty body, otherwise it wouldn't have a result
-				assert( ! stmts.empty() );
-				assertf( ! stmtExpr->returnDecls.empty() || stmtExpr->dtors.empty(), "StmtExpr returns non-void, but no return decls: %s", toString( stmtExpr ).c_str() );
-				// if there is a return decl, add a use as the last statement; will not have return decl on non-constructable returns
-				if ( ! stmtExpr->returnDecls.empty() ) {
-					stmts.push_back( new ExprStmt( new VariableExpr( stmtExpr->returnDecls.front() ) ) );
-				}
-				stmtExpr->returnDecls.clear();
-				stmtExpr->dtors.clear();
-			}
-			assert( stmtExpr->returnDecls.empty() );
-			assert( stmtExpr->dtors.empty() );
-		}
-
-		void FixCopyCtors::premutate( UniqueExpr * unqExpr ) {
-			visit_children = false;
-			unqCount[ unqExpr->get_id() ]--;
-			static std::unordered_map< int, std::list< Statement * > > dtors;
-			static std::unordered_map< int, UniqueExpr * > unqMap;
-			// has to be done to clean up ImplicitCopyCtorExpr nodes, even when this node was skipped in previous passes
-			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 ( unqCount[ unqExpr->get_id() ] == 0 ) {  // insert destructor after the last use of the unique expression
-					stmtsToAddAfter.splice( stmtsToAddAfter.end(), dtors[ unqExpr->get_id() ] );
-				}
-				return;
-			}
-			PassVisitor<FixCopyCtors> fixer( unqCount );
-			unqExpr->set_expr( unqExpr->get_expr()->acceptMutator( fixer ) ); // stmtexprs contained should not be separately fixed, so this must occur after the lookup
-			stmtsToAddBefore.splice( stmtsToAddBefore.end(), fixer.pass.stmtsToAddBefore );
-			unqMap[unqExpr->get_id()] = unqExpr;
-			if ( unqCount[ unqExpr->get_id() ] == 0 ) {  // insert destructor after the last use of the unique expression
-				stmtsToAddAfter.splice( stmtsToAddAfter.end(), dtors[ unqExpr->get_id() ] );
-			} else { // remember dtors for last instance of unique expr
-				dtors[ unqExpr->get_id() ] = fixer.pass.stmtsToAddAfter;
-			}
-			return;
+				unqExpr->expr = unqMap[unqExpr->get_id()]->expr->clone();
+				delete unqExpr->result;
+				unqExpr->result = maybeClone( unqExpr->expr->result );
+			}
 		}
 
@@ -844,4 +871,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, stmtsToAddBefore );
+								objDecl->attributes.push_back( new Attribute( "cleanup", { new VariableExpr( dtorFunc ) } ) );
+								ctorInit->dtor = nullptr;
+							} // if
+						}
 					} // if
 				} else if ( Initializer * init = ctorInit->init ) {
@@ -886,36 +928,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
@@ -930,29 +940,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
@@ -982,22 +967,4 @@
 			if ( ! diff.empty() ) {
 				SemanticError( stmt, std::string("jump to label '") + stmt->get_target().get_name() + "' crosses initialization of " + (*diff.begin())->get_name() + " " );
-			} // 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
 		}
@@ -1116,8 +1083,32 @@
 							callStmt->acceptMutator( *visitor );
 							if ( isCtor ) {
-								function->get_statements()->push_front( callStmt );
-							} else {
+								function->statements->push_front( callStmt );
+							} else { // TODO: don't generate destructor function/object for intrinsic calls
 								// destructor statements should be added at the end
-								function->get_statements()->push_back( callStmt );
+								// function->get_statements()->push_back( callStmt );
+
+								// Optimization: do not need to call intrinsic destructors on members
+								if ( isIntrinsicSingleArgCallStmt( callStmt ) ) continue;;
+
+								// __Destructor _dtor0 = { (void *)&b.a1, (void (*)(void *)_destroy_A };
+								std::list< Statement * > stmtsToAdd;
+
+								static UniqueName memberDtorNamer = { "__memberDtor" };
+								assertf( Validate::dtorStruct, "builtin __Destructor not found." );
+								assertf( Validate::dtorStructDestroy, "builtin __destroy_Destructor not found." );
+
+								Expression * thisExpr = new CastExpr( new AddressExpr( new VariableExpr( thisParam ) ), new PointerType( Type::Qualifiers(), new VoidType( Type::Qualifiers() ) ) );
+								Expression * dtorExpr = new VariableExpr( getDtorFunc( thisParam, callStmt, stmtsToAdd ) );
+
+								// cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
+								FunctionType * dtorFtype = new FunctionType( Type::Qualifiers(), false );
+								dtorFtype->parameters.push_back( ObjectDecl::newObject( "", new PointerType( Type::Qualifiers(), new VoidType( Type::Qualifiers() ) ), nullptr ) );
+								Type * dtorType = new PointerType( Type::Qualifiers(), dtorFtype );
+
+								ObjectDecl * destructor = ObjectDecl::newObject( memberDtorNamer.newName(), new StructInstType( Type::Qualifiers(), Validate::dtorStruct ), new ListInit( { new SingleInit( thisExpr ), new SingleInit( new CastExpr( dtorExpr, dtorType ) ) } ) );
+								function->statements->push_front( new DeclStmt( destructor ) );
+								destructor->attributes.push_back( new Attribute( "cleanup", { new VariableExpr( Validate::dtorStructDestroy ) } ) );
+
+								function->statements->kids.splice( function->statements->kids.begin(), stmtsToAdd );
 							}
 						} catch ( SemanticErrorException & error ) {
Index: src/InitTweak/GenInit.cc
===================================================================
--- src/InitTweak/GenInit.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/InitTweak/GenInit.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -15,31 +15,32 @@
 #include "GenInit.h"
 
-#include <stddef.h>                // for NULL
-#include <algorithm>               // for any_of
-#include <cassert>                 // for assert, strict_dynamic_cast, assertf
-#include <iterator>                // for back_inserter, inserter, back_inse...
-#include <list>                    // for _List_iterator, list
+#include <stddef.h>                    // for NULL
+#include <algorithm>                   // for any_of
+#include <cassert>                     // for assert, strict_dynamic_cast, assertf
+#include <iterator>                    // for back_inserter, inserter, back_inse...
+#include <list>                        // for _List_iterator, list
 
 #include "CodeGen/OperatorTable.h"
-#include "Common/PassVisitor.h"    // for PassVisitor, WithGuards, WithShort...
-#include "Common/SemanticError.h"  // for SemanticError
-#include "Common/UniqueName.h"     // for UniqueName
-#include "Common/utility.h"        // for ValueGuard, maybeClone
-#include "GenPoly/GenPoly.h"       // for getFunctionType, isPolyType
-#include "GenPoly/ScopedSet.h"     // for ScopedSet, ScopedSet<>::const_iter...
-#include "InitTweak.h"             // for isConstExpr, InitExpander, checkIn...
-#include "Parser/LinkageSpec.h"    // for isOverridable, C
+#include "Common/PassVisitor.h"        // for PassVisitor, WithGuards, WithShort...
+#include "Common/SemanticError.h"      // for SemanticError
+#include "Common/UniqueName.h"         // for UniqueName
+#include "Common/utility.h"            // for ValueGuard, maybeClone
+#include "GenPoly/GenPoly.h"           // for getFunctionType, isPolyType
+#include "GenPoly/ScopedSet.h"         // for ScopedSet, ScopedSet<>::const_iter...
+#include "InitTweak.h"                 // for isConstExpr, InitExpander, checkIn...
+#include "Parser/LinkageSpec.h"        // for isOverridable, C
 #include "ResolvExpr/Resolver.h"
-#include "SymTab/Autogen.h"        // for genImplicitCall, SizeType
-#include "SymTab/Mangler.h"        // for Mangler
-#include "SynTree/Declaration.h"   // for ObjectDecl, DeclarationWithType
-#include "SynTree/Expression.h"    // for VariableExpr, UntypedExpr, Address...
-#include "SynTree/Initializer.h"   // for ConstructorInit, SingleInit, Initi...
-#include "SynTree/Label.h"         // for Label
-#include "SynTree/Mutator.h"       // for mutateAll
-#include "SynTree/Statement.h"     // for CompoundStmt, ImplicitCtorDtorStmt
-#include "SynTree/Type.h"          // for Type, ArrayType, Type::Qualifiers
-#include "SynTree/Visitor.h"       // for acceptAll, maybeAccept
-#include "Tuples/Tuples.h"         // for maybeImpure
+#include "SymTab/Autogen.h"            // for genImplicitCall
+#include "SymTab/Mangler.h"            // for Mangler
+#include "SynTree/Declaration.h"       // for ObjectDecl, DeclarationWithType
+#include "SynTree/Expression.h"        // for VariableExpr, UntypedExpr, Address...
+#include "SynTree/Initializer.h"       // for ConstructorInit, SingleInit, Initi...
+#include "SynTree/Label.h"             // for Label
+#include "SynTree/Mutator.h"           // for mutateAll
+#include "SynTree/Statement.h"         // for CompoundStmt, ImplicitCtorDtorStmt
+#include "SynTree/Type.h"              // for Type, ArrayType, Type::Qualifiers
+#include "SynTree/Visitor.h"           // for acceptAll, maybeAccept
+#include "Tuples/Tuples.h"             // for maybeImpure
+#include "Validate/FindSpecialDecls.h" // for SizeType
 
 namespace InitTweak {
@@ -186,5 +187,5 @@
 
 			// need to resolve array dimensions in order to accurately determine if constexpr
-			ResolvExpr::findSingleExpression( arrayType->dimension, SymTab::SizeType->clone(), indexer );
+			ResolvExpr::findSingleExpression( arrayType->dimension, Validate::SizeType->clone(), indexer );
 			// array is variable-length when the dimension is not constexpr
 			arrayType->isVarLen = ! isConstExpr( arrayType->dimension );
@@ -192,5 +193,5 @@
 			if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return;
 
-			ObjectDecl * arrayDimension = new ObjectDecl( dimensionName.newName(), storageClasses, LinkageSpec::C, 0, SymTab::SizeType->clone(), new SingleInit( arrayType->get_dimension() ) );
+			ObjectDecl * arrayDimension = new ObjectDecl( dimensionName.newName(), storageClasses, LinkageSpec::C, 0, Validate::SizeType->clone(), new SingleInit( arrayType->get_dimension() ) );
 			arrayDimension->get_type()->set_const( true );
 
Index: src/InitTweak/InitTweak.cc
===================================================================
--- src/InitTweak/InitTweak.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/InitTweak/InitTweak.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -339,5 +339,5 @@
 		std::list< Expression * > matches;
 		collectCtorDtorCalls( stmt, matches );
-		assert( matches.size() <= 1 );
+		assertf( matches.size() <= 1, "%zd constructor/destructors found in %s", matches.size(), toString( stmt ).c_str() );
 		return matches.size() == 1 ? matches.front() : nullptr;
 	}
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/Makefile.am	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -56,10 +56,12 @@
 ARFLAGS     = cr
 
-demangler_SOURCES = SymTab/demangler.cc
+demangler_SOURCES = SymTab/demangler.cc # test driver for the demangler, also useful as a sanity check that libdemangle.a is complete
 
 demangler_LDADD = libdemangle.a     # yywrap
 
 noinst_LIBRARIES = libdemangle.a
-libdemangle_a_SOURCES = SymTab/Demangle.cc SymTab/ManglerCommon.cc \
+libdemangle_a_SOURCES = \
+	SymTab/Demangle.cc \
+	SymTab/ManglerCommon.cc \
 	SynTree/Type.cc \
 	SynTree/VoidType.cc \
@@ -99,4 +101,5 @@
 	CodeGen/CodeGenerator.cc \
 	CodeGen/FixMain.cc \
+	CodeGen/Generate.cc \
 	CodeGen/GenType.cc \
 	CodeGen/OperatorTable.cc \
@@ -144,5 +147,7 @@
 	Tuples/TupleAssignment.cc \
 	Tuples/TupleExpansion.cc \
-	Validate/HandleAttributes.cc
+	Validate/HandleAttributes.cc \
+	Validate/FindSpecialDecls.cc
+
 
 MAINTAINERCLEANFILES += ${libdir}/${notdir ${cfa_cpplib_PROGRAMS}}
Index: src/Makefile.in
===================================================================
--- src/Makefile.in	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/Makefile.in	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -184,8 +184,8 @@
 	SynTree/DeclReplacer.$(OBJEXT) CompilationState.$(OBJEXT) \
 	CodeGen/CodeGenerator.$(OBJEXT) CodeGen/FixMain.$(OBJEXT) \
-	CodeGen/GenType.$(OBJEXT) CodeGen/OperatorTable.$(OBJEXT) \
-	Common/Assert.$(OBJEXT) Common/Eval.$(OBJEXT) \
-	Common/SemanticError.$(OBJEXT) Common/UniqueName.$(OBJEXT) \
-	Concurrency/Keywords.$(OBJEXT) \
+	CodeGen/Generate.$(OBJEXT) CodeGen/GenType.$(OBJEXT) \
+	CodeGen/OperatorTable.$(OBJEXT) Common/Assert.$(OBJEXT) \
+	Common/Eval.$(OBJEXT) Common/SemanticError.$(OBJEXT) \
+	Common/UniqueName.$(OBJEXT) Concurrency/Keywords.$(OBJEXT) \
 	ControlStruct/ForExprMutator.$(OBJEXT) \
 	ControlStruct/LabelFixer.$(OBJEXT) \
@@ -217,5 +217,6 @@
 	Tuples/Explode.$(OBJEXT) Tuples/TupleAssignment.$(OBJEXT) \
 	Tuples/TupleExpansion.$(OBJEXT) \
-	Validate/HandleAttributes.$(OBJEXT)
+	Validate/HandleAttributes.$(OBJEXT) \
+	Validate/FindSpecialDecls.$(OBJEXT)
 libdemangle_a_OBJECTS = $(am_libdemangle_a_OBJECTS)
 am__installdirs = "$(DESTDIR)$(cfa_cpplibdir)"
@@ -294,4 +295,5 @@
 	Tuples/TupleExpansion.$(OBJEXT) Tuples/Explode.$(OBJEXT) \
 	Validate/HandleAttributes.$(OBJEXT) \
+	Validate/FindSpecialDecls.$(OBJEXT) \
 	Virtual/ExpandCasts.$(OBJEXT)
 am____driver_cfa_cpp_OBJECTS = $(am__objects_1)
@@ -616,5 +618,5 @@
 	Tuples/TupleAssignment.cc Tuples/TupleExpansion.cc \
 	Tuples/Explode.cc Validate/HandleAttributes.cc \
-	Virtual/ExpandCasts.cc
+	Validate/FindSpecialDecls.cc Virtual/ExpandCasts.cc
 MAINTAINERCLEANFILES = ${libdir}/${notdir ${cfa_cpplib_PROGRAMS}}
 MOSTLYCLEANFILES = Parser/gcc-flags.h Parser/lex.cc Parser/parser.cc \
@@ -630,8 +632,10 @@
 AM_LDFLAGS = @HOST_FLAGS@ -Xlinker -export-dynamic
 ARFLAGS = cr
-demangler_SOURCES = SymTab/demangler.cc
+demangler_SOURCES = SymTab/demangler.cc # test driver for the demangler, also useful as a sanity check that libdemangle.a is complete
 demangler_LDADD = libdemangle.a     # yywrap
 noinst_LIBRARIES = libdemangle.a
-libdemangle_a_SOURCES = SymTab/Demangle.cc SymTab/ManglerCommon.cc \
+libdemangle_a_SOURCES = \
+	SymTab/Demangle.cc \
+	SymTab/ManglerCommon.cc \
 	SynTree/Type.cc \
 	SynTree/VoidType.cc \
@@ -671,4 +675,5 @@
 	CodeGen/CodeGenerator.cc \
 	CodeGen/FixMain.cc \
+	CodeGen/Generate.cc \
 	CodeGen/GenType.cc \
 	CodeGen/OperatorTable.cc \
@@ -716,5 +721,6 @@
 	Tuples/TupleAssignment.cc \
 	Tuples/TupleExpansion.cc \
-	Validate/HandleAttributes.cc
+	Validate/HandleAttributes.cc \
+	Validate/FindSpecialDecls.cc
 
 all: $(BUILT_SOURCES)
@@ -849,4 +855,6 @@
 	CodeGen/$(DEPDIR)/$(am__dirstamp)
 CodeGen/FixMain.$(OBJEXT): CodeGen/$(am__dirstamp) \
+	CodeGen/$(DEPDIR)/$(am__dirstamp)
+CodeGen/Generate.$(OBJEXT): CodeGen/$(am__dirstamp) \
 	CodeGen/$(DEPDIR)/$(am__dirstamp)
 CodeGen/GenType.$(OBJEXT): CodeGen/$(am__dirstamp) \
@@ -996,4 +1004,6 @@
 Validate/HandleAttributes.$(OBJEXT): Validate/$(am__dirstamp) \
 	Validate/$(DEPDIR)/$(am__dirstamp)
+Validate/FindSpecialDecls.$(OBJEXT): Validate/$(am__dirstamp) \
+	Validate/$(DEPDIR)/$(am__dirstamp)
 
 libdemangle.a: $(libdemangle_a_OBJECTS) $(libdemangle_a_DEPENDENCIES) $(EXTRA_libdemangle_a_DEPENDENCIES) 
@@ -1050,6 +1060,4 @@
 	echo " rm -f" $$list; \
 	rm -f $$list
-CodeGen/Generate.$(OBJEXT): CodeGen/$(am__dirstamp) \
-	CodeGen/$(DEPDIR)/$(am__dirstamp)
 CodeGen/FixNames.$(OBJEXT): CodeGen/$(am__dirstamp) \
 	CodeGen/$(DEPDIR)/$(am__dirstamp)
@@ -1270,4 +1278,5 @@
 @AMDEP_TRUE@@am__include@ @am__quote@Tuples/$(DEPDIR)/TupleAssignment.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Tuples/$(DEPDIR)/TupleExpansion.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@Validate/$(DEPDIR)/FindSpecialDecls.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Validate/$(DEPDIR)/HandleAttributes.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@Virtual/$(DEPDIR)/ExpandCasts.Po@am__quote@
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/Parser/parser.yy	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -1290,7 +1290,7 @@
 
 handler_clause:
-	handler_key '(' push exception_declaration pop handler_predicate_opt ')' compound_statement pop
+	handler_key '(' push exception_declaration pop handler_predicate_opt ')' compound_statement
 		{ $$ = new StatementNode( build_catch( $1, $4, $6, $8 ) ); }
-	| handler_clause handler_key '(' push exception_declaration pop handler_predicate_opt ')' compound_statement pop
+	| handler_clause handler_key '(' push exception_declaration pop handler_predicate_opt ')' compound_statement
 		{ $$ = (StatementNode *)$1->set_last( new StatementNode( build_catch( $2, $5, $7, $9 ) ) ); }
 	;
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/ResolvExpr/Resolver.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -43,4 +43,5 @@
 #include "typeops.h"                     // for extractResultType
 #include "Unify.h"                       // for unify
+#include "Validate/FindSpecialDecls.h"   // for SizeType
 
 using namespace std;
@@ -235,5 +236,5 @@
 				winner.cost = winner.cvtCost;
 			}
-			
+
 			// produce ambiguous errors, if applicable
 			if ( winners.size() != 1 ) {
@@ -255,5 +256,5 @@
 
 			// xxx - check for ambiguous expressions
-			
+
 			// output selected choice
 			alt = std::move( choice );
@@ -402,8 +403,8 @@
 
 	void Resolver::previsit( ObjectDecl *objectDecl ) {
-		// To handle initialization of routine pointers, e.g., int (*fp)(int) = foo(), means that 
-		// class-variable initContext is changed multiple time because the LHS is analysed twice. 
-		// The second analysis changes initContext because of a function type can contain object 
-		// declarations in the return and parameter types. So each value of initContext is 
+		// To handle initialization of routine pointers, e.g., int (*fp)(int) = foo(), means that
+		// class-variable initContext is changed multiple time because the LHS is analysed twice.
+		// The second analysis changes initContext because of a function type can contain object
+		// declarations in the return and parameter types. So each value of initContext is
 		// retained, so the type on the first analysis is preserved and used for selecting the RHS.
 		GuardValue( currentObject );
@@ -419,5 +420,5 @@
 	void Resolver::handlePtrType( PtrType * type ) {
 		if ( type->get_dimension() ) {
-			findSingleExpression( type->dimension, SymTab::SizeType->clone(), indexer );
+			findSingleExpression( type->dimension, Validate::SizeType->clone(), indexer );
 		}
 	}
@@ -442,7 +443,7 @@
 
 	void Resolver::postvisit( FunctionDecl *functionDecl ) {
-		// default value expressions have an environment which shouldn't be there and trips up 
+		// default value expressions have an environment which shouldn't be there and trips up
 		// later passes.
-		// xxx - it might be necessary to somehow keep the information from this environment, but I 
+		// xxx - it might be necessary to somehow keep the information from this environment, but I
 		// can't currently see how it's useful.
 		for ( Declaration * d : functionDecl->type->parameters ) {
@@ -795,11 +796,11 @@
 		initExpr->expr = nullptr;
 		std::swap( initExpr->env, newExpr->env );
-		// InitExpr may have inferParams in the case where the expression specializes a function 
-		// pointer, and newExpr may already have inferParams of its own, so a simple swap is not 
+		// InitExpr may have inferParams in the case where the expression specializes a function
+		// pointer, and newExpr may already have inferParams of its own, so a simple swap is not
 		// sufficient.
 		newExpr->spliceInferParams( initExpr );
 		delete initExpr;
 
-		// get the actual object's type (may not exactly match what comes back from the resolver 
+		// get the actual object's type (may not exactly match what comes back from the resolver
 		// due to conversions)
 		Type * initContext = currentObject.getCurrentType();
@@ -814,5 +815,5 @@
 					if ( isCharType( pt->get_base() ) ) {
 						if ( CastExpr *ce = dynamic_cast< CastExpr * >( newExpr ) ) {
-							// strip cast if we're initializing a char[] with a char *, 
+							// strip cast if we're initializing a char[] with a char *,
 							// e.g.  char x[] = "hello";
 							newExpr = ce->get_arg();
@@ -837,9 +838,9 @@
 		// move cursor into brace-enclosed initializer-list
 		currentObject.enterListInit();
-		// xxx - fix this so that the list isn't copied, iterator should be used to change current 
+		// xxx - fix this so that the list isn't copied, iterator should be used to change current
 		// element
 		std::list<Designation *> newDesignations;
 		for ( auto p : group_iterate(listInit->get_designations(), listInit->get_initializers()) ) {
-			// iterate designations and initializers in pairs, moving the cursor to the current 
+			// iterate designations and initializers in pairs, moving the cursor to the current
 			// designated object and resolving the initializer against that object.
 			Designation * des = std::get<0>(p);
Index: src/SymTab/Autogen.cc
===================================================================
--- src/SymTab/Autogen.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/SymTab/Autogen.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -41,9 +41,7 @@
 
 namespace SymTab {
-	Type * SizeType = 0;
-
 	/// Data used to generate functions generically. Specifically, the name of the generated function and a function which generates the routine protoype
 	struct FuncData {
-		typedef FunctionType * (*TypeGen)( Type * );
+		typedef FunctionType * (*TypeGen)( Type *, bool );
 		FuncData( const std::string & fname, const TypeGen & genType ) : fname( fname ), genType( genType ) {}
 		std::string fname;
@@ -231,8 +229,11 @@
 
 	/// given type T, generate type of default ctor/dtor, i.e. function type void (*) (T *)
-	FunctionType * genDefaultType( Type * paramType ) {
-		const auto & typeParams = getGenericParams( paramType );
+	FunctionType * genDefaultType( Type * paramType, bool maybePolymorphic ) {
 		FunctionType *ftype = new FunctionType( Type::Qualifiers(), false );
-		cloneAll( typeParams, ftype->forall );
+		if ( maybePolymorphic ) {
+			// only copy in
+			const auto & typeParams = getGenericParams( paramType );
+			cloneAll( typeParams, ftype->forall );
+		}
 		ObjectDecl *dstParam = new ObjectDecl( "_dst", Type::StorageClasses(), LinkageSpec::Cforall, nullptr, new ReferenceType( Type::Qualifiers(), paramType->clone() ), nullptr );
 		ftype->parameters.push_back( dstParam );
@@ -241,6 +242,6 @@
 
 	/// given type T, generate type of copy ctor, i.e. function type void (*) (T *, T)
-	FunctionType * genCopyType( Type * paramType ) {
-		FunctionType *ftype = genDefaultType( paramType );
+	FunctionType * genCopyType( Type * paramType, bool maybePolymorphic ) {
+		FunctionType *ftype = genDefaultType( paramType, maybePolymorphic );
 		ObjectDecl *srcParam = new ObjectDecl( "_src", Type::StorageClasses(), LinkageSpec::Cforall, nullptr, paramType->clone(), nullptr );
 		ftype->parameters.push_back( srcParam );
@@ -249,6 +250,6 @@
 
 	/// given type T, generate type of assignment, i.e. function type T (*) (T *, T)
-	FunctionType * genAssignType( Type * paramType ) {
-		FunctionType *ftype = genCopyType( paramType );
+	FunctionType * genAssignType( Type * paramType, bool maybePolymorphic ) {
+		FunctionType *ftype = genCopyType( paramType, maybePolymorphic );
 		ObjectDecl *returnVal = new ObjectDecl( "_ret", Type::StorageClasses(), LinkageSpec::Cforall, nullptr, paramType->clone(), nullptr );
 		ftype->returnVals.push_back( returnVal );
@@ -308,5 +309,5 @@
 		for ( const FuncData & d : data ) {
 			// generate a function (?{}, ?=?, ^?{}) based on the current FuncData.
-			FunctionType * ftype = d.genType( type );
+			FunctionType * ftype = d.genType( type, true );
 
 			// destructor for concurrent type must be mutex
Index: src/SymTab/Autogen.h
===================================================================
--- src/SymTab/Autogen.h	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/SymTab/Autogen.h	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -37,20 +37,15 @@
 	bool isUnnamedBitfield( ObjectDecl * obj );
 
-	/// size_t type - set when size_t typedef is seen. Useful in a few places,
-	/// such as in determining array dimension type
-	extern Type * SizeType;
-
-	/// intrinsic dereference operator for unqualified types - set when *? function is seen in FindSpecialDeclarations.
-	/// Useful for creating dereference ApplicationExprs without a full resolver pass.
-	extern FunctionDecl * dereferenceOperator;
-
-	// generate the type of an assignment function for paramType
-	FunctionType * genAssignType( Type * paramType );
-
-	// generate the type of a default constructor or destructor for paramType
-	FunctionType * genDefaultType( Type * paramType );
-
-	// generate the type of a copy constructor for paramType
-	FunctionType * genCopyType( Type * paramType );
+	/// generate the type of an assignment function for paramType.
+	/// maybePolymorphic is true if the resulting FunctionType is allowed to be polymorphic
+	FunctionType * genAssignType( Type * paramType, bool maybePolymorphic = true );
+
+	/// generate the type of a default constructor or destructor for paramType.
+	/// maybePolymorphic is true if the resulting FunctionType is allowed to be polymorphic
+	FunctionType * genDefaultType( Type * paramType, bool maybePolymorphic = true );
+
+	/// generate the type of a copy constructor for paramType.
+	/// maybePolymorphic is true if the resulting FunctionType is allowed to be polymorphic
+	FunctionType * genCopyType( Type * paramType, bool maybePolymorphic = true );
 
 	/// inserts into out a generated call expression to function fname with arguments dstParam and srcParam. Intended to be used with generated ?=?, ?{}, and ^?{} calls.
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/SymTab/Validate.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -75,4 +75,5 @@
 #include "SynTree/Visitor.h"           // for Visitor
 #include "Validate/HandleAttributes.h" // for handleAttributes
+#include "Validate/FindSpecialDecls.h" // for FindSpecialDecls
 
 class CompoundStmt;
@@ -287,9 +288,4 @@
 	};
 
-	FunctionDecl * dereferenceOperator = nullptr;
-	struct FindSpecialDeclarations final {
-		void previsit( FunctionDecl * funcDecl );
-	};
-
 	void validate( std::list< Declaration * > &translationUnit, __attribute__((unused)) bool doDebug ) {
 		PassVisitor<EnumAndPointerDecay> epc;
@@ -298,5 +294,4 @@
 		PassVisitor<CompoundLiteral> compoundliteral;
 		PassVisitor<ValidateGenericParameters> genericParams;
-		PassVisitor<FindSpecialDeclarations> finder;
 		PassVisitor<LabelAddressFixer> labelAddrFixer;
 		PassVisitor<HoistTypeDecls> hoistDecls;
@@ -325,5 +320,5 @@
 		FixObjectType::fix( translationUnit );
 		ArrayLength::computeLength( translationUnit );
-		acceptAll( translationUnit, finder ); // xxx - remove this pass soon
+		Validate::findSpecialDecls( translationUnit );
 		mutateAll( translationUnit, labelAddrFixer );
 		Validate::handleAttributes( translationUnit );
@@ -884,9 +879,9 @@
 		if ( eliminator.pass.typedefNames.count( "size_t" ) ) {
 			// grab and remember declaration of size_t
-			SizeType = eliminator.pass.typedefNames["size_t"].first->base->clone();
+			Validate::SizeType = eliminator.pass.typedefNames["size_t"].first->base->clone();
 		} else {
 			// xxx - missing global typedef for size_t - default to long unsigned int, even though that may be wrong
 			// eventually should have a warning for this case.
-			SizeType = new BasicType( Type::Qualifiers(), BasicType::LongUnsignedInt );
+			Validate::SizeType = new BasicType( Type::Qualifiers(), BasicType::LongUnsignedInt );
 		}
 	}
@@ -1276,5 +1271,5 @@
 			// need to resolve array dimensions early so that constructor code can correctly determine
 			// if a type is a VLA (and hence whether its elements need to be constructed)
-			ResolvExpr::findSingleExpression( type->dimension, SymTab::SizeType->clone(), indexer );
+			ResolvExpr::findSingleExpression( type->dimension, Validate::SizeType->clone(), indexer );
 
 			// must re-evaluate whether a type is a VLA, now that more information is available
@@ -1313,15 +1308,4 @@
 		return addrExpr;
 	}
-
-	void FindSpecialDeclarations::previsit( FunctionDecl * funcDecl ) {
-		if ( ! dereferenceOperator ) {
-			if ( funcDecl->get_name() == "*?" && funcDecl->get_linkage() == LinkageSpec::Intrinsic ) {
-				FunctionType * ftype = funcDecl->get_functionType();
-				if ( ftype->get_parameters().size() == 1 && ftype->get_parameters().front()->get_type()->get_qualifiers() == Type::Qualifiers() ) {
-					dereferenceOperator = funcDecl;
-				}
-			}
-		}
-	}
 } // namespace SymTab
 
Index: src/SynTree/DeclReplacer.cc
===================================================================
--- src/SynTree/DeclReplacer.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/SynTree/DeclReplacer.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -38,4 +38,16 @@
 			void previsit( TypeInstType * inst );
 		};
+
+		/// Mutator that replaces uses of declarations with arbitrary expressions, according to the supplied mapping
+		struct ExprDeclReplacer {
+		private:
+			const ExprMap & exprMap;
+			bool debug;
+		public:
+			ExprDeclReplacer( const ExprMap & exprMap, bool debug = false );
+
+			// replace variable with new node from expr map
+			Expression * postmutate( VariableExpr * varExpr );
+		};
 	}
 
@@ -53,4 +65,9 @@
 		DeclMap declMap;
 		replace( node, declMap, typeMap, debug );
+	}
+
+	void replace( BaseSyntaxNode *& node, const ExprMap & exprMap, bool debug ) {
+		PassVisitor<ExprDeclReplacer> replacer( exprMap, debug );
+		node = maybeMutate( node, replacer );
 	}
 
@@ -79,4 +96,19 @@
 			}
 		}
+
+		ExprDeclReplacer::ExprDeclReplacer( const ExprMap & exprMap, bool debug ) : exprMap( exprMap ), debug( debug ) {}
+
+		Expression * ExprDeclReplacer::postmutate( VariableExpr * varExpr ) {
+			if ( exprMap.count( varExpr->var ) ) {
+				Expression * replacement = exprMap.at( varExpr->var )->clone();
+				if ( debug ) {
+					std::cerr << "replacing variable reference: " << (void*)varExpr->var << " " << varExpr->var << " with " << (void*)replacement << " " << replacement << std::endl;
+				}
+				std::swap( varExpr->env, replacement->env );
+				delete varExpr;
+				return replacement;
+			}
+			return varExpr;
+		}
 	}
 } // namespace VarExprReplacer
Index: src/SynTree/DeclReplacer.h
===================================================================
--- src/SynTree/DeclReplacer.h	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/SynTree/DeclReplacer.h	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -26,8 +26,19 @@
 	typedef std::map< DeclarationWithType *, DeclarationWithType * > DeclMap;
 	typedef std::map< TypeDecl *, TypeDecl * > TypeMap;
+	typedef std::map< DeclarationWithType *, Expression * > ExprMap;
 
 	void replace( BaseSyntaxNode * node, const DeclMap & declMap, bool debug = false );
 	void replace( BaseSyntaxNode * node, const TypeMap & typeMap, bool debug = false );
 	void replace( BaseSyntaxNode * node, const DeclMap & declMap, const TypeMap & typeMap, bool debug = false );
+
+	void replace( BaseSyntaxNode *& node, const ExprMap & exprMap, bool debug = false);
+	template<typename T>
+		void replace( T *& node, const ExprMap & exprMap, bool debug = false ) {
+		if ( ! node ) return;
+		BaseSyntaxNode * arg = node;
+		replace( arg, exprMap, debug );
+		node = dynamic_cast<T *>( arg );
+		assertf( node, "DeclReplacer fundamentally changed the type of its argument." );
+	}
 }
 
Index: src/SynTree/Expression.cc
===================================================================
--- src/SynTree/Expression.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/SynTree/Expression.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -538,11 +538,8 @@
 	assert( callExpr );
 	assert( callExpr->result );
-	set_result( callExpr->get_result()->clone() );
+	set_result( callExpr->result->clone() );
 }
 
 ImplicitCopyCtorExpr::ImplicitCopyCtorExpr( const ImplicitCopyCtorExpr & other ) : Expression( other ), callExpr( maybeClone( other.callExpr ) ) {
-	cloneAll( other.tempDecls, tempDecls );
-	cloneAll( other.returnDecls, returnDecls );
-	cloneAll( other.dtors, dtors );
 }
 
@@ -550,7 +547,4 @@
 	set_env( nullptr ); // ImplicitCopyCtorExpr does not take ownership of an environment
 	delete callExpr;
-	deleteAll( tempDecls );
-	deleteAll( returnDecls );
-	deleteAll( dtors );
 }
 
@@ -558,9 +552,4 @@
 	os <<  "Implicit Copy Constructor Expression: " << std::endl << indent+1;
 	callExpr->print( os, indent+1 );
-	os << std::endl << indent << "... with temporaries:" << std::endl;
-	printAll( tempDecls, os, indent+1 );
-	os << std::endl << indent << "... with return temporaries:" << std::endl;
-	printAll( returnDecls, os, indent+1 );
-	Expression::print( os, indent );
 }
 
Index: src/SynTree/Expression.h
===================================================================
--- src/SynTree/Expression.h	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/SynTree/Expression.h	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -593,19 +593,9 @@
 class ImplicitCopyCtorExpr : public Expression {
 public:
-	ApplicationExpr * callExpr;
-	std::list< ObjectDecl * > tempDecls;
-	std::list< ObjectDecl * > returnDecls;
-	std::list< Expression * > dtors;
+	ApplicationExpr * callExpr = nullptr;
 
 	ImplicitCopyCtorExpr( ApplicationExpr * callExpr );
 	ImplicitCopyCtorExpr( const ImplicitCopyCtorExpr & other );
 	virtual ~ImplicitCopyCtorExpr();
-
-	ApplicationExpr * get_callExpr() const { return callExpr; }
-	void set_callExpr( ApplicationExpr * newValue ) { callExpr = newValue; }
-
-	std::list< ObjectDecl * > & get_tempDecls() { return tempDecls; }
-	std::list< ObjectDecl * > & get_returnDecls() { return returnDecls; }
-	std::list< Expression * > & get_dtors() { return dtors; }
 
 	virtual ImplicitCopyCtorExpr * clone() const { return new ImplicitCopyCtorExpr( * this ); }
Index: src/Validate/FindSpecialDecls.cc
===================================================================
--- src/Validate/FindSpecialDecls.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
+++ src/Validate/FindSpecialDecls.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -0,0 +1,100 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// FindSpecialDecls.cc --
+//
+// Author           : Rob Schluntz
+// Created On       : Thu Aug 30 09:49:43 2018
+// Last Modified By : Rob Schluntz
+// Last Modified On : Thu Aug 30 09:55:25 2018
+// Update Count     : 2
+//
+
+#include "FindSpecialDecls.h"
+
+#include "Common/PassVisitor.h"
+#include "SynTree/Declaration.h"
+#include "SynTree/Type.h"
+
+// NOTE: currently, it is assumed that every special declaration occurs at the top-level,
+// so function bodies, aggregate bodies, object initializers, etc. are not visited.
+// If this assumption changes, e.g., with the introduction of namespaces, remove the visit_children assignments.
+
+namespace Validate {
+	Type * SizeType = nullptr;
+  FunctionDecl * dereferenceOperator = nullptr;
+  StructDecl * dtorStruct = nullptr;
+  FunctionDecl * dtorStructDestroy = nullptr;
+
+	namespace {
+		struct FindSpecialDecls final : public WithShortCircuiting {
+			void previsit( ObjectDecl * objDecl );
+			void previsit( FunctionDecl * funcDecl );
+			void previsit( StructDecl * structDecl );
+			void previsit( UnionDecl * unionDecl );
+			void previsit( EnumDecl * enumDecl );
+			void previsit( TraitDecl * traitDecl );
+		};
+	} // namespace
+
+	void findSpecialDecls( std::list< Declaration * > &translationUnit ) {
+		PassVisitor<FindSpecialDecls> finder;
+		acceptAll( translationUnit, finder );
+		// TODO: conditionally generate 'fake' declarations for missing features, so that
+		// translation can proceed in the event that builtins, prelude, etc. are missing.
+	}
+
+	namespace {
+		void FindSpecialDecls::previsit( ObjectDecl * ) {
+			visit_children = false;
+		}
+
+		void FindSpecialDecls::previsit( FunctionDecl * funcDecl ) {
+			visit_children = false;
+			if ( ! dereferenceOperator && funcDecl->name == "*?" && funcDecl->linkage == LinkageSpec::Intrinsic ) {
+				// find and remember the intrinsic dereference operator for object pointers
+				FunctionType * ftype = funcDecl->type;
+				if ( ftype->parameters.size() == 1 ) {
+					PointerType * ptrType = strict_dynamic_cast<PointerType *>( ftype->parameters.front()->get_type() );
+					if ( ptrType->base->get_qualifiers() == Type::Qualifiers() ) {
+						TypeInstType * inst = dynamic_cast<TypeInstType *>( ptrType->base );
+						if ( inst && ! inst->get_isFtype() ) {
+							dereferenceOperator = funcDecl;
+						}
+					}
+				}
+			} else if ( ! dtorStructDestroy && funcDecl->name == "__destroy_Destructor" ) {
+				dtorStructDestroy = funcDecl;
+			}
+		}
+
+		void FindSpecialDecls::previsit( StructDecl * structDecl ) {
+			visit_children = false;
+			if ( ! dtorStruct && structDecl->name == "__Destructor" ) {
+				dtorStruct = structDecl;
+			}
+		}
+
+		void FindSpecialDecls::previsit( UnionDecl * ) {
+			visit_children = false;
+		}
+
+		void FindSpecialDecls::previsit( EnumDecl * ) {
+			visit_children = false;
+		}
+
+		void FindSpecialDecls::previsit( TraitDecl * ) {
+			visit_children = false;
+		}
+
+	} // namespace
+} // namespace Validate
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Validate/FindSpecialDecls.h
===================================================================
--- src/Validate/FindSpecialDecls.h	(revision a200795e703916156fb98c276dcb4da596a519bd)
+++ src/Validate/FindSpecialDecls.h	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -0,0 +1,46 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// FindSpecialDeclarations.h --
+//
+// Author           : Rob Schluntz
+// Created On       : Thu Aug 30 09:49:02 2018
+// Last Modified By : Rob Schluntz
+// Last Modified On : Thu Aug 30 09:51:12 2018
+// Update Count     : 2
+//
+
+#pragma once
+
+#include <list>  // for list
+
+class Declaration;
+class FunctionDecl;
+class StructDecl;
+class Type;
+
+namespace Validate {
+	/// size_t type - set when size_t typedef is seen. Useful in a few places,
+	/// such as in determining array dimension type
+	extern Type * SizeType;
+
+	/// intrinsic dereference operator for unqualified types - set when *? function is seen in FindSpecialDeclarations.
+	/// Useful for creating dereference ApplicationExprs without a full resolver pass.
+	extern FunctionDecl * dereferenceOperator;
+
+	/// special built-in functions and data structures necessary for destructor generation
+	extern StructDecl * dtorStruct;
+	extern FunctionDecl * dtorStructDestroy;
+
+	/// find and remember some of the special declarations that are useful for generating code, so that they do not have to be discovered multiple times.
+	void findSpecialDecls( std::list< Declaration * > & translationUnit );
+} // namespace Validate
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Validate/module.mk
===================================================================
--- src/Validate/module.mk	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/Validate/module.mk	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -15,3 +15,4 @@
 ###############################################################################
 
-SRC += Validate/HandleAttributes.cc
+SRC += Validate/HandleAttributes.cc \
+	Validate/FindSpecialDecls.cc
Index: src/Virtual/ExpandCasts.cc
===================================================================
--- src/Virtual/ExpandCasts.cc	(revision 4fd45bc68a860b66735e58a7cc9d93a896457835)
+++ src/Virtual/ExpandCasts.cc	(revision a200795e703916156fb98c276dcb4da596a519bd)
@@ -147,16 +147,16 @@
 				//		)
 				//	),
-			new UntypedExpr( new NameExpr( "__cfa__virtual_cast" ), {
+			new ApplicationExpr( VariableExpr::functionPointer( vcast_decl ), {
 					new CastExpr(
 						new AddressExpr( new VariableExpr( table ) ),
 						pointer_to_pvt(1)
-						),
+					),
 					new CastExpr(
 						castExpr->get_arg(),
 						pointer_to_pvt(2)
-						)
-				} ),
+					)
+			} ),
 			castExpr->get_result()->clone()
-			);
+		);
 
 		castExpr->set_arg( nullptr );
