Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision 504eb72fc3a32710df1a99fba0601f0cbc45cc12)
+++ src/AST/Expr.cpp	(revision 2890212a2b66d8b4d2e668cc354f6205f1c666bd)
@@ -20,4 +20,5 @@
 #include <vector>
 
+#include "Copy.hpp"                // for shallowCopy
 #include "Eval.hpp"                // for call
 #include "GenericSubstitution.hpp"
@@ -174,6 +175,7 @@
 	assert( var );
 	assert( var->get_type() );
-	result = var->get_type();
-	add_qualifiers( result, CV::Lvalue );
+	auto r = shallowCopy( var->get_type() );
+	r->qualifiers |= CV::Lvalue;
+	result = r;
 }
 
Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision 504eb72fc3a32710df1a99fba0601f0cbc45cc12)
+++ src/AST/Node.hpp	(revision 2890212a2b66d8b4d2e668cc354f6205f1c666bd)
@@ -96,5 +96,5 @@
 	assertf(
 		node->weak_count == 0,
-		"Error: mutating node with weak references to it will invalided some references"
+		"Error: mutating node with weak references to it will invalidate some references"
 	);
 	return node->clone();
@@ -106,5 +106,5 @@
 	// skip mutate if equivalent
 	if ( node->*field == val ) return node;
-	
+
 	// mutate and return
 	node_t * ret = mutate( node );
Index: src/AST/TypeEnvironment.hpp
===================================================================
--- src/AST/TypeEnvironment.hpp	(revision 504eb72fc3a32710df1a99fba0601f0cbc45cc12)
+++ src/AST/TypeEnvironment.hpp	(revision 2890212a2b66d8b4d2e668cc354f6205f1c666bd)
@@ -38,20 +38,20 @@
 /// Adding this comparison operator significantly improves assertion satisfaction run time for
 /// some cases. The current satisfaction algorithm's speed partially depends on the order of
-/// assertions. Assertions which have fewer possible matches should appear before assertions 
-/// which have more possible matches. This seems to imply that this could be further improved 
-/// by providing an indexer as an additional argument and ordering based on the number of 
+/// assertions. Assertions which have fewer possible matches should appear before assertions
+/// which have more possible matches. This seems to imply that this could be further improved
+/// by providing an indexer as an additional argument and ordering based on the number of
 /// matches of the same kind (object, function) for the names of the declarations.
 ///
-/// I've seen a TU go from 54 minutes to 1 minute 34 seconds with the addition of this 
+/// I've seen a TU go from 54 minutes to 1 minute 34 seconds with the addition of this
 /// comparator.
 ///
-/// Note: since this compares pointers for position, minor changes in the source file that 
-/// affect memory layout can alter compilation time in unpredictable ways. For example, the 
-/// placement of a line directive can reorder type pointers with respect to each other so that 
-/// assertions are seen in different orders, causing a potentially different number of 
-/// unification calls when resolving assertions. I've seen a TU go from 36 seconds to 27 
-/// seconds by reordering line directives alone, so it would be nice to fix this comparison so 
-/// that assertions compare more consistently. I've tried to modify this to compare on mangle 
-/// name instead of type as the second comparator, but this causes some assertions to never be 
+/// Note: since this compares pointers for position, minor changes in the source file that
+/// affect memory layout can alter compilation time in unpredictable ways. For example, the
+/// placement of a line directive can reorder type pointers with respect to each other so that
+/// assertions are seen in different orders, causing a potentially different number of
+/// unification calls when resolving assertions. I've seen a TU go from 36 seconds to 27
+/// seconds by reordering line directives alone, so it would be nice to fix this comparison so
+/// that assertions compare more consistently. I've tried to modify this to compare on mangle
+/// name instead of type as the second comparator, but this causes some assertions to never be
 /// recorded. More investigation is needed.
 struct AssertCompare {
@@ -87,5 +87,5 @@
 void print( std::ostream &, const OpenVarSet &, Indenter indent = {} );
 
-/// Represents an equivalence class of bound type variables, optionally with the concrete type 
+/// Represents an equivalence class of bound type variables, optionally with the concrete type
 /// they bind to.
 struct EqvClass {
@@ -96,7 +96,7 @@
 
 	EqvClass() : vars(), bound(), allowWidening( true ), data() {}
-	
+
 	/// Copy-with-bound constructor
-	EqvClass( const EqvClass & o, const Type * b ) 
+	EqvClass( const EqvClass & o, const Type * b )
 	: vars( o.vars ), bound( b ), allowWidening( o.allowWidening ), data( o.data ) {}
 
@@ -143,16 +143,16 @@
 	void writeToSubstitution( TypeSubstitution & sub ) const;
 
-	template< typename node_t, enum Node::ref_type ref_t >
-	int apply( ptr_base< node_t, ref_t > & type ) const {
+	template< typename node_t >
+	auto apply( node_t && type ) const {
 		TypeSubstitution sub;
 		writeToSubstitution( sub );
-		return sub.apply( type );
-	}
-
-	template< typename node_t, enum Node::ref_type ref_t >
-	int applyFree( ptr_base< node_t, ref_t > & type ) const {
+		return sub.apply( std::forward<node_t>(type) );
+	}
+
+	template< typename node_t >
+	auto applyFree( node_t && type ) const {
 		TypeSubstitution sub;
 		writeToSubstitution( sub );
-		return sub.applyFree( type );
+		return sub.applyFree( std::forward<node_t>(type) );
 	}
 
@@ -173,16 +173,16 @@
 	void addActual( const TypeEnvironment & actualEnv, OpenVarSet & openVars );
 
-	/// Binds the type class represented by `typeInst` to the type `bindTo`; will add the class if 
+	/// Binds the type class represented by `typeInst` to the type `bindTo`; will add the class if
 	/// needed. Returns false on failure.
-	bool bindVar( 
-		const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data, 
-		AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 
+	bool bindVar(
+		const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data,
+		AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars,
 		ResolvExpr::WidenMode widen, const SymbolTable & symtab );
-	
-	/// Binds the type classes represented by `var1` and `var2` together; will add one or both 
+
+	/// Binds the type classes represented by `var1` and `var2` together; will add one or both
 	/// classes if needed. Returns false on failure.
-	bool bindVarToVar( 
-		const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data, 
-		AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 
+	bool bindVarToVar(
+		const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data,
+		AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars,
 		ResolvExpr::WidenMode widen, const SymbolTable & symtab );
 
@@ -199,10 +199,10 @@
 
 	/// Unifies the type bound of `to` with the type bound of `from`, returning false if fails
-	bool mergeBound( 
+	bool mergeBound(
 		EqvClass & to, const EqvClass & from, OpenVarSet & openVars, const SymbolTable & symtab );
 
 	/// Merges two type classes from local environment, returning false if fails
-	bool mergeClasses( 
-		ClassList::iterator to, ClassList::iterator from, OpenVarSet & openVars, 
+	bool mergeClasses(
+		ClassList::iterator to, ClassList::iterator from, OpenVarSet & openVars,
 		const SymbolTable & symtab );
 
Index: src/AST/TypeSubstitution.cpp
===================================================================
--- src/AST/TypeSubstitution.cpp	(revision 504eb72fc3a32710df1a99fba0601f0cbc45cc12)
+++ src/AST/TypeSubstitution.cpp	(revision 2890212a2b66d8b4d2e668cc354f6205f1c666bd)
@@ -146,5 +146,5 @@
 		ptr<Type> newType = i->second; // force clone if needed
 		add_qualifiers( newType, inst->qualifiers );
-		// Note: need to recursively apply substitution to the new type because normalize does not 
+		// Note: need to recursively apply substitution to the new type because normalize does not
 		// substitute bound vars, but bound vars must be substituted when not in freeOnly mode.
 		newType = newType->accept( *visitor );
@@ -159,5 +159,4 @@
 	} else {
 		subCount++;
-		delete nameExpr;
 		return i->second;
 	} // if
Index: src/AST/TypeSubstitution.hpp
===================================================================
--- src/AST/TypeSubstitution.hpp	(revision 504eb72fc3a32710df1a99fba0601f0cbc45cc12)
+++ src/AST/TypeSubstitution.hpp	(revision 2890212a2b66d8b4d2e668cc354f6205f1c666bd)
@@ -44,13 +44,19 @@
 	TypeSubstitution &operator=( const TypeSubstitution &other );
 
-	template< typename SynTreeClass > int apply( const SynTreeClass *& input ) const;
-	template< typename SynTreeClass > int applyFree( const SynTreeClass *& input ) const;
+	template< typename SynTreeClass >
+	struct ApplyResult {
+		const SynTreeClass * node;
+		int count;
+	};
+
+	template< typename SynTreeClass > ApplyResult<SynTreeClass> apply( const SynTreeClass * input ) const;
+	template< typename SynTreeClass > ApplyResult<SynTreeClass> applyFree( const SynTreeClass * input ) const;
 
 	template< typename node_t, enum Node::ref_type ref_t >
 	int apply( ptr_base< node_t, ref_t > & input ) const {
 		const node_t * p = input.get();
-		int ret = apply(p);
-		input = p;
-		return ret;
+		auto ret = apply(p);
+		input = ret.node;
+		return ret.count;
 	}
 
@@ -58,7 +64,7 @@
 	int applyFree( ptr_base< node_t, ref_t > & input ) const {
 		const node_t * p = input.get();
-		int ret = applyFree(p);
-		input = p;
-		return ret;
+		auto ret = applyFree(p);
+		input = ret.node;
+		return ret.count;
 	}
 
@@ -175,23 +181,17 @@
 
 template< typename SynTreeClass >
-int TypeSubstitution::apply( const SynTreeClass *& input ) const {
+TypeSubstitution::ApplyResult<SynTreeClass> TypeSubstitution::apply( const SynTreeClass * input ) const {
 	assert( input );
 	Pass<Substituter> sub( *this, false );
 	input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) );
-///	std::cerr << "substitution result is: ";
-///	newType->print( std::cerr );
-///	std::cerr << std::endl;
-	return sub.pass.subCount;
+	return { input, sub.pass.subCount };
 }
 
 template< typename SynTreeClass >
-int TypeSubstitution::applyFree( const SynTreeClass *& input ) const {
+TypeSubstitution::ApplyResult<SynTreeClass> TypeSubstitution::applyFree( const SynTreeClass * input ) const {
 	assert( input );
 	Pass<Substituter> sub( *this, true );
 	input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) );
-///	std::cerr << "substitution result is: ";
-///	newType->print( std::cerr );
-///	std::cerr << std::endl;
-	return sub.pass.subCount;
+	return { input, sub.pass.subCount };
 }
 
Index: src/ResolvExpr/CandidateFinder.cpp
===================================================================
--- src/ResolvExpr/CandidateFinder.cpp	(revision 504eb72fc3a32710df1a99fba0601f0cbc45cc12)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision 2890212a2b66d8b4d2e668cc354f6205f1c666bd)
@@ -54,5 +54,5 @@
 		return new ast::CastExpr{ expr, expr->result->stripReferences() };
 	}
-	
+
 	return expr;
 }
@@ -61,7 +61,7 @@
 UniqueId globalResnSlot = 0;
 
-Cost computeConversionCost( 
-	const ast::Type * argType, const ast::Type * paramType, const ast::SymbolTable & symtab, 
-	const ast::TypeEnvironment & env 
+Cost computeConversionCost(
+	const ast::Type * argType, const ast::Type * paramType, const ast::SymbolTable & symtab,
+	const ast::TypeEnvironment & env
 ) {
 	PRINT(
@@ -107,12 +107,12 @@
 
 	/// Computes conversion cost for a given expression to a given type
-	const ast::Expr * computeExpressionConversionCost( 
-		const ast::Expr * arg, const ast::Type * paramType, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, Cost & outCost 
+	const ast::Expr * computeExpressionConversionCost(
+		const ast::Expr * arg, const ast::Type * paramType, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, Cost & outCost
 	) {
 		Cost convCost = computeConversionCost( arg->result, paramType, symtab, env );
 		outCost += convCost;
 
-		// If there is a non-zero conversion cost, ignoring poly cost, then the expression requires 
-		// conversion. Ignore poly cost for now, since this requires resolution of the cast to 
+		// If there is a non-zero conversion cost, ignoring poly cost, then the expression requires
+		// conversion. Ignore poly cost for now, since this requires resolution of the cast to
 		// infer parameters and this does not currently work for the reason stated below
 		Cost tmpCost = convCost;
@@ -123,16 +123,16 @@
 			return new ast::CastExpr{ arg, newType };
 
-			// xxx - *should* be able to resolve this cast, but at the moment pointers are not 
-			// castable to zero_t, but are implicitly convertible. This is clearly inconsistent, 
+			// xxx - *should* be able to resolve this cast, but at the moment pointers are not
+			// castable to zero_t, but are implicitly convertible. This is clearly inconsistent,
 			// once this is fixed it should be possible to resolve the cast.
-			// xxx - this isn't working, it appears because type1 (parameter) is seen as widenable, 
-			// but it shouldn't be because this makes the conversion from DT* to DT* since 
+			// xxx - this isn't working, it appears because type1 (parameter) is seen as widenable,
+			// but it shouldn't be because this makes the conversion from DT* to DT* since
 			// commontype(zero_t, DT*) is DT*, rather than nothing
 
 			// CandidateFinder finder{ symtab, env };
 			// finder.find( arg, ResolvMode::withAdjustment() );
-			// assertf( finder.candidates.size() > 0, 
+			// assertf( finder.candidates.size() > 0,
 			// 	"Somehow castable expression failed to find alternatives." );
-			// assertf( finder.candidates.size() == 1, 
+			// assertf( finder.candidates.size() == 1,
 			// 	"Somehow got multiple alternatives for known cast expression." );
 			// return finder.candidates.front()->expr;
@@ -143,6 +143,6 @@
 
 	/// Computes conversion cost for a given candidate
-	Cost computeApplicationConversionCost( 
-		CandidateRef cand, const ast::SymbolTable & symtab 
+	Cost computeApplicationConversionCost(
+		CandidateRef cand, const ast::SymbolTable & symtab
 	) {
 		auto appExpr = cand->expr.strict_as< ast::ApplicationExpr >();
@@ -167,9 +167,9 @@
 				if ( function->isVarArgs ) {
 					convCost.incUnsafe();
-					PRINT( std::cerr << "end of params with varargs function: inc unsafe: " 
+					PRINT( std::cerr << "end of params with varargs function: inc unsafe: "
 						<< convCost << std::endl; ; )
 					// convert reference-typed expressions into value-typed expressions
-					cand->expr = ast::mutate_field_index( 
-						appExpr, &ast::ApplicationExpr::args, i, 
+					cand->expr = ast::mutate_field_index(
+						appExpr, &ast::ApplicationExpr::args, i,
 						referenceToRvalueConversion( args[i], convCost ) );
 					continue;
@@ -180,5 +180,5 @@
 				// Default arguments should be free - don't include conversion cost.
 				// Unwrap them here because they are not relevant to the rest of the system
-				cand->expr = ast::mutate_field_index( 
+				cand->expr = ast::mutate_field_index(
 					appExpr, &ast::ApplicationExpr::args, i, def->expr );
 				++param;
@@ -188,7 +188,7 @@
 			// mark conversion cost and also specialization cost of param type
 			const ast::Type * paramType = (*param)->get_type();
-			cand->expr = ast::mutate_field_index( 
-				appExpr, &ast::ApplicationExpr::args, i, 
-				computeExpressionConversionCost( 
+			cand->expr = ast::mutate_field_index(
+				appExpr, &ast::ApplicationExpr::args, i,
+				computeExpressionConversionCost(
 					args[i], paramType, symtab, cand->env, convCost ) );
 			convCost.decSpec( specCost( paramType ) );
@@ -198,5 +198,5 @@
 		if ( param != params.end() ) return Cost::infinity;
 
-		// specialization cost of return types can't be accounted for directly, it disables 
+		// specialization cost of return types can't be accounted for directly, it disables
 		// otherwise-identical calls, like this example based on auto-newline in the I/O lib:
 		//
@@ -215,7 +215,7 @@
 	}
 
-	void makeUnifiableVars( 
-		const ast::ParameterizedType * type, ast::OpenVarSet & unifiableVars, 
-		ast::AssertionSet & need 
+	void makeUnifiableVars(
+		const ast::ParameterizedType * type, ast::OpenVarSet & unifiableVars,
+		ast::AssertionSet & need
 	) {
 		for ( const ast::TypeDecl * tyvar : type->forall ) {
@@ -254,29 +254,29 @@
 
 		ArgPack()
-		: parent( 0 ), expr(), cost( Cost::zero ), env(), need(), have(), open(), nextArg( 0 ), 
+		: parent( 0 ), expr(), cost( Cost::zero ), env(), need(), have(), open(), nextArg( 0 ),
 		  tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {}
-		
-		ArgPack( 
-			const ast::TypeEnvironment & env, const ast::AssertionSet & need, 
+
+		ArgPack(
+			const ast::TypeEnvironment & env, const ast::AssertionSet & need,
 			const ast::AssertionSet & have, const ast::OpenVarSet & open )
-		: parent( 0 ), expr(), cost( Cost::zero ), env( env ), need( need ), have( have ), 
+		: parent( 0 ), expr(), cost( Cost::zero ), env( env ), need( need ), have( have ),
 		  open( open ), nextArg( 0 ), tupleStart( 0 ), nextExpl( 0 ), explAlt( 0 ) {}
-		
+
 		ArgPack(
-			std::size_t parent, const ast::Expr * expr, ast::TypeEnvironment && env, 
-			ast::AssertionSet && need, ast::AssertionSet && have, ast::OpenVarSet && open, 
-			unsigned nextArg, unsigned tupleStart = 0, Cost cost = Cost::zero, 
+			std::size_t parent, const ast::Expr * expr, ast::TypeEnvironment && env,
+			ast::AssertionSet && need, ast::AssertionSet && have, ast::OpenVarSet && open,
+			unsigned nextArg, unsigned tupleStart = 0, Cost cost = Cost::zero,
 			unsigned nextExpl = 0, unsigned explAlt = 0 )
 		: parent(parent), expr( expr ), cost( cost ), env( move( env ) ), need( move( need ) ),
 		  have( move( have ) ), open( move( open ) ), nextArg( nextArg ), tupleStart( tupleStart ),
 		  nextExpl( nextExpl ), explAlt( explAlt ) {}
-		
+
 		ArgPack(
-			const ArgPack & o, ast::TypeEnvironment && env, ast::AssertionSet && need, 
+			const ArgPack & o, ast::TypeEnvironment && env, ast::AssertionSet && need,
 			ast::AssertionSet && have, ast::OpenVarSet && open, unsigned nextArg, Cost added )
-		: parent( o.parent ), expr( o.expr ), cost( o.cost + added ), env( move( env ) ), 
-		  need( move( need ) ), have( move( have ) ), open( move( open ) ), nextArg( nextArg ), 
+		: parent( o.parent ), expr( o.expr ), cost( o.cost + added ), env( move( env ) ),
+		  need( move( need ) ), have( move( have ) ), open( move( open ) ), nextArg( nextArg ),
 		  tupleStart( o.tupleStart ), nextExpl( 0 ), explAlt( 0 ) {}
-		
+
 		/// true if this pack is in the middle of an exploded argument
 		bool hasExpl() const { return nextExpl > 0; }
@@ -286,5 +286,5 @@
 			return args[ nextArg-1 ][ explAlt ];
 		}
-		
+
 		/// Ends a tuple expression, consolidating the appropriate args
 		void endTuple( const std::vector< ArgPack > & packs ) {
@@ -307,8 +307,8 @@
 
 	/// Instantiates an argument to match a parameter, returns false if no matching results left
-	bool instantiateArgument( 
-		const ast::Type * paramType, const ast::Init * init, const ExplodedArgs_new & args, 
-		std::vector< ArgPack > & results, std::size_t & genStart, const ast::SymbolTable & symtab, 
-		unsigned nTuples = 0 
+	bool instantiateArgument(
+		const ast::Type * paramType, const ast::Init * init, const ExplodedArgs_new & args,
+		std::vector< ArgPack > & results, std::size_t & genStart, const ast::SymbolTable & symtab,
+		unsigned nTuples = 0
 	) {
 		if ( auto tupleType = dynamic_cast< const ast::TupleType * >( paramType ) ) {
@@ -318,5 +318,5 @@
 				// xxx - dropping initializer changes behaviour from previous, but seems correct
 				// ^^^ need to handle the case where a tuple has a default argument
-				if ( ! instantiateArgument( 
+				if ( ! instantiateArgument(
 					type, nullptr, args, results, genStart, symtab, nTuples ) ) return false;
 				nTuples = 0;
@@ -329,5 +329,5 @@
 		} else if ( const ast::TypeInstType * ttype = Tuples::isTtype( paramType ) ) {
 			// paramType is a ttype, consumes all remaining arguments
-			
+
 			// completed tuples; will be spliced to end of results to finish
 			std::vector< ArgPack > finalResults{};
@@ -342,5 +342,5 @@
 				for ( std::size_t i = genStart; i < genEnd; ++i ) {
 					unsigned nextArg = results[i].nextArg;
-					
+
 					// use next element of exploded tuple if present
 					if ( results[i].hasExpl() ) {
@@ -352,5 +352,5 @@
 						results.emplace_back(
 							i, expl.exprs[ results[i].nextExpl ], copy( results[i].env ),
-							copy( results[i].need ), copy( results[i].have ), 
+							copy( results[i].need ), copy( results[i].have ),
 							copy( results[i].open ), nextArg, nTuples, Cost::zero, nextExpl,
 							results[i].explAlt );
@@ -398,8 +398,8 @@
 
 						// check unification for ttype before adding to final
-						if ( 
-							unify( 
+						if (
+							unify(
 								ttype, argType, newResult.env, newResult.need, newResult.have,
-								newResult.open, symtab ) 
+								newResult.open, symtab )
 						) {
 							finalResults.emplace_back( move( newResult ) );
@@ -422,7 +422,7 @@
 						if ( expl.exprs.empty() ) {
 							results.emplace_back(
-								results[i], move( env ), copy( results[i].need ), 
+								results[i], move( env ), copy( results[i].need ),
 								copy( results[i].have ), move( open ), nextArg + 1, expl.cost );
-							
+
 							continue;
 						}
@@ -430,6 +430,6 @@
 						// add new result
 						results.emplace_back(
-							i, expl.exprs.front(), move( env ), copy( results[i].need ), 
-							copy( results[i].have ), move( open ), nextArg + 1, nTuples, 
+							i, expl.exprs.front(), move( env ), copy( results[i].need ),
+							copy( results[i].have ), move( open ), nextArg + 1, nTuples,
 							expl.cost, expl.exprs.size() == 1 ? 0 : 1, j );
 					}
@@ -477,5 +477,5 @@
 
 					results.emplace_back(
-						i, expr, move( env ), move( need ), move( have ), move( open ), nextArg, 
+						i, expr, move( env ), move( need ), move( have ), move( open ), nextArg,
 						nTuples, Cost::zero, nextExpl, results[i].explAlt );
 				}
@@ -493,5 +493,5 @@
 					if ( unify( paramType, cnst->result, env, need, have, open, symtab ) ) {
 						results.emplace_back(
-							i, new ast::DefaultArgExpr{ cnst->location, cnst }, move( env ), 
+							i, new ast::DefaultArgExpr{ cnst->location, cnst }, move( env ),
 							move( need ), move( have ), move( open ), nextArg, nTuples );
 					}
@@ -515,7 +515,7 @@
 				if ( expl.exprs.empty() ) {
 					results.emplace_back(
-						results[i], move( env ), move( need ), move( have ), move( open ), 
+						results[i], move( env ), move( need ), move( have ), move( open ),
 						nextArg + 1, expl.cost );
-					
+
 					continue;
 				}
@@ -537,5 +537,5 @@
 					// add new result
 					results.emplace_back(
-						i, expr, move( env ), move( need ), move( have ), move( open ), 
+						i, expr, move( env ), move( need ), move( have ), move( open ),
 						nextArg + 1, nTuples, expl.cost, expl.exprs.size() == 1 ? 0 : 1, j );
 				}
@@ -550,19 +550,19 @@
 
 	/// Generate a cast expression from `arg` to `toType`
-	const ast::Expr * restructureCast( 
+	const ast::Expr * restructureCast(
 		ast::ptr< ast::Expr > & arg, const ast::Type * toType, ast::GeneratedFlag isGenerated = ast::GeneratedCast
 	) {
-		if ( 
-			arg->result->size() > 1 
-			&& ! toType->isVoid() 
-			&& ! dynamic_cast< const ast::ReferenceType * >( toType ) 
+		if (
+			arg->result->size() > 1
+			&& ! toType->isVoid()
+			&& ! dynamic_cast< const ast::ReferenceType * >( toType )
 		) {
-			// Argument is a tuple and the target type is neither void nor a reference. Cast each 
-			// member of the tuple to its corresponding target type, producing the tuple of those 
-			// cast expressions. If there are more components of the tuple than components in the 
-			// target type, then excess components do not come out in the result expression (but 
+			// Argument is a tuple and the target type is neither void nor a reference. Cast each
+			// member of the tuple to its corresponding target type, producing the tuple of those
+			// cast expressions. If there are more components of the tuple than components in the
+			// target type, then excess components do not come out in the result expression (but
 			// UniqueExpr ensures that the side effects will still be produced)
 			if ( Tuples::maybeImpureIgnoreUnique( arg ) ) {
-				// expressions which may contain side effects require a single unique instance of 
+				// expressions which may contain side effects require a single unique instance of
 				// the expression
 				arg = new ast::UniqueExpr{ arg->location, arg };
@@ -572,5 +572,5 @@
 				// cast each component
 				ast::ptr< ast::Expr > idx = new ast::TupleIndexExpr{ arg->location, arg, i };
-				components.emplace_back( 
+				components.emplace_back(
 					restructureCast( idx, toType->getComponent( i ), isGenerated ) );
 			}
@@ -601,7 +601,7 @@
 
 		Finder( CandidateFinder & f )
-		: symtab( f.localSyms ), selfFinder( f ), candidates( f.candidates ), tenv( f.env ), 
+		: symtab( f.localSyms ), selfFinder( f ), candidates( f.candidates ), tenv( f.env ),
 		  targetType( f.targetType ) {}
-		
+
 		void previsit( const ast::Node * ) { visit_children = false; }
 
@@ -638,9 +638,9 @@
 
 		/// Completes a function candidate with arguments located
-		void validateFunctionCandidate( 
-			const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results, 
-			CandidateList & out 
+		void validateFunctionCandidate(
+			const CandidateRef & func, ArgPack & result, const std::vector< ArgPack > & results,
+			CandidateList & out
 		) {
-			ast::ApplicationExpr * appExpr = 
+			ast::ApplicationExpr * appExpr =
 				new ast::ApplicationExpr{ func->expr->location, func->expr };
 			// sum cost and accumulate arguments
@@ -656,5 +656,5 @@
 			appExpr->args = move( vargs );
 			// build and validate new candidate
-			auto newCand = 
+			auto newCand =
 				std::make_shared<Candidate>( appExpr, result.env, result.open, result.need, cost );
 			PRINT(
@@ -668,5 +668,5 @@
 		/// Builds a list of candidates for a function, storing them in out
 		void makeFunctionCandidates(
-			const CandidateRef & func, const ast::FunctionType * funcType, 
+			const CandidateRef & func, const ast::FunctionType * funcType,
 			const ExplodedArgs_new & args, CandidateList & out
 		) {
@@ -675,5 +675,5 @@
 			ast::TypeEnvironment funcEnv{ func->env };
 			makeUnifiableVars( funcType, funcOpen, funcNeed );
-			// add all type variables as open variables now so that those not used in the 
+			// add all type variables as open variables now so that those not used in the
 			// parameter list are still considered open
 			funcEnv.add( funcType->forall );
@@ -682,6 +682,6 @@
 				// attempt to narrow based on expected target type
 				const ast::Type * returnType = funcType->returns.front()->get_type();
-				if ( ! unify( 
-					returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab ) 
+				if ( ! unify(
+					returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab )
 				) {
 					// unification failed, do not pursue this candidate
@@ -697,7 +697,7 @@
 			for ( const ast::DeclWithType * param : funcType->params ) {
 				auto obj = strict_dynamic_cast< const ast::ObjectDecl * >( param );
-				// Try adding the arguments corresponding to the current parameter to the existing 
+				// Try adding the arguments corresponding to the current parameter to the existing
 				// matches
-				if ( ! instantiateArgument( 
+				if ( ! instantiateArgument(
 					obj->type, obj->init, args, results, genStart, symtab ) ) return;
 			}
@@ -749,6 +749,6 @@
 							if ( expl.exprs.empty() ) {
 								results.emplace_back(
-									results[i], move( env ), copy( results[i].need ), 
-									copy( results[i].have ), move( open ), nextArg + 1, 
+									results[i], move( env ), copy( results[i].need ),
+									copy( results[i].have ), move( open ), nextArg + 1,
 									expl.cost );
 
@@ -759,5 +759,5 @@
 							results.emplace_back(
 								i, expl.exprs.front(), move( env ), copy( results[i].need ),
-								copy( results[i].have ), move( open ), nextArg + 1, 0, expl.cost, 
+								copy( results[i].have ), move( open ), nextArg + 1, 0, expl.cost,
 								expl.exprs.size() == 1 ? 0 : 1, j );
 						}
@@ -779,11 +779,11 @@
 		/// Adds implicit struct-conversions to the alternative list
 		void addAnonConversions( const CandidateRef & cand ) {
-			// adds anonymous member interpretations whenever an aggregate value type is seen. 
-			// it's okay for the aggregate expression to have reference type -- cast it to the 
+			// adds anonymous member interpretations whenever an aggregate value type is seen.
+			// it's okay for the aggregate expression to have reference type -- cast it to the
 			// base type to treat the aggregate as the referenced value
 			ast::ptr< ast::Expr > aggrExpr( cand->expr );
 			ast::ptr< ast::Type > & aggrType = aggrExpr.get_and_mutate()->result;
 			cand->env.apply( aggrType );
-			
+
 			if ( aggrType.as< ast::ReferenceType >() ) {
 				aggrExpr = new ast::CastExpr{ aggrExpr, aggrType->stripReferences() };
@@ -798,13 +798,13 @@
 
 		/// Adds aggregate member interpretations
-		void addAggMembers( 
-			const ast::ReferenceToType * aggrInst, const ast::Expr * expr, 
-			const Candidate & cand, const Cost & addedCost, const std::string & name 
+		void addAggMembers(
+			const ast::ReferenceToType * aggrInst, const ast::Expr * expr,
+			const Candidate & cand, const Cost & addedCost, const std::string & name
 		) {
 			for ( const ast::Decl * decl : aggrInst->lookup( name ) ) {
 				auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( decl );
-				CandidateRef newCand = std::make_shared<Candidate>( 
+				CandidateRef newCand = std::make_shared<Candidate>(
 					cand, new ast::MemberExpr{ expr->location, dwt, expr }, addedCost );
-				// add anonymous member interpretations whenever an aggregate value type is seen 
+				// add anonymous member interpretations whenever an aggregate value type is seen
 				// as a member expression
 				addAnonConversions( newCand );
@@ -814,15 +814,15 @@
 
 		/// Adds tuple member interpretations
-		void addTupleMembers( 
-			const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand, 
-			const Cost & addedCost, const ast::Expr * member 
+		void addTupleMembers(
+			const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand,
+			const Cost & addedCost, const ast::Expr * member
 		) {
 			if ( auto constantExpr = dynamic_cast< const ast::ConstantExpr * >( member ) ) {
-				// get the value of the constant expression as an int, must be between 0 and the 
+				// get the value of the constant expression as an int, must be between 0 and the
 				// length of the tuple to have meaning
 				long long val = constantExpr->intValue();
 				if ( val >= 0 && (unsigned long long)val < tupleType->size() ) {
 					addCandidate(
-						cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val }, 
+						cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val },
 						addedCost );
 				}
@@ -836,7 +836,7 @@
 			if ( funcFinder.candidates.empty() ) return;
 
-			std::vector< CandidateFinder > argCandidates = 
+			std::vector< CandidateFinder > argCandidates =
 				selfFinder.findSubExprs( untypedExpr->args );
-			
+
 			// take care of possible tuple assignments
 			// if not tuple assignment, handled as normal function call
@@ -876,15 +876,15 @@
 						if ( auto function = pointer->base.as< ast::FunctionType >() ) {
 							CandidateRef newFunc{ new Candidate{ *func } };
-							newFunc->expr = 
+							newFunc->expr =
 								referenceToRvalueConversion( newFunc->expr, newFunc->cost );
 							makeFunctionCandidates( newFunc, function, argExpansions, found );
 						}
-					} else if ( 
-						auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult ) 
+					} else if (
+						auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult )
 					) {
 						if ( const ast::EqvClass * clz = func->env.lookup( inst->name ) ) {
 							if ( auto function = clz->bound.as< ast::FunctionType >() ) {
 								CandidateRef newFunc{ new Candidate{ *func } };
-								newFunc->expr = 
+								newFunc->expr =
 									referenceToRvalueConversion( newFunc->expr, newFunc->cost );
 								makeFunctionCandidates( newFunc, function, argExpansions, found );
@@ -900,5 +900,5 @@
 				std::vector< ExplodedArg > funcE;
 				funcE.reserve( funcFinder.candidates.size() );
-				for ( const CandidateRef & func : funcFinder ) { 
+				for ( const CandidateRef & func : funcFinder ) {
 					funcE.emplace_back( *func, symtab );
 				}
@@ -912,5 +912,5 @@
 							if ( auto function = pointer->base.as< ast::FunctionType >() ) {
 								CandidateRef newOp{ new Candidate{ *op} };
-								newOp->expr = 
+								newOp->expr =
 									referenceToRvalueConversion( newOp->expr, newOp->cost );
 								makeFunctionCandidates( newOp, function, argExpansions, found );
@@ -921,5 +921,5 @@
 			}
 
-			// Implement SFINAE; resolution errors are only errors if there aren't any non-error 
+			// Implement SFINAE; resolution errors are only errors if there aren't any non-error
 			// candidates
 			if ( found.empty() && ! errors.isEmpty() ) { throw errors; }
@@ -933,5 +933,5 @@
 					auto pointer = appExpr->func->result.strict_as< ast::PointerType >();
 					auto function = pointer->base.strict_as< ast::FunctionType >();
-					
+
 					std::cerr << "Case +++++++++++++ " << appExpr->func << std::endl;
 					std::cerr << "parameters are:" << std::endl;
@@ -956,6 +956,6 @@
 			promoteCvtCost( winners );
 
-			// function may return a struct/union value, in which case we need to add candidates 
-			// for implicit conversions to each of the anonymous members, which must happen after 
+			// function may return a struct/union value, in which case we need to add candidates
+			// for implicit conversions to each of the anonymous members, which must happen after
 			// `findMinCost`, since anon conversions are never the cheapest
 			for ( const CandidateRef & c : winners ) {
@@ -965,5 +965,5 @@
 
 			if ( candidates.empty() && targetType && ! targetType->isVoid() ) {
-				// If resolution is unsuccessful with a target type, try again without, since it 
+				// If resolution is unsuccessful with a target type, try again without, since it
 				// will sometimes succeed when it wouldn't with a target type binding.
 				// For example:
@@ -1015,7 +1015,7 @@
 				cand->env.extractOpenVars( open );
 
-				// It is possible that a cast can throw away some values in a multiply-valued 
-				// expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the 
-				// subexpression results that are cast directly. The candidate is invalid if it 
+				// It is possible that a cast can throw away some values in a multiply-valued
+				// expression, e.g. cast-to-void, one value to zero. Figure out the prefix of the
+				// subexpression results that are cast directly. The candidate is invalid if it
 				// has fewer results than there are types to cast to.
 				int discardedValues = cand->expr->result->size() - toType->size();
@@ -1036,7 +1036,7 @@
 					// count one safe conversion for each value that is thrown away
 					thisCost.incSafe( discardedValues );
-					CandidateRef newCand = std::make_shared<Candidate>( 
-						restructureCast( cand->expr, toType, castExpr->isGenerated ), 
-						copy( cand->env ), move( open ), move( need ), cand->cost, 
+					CandidateRef newCand = std::make_shared<Candidate>(
+						restructureCast( cand->expr, toType, castExpr->isGenerated ),
+						copy( cand->env ), move( open ), move( need ), cand->cost,
 						cand->cost + thisCost );
 					inferParameters( newCand, matches );
@@ -1056,6 +1056,6 @@
 			finder.find( castExpr->arg, ResolvMode::withoutPrune() );
 			for ( CandidateRef & r : finder.candidates ) {
-				addCandidate( 
-					*r, 
+				addCandidate(
+					*r,
 					new ast::VirtualCastExpr{ castExpr->location, r->expr, castExpr->result } );
 			}
@@ -1066,5 +1066,5 @@
 			aggFinder.find( memberExpr->aggregate, ResolvMode::withAdjustment() );
 			for ( CandidateRef & agg : aggFinder.candidates ) {
-				// it's okay for the aggregate expression to have reference type -- cast it to the 
+				// it's okay for the aggregate expression to have reference type -- cast it to the
 				// base type to treat the aggregate as the referenced value
 				Cost addedCost = Cost::zero;
@@ -1073,8 +1073,8 @@
 				// find member of the given type
 				if ( auto structInst = agg->expr->result.as< ast::StructInstType >() ) {
-					addAggMembers( 
+					addAggMembers(
 						structInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
 				} else if ( auto unionInst = agg->expr->result.as< ast::UnionInstType >() ) {
-					addAggMembers( 
+					addAggMembers(
 						unionInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
 				} else if ( auto tupleType = agg->expr->result.as< ast::TupleType >() ) {
@@ -1096,5 +1096,5 @@
 
 				CandidateRef newCand = std::make_shared<Candidate>(
-					newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero, 
+					newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero,
 					cost );
 				PRINT(
@@ -1106,8 +1106,8 @@
 					std::cerr << std::endl;
 				)
-				newCand->expr = ast::mutate_field( 
-					newCand->expr.get(), &ast::Expr::result, 
+				newCand->expr = ast::mutate_field(
+					newCand->expr.get(), &ast::Expr::result,
 					renameTyVars( newCand->expr->result ) );
-				// add anonymous member interpretations whenever an aggregate value type is seen 
+				// add anonymous member interpretations whenever an aggregate value type is seen
 				// as a name expression
 				addAnonConversions( newCand );
@@ -1119,5 +1119,5 @@
 			// not sufficient to just pass `variableExpr` here, type might have changed since
 			// creation
-			addCandidate( 
+			addCandidate(
 				new ast::VariableExpr{ variableExpr->location, variableExpr->var }, tenv );
 		}
@@ -1129,7 +1129,7 @@
 		void postvisit( const ast::SizeofExpr * sizeofExpr ) {
 			if ( sizeofExpr->type ) {
-				addCandidate( 
-					new ast::SizeofExpr{ 
-						sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) }, 
+				addCandidate(
+					new ast::SizeofExpr{
+						sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) },
 					tenv );
 			} else {
@@ -1140,5 +1140,5 @@
 				CandidateList winners = findMinCost( finder.candidates );
 				if ( winners.size() != 1 ) {
-					SemanticError( 
+					SemanticError(
 						sizeofExpr->expr.get(), "Ambiguous expression in sizeof operand: " );
 				}
@@ -1153,7 +1153,7 @@
 		void postvisit( const ast::AlignofExpr * alignofExpr ) {
 			if ( alignofExpr->type ) {
-				addCandidate( 
-					new ast::AlignofExpr{ 
-						alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) }, 
+				addCandidate(
+					new ast::AlignofExpr{
+						alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) },
 					tenv );
 			} else {
@@ -1164,5 +1164,5 @@
 				CandidateList winners = findMinCost( finder.candidates );
 				if ( winners.size() != 1 ) {
-					SemanticError( 
+					SemanticError(
 						alignofExpr->expr.get(), "Ambiguous expression in alignof operand: " );
 				}
@@ -1171,5 +1171,5 @@
 				choice->expr = referenceToRvalueConversion( choice->expr, choice->cost );
 				choice->cost = Cost::zero;
-				addCandidate( 
+				addCandidate(
 					*choice, new ast::AlignofExpr{ alignofExpr->location, choice->expr } );
 			}
@@ -1184,5 +1184,5 @@
 			for ( const ast::Decl * member : aggInst->lookup( offsetofExpr->member ) ) {
 				auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( member );
-				addCandidate( 
+				addCandidate(
 					new ast::OffsetofExpr{ offsetofExpr->location, aggInst, dwt }, tenv );
 			}
@@ -1217,5 +1217,5 @@
 
 					addCandidate(
-						new ast::LogicalExpr{ 
+						new ast::LogicalExpr{
 							logicalExpr->location, r1->expr, r2->expr, logicalExpr->isAnd },
 						move( env ), move( open ), move( need ), r1->cost + r2->cost );
@@ -1255,19 +1255,19 @@
 						ast::AssertionSet have;
 
-						// unify true and false results, then infer parameters to produce new 
+						// unify true and false results, then infer parameters to produce new
 						// candidates
 						ast::ptr< ast::Type > common;
-						if ( 
-							unify( 
-								r2->expr->result, r3->expr->result, env, need, have, open, symtab, 
-								common ) 
+						if (
+							unify(
+								r2->expr->result, r3->expr->result, env, need, have, open, symtab,
+								common )
 						) {
 							// generate typed expression
-							ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{ 
+							ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{
 								conditionalExpr->location, r1->expr, r2->expr, r3->expr };
 							newExpr->result = common ? common : r2->expr->result;
 							// convert both options to result type
 							Cost cost = r1->cost + r2->cost + r3->cost;
-							newExpr->arg2 = computeExpressionConversionCost( 
+							newExpr->arg2 = computeExpressionConversionCost(
 								newExpr->arg2, newExpr->result, symtab, env, cost );
 							newExpr->arg3 = computeExpressionConversionCost(
@@ -1286,5 +1286,5 @@
 			ast::TypeEnvironment env{ tenv };
 			ast::ptr< ast::Expr > arg1 = resolveInVoidContext( commaExpr->arg1, symtab, env );
-			
+
 			CandidateFinder finder2{ symtab, env };
 			finder2.find( commaExpr->arg2, ResolvMode::withAdjustment() );
@@ -1329,16 +1329,16 @@
 
 					ast::ptr< ast::Type > common;
-					if ( 
-						unify( 
-							r1->expr->result, r2->expr->result, env, need, have, open, symtab, 
-							common ) 
+					if (
+						unify(
+							r1->expr->result, r2->expr->result, env, need, have, open, symtab,
+							common )
 					) {
 						// generate new expression
-						ast::RangeExpr * newExpr = 
+						ast::RangeExpr * newExpr =
 							new ast::RangeExpr{ rangeExpr->location, r1->expr, r2->expr };
 						newExpr->result = common ? common : r1->expr->result;
 						// add candidate
 						CandidateRef newCand = std::make_shared<Candidate>(
-							newExpr, move( env ), move( open ), move( need ), 
+							newExpr, move( env ), move( open ), move( need ),
 							r1->cost + r2->cost );
 						inferParameters( newCand, candidates );
@@ -1349,5 +1349,5 @@
 
 		void postvisit( const ast::UntypedTupleExpr * tupleExpr ) {
-			std::vector< CandidateFinder > subCandidates = 
+			std::vector< CandidateFinder > subCandidates =
 				selfFinder.findSubExprs( tupleExpr->exprs );
 			std::vector< CandidateList > possibilities;
@@ -1369,5 +1369,5 @@
 
 				addCandidate(
-					new ast::TupleExpr{ tupleExpr->location, move( exprs ) }, 
+					new ast::TupleExpr{ tupleExpr->location, move( exprs ) },
 					move( env ), move( open ), move( need ), sumCost( subs ) );
 			}
@@ -1411,7 +1411,7 @@
 				toType = SymTab::validateType( initExpr->location, toType, symtab );
 				toType = adjustExprType( toType, tenv, symtab );
-				// The call to find must occur inside this loop, otherwise polymorphic return 
-				// types are not bound to the initialization type, since return type variables are 
-				// only open for the duration of resolving the UntypedExpr. 
+				// The call to find must occur inside this loop, otherwise polymorphic return
+				// types are not bound to the initialization type, since return type variables are
+				// only open for the duration of resolving the UntypedExpr.
 				CandidateFinder finder{ symtab, tenv, toType };
 				finder.find( initExpr->expr, ResolvMode::withAdjustment() );
@@ -1425,7 +1425,7 @@
 					)
 
-					// It is possible that a cast can throw away some values in a multiply-valued 
-					// expression, e.g. cast-to-void, one value to zero. Figure out the prefix of 
-					// the subexpression results that are cast directly. The candidate is invalid 
+					// It is possible that a cast can throw away some values in a multiply-valued
+					// expression, e.g. cast-to-void, one value to zero. Figure out the prefix of
+					// the subexpression results that are cast directly. The candidate is invalid
 					// if it has fewer results than there are types to cast to.
 					int discardedValues = cand->expr->result->size() - toType->size();
@@ -1435,12 +1435,12 @@
 					unify( toType, cand->expr->result, env, need, have, open, symtab );
 					Cost thisCost = castCost( cand->expr->result, toType, symtab, env );
-					
+
 					if ( thisCost != Cost::infinity ) {
 						// count one safe conversion for each value that is thrown away
 						thisCost.incSafe( discardedValues );
-						CandidateRef newCand = std::make_shared<Candidate>( 
-							new ast::InitExpr{ 
-								initExpr->location, restructureCast( cand->expr, toType ), 
-								initAlt.designation }, 
+						CandidateRef newCand = std::make_shared<Candidate>(
+							new ast::InitExpr{
+								initExpr->location, restructureCast( cand->expr, toType ),
+								initAlt.designation },
 							copy( cand->env ), move( open ), move( need ), cand->cost, thisCost );
 						inferParameters( newCand, matches );
@@ -1468,5 +1468,5 @@
 	};
 
-	/// Prunes a list of candidates down to those that have the minimum conversion cost for a given 
+	/// Prunes a list of candidates down to those that have the minimum conversion cost for a given
 	/// return type. Skips ambiguous candidates.
 	CandidateList pruneCandidates( CandidateList & candidates ) {
@@ -1493,5 +1493,5 @@
 				if ( candidate->cost < found->second.candidate->cost ) {
 					PRINT(
-						std::cerr << "cost " << candidate->cost << " beats " 
+						std::cerr << "cost " << candidate->cost << " beats "
 							<< found->second.candidate->cost << std::endl;
 					)
@@ -1499,6 +1499,6 @@
 					found->second = PruneStruct{ candidate };
 				} else if ( candidate->cost == found->second.candidate->cost ) {
-					// if one of the candidates contains a deleted identifier, can pick the other, 
-					// since deleted expressions should not be ambiguous if there is another option 
+					// if one of the candidates contains a deleted identifier, can pick the other,
+					// since deleted expressions should not be ambiguous if there is another option
 					// that is at least as good
 					if ( findDeletedExpr( candidate->expr ) ) {
@@ -1514,5 +1514,5 @@
 				} else {
 					PRINT(
-						std::cerr << "cost " << candidate->cost << " loses to " 
+						std::cerr << "cost " << candidate->cost << " loses to "
 							<< found->second.candidate->cost << std::endl;
 					)
@@ -1529,10 +1529,10 @@
 
 			CandidateRef cand = target.second.candidate;
-			
+
 			ast::ptr< ast::Type > newResult = cand->expr->result;
 			cand->env.applyFree( newResult );
 			cand->expr = ast::mutate_field(
 				cand->expr.get(), &ast::Expr::result, move( newResult ) );
-			
+
 			out.emplace_back( cand );
 		}
@@ -1582,5 +1582,5 @@
 
 		CandidateList pruned = pruneCandidates( candidates );
-		
+
 		if ( mode.failFast && pruned.empty() ) {
 			std::ostringstream stream;
@@ -1601,15 +1601,15 @@
 		)
 		PRINT(
-			std::cerr << "there are " << candidates.size() << " alternatives after elimination" 
+			std::cerr << "there are " << candidates.size() << " alternatives after elimination"
 				<< std::endl;
 		)
 	}
 
-	// adjust types after pruning so that types substituted by pruneAlternatives are correctly 
+	// adjust types after pruning so that types substituted by pruneAlternatives are correctly
 	// adjusted
 	if ( mode.adjust ) {
 		for ( CandidateRef & r : candidates ) {
-			r->expr = ast::mutate_field( 
-				r->expr.get(), &ast::Expr::result, 
+			r->expr = ast::mutate_field(
+				r->expr.get(), &ast::Expr::result,
 				adjustExprType( r->expr->result, r->env, localSyms ) );
 		}
@@ -1624,6 +1624,6 @@
 }
 
-std::vector< CandidateFinder > CandidateFinder::findSubExprs( 
-	const std::vector< ast::ptr< ast::Expr > > & xs 
+std::vector< CandidateFinder > CandidateFinder::findSubExprs(
+	const std::vector< ast::ptr< ast::Expr > > & xs
 ) {
 	std::vector< CandidateFinder > out;
@@ -1632,5 +1632,5 @@
 		out.emplace_back( localSyms, env );
 		out.back().find( x, ResolvMode::withAdjustment() );
-		
+
 		PRINT(
 			std::cerr << "findSubExprs" << std::endl;
Index: src/ResolvExpr/CurrentObject.cc
===================================================================
--- src/ResolvExpr/CurrentObject.cc	(revision 504eb72fc3a32710df1a99fba0601f0cbc45cc12)
+++ src/ResolvExpr/CurrentObject.cc	(revision 2890212a2b66d8b4d2e668cc354f6205f1c666bd)
@@ -25,4 +25,5 @@
 #include "AST/Init.hpp"                // for Designation
 #include "AST/Node.hpp"                // for readonly
+#include "AST/Print.hpp"                // for readonly
 #include "AST/Type.hpp"
 #include "Common/Indenter.h"           // for Indenter, operator<<
@@ -596,6 +597,6 @@
 		SimpleIterator( const CodeLocation & loc, const Type * t ) : location( loc ), type( t ) {}
 
-		void setPosition( 
-			std::deque< ptr< Expr > >::const_iterator begin, 
+		void setPosition(
+			std::deque< ptr< Expr > >::const_iterator begin,
 			std::deque< ptr< Expr > >::const_iterator end
 		) override {
@@ -637,5 +638,5 @@
 			auto res = eval(expr);
 			if ( ! res.second ) {
-				SemanticError( location, 
+				SemanticError( location,
 					toString("Array designator must be a constant expression: ", expr ) );
 			}
@@ -644,5 +645,5 @@
 
 	public:
-		ArrayIterator( const CodeLocation & loc, const ArrayType * at ) 
+		ArrayIterator( const CodeLocation & loc, const ArrayType * at )
 		: location( loc ), array( at ), base( at->base ) {
 			PRINT( std::cerr << "Creating array iterator: " << at << std::endl; )
@@ -655,6 +656,6 @@
 
 		void setPosition( const Expr * expr ) {
-			// need to permit integer-constant-expressions, including: integer constants, 
-			// enumeration constants, character constants, sizeof expressions, alignof expressions, 
+			// need to permit integer-constant-expressions, including: integer constants,
+			// enumeration constants, character constants, sizeof expressions, alignof expressions,
 			// cast expressions
 			if ( auto constExpr = dynamic_cast< const ConstantExpr * >( expr ) ) {
@@ -662,22 +663,22 @@
 					index = constExpr->intValue();
 				} catch ( SemanticErrorException & ) {
-					SemanticError( expr, 
+					SemanticError( expr,
 						"Constant expression of non-integral type in array designator: " );
 				}
 			} else if ( auto castExpr = dynamic_cast< const CastExpr * >( expr ) ) {
 				setPosition( castExpr->arg );
-			} else if ( 
-				dynamic_cast< const SizeofExpr * >( expr ) 
-				|| dynamic_cast< const AlignofExpr * >( expr ) 
+			} else if (
+				dynamic_cast< const SizeofExpr * >( expr )
+				|| dynamic_cast< const AlignofExpr * >( expr )
 			) {
 				index = 0;
 			} else {
-				assertf( false, 
+				assertf( false,
 					"bad designator given to ArrayIterator: %s", toString( expr ).c_str() );
 			}
 		}
 
-		void setPosition( 
-			std::deque< ptr< Expr > >::const_iterator begin, 
+		void setPosition(
+			std::deque< ptr< Expr > >::const_iterator begin,
 			std::deque< ptr< Expr > >::const_iterator end
 		) override {
@@ -758,8 +759,8 @@
 		}
 
-		AggregateIterator( 
-			const CodeLocation & loc, const std::string k, const std::string & n, const Type * i, 
+		AggregateIterator(
+			const CodeLocation & loc, const std::string k, const std::string & n, const Type * i,
 			const MemberList & ms )
-		: location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ), 
+		: location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ),
 		  sub( genericSubstitution( i ) ) {
 			PRINT( std::cerr << "Creating " << kind << "(" << name << ")"; )
@@ -768,6 +769,6 @@
 
 	public:
-		void setPosition( 
-			std::deque< ptr< Expr > >::const_iterator begin, 
+		void setPosition(
+			std::deque< ptr< Expr > >::const_iterator begin,
 			std::deque< ptr< Expr > >::const_iterator end
 		) final {
@@ -786,8 +787,8 @@
 					return;
 				}
-				assertf( false, 
+				assertf( false,
 					"could not find member in %s: %s", kind.c_str(), toString( varExpr ).c_str() );
 			} else {
-				assertf( false, 
+				assertf( false,
 					"bad designator given to %s: %s", kind.c_str(), toString( *begin ).c_str() );
 			}
@@ -842,5 +843,5 @@
 				for ( InitAlternative & alt : ret ) {
 					PRINT( std::cerr << "iterating and adding designators" << std::endl; )
-					alt.designation.get_and_mutate()->designators.emplace_front( 
+					alt.designation.get_and_mutate()->designators.emplace_front(
 						new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
 				}
@@ -897,7 +898,7 @@
 	class TupleIterator final : public AggregateIterator {
 	public:
-		TupleIterator( const CodeLocation & loc, const TupleType * inst ) 
-		: AggregateIterator( 
-			loc, "TupleIterator", toString("Tuple", inst->size()), inst, inst->members 
+		TupleIterator( const CodeLocation & loc, const TupleType * inst )
+		: AggregateIterator(
+			loc, "TupleIterator", toString("Tuple", inst->size()), inst, inst->members
 		) {}
 
@@ -926,7 +927,7 @@
 				return new UnionIterator{ loc, uit };
 			} else {
-				assertf( 
-					dynamic_cast< const EnumInstType * >( aggr ) 
-						|| dynamic_cast< const TypeInstType * >( aggr ), 
+				assertf(
+					dynamic_cast< const EnumInstType * >( type )
+						|| dynamic_cast< const TypeInstType * >( type ),
 					"Encountered unhandled ReferenceToType in createMemberIterator: %s",
 						toString( type ).c_str() );
@@ -949,5 +950,5 @@
 		using DesignatorChain = std::deque< ptr< Expr > >;
 		PRINT( std::cerr << "___findNext" << std::endl; )
-		
+
 		// find all the d's
 		std::vector< DesignatorChain > desigAlts{ {} }, newDesigAlts;
@@ -1013,5 +1014,5 @@
 		// set new designators
 		assertf( ! objStack.empty(), "empty object stack when setting designation" );
-		Designation * actualDesignation = 
+		Designation * actualDesignation =
 			new Designation{ designation->location, DesignatorChain{d} };
 		objStack.back()->setPosition( d ); // destroys d
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 504eb72fc3a32710df1a99fba0601f0cbc45cc12)
+++ src/ResolvExpr/Resolver.cc	(revision 2890212a2b66d8b4d2e668cc354f6205f1c666bd)
@@ -1054,5 +1054,5 @@
 			const ast::Expr * postmutate( const ast::CastExpr * castExpr ) {
 				if (
-					castExpr->isGenerated
+					castExpr->isGenerated == ast::GeneratedCast
 					&& typesCompatible( castExpr->arg->result, castExpr->result )
 				) {
@@ -1321,5 +1321,5 @@
 		// in case we decide to allow nested enums
 		GuardValue( inEnumDecl );
-		inEnumDecl = false;
+		inEnumDecl = true;
 	}
 
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision 504eb72fc3a32710df1a99fba0601f0cbc45cc12)
+++ src/ResolvExpr/Unify.cc	(revision 2890212a2b66d8b4d2e668cc354f6205f1c666bd)
@@ -25,7 +25,9 @@
 #include <vector>
 
+#include "AST/Copy.hpp"
 #include "AST/Decl.hpp"
 #include "AST/Node.hpp"
 #include "AST/Pass.hpp"
+#include "AST/Print.hpp"
 #include "AST/Type.hpp"
 #include "AST/TypeEnvironment.hpp"
@@ -135,6 +137,5 @@
 		findOpenVars( newSecond, open, closed, need, have, FirstOpen );
 
-		return unifyExact(
-			newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
+		return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
 	}
 
@@ -148,13 +149,5 @@
 		newFirst->get_qualifiers() = Type::Qualifiers();
 		newSecond->get_qualifiers() = Type::Qualifiers();
-///   std::cerr << "first is ";
-///   first->print( std::cerr );
-///   std::cerr << std::endl << "second is ";
-///   second->print( std::cerr );
-///   std::cerr << std::endl << "newFirst is ";
-///   newFirst->print( std::cerr );
-///   std::cerr << std::endl << "newSecond is ";
-///   newSecond->print( std::cerr );
-///   std::cerr << std::endl;
+
 		bool result = unifyExact( newFirst, newSecond, newEnv, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
 		delete newFirst;
@@ -170,12 +163,14 @@
 		ast::AssertionSet need, have;
 
-		ast::ptr<ast::Type> newFirst{ first }, newSecond{ second };
-		env.apply( newFirst );
-		env.apply( newSecond );
-		reset_qualifiers( newFirst );
-		reset_qualifiers( newSecond );
+		ast::Type * newFirst  = shallowCopy( first  );
+		ast::Type * newSecond = shallowCopy( second );
+		newFirst ->qualifiers = {};
+		newSecond->qualifiers = {};
+		#warning memory leak
 
 		return unifyExact(
-			newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
+			env.apply( newFirst  ).node,
+			env.apply( newSecond ).node,
+			newEnv, need, have, open, noWiden(), symtab );
 	}
 
@@ -326,12 +321,6 @@
 
 	void markAssertionSet( AssertionSet &assertions, DeclarationWithType *assert ) {
-///   std::cerr << "assertion set is" << std::endl;
-///   printAssertionSet( assertions, std::cerr, 8 );
-///   std::cerr << "looking for ";
-///   assert->print( std::cerr );
-///   std::cerr << std::endl;
 		AssertionSet::iterator i = assertions.find( assert );
 		if ( i != assertions.end() ) {
-///     std::cerr << "found it!" << std::endl;
 			i->second.isUsed = true;
 		} // if
@@ -1202,15 +1191,15 @@
 		// force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and
 		// type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1
-		ast::ptr<ast::Type> t1{ type1 }, t2{ type2 };
-		reset_qualifiers( t1 );
-		reset_qualifiers( t2 );
+		ast::Type * t1 = shallowCopy(type1.get());
+		ast::Type * t2 = shallowCopy(type2.get());
+		t1->qualifiers = {};
+		t2->qualifiers = {};
+		#warning memory leak
 
 		if ( unifyExact( t1, t2, env, need, have, open, widen, symtab ) ) {
-			t1 = nullptr; t2 = nullptr; // release t1, t2 to avoid spurious clones
-
 			// if exact unification on unqualified types, try to merge qualifiers
 			if ( q1 == q2 || ( ( q1 > q2 || widen.first ) && ( q2 > q1 || widen.second ) ) ) {
-				common = type1;
-				reset_qualifiers( common, q1 | q2 );
+				t1->qualifiers = q1 | q2;
+				common = t1;
 				return true;
 			} else {
@@ -1219,8 +1208,8 @@
 
 		} else if (( common = commonType( t1, t2, widen, symtab, env, open ) )) {
-			t1 = nullptr; t2 = nullptr; // release t1, t2 to avoid spurious clones
-
 			// no exact unification, but common type
-			reset_qualifiers( common, q1 | q2 );
+			auto c = shallowCopy(common.get());
+			c->qualifiers = q1 | q2;
+			common = c;
 			return true;
 		} else {
