Index: libcfa/prelude/builtins.c
===================================================================
--- libcfa/prelude/builtins.c	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ libcfa/prelude/builtins.c	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -111,4 +111,27 @@
 static inline unsigned int ?\=?( unsigned int & x, unsigned long int y ) { x = x \ y; return x; }
 
+// type that wraps a pointer and a destructor-like function - used in generating implicit destructor calls for struct members in user-defined functions
+forall(dtype T)
+struct __Destructor {
+  T * object;
+  void (*dtor)(T *);
+};
+
+// defined destructor in the case that non-generated code wants to use __Destructor
+forall(dtype T)
+static inline void ^?{}(__Destructor(T) & x) {
+	if (x.object && x.dtor) {
+	  x.dtor(x.object);
+	}
+}
+
+// easy interface into __Destructor's destructor for easy codegen purposes
+extern "C" {
+  forall(dtype T)
+  static inline void __destroy_Destructor(__Destructor(T) * dtor) {
+    ^(*dtor){};
+  }
+}
+
 // Local Variables: //
 // mode: c //
Index: src/ControlStruct/ExceptTranslate.cc
===================================================================
--- src/ControlStruct/ExceptTranslate.cc	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/ControlStruct/ExceptTranslate.cc	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -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/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/InitTweak/FixInit.cc	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -54,4 +54,5 @@
 #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
 
@@ -162,10 +163,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:
@@ -207,4 +204,6 @@
 			static void generate( std::list< Declaration * > & translationUnit );
 
+			void premutate( StructDecl * structDecl );
+
 			void premutate( FunctionDecl * funcDecl );
 			DeclarationWithType * postmutate( FunctionDecl * funcDecl );
@@ -228,4 +227,8 @@
 			bool isCtor = false; // true if current function is a constructor
 			StructDecl * structDecl = nullptr;
+
+			// special built-in functions necessary for this to work
+			StructDecl * dtorStruct = nullptr;
+			FunctionDecl * dtorStructDestroy = nullptr;
 		};
 
@@ -732,4 +735,55 @@
 		}
 
+		DeclarationWithType * getDtorFunc( ObjectDecl * objDecl, Statement * input, std::list< Statement * > & stmtsToAdd ) {
+			// unwrap implicit statement wrapper
+			Statement * dtor = input;
+			if ( ImplicitCtorDtorStmt * implicit = dynamic_cast< ImplicitCtorDtorStmt * >( input ) ) {
+				// dtor = implicit->callStmt;
+				// implicit->callStmt = nullptr;
+			}
+			assert( dtor );
+			std::list< Expression * > matches;
+			collectCtorDtorCalls( dtor, matches );
+
+			if ( dynamic_cast< ExprStmt * >( dtor ) ) {
+				// only one destructor call in the expression
+				if ( matches.size() == 1 ) {
+					DeclarationWithType * func = getFunction( matches.front() );
+					assertf( func, "getFunction failed to find function in %s", toString( matches.front() ).c_str() );
+
+					// cleanup argument must be a function, not an object (including function pointer)
+					if ( FunctionDecl * dtorFunc = dynamic_cast< FunctionDecl * > ( func ) ) {
+						if ( dtorFunc->type->forall.empty() ) {
+							// simple case where the destructor is a monomorphic function call - can simply
+							// use that function as the cleanup function.
+							delete dtor;
+							return func;
+						}
+					}
+				}
+			}
+
+			// otherwise the cleanup is more complicated - need to build a single argument cleanup function that
+			// wraps the more complicated code.
+			static UniqueName dtorNamer( "__cleanup_dtor" );
+			FunctionDecl * dtorFunc = FunctionDecl::newFunction( dtorNamer.newName(), SymTab::genDefaultType( objDecl->type->stripReferences(), false ), new CompoundStmt() );
+			stmtsToAdd.push_back( new DeclStmt( dtorFunc ) );
+
+			// the original code contains uses of objDecl - replace them with the newly generated 'this' parameter.
+			ObjectDecl * thisParam = getParamThis( dtorFunc->type );
+			Expression * replacement = new VariableExpr( thisParam );
+
+			Type * base = replacement->result->stripReferences();
+			if ( dynamic_cast< ArrayType * >( base ) || dynamic_cast< TupleType * > ( base ) ) {
+				// need to cast away reference for array types, since the destructor is generated without the reference type,
+				// and for tuple types since tuple indexing does not work directly on a reference
+				replacement = new CastExpr( replacement, base->clone() );
+			}
+			DeclReplacer::replace( dtor, { std::make_pair( objDecl, replacement ) } );
+			dtorFunc->statements->push_back( strict_dynamic_cast<Statement *>( dtor ) );
+
+			return dtorFunc;
+		}
+
 		DeclarationWithType * FixInit::postmutate( ObjectDecl *objDecl ) {
 			// since this removes the init field from objDecl, it must occur after children are mutated (i.e. postmutate)
@@ -844,4 +898,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 +955,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 +967,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 +994,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
 		}
@@ -1025,4 +1019,10 @@
 		}
 
+		void GenStructMemberCalls::premutate( StructDecl * structDecl ) {
+			if ( ! dtorStruct && structDecl->name == "__Destructor" ) {
+				dtorStruct = structDecl;
+			}
+		}
+
 		void GenStructMemberCalls::premutate( FunctionDecl * funcDecl ) {
 			GuardValue( function );
@@ -1037,4 +1037,9 @@
 			unhandled.clear();
 			usedUninit.clear();
+
+			if ( ! dtorStructDestroy && funcDecl->name == "__destroy_Destructor" ) {
+				dtorStructDestroy = funcDecl;
+				return;
+			}
 
 			function = funcDecl;
@@ -1048,4 +1053,5 @@
 				if ( structType ) {
 					structDecl = structType->get_baseStruct();
+					if ( structDecl == dtorStruct ) return;
 					for ( Declaration * member : structDecl->get_members() ) {
 						if ( ObjectDecl * field = dynamic_cast< ObjectDecl * >( member ) ) {
@@ -1116,8 +1122,29 @@
 							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 );
+
+								// __Destructor _dtor0 = { (void *)&b.a1, (void (*)(void *)_destroy_A };
+								std::list< Statement * > stmtsToAdd;
+
+								static UniqueName memberDtorNamer = { "__memberDtor" };
+								assertf( dtorStruct, "builtin __Destructor not found." );
+								assertf( 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(), 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( dtorStructDestroy ) } ) );
+
+								function->statements->kids.splice( function->statements->kids.begin(), stmtsToAdd );
 							}
 						} catch ( SemanticErrorException & error ) {
Index: src/InitTweak/InitTweak.cc
===================================================================
--- src/InitTweak/InitTweak.cc	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/InitTweak/InitTweak.cc	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -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 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/Makefile.am	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -94,4 +94,5 @@
   CodeGen/CodeGenerator.cc \
   CodeGen/FixMain.cc \
+  CodeGen/Generate.cc \
   CodeGen/GenType.cc \
   CodeGen/OperatorTable.cc \
Index: src/Makefile.in
===================================================================
--- src/Makefile.in	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/Makefile.in	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -182,8 +182,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) \
@@ -621,4 +621,5 @@
   CodeGen/CodeGenerator.cc \
   CodeGen/FixMain.cc \
+  CodeGen/Generate.cc \
   CodeGen/GenType.cc \
   CodeGen/OperatorTable.cc \
@@ -797,4 +798,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) \
@@ -987,6 +990,4 @@
 clean-cfa_cpplibPROGRAMS:
 	-test -z "$(cfa_cpplib_PROGRAMS)" || rm -f $(cfa_cpplib_PROGRAMS)
-CodeGen/Generate.$(OBJEXT): CodeGen/$(am__dirstamp) \
-	CodeGen/$(DEPDIR)/$(am__dirstamp)
 CodeGen/FixNames.$(OBJEXT): CodeGen/$(am__dirstamp) \
 	CodeGen/$(DEPDIR)/$(am__dirstamp)
Index: src/SymTab/Autogen.cc
===================================================================
--- src/SymTab/Autogen.cc	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/SymTab/Autogen.cc	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -45,5 +45,5 @@
 	/// 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 +231,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 +244,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 +252,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 +311,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 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/SymTab/Autogen.h	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -45,12 +45,15 @@
 	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 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/SymTab/Validate.cc	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -1333,8 +1333,15 @@
 	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;
+			// find and remember the intrinsic dereference operator for object pointers
+			if ( funcDecl->name == "*?" && funcDecl->linkage == LinkageSpec::Intrinsic ) {
+				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;
+						}
+					}
 				}
 			}
Index: src/SynTree/DeclReplacer.cc
===================================================================
--- src/SynTree/DeclReplacer.cc	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/SynTree/DeclReplacer.cc	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -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 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ src/SynTree/DeclReplacer.h	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -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: tests/raii/.expect/memberCtors-ERR1.txt
===================================================================
--- tests/raii/.expect/memberCtors-ERR1.txt	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ tests/raii/.expect/memberCtors-ERR1.txt	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -1,1 +1,1 @@
-raii/memberCtors.c:71:1 error: in void ?{}(B &b), field a2 used before being constructed
+raii/memberCtors.c:92:1 error: in void ?{}(B &b), field a2 used before being constructed
Index: tests/raii/.expect/memberCtors.txt
===================================================================
--- tests/raii/.expect/memberCtors.txt	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ tests/raii/.expect/memberCtors.txt	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -1,213 +1,219 @@
 Before declaration of b1
-constructing int
-constructing int
-constructing int
-constructing int
-constructing int
-constructing int
-begin construct B
+constructing int id: 0
+constructing int id: 1
+constructing int id: 2
+default construct A 0
+constructing int id: 3
+constructing int id: 4
+constructing int id: 5
+default construct A 1
+begin construct B id: 0
 assign b.a2
-constructing int
-constructing int
-begin construct A
-construct a.x
-constructing int: 1001
-assign a.y
-assigning int: 0 0
-end construct A
-copy constructing int: 0
-copy constructing int: 0
-begin copy construct A
-copy construct this.x
-copy constructing int: 1001
-assign this.y
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-end copy construct A
-begin ?=? A
-copy constructing int: 1001
-destructing int: 1001
-destructing int: 1001
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
+constructing int id: 6
+constructing int id: 7
+begin construct A id: 2
+construct a.x
+constructing int: 1001 id: 8
+assign a.y
+assigning int: 0 0 id: 6
+end construct A
+copy constructing int: 0 id: 9
+copy constructing int: 0 id: 10
+begin copy construct A id: 3
+copy construct this.x
+copy constructing int: 1001 id: 11
+assign this.y
+copy constructing int: 0 id: 12
+destructing int: 0 id: 12
+destructing int: 0 id: 12
+end copy construct A
+begin ?=? A id: 0
+copy constructing int: 1001 id: 13
+destructing int: 1001 id: 13
+destructing int: 1001 id: 13
+copy constructing int: 0 id: 14
+destructing int: 0 id: 14
+destructing int: 0 id: 14
+copy constructing int: 0 id: 15
+destructing int: 0 id: 15
+destructing int: 0 id: 15
 end ?=? A
-copy constructing int: 0
-copy constructing int: 0
-begin copy construct A
-copy construct this.x
-copy constructing int: 1001
-assign this.y
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-end copy construct A
-destructing int: 0
-destructing int: 0
-destructing int: 1001
-destructing int: 0
-destructing int: 0
-destructing int: 1001
+copy constructing int: 0 id: 16
+copy constructing int: 0 id: 17
+begin copy construct A id: 4
+copy construct this.x
+copy constructing int: 1001 id: 18
+assign this.y
+copy constructing int: 0 id: 19
+destructing int: 0 id: 19
+destructing int: 0 id: 19
+end copy construct A
+destructing int: 0 id: 17
+destructing int: 0 id: 19
+destructing int: 1001 id: 18
+destructing int: 0 id: 10
+destructing int: 0 id: 12
+destructing int: 1001 id: 11
 construct b.a1
-constructing int
-constructing int
-begin construct A
-construct a.x
-constructing int: 1000
-assign a.y
-assigning int: 0 0
+constructing int id: 20
+constructing int id: 21
+begin construct A id: 5
+construct a.x
+constructing int: 1000 id: 22
+assign a.y
+assigning int: 0 0 id: 20
 end construct A
 end construct B
-destructing int: 0
-destructing int: 0
-destructing int: 1001
+destructing int: 0 id: 7
+destructing int: 0 id: 6
+destructing int: 1001 id: 8
 Before declaration of b2
-copy constructing int: 0
-copy constructing int: 0
-begin copy construct A
-copy construct this.x
-copy constructing int: 1000
-assign this.y
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-end copy construct A
-copy constructing int: 0
-copy constructing int: 0
-begin copy construct A
-copy construct this.x
-copy constructing int: 1001
-assign this.y
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-end copy construct A
-copy constructing int: 0
-copy constructing int: 0
-begin copy construct A
-copy construct this.x
-copy constructing int: 0
-assign this.y
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
+copy constructing int: 0 id: 23
+copy constructing int: 0 id: 24
+begin copy construct A id: 6
+copy construct this.x
+copy constructing int: 1000 id: 25
+assign this.y
+copy constructing int: 0 id: 26
+destructing int: 0 id: 26
+destructing int: 0 id: 26
+end copy construct A
+copy constructing int: 0 id: 27
+copy constructing int: 0 id: 28
+begin copy construct A id: 7
+copy construct this.x
+copy constructing int: 1001 id: 29
+assign this.y
+copy constructing int: 0 id: 30
+destructing int: 0 id: 30
+destructing int: 0 id: 30
+end copy construct A
+copy constructing int: 0 id: 31
+copy constructing int: 0 id: 32
+begin copy construct A id: 8
+copy construct this.x
+copy constructing int: 0 id: 33
+assign this.y
+copy constructing int: 0 id: 34
+destructing int: 0 id: 34
+destructing int: 0 id: 34
 end copy construct A
 End of main
-constructing int
-constructing int
-begin construct A
-construct a.x
-constructing int: 999
-assign a.y
-assigning int: 0 0
-end construct A
-copy constructing int: 0
-copy constructing int: 0
-begin copy construct A
-copy construct this.x
-copy constructing int: 999
-assign this.y
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-end copy construct A
-begin ?=? A
-copy constructing int: 999
-destructing int: 999
-destructing int: 999
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
+begin destruct B id: 1
+constructing int id: 35
+constructing int id: 36
+begin construct A id: 9
+construct a.x
+constructing int: 999 id: 37
+assign a.y
+assigning int: 0 0 id: 35
+end construct A
+copy constructing int: 0 id: 38
+copy constructing int: 0 id: 39
+begin copy construct A id: 10
+copy construct this.x
+copy constructing int: 999 id: 40
+assign this.y
+copy constructing int: 0 id: 41
+destructing int: 0 id: 41
+destructing int: 0 id: 41
+end copy construct A
+begin ?=? A id: 7
+copy constructing int: 999 id: 42
+destructing int: 999 id: 42
+destructing int: 999 id: 42
+copy constructing int: 0 id: 43
+destructing int: 0 id: 43
+destructing int: 0 id: 43
+copy constructing int: 0 id: 44
+destructing int: 0 id: 44
+destructing int: 0 id: 44
 end ?=? A
-copy constructing int: 0
-copy constructing int: 0
-begin copy construct A
-copy construct this.x
-copy constructing int: 999
-assign this.y
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-end copy construct A
-destructing int: 0
-destructing int: 0
-destructing int: 999
-destructing int: 0
-destructing int: 0
-destructing int: 999
-destructing int: 0
-destructing int: 0
-destructing int: 1000
-destructing int: 0
-destructing int: 0
-destructing int: 999
-destructing int: 0
-destructing int: 0
-destructing int: 0
-destructing int: 0
-destructing int: 0
-destructing int: 999
-constructing int
-constructing int
-begin construct A
-construct a.x
-constructing int: 999
-assign a.y
-assigning int: 0 0
-end construct A
-copy constructing int: 0
-copy constructing int: 0
-begin copy construct A
-copy construct this.x
-copy constructing int: 999
-assign this.y
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-end copy construct A
-begin ?=? A
-copy constructing int: 999
-destructing int: 999
-destructing int: 999
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
+copy constructing int: 0 id: 45
+copy constructing int: 0 id: 46
+begin copy construct A id: 11
+copy construct this.x
+copy constructing int: 999 id: 47
+assign this.y
+copy constructing int: 0 id: 48
+destructing int: 0 id: 48
+destructing int: 0 id: 48
+end copy construct A
+destructing int: 0 id: 46
+destructing int: 0 id: 48
+destructing int: 999 id: 47
+destructing int: 0 id: 39
+destructing int: 0 id: 41
+destructing int: 999 id: 40
+destructing int: 0 id: 24
+destructing int: 0 id: 26
+destructing int: 1000 id: 25
+end destruct B
+destructing int: 0 id: 36
+destructing int: 0 id: 35
+destructing int: 999 id: 37
+destructing int: 0 id: 32
+destructing int: 0 id: 34
+destructing int: 0 id: 33
+destructing int: 0 id: 44
+destructing int: 0 id: 43
+destructing int: 999 id: 42
+begin destruct B id: 2
+constructing int id: 49
+constructing int id: 50
+begin construct A id: 12
+construct a.x
+constructing int: 999 id: 51
+assign a.y
+assigning int: 0 0 id: 49
+end construct A
+copy constructing int: 0 id: 52
+copy constructing int: 0 id: 53
+begin copy construct A id: 13
+copy construct this.x
+copy constructing int: 999 id: 54
+assign this.y
+copy constructing int: 0 id: 55
+destructing int: 0 id: 55
+destructing int: 0 id: 55
+end copy construct A
+begin ?=? A id: 0
+copy constructing int: 999 id: 56
+destructing int: 999 id: 56
+destructing int: 999 id: 56
+copy constructing int: 0 id: 57
+destructing int: 0 id: 57
+destructing int: 0 id: 57
+copy constructing int: 0 id: 58
+destructing int: 0 id: 58
+destructing int: 0 id: 58
 end ?=? A
-copy constructing int: 0
-copy constructing int: 0
-begin copy construct A
-copy construct this.x
-copy constructing int: 999
-assign this.y
-copy constructing int: 0
-destructing int: 0
-destructing int: 0
-end copy construct A
-destructing int: 0
-destructing int: 0
-destructing int: 999
-destructing int: 0
-destructing int: 0
-destructing int: 999
-destructing int: 0
-destructing int: 0
-destructing int: 1000
-destructing int: 0
-destructing int: 0
-destructing int: 999
-destructing int: 0
-destructing int: 0
-destructing int: 0
-destructing int: 0
-destructing int: 0
-destructing int: 999
+copy constructing int: 0 id: 59
+copy constructing int: 0 id: 60
+begin copy construct A id: 14
+copy construct this.x
+copy constructing int: 999 id: 61
+assign this.y
+copy constructing int: 0 id: 62
+destructing int: 0 id: 62
+destructing int: 0 id: 62
+end copy construct A
+destructing int: 0 id: 60
+destructing int: 0 id: 62
+destructing int: 999 id: 61
+destructing int: 0 id: 53
+destructing int: 0 id: 55
+destructing int: 999 id: 54
+destructing int: 0 id: 21
+destructing int: 0 id: 20
+destructing int: 1000 id: 22
+end destruct B
+destructing int: 0 id: 50
+destructing int: 0 id: 49
+destructing int: 999 id: 51
+destructing int: 0 id: 5
+destructing int: 0 id: 4
+destructing int: 0 id: 3
+destructing int: 0 id: 58
+destructing int: 0 id: 57
+destructing int: 999 id: 56
Index: tests/raii/memberCtors.c
===================================================================
--- tests/raii/memberCtors.c	(revision 42655e83c96028fe7ca37b4315fc53f90d546634)
+++ tests/raii/memberCtors.c	(revision a715b5c08c27a59aa8a90d183fb68e349cd38822)
@@ -1,40 +1,57 @@
 struct WrappedInt {
   int x;
+  int id;
 };
+int intID = 0;
 
 void ?{}(WrappedInt & this) {
-  printf("constructing int\n");
+  this.id = intID++;
+  printf("constructing int id: %d\n", this.id);
   this.x = 0;
 }
 
 void ?{}(WrappedInt & this, WrappedInt other) {
-  printf("copy constructing int: %d\n", other.x);
+  this.id = intID++;
+  printf("copy constructing int: %d id: %d\n", other.x, this.id);
   this.x = other.x;
 }
 
 void ?{}(WrappedInt & this, int x) {
-  printf("constructing int: %d\n", x);
+  this.id = intID++;
+  printf("constructing int: %d id: %d\n", x, this.id);
   this.x = x;
 }
 
 void ^?{}(WrappedInt & this) {
-  printf("destructing int: %d\n", this.x);
+  printf("destructing int: %d id: %d\n", this.x, this.id);
 }
 
-void ?=?(WrappedInt & this, int x) {
-  printf("assigning int: %d %d\n", this.x, x);
+/* WrappedInt */ void ?=?(WrappedInt & this, int x) {
+  printf("assigning int: %d %d id: %d\n", this.x, x, this.id);
   this.x = x;
+  // return this;
 }
+
+// WrappedInt ?=?(WrappedInt & this, WrappedInt other) {
+//   printf("assigning int: %d %d\n", this.x, other.x);
+//   this.x = other.x;
+//   return this;
+// }
 
 struct A {
   WrappedInt x, y, z;
+  int id;
 };
+int AID = 0;
 
 void ?{}(A & a) {
   // currently must define default ctor, since there's no "= default" syntax
+  a.id = AID++;
+  printf("default construct A %d\n", a.id);
 }
 
 void ?{}(A & a, int x) {
-  printf("begin construct A\n");
+  a.id = AID++;
+  printf("begin construct A id: %d\n", a.id);
   printf("construct a.x\n");
   (a.x){ x+999 };
@@ -45,5 +62,6 @@
 
 void ?{}(A & this, A other) {
-  printf("begin copy construct A\n");
+  this.id = AID++;
+  printf("begin copy construct A id: %d\n", this.id);
   printf("copy construct this.x\n");
   (this.x){ other.x };
@@ -54,5 +72,5 @@
 
 A ?=?(A & this, A other) {
-  printf("begin ?=? A\n");
+  printf("begin ?=? A id: %d\n", this.id);
   this.x = other.x;
   this.y = other.y;
@@ -64,8 +82,11 @@
 struct B {
   A a1, a2, a3;
+  int id;
 };
+int BID = 0;
 
 void ?{}(B & b) {
-  printf("begin construct B\n");
+  b.id = BID++;
+  printf("begin construct B id: %d\n", b.id);
   printf("assign b.a2\n");
   b.a2 = (A) { 2 };
@@ -79,11 +100,14 @@
 
 void ^?{}(B & b) {
+  b.id = BID++;
+  printf("begin destruct B id: %d\n", b.id);
   b.a2 = (A) { 0 };
   ^(b.a1){};
+  printf("end destruct B\n");
 } // a2, a3 never destructed - will be automatically destructed
 
 int main() {
   printf("Before declaration of b1\n");
-  B b1;
+  B b1;  // b1 = { { 1000, 0, 0 }, { 1001, 0, 0 }, { 0, 0, 0 } }
   printf("Before declaration of b2\n");
   B b2 = b1;
