Index: src/AST/Attribute.hpp
===================================================================
--- src/AST/Attribute.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Attribute.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -51,4 +51,6 @@
 	template<typename node_t>
 	friend node_t * mutate(const node_t * node);
+	template<typename node_t>
+    friend node_t * shallowCopy(const node_t * node);
 };
 
Index: src/AST/CVQualifiers.hpp
===================================================================
--- src/AST/CVQualifiers.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/CVQualifiers.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -27,12 +27,11 @@
 		Restrict = 1 << 1,
 		Volatile = 1 << 2,
-		Lvalue   = 1 << 3,
-		Mutex    = 1 << 4,
-		Atomic   = 1 << 5,
-		NumQualifiers = 6
+		Mutex    = 1 << 3,
+		Atomic   = 1 << 4,
+		NumQualifiers = 5
 	};
 
 	/// Mask for equivalence-preserving qualfiers
-	enum { EquivQualifiers = ~(Restrict | Lvalue) };
+	enum { EquivQualifiers = ~Restrict };
 
 	/// Underlying data for qualifiers
@@ -44,5 +43,4 @@
 				bool is_restrict : 1;
 				bool is_volatile : 1;
-				bool is_lvalue   : 1;
 				bool is_mutex    : 1;
 				bool is_atomic   : 1;
Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Convert.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -620,4 +620,15 @@
 
 		tgt->result = get<Type>().accept1(src->result);
+		// Unconditionally use a clone of the result type.
+		// We know this will leak some objects: much of the immediate conversion result.
+		// In some cases, using the conversion result directly gives unintended object sharing.
+		// A parameter (ObjectDecl, a child of a FunctionType) is shared by the weak-ref cache.
+		// But tgt->result must be fully owned privately by tgt.
+		// Applying these conservative copies here means
+		// - weak references point at the declaration's copy, not these expr.result copies (good)
+		// - we copy more objects than really needed (bad, tolerated)
+		if (tgt->result) {
+			tgt->result = tgt->result->clone();
+		}
 		return visitBaseExpr_skipResultType(src, tgt);
 	}
@@ -2117,5 +2128,6 @@
 				old->location,
 				GET_ACCEPT_1(member, DeclWithType),
-				GET_ACCEPT_1(aggregate, Expr)
+				GET_ACCEPT_1(aggregate, Expr),
+				ast::MemberExpr::NoOpConstructionChosen
 			)
 		);
Index: src/AST/Copy.hpp
===================================================================
--- src/AST/Copy.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
+++ src/AST/Copy.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -0,0 +1,126 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Copy.hpp -- Provides functions to copy the AST.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Jul 10 16:13:00 2019
+// Last Modified By : Andrew Beach
+// Last Modified On : Thr Jul 11 10:38:00 2019
+// Update Count     : 0
+//
+
+#include "Decl.hpp"
+#include "Expr.hpp"
+#include "Pass.hpp"
+#include "Stmt.hpp"
+#include "Type.hpp"
+
+namespace ast {
+
+template<typename node_t>
+node_t * shallowCopy( const node_t * node );
+/* Create a shallow copy of the node given.
+ *
+ * The new node has all the same primitive field values and points to the
+ * same children nodes as the parent.
+ */
+
+template<typename node_t>
+node_t * deepCopy( const node_t * localRoot );
+/* Create a deep copy of the tree rooted at localRoot.
+ *
+ * This creates a copy of every node in the sub-tree (reachable by strong
+ * reference from local_root) and updates any readonly pointers on those nodes
+ * that point to another node in the sub-tree to the new version of that node.
+ */
+
+class DeepCopyCore {
+	std::unordered_map< const Node *, const Node * > nodeCache;
+	std::unordered_set< readonly<Node> * > readonlyCache;
+
+public:
+	template<typename node_t>
+	const node_t * previsit( const node_t * node ) {
+		const node_t * copy = shallowCopy( node );
+		nodeCache.insert( std::make_pair( node, copy ) );
+		return copy;
+	}
+
+	void postvisit( const AggregateDecl * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->parent );
+	}
+
+	void postvisit( const StructInstType * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->base );
+	}
+
+	void postvisit( const UnionInstType * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->base );
+	}
+
+	void postvisit( const EnumInstType * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->base );
+	}
+
+	void postvisit( const TraitInstType * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->base );
+	}
+
+	void postvisit( const TypeInstType * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->base );
+	}
+
+	void postvisit( const ImplicitCtorDtorStmt * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->callStmt );
+	}
+
+	void postvisit( const MemberExpr * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->member );
+	}
+
+	void postvisit( const VariableExpr * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->var );
+	}
+
+	void postvisit( const OffsetofExpr * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->member );
+	}
+
+	void postvisit( const DeletedExpr * node ) {
+		readonlyCache.insert( (readonly<Node> *) & node->deleteStmt );
+	}
+
+	void readonlyUpdates() {
+		for ( readonly<Node> * ptr : readonlyCache ) {
+			auto it = nodeCache.find( ptr->get() );
+			if ( nodeCache.end() != it ) {
+				*ptr = it->second;
+			}
+		}
+	}
+};
+
+template<typename node_t>
+node_t * shallowCopy( const node_t * localRoot ) {
+	return localRoot->clone();
+}
+
+template<typename node_t>
+node_t * deepCopy( const node_t * localRoot ) {
+	Pass< DeepCopyCore > dc;
+	node_t const * newRoot = localRoot->accept( dc );
+	dc.pass.readonlyUpdates();
+	return const_cast< node_t * >( newRoot );
+}
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Decl.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -50,5 +50,7 @@
 
 const Type * FunctionDecl::get_type() const { return type.get(); }
-void FunctionDecl::set_type(Type * t) { type = strict_dynamic_cast< FunctionType* >( t ); }
+void FunctionDecl::set_type( const Type * t ) {
+	type = strict_dynamic_cast< const FunctionType * >( t );
+}
 
 // --- TypeDecl
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Decl.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -33,5 +33,7 @@
 
 // Must be included in *all* AST classes; should be #undef'd at the end of the file
-#define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
+#define MUTATE_FRIEND \
+    template<typename node_t> friend node_t * mutate(const node_t * node); \
+	template<typename node_t> friend node_t * shallowCopy(const node_t * node);
 
 namespace ast {
@@ -88,5 +90,5 @@
 	virtual const Type * get_type() const = 0;
 	/// Set type of this declaration. May be verified by subclass
-	virtual void set_type(Type *) = 0;
+	virtual void set_type( const Type * ) = 0;
 
 	const DeclWithType * accept( Visitor & v ) const override = 0;
@@ -111,5 +113,5 @@
 
 	const Type* get_type() const override { return type; }
-	void set_type( Type * ty ) override { type = ty; }
+	void set_type( const Type * ty ) override { type = ty; }
 
 	const DeclWithType * accept( Visitor& v ) const override { return v.visit( this ); }
@@ -133,5 +135,5 @@
 
 	const Type * get_type() const override;
-	void set_type(Type * t) override;
+	void set_type( const Type * t ) override;
 
 	bool has_body() const { return stmts; }
@@ -150,6 +152,7 @@
 	std::vector<ptr<DeclWithType>> assertions;
 
-	NamedTypeDecl( const CodeLocation& loc, const std::string& name, Storage::Classes storage,
-		Type* b, Linkage::Spec spec = Linkage::Cforall )
+	NamedTypeDecl(
+		const CodeLocation & loc, const std::string & name, Storage::Classes storage,
+		const Type * b, Linkage::Spec spec = Linkage::Cforall )
 	: Decl( loc, name, storage, spec ), base( b ), params(), assertions() {}
 
@@ -186,8 +189,9 @@
 	};
 
-	TypeDecl( const CodeLocation & loc, const std::string & name, Storage::Classes storage, Type * b,
-			  Kind k, bool s, Type * i = nullptr )
-		: NamedTypeDecl( loc, name, storage, b ), kind( k ), sized( k == Ttype || s ),
-		init( i ) {}
+	TypeDecl(
+		const CodeLocation & loc, const std::string & name, Storage::Classes storage,
+		const Type * b, TypeVar::Kind k, bool s, const Type * i = nullptr )
+	: NamedTypeDecl( loc, name, storage, b ), kind( k ), sized( k == TypeVar::Ttype || s ),
+	  init( i ) {}
 
 	const char * typeString() const override;
Index: src/AST/Eval.hpp
===================================================================
--- src/AST/Eval.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
+++ src/AST/Eval.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -0,0 +1,37 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Eval.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Fri Jun 28 14:00:00 2019
+// Last Modified By : Aaron B. Moss
+// Created On       : Fri Jun 28 14:00:00 2019
+// Update Count     : 1
+//
+
+#include <string>
+#include <utility>
+
+#include "Expr.hpp"
+
+namespace ast {
+
+/// Create a new UntypedExpr with the given arguments
+template< typename... Args >
+UntypedExpr * call( const CodeLocation & loc, const std::string & name, Args &&... args ) {
+	return new UntypedExpr { 
+		loc, new NameExpr { loc, name }, 
+		std::vector< ptr< Expr > > { std::forward< Args >( args )... } };
+}
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Expr.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -20,5 +20,8 @@
 #include <vector>
 
+#include "Copy.hpp"                // for shallowCopy
+#include "Eval.hpp"                // for call
 #include "GenericSubstitution.hpp"
+#include "LinkageSpec.hpp"
 #include "Stmt.hpp"
 #include "Type.hpp"
@@ -27,9 +30,18 @@
 #include "Common/SemanticError.h"
 #include "GenPoly/Lvalue.h"        // for referencesPermissable
-#include "InitTweak/InitTweak.h"   // for getPointerBase
+#include "InitTweak/InitTweak.h"   // for getFunction, getPointerBase
 #include "ResolvExpr/typeops.h"    // for extractResultType
 #include "Tuples/Tuples.h"         // for makeTupleType
 
 namespace ast {
+
+namespace {
+	std::set<std::string> const lvalueFunctionNames = {"*?", "?[?]"};
+}
+
+// --- Expr
+bool Expr::get_lvalue() const {
+	return false;
+}
 
 // --- ApplicationExpr
@@ -46,4 +58,11 @@
 }
 
+bool ApplicationExpr::get_lvalue() const {
+	if ( const DeclWithType * func = InitTweak::getFunction( this ) ) {
+		return func->linkage == Linkage::Intrinsic && lvalueFunctionNames.count( func->name );
+	}
+	return false;
+}
+
 // --- UntypedExpr
 
@@ -51,7 +70,5 @@
 	assert( arg );
 
-	UntypedExpr * ret = new UntypedExpr{
-		loc, new NameExpr{loc, "*?"}, std::vector<ptr<Expr>>{ ptr<Expr>{ arg } }
-	};
+	UntypedExpr * ret = call( loc, "*?", arg );
 	if ( const Type * ty = arg->result ) {
 		const Type * base = InitTweak::getPointerBase( ty );
@@ -65,8 +82,12 @@
 			// base type
 			ret->result = base;
-			add_qualifiers( ret->result, CV::Lvalue );
 		}
 	}
 	return ret;
+}
+
+bool UntypedExpr::get_lvalue() const {
+	std::string fname = InitTweak::getFunctionName( this );
+	return lvalueFunctionNames.count( fname );
 }
 
@@ -74,7 +95,5 @@
 	assert( lhs && rhs );
 
-	UntypedExpr * ret = new UntypedExpr{
-		loc, new NameExpr{loc, "?=?"}, std::vector<ptr<Expr>>{ ptr<Expr>{ lhs }, ptr<Expr>{ rhs } }
-	};
+	UntypedExpr * ret = call( loc, "?=?", lhs, rhs );
 	if ( lhs->result && rhs->result ) {
 		// if both expressions are typed, assumes that this assignment is a C bitwise assignment,
@@ -108,8 +127,7 @@
 AddressExpr::AddressExpr( const CodeLocation & loc, const Expr * a ) : Expr( loc ), arg( a ) {
 	if ( arg->result ) {
-		if ( arg->result->is_lvalue() ) {
+		if ( arg->get_lvalue() ) {
 			// lvalue, retains all levels of reference, and gains a pointer inside the references
 			Type * res = addrType( arg->result );
-			res->set_lvalue( false ); // result of & is never an lvalue
 			result = res;
 		} else {
@@ -118,5 +136,4 @@
 					dynamic_cast< const ReferenceType * >( arg->result.get() ) ) {
 				Type * res = addrType( refType->base );
-				res->set_lvalue( false ); // result of & is never an lvalue
 				result = res;
 			} else {
@@ -139,8 +156,19 @@
 : Expr( loc, new VoidType{} ), arg( a ), isGenerated( g ) {}
 
+bool CastExpr::get_lvalue() const {
+	// This is actually wrong by C, but it works with our current set-up.
+	return arg->get_lvalue();
+}
+
 // --- KeywordCastExpr
 
 const char * KeywordCastExpr::targetString() const {
 	return AggregateDecl::aggrString( target );
+}
+
+// --- UntypedMemberExpr
+
+bool UntypedMemberExpr::get_lvalue() const {
+	return aggregate->get_lvalue();
 }
 
@@ -153,10 +181,59 @@
 	assert( aggregate->result );
 
-	// take ownership of member type
-	result = mem->get_type();
+	// Deep copy on result type avoids mutation on transitively multiply referenced object.
+	//
+	// Example, adapted from parts of builtins and bootloader:
+	//
+	// forall(dtype T)
+	// struct __Destructor {
+	//   T * object;
+	//   void (*dtor)(T *);
+	// };
+	//
+	// forall(dtype S)
+	// void foo(__Destructor(S) &d) {
+	//   if (d.dtor) {  // here
+	//   }
+	// }
+	//
+	// Let e be the "d.dtor" guard espression, which is MemberExpr after resolve.  Let d be the
+	// declaration of member __Destructor.dtor (an ObjectDecl), as accessed via the top-level
+	// declaration of __Destructor.  Consider the types e.result and d.type.  In the old AST, one
+	// is a clone of the other.  Ordinary new-AST use would set them up as a multiply-referenced
+	// object.
+	//
+	// e.result: PointerType
+	// .base: FunctionType
+	// .params.front(): ObjectDecl, the anonymous parameter of type T*
+	// .type: PointerType
+	// .base: TypeInstType
+	// let x = that
+	// let y = similar, except start from d.type
+	//
+	// Consider two code lines down, genericSubstitution(...).apply(result).
+	//
+	// Applying this chosen-candidate's type substitution means modifying x, substituting
+	// S for T.  This mutation should affect x and not y.
+
+	result = deepCopy(mem->get_type());
+
 	// substitute aggregate generic parameters into member type
 	genericSubstitution( aggregate->result ).apply( result );
-	// ensure lvalue and appropriate restrictions from aggregate type
-	add_qualifiers( result, aggregate->result->qualifiers | CV::Lvalue );
+	// ensure appropriate restrictions from aggregate type
+	add_qualifiers( result, aggregate->result->qualifiers );
+}
+
+MemberExpr::MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg,
+    MemberExpr::NoOpConstruction overloadSelector )
+: Expr( loc ), member( mem ), aggregate( agg ) {
+	assert( member );
+	assert( aggregate );
+	assert( aggregate->result );
+	(void) overloadSelector;
+}
+
+bool MemberExpr::get_lvalue() const {
+	// This is actually wrong by C, but it works with our current set-up.
+	return true;
 }
 
@@ -170,6 +247,10 @@
 	assert( var );
 	assert( var->get_type() );
-	result = var->get_type();
-	add_qualifiers( result, CV::Lvalue );
+	result = shallowCopy( var->get_type() );
+}
+
+bool VariableExpr::get_lvalue() const {
+	// It isn't always an lvalue, but it is never an rvalue.
+	return true;
 }
 
@@ -258,4 +339,11 @@
 : Expr( loc, new BasicType{ BasicType::SignedInt } ), arg1( a1 ), arg2( a2 ), isAnd( ia ) {}
 
+// --- CommaExpr
+bool CommaExpr::get_lvalue() const {
+	// This is wrong by C, but the current implementation uses it.
+	// (ex: Specialize, Lvalue and Box)
+	return arg2->get_lvalue();
+}
+
 // --- ConstructorExpr
 
@@ -276,5 +364,8 @@
 	assert( t && i );
 	result = t;
-	add_qualifiers( result, CV::Lvalue );
+}
+
+bool CompoundLiteralExpr::get_lvalue() const {
+	return true;
 }
 
@@ -293,5 +384,8 @@
 	// like MemberExpr, TupleIndexExpr is always an lvalue
 	result = type->types[ index ];
-	add_qualifiers( result, CV::Lvalue );
+}
+
+bool TupleIndexExpr::get_lvalue() const {
+	return tuple->get_lvalue();
 }
 
Index: src/AST/Expr.hpp
===================================================================
--- src/AST/Expr.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Expr.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -31,5 +31,8 @@
 
 // Must be included in *all* AST classes; should be #undef'd at the end of the file
-#define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
+#define MUTATE_FRIEND \
+    template<typename node_t> friend node_t * mutate(const node_t * node); \
+	template<typename node_t> friend node_t * shallowCopy(const node_t * node);
+
 
 class ConverterOldToNew;
@@ -185,4 +188,5 @@
 
 	Expr * set_extension( bool ex ) { extension = ex; return this; }
+	virtual bool get_lvalue() const;
 
 	virtual const Expr * accept( Visitor & v ) const override = 0;
@@ -201,4 +205,6 @@
 	ApplicationExpr( const CodeLocation & loc, const Expr * f, std::vector<ptr<Expr>> && as = {} );
 
+	bool get_lvalue() const final;
+
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
 private:
@@ -215,4 +221,6 @@
 	UntypedExpr( const CodeLocation & loc, const Expr * f, std::vector<ptr<Expr>> && as = {} )
 	: Expr( loc ), func( f ), args( std::move(as) ) {}
+
+	bool get_lvalue() const final;
 
 	/// Creates a new dereference expression
@@ -291,4 +299,6 @@
 	CastExpr( const Expr * a ) : CastExpr( a->location, a, GeneratedCast ) {}
 
+	bool get_lvalue() const final;
+
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
 private:
@@ -338,4 +348,6 @@
 	: Expr( loc ), member( mem ), aggregate( agg ) { assert( aggregate ); }
 
+	bool get_lvalue() const final;
+
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
 private:
@@ -352,8 +364,17 @@
 	MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg );
 
+	bool get_lvalue() const final;
+
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
 private:
 	MemberExpr * clone() const override { return new MemberExpr{ *this }; }
 	MUTATE_FRIEND
+
+	// Custructor overload meant only for AST conversion
+	enum NoOpConstruction { NoOpConstructionChosen };
+	MemberExpr( const CodeLocation & loc, const DeclWithType * mem, const Expr * agg,
+	    NoOpConstruction overloadSelector );
+	friend class ::ConverterOldToNew;
+	friend class ::ConverterNewToOld;
 };
 
@@ -365,4 +386,6 @@
 	VariableExpr( const CodeLocation & loc );
 	VariableExpr( const CodeLocation & loc, const DeclWithType * v );
+
+	bool get_lvalue() const final;
 
 	/// generates a function pointer for a given function
@@ -532,5 +555,9 @@
 
 	CommaExpr( const CodeLocation & loc, const Expr * a1, const Expr * a2 )
-	: Expr( loc ), arg1( a1 ), arg2( a2 ) {}
+	: Expr( loc ), arg1( a1 ), arg2( a2 ) {
+		this->result = a2->result;
+	}
+
+	bool get_lvalue() const final;
 
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
@@ -605,4 +632,6 @@
 	CompoundLiteralExpr( const CodeLocation & loc, const Type * t, const Init * i );
 
+	bool get_lvalue() const final;
+
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
 private:
@@ -660,4 +689,6 @@
 
 	TupleIndexExpr( const CodeLocation & loc, const Expr * t, unsigned i );
+
+	bool get_lvalue() const final;
 
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
Index: src/AST/ForallSubstitutionTable.cpp
===================================================================
--- src/AST/ForallSubstitutionTable.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
+++ src/AST/ForallSubstitutionTable.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -0,0 +1,54 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ForallSubstitutionTable.cpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Thu Jun 27 14:00:00 2019
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Thu Jun 27 14:00:00 2019
+// Update Count     : 1
+//
+
+#include "ForallSubstitutionTable.hpp"
+
+#include <cassert>
+#include <vector>
+
+#include "Copy.hpp"                // for shallowCopy
+#include "Decl.hpp"
+#include "Node.hpp"
+#include "Type.hpp"
+#include "Visitor.hpp"
+
+namespace ast {
+
+std::vector< ptr< TypeDecl > > ForallSubstitutionTable::clone(
+	const std::vector< ptr< TypeDecl > > & forall, Visitor & v
+) {
+	std::vector< ptr< TypeDecl > > new_forall;
+	new_forall.reserve( forall.size() );
+
+	for ( const ast::TypeDecl * d : forall ) {
+		// create cloned type decl and insert into substitution map before further mutation
+		auto new_d = shallowCopy( d );
+		decls.insert( d, new_d );
+		// perform other mutations and add to output
+		auto newer_d = v.visit( new_d );
+		assert( new_d == newer_d && "Newly cloned TypeDecl must retain identity" );
+		new_forall.emplace_back( new_d );
+	}
+
+	return new_forall;
+}
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/ForallSubstitutionTable.hpp
===================================================================
--- src/AST/ForallSubstitutionTable.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
+++ src/AST/ForallSubstitutionTable.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -0,0 +1,57 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ForallSubstitutionTable.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Thu Jun 27 14:00:00 2019
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Thu Jun 27 14:00:00 2019
+// Update Count     : 1
+//
+
+#pragma once
+
+#include <vector>
+
+#include "Node.hpp"  // for ptr
+#include "Common/ScopedMap.h"
+
+namespace ast {
+
+class TypeDecl;
+class Visitor;
+
+/// Wrapper for TypeDecl substitution table
+class ForallSubstitutionTable {
+	ScopedMap< const TypeDecl *, const TypeDecl * > decls;
+
+public:
+	/// Replaces given declaration with value in the table, if present, otherwise returns argument
+	const TypeDecl * replace( const TypeDecl * d ) {
+		auto it = decls.find( d );
+		if ( it != decls.end() ) return it->second;
+		return d;
+	}
+
+	/// Builds a new forall list mutated according to the given visitor
+	std::vector< ptr< TypeDecl > > clone( 
+		const std::vector< ptr< TypeDecl > > & forall, Visitor & v );
+
+	/// Introduces a new lexical scope
+	void beginScope() { decls.beginScope(); }
+
+	/// Concludes a lexical scope
+	void endScope() { decls.endScope(); }
+};
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/ForallSubstitutor.hpp
===================================================================
--- src/AST/ForallSubstitutor.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
+++ src/AST/ForallSubstitutor.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -0,0 +1,58 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ForallSubstitutor.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Wed Jun 26 15:00:00 2019
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Wed Jun 26 15:00:00 2019
+// Update Count     : 1
+//
+
+#include "Pass.hpp"
+
+namespace ast {
+
+class Expr;
+
+/// Visitor that correctly substitutes TypeDecl while maintaining TypeInstType bindings.
+/// Also has some convenience methods to mutate fields.
+struct ForallSubstitutor : public WithForallSubstitutor, public WithVisitorRef<ForallSubstitutor> {
+	/// Substitute TypeInstType base type
+	readonly< TypeDecl > operator() ( const readonly< TypeDecl > & o ) {
+		return subs.replace( o );
+	}
+	
+	/// Make new forall-list clone
+	ParameterizedType::ForallList operator() ( const ParameterizedType::ForallList & o ) {
+		return subs.clone( o, *visitor );
+	}
+
+	/// Substitute parameter/return type
+	std::vector< ptr< DeclWithType > > operator() ( const std::vector< ptr< DeclWithType > > & o ) {
+		std::vector< ptr< DeclWithType > > n;
+		n.reserve( o.size() );
+		for ( const DeclWithType * d : o ) { n.emplace_back( d->accept( *visitor ) ); }
+		return n;
+	}
+
+	/// Substitute type parameter list
+	std::vector< ptr< Expr > > operator() ( const std::vector< ptr< Expr > > & o ) {
+		std::vector< ptr< Expr > > n;
+		n.reserve( o.size() );
+		for ( const Expr * d : o ) { n.emplace_back( d->accept( *visitor ) ); }
+		return n;
+	}
+};
+
+} // namespace ast
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/Init.hpp
===================================================================
--- src/AST/Init.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Init.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -25,5 +25,7 @@
 
 // Must be included in *all* AST classes; should be #undef'd at the end of the file
-#define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
+#define MUTATE_FRIEND \
+    template<typename node_t> friend node_t * mutate(const node_t * node); \
+	template<typename node_t> friend node_t * shallowCopy(const node_t * node);
 
 namespace ast {
Index: src/AST/Node.cpp
===================================================================
--- src/AST/Node.cpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Node.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -17,4 +17,5 @@
 #include "Fwd.hpp"
 
+#include <csignal>  // MEMORY DEBUG -- for raise
 #include <iostream>
 
@@ -29,12 +30,33 @@
 #include "Print.hpp"
 
-template< typename node_t, enum ast::Node::ref_type ref_t >
-void ast::ptr_base<node_t, ref_t>::_inc( const node_t * node ) { node->increment(ref_t); }
-
-template< typename node_t, enum ast::Node::ref_type ref_t >
-void ast::ptr_base<node_t, ref_t>::_dec( const node_t * node ) { node->decrement(ref_t); }
-
-template< typename node_t, enum ast::Node::ref_type ref_t >
-void ast::ptr_base<node_t, ref_t>::_check() const { if(node) assert(node->was_ever_strong == false || node->strong_count > 0); }
+/// MEMORY DEBUG -- allows breaking on ref-count changes of dynamically chosen object.
+/// Process to use in GDB:
+///   break ast::Node::_trap()
+///   run
+///   set variable MEM_TRAP_OBJ = <target>
+///   disable <first breakpoint>
+///   continue
+void * MEM_TRAP_OBJ = nullptr;
+
+void _trap( const void * node ) {
+	if ( node == MEM_TRAP_OBJ ) std::raise(SIGTRAP);
+}
+
+template< typename node_t, enum ast::Node::ref_type ref_t >
+void ast::ptr_base<node_t, ref_t>::_inc( const node_t * node ) {
+	node->increment(ref_t);
+	_trap( node );
+}
+
+template< typename node_t, enum ast::Node::ref_type ref_t >
+void ast::ptr_base<node_t, ref_t>::_dec( const node_t * node, bool do_delete ) {
+	_trap( node );
+	node->decrement(ref_t, do_delete );
+}
+
+template< typename node_t, enum ast::Node::ref_type ref_t >
+void ast::ptr_base<node_t, ref_t>::_check() const { 
+	// if(node) assert(node->was_ever_strong == false || node->strong_count > 0);
+}
 
 template< typename node_t, enum ast::Node::ref_type ref_t >
Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Node.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -38,5 +38,5 @@
 	Node& operator= (const Node&) = delete;
 	Node& operator= (Node&&) = delete;
-	virtual ~Node() = default;
+	virtual ~Node() {}
 
 	virtual const Node * accept( Visitor & v ) const = 0;
@@ -57,4 +57,6 @@
 	template<typename node_t>
 	friend node_t * mutate(const node_t * node);
+	template<typename node_t>
+	friend node_t * shallowCopy(const node_t * node);
 
 	mutable size_t strong_count = 0;
@@ -69,5 +71,5 @@
 	}
 
-	void decrement(ast::Node::ref_type ref) const {
+	void decrement(ast::Node::ref_type ref, bool do_delete = true) const {
 		switch (ref) {
 			case ref_type::strong: strong_count--; break;
@@ -75,5 +77,5 @@
 		}
 
-		if(!strong_count && !weak_count) {
+		if( do_delete && !strong_count && !weak_count) {
 			delete this;
 		}
@@ -94,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();
@@ -104,5 +106,5 @@
 	// skip mutate if equivalent
 	if ( node->*field == val ) return node;
-	
+
 	// mutate and return
 	node_t * ret = mutate( node );
@@ -123,4 +125,13 @@
 	(ret->*field)[i] = std::forward< field_t >( val );
 	return ret;
+}
+
+/// Mutate an entire indexed collection by cloning to accepted value
+template<typename node_t, typename parent_t, typename coll_t>
+const node_t * mutate_each( const node_t * node, coll_t parent_t::* field, Visitor & v ) {
+	for ( unsigned i = 0; i < (node->*field).size(); ++i ) {
+		node = mutate_field_index( node, field, i, (node->*field)[i]->accept( v ) );
+	}
+	return node;
 }
 
@@ -219,4 +230,13 @@
 	operator const node_t * () const { _check(); return node; }
 
+	const node_t * release() {
+		const node_t * ret = node;
+		if ( node ) {
+			_dec(node, false);
+			node = nullptr;
+		}
+		return ret;
+	}
+
 	/// wrapper for convenient access to dynamic_cast
 	template<typename o_node_t>
@@ -244,5 +264,5 @@
 
 	void _inc( const node_t * other );
-	void _dec( const node_t * other );
+	void _dec( const node_t * other, bool do_delete = true );
 	void _check() const;
 
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Pass.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -35,4 +35,6 @@
 #include "AST/SymbolTable.hpp"
 
+#include "AST/ForallSubstitutionTable.hpp"
+
 // Private prelude header, needed for some of the magic tricks this class pulls off
 #include "AST/Pass.proto.hpp"
@@ -46,20 +48,21 @@
 //
 // Several additional features are available through inheritance
-// | WithTypeSubstitution - provides polymorphic const TypeSubstitution * env for the
-//                          current expression
-// | WithStmtsToAdd       - provides the ability to insert statements before or after the current
-//                          statement by adding new statements into stmtsToAddBefore or
-//                          stmtsToAddAfter respectively.
-// | WithDeclsToAdd       - provides the ability to insert declarations before or after the current
-//                          declarations by adding new DeclStmt into declsToAddBefore or
-//                          declsToAddAfter respectively.
-// | WithShortCircuiting  - provides the ability to skip visiting child nodes; set visit_children
-//                          to false in pre{visit,visit} to skip visiting children
-// | WithGuards           - provides the ability to save/restore data like a LIFO stack; to save,
-//                          call GuardValue with the variable to save, the variable will
-//                          automatically be restored to its previous value after the corresponding
-//                          postvisit/postmutate teminates.
-// | WithVisitorRef       - provides an pointer to the templated visitor wrapper
-// | WithSymbolTable      - provides symbol table functionality
+// | WithTypeSubstitution  - provides polymorphic const TypeSubstitution * env for the
+//                           current expression
+// | WithStmtsToAdd        - provides the ability to insert statements before or after the current
+//                           statement by adding new statements into stmtsToAddBefore or
+//                           stmtsToAddAfter respectively.
+// | WithDeclsToAdd        - provides the ability to insert declarations before or after the
+//                           current declarations by adding new DeclStmt into declsToAddBefore or
+//                           declsToAddAfter respectively.
+// | WithShortCircuiting   - provides the ability to skip visiting child nodes; set visit_children
+//                           to false in pre{visit,visit} to skip visiting children
+// | WithGuards            - provides the ability to save/restore data like a LIFO stack; to save,
+//                           call GuardValue with the variable to save, the variable will
+//                           automatically be restored to its previous value after the
+//                           corresponding postvisit/postmutate teminates.
+// | WithVisitorRef        - provides an pointer to the templated visitor wrapper
+// | WithSymbolTable       - provides symbol table functionality
+// | WithForallSubstitutor - maintains links between TypeInstType and TypeDecl under mutation
 //-------------------------------------------------------------------------------------------------
 template< typename pass_t >
@@ -202,4 +205,8 @@
 	container_t< ptr<node_t> > call_accept( const container_t< ptr<node_t> > & container );
 
+	/// Mutate forall-list, accounting for presence of type substitution map
+	template<typename node_t>
+	void mutate_forall( const node_t *& );
+
 public:
 	/// Logic to call the accept and mutate the parent if needed, delegates call to accept
@@ -210,6 +217,6 @@
 	/// Internal RAII guard for symbol table features
 	struct guard_symtab {
-		guard_symtab( Pass<pass_t> & pass ): pass( pass ) { __pass::symtab::enter(pass, 0); }
-		~guard_symtab()                                   { __pass::symtab::leave(pass, 0); }
+		guard_symtab( Pass<pass_t> & pass ): pass( pass ) { __pass::symtab::enter(pass.pass, 0); }
+		~guard_symtab()                                   { __pass::symtab::leave(pass.pass, 0); }
 		Pass<pass_t> & pass;
 	};
@@ -217,7 +224,16 @@
 	/// Internal RAII guard for scope features
 	struct guard_scope {
-		guard_scope( Pass<pass_t> & pass ): pass( pass ) { __pass::scope::enter(pass, 0); }
-		~guard_scope()                                   { __pass::scope::leave(pass, 0); }
+		guard_scope( Pass<pass_t> & pass ): pass( pass ) { __pass::scope::enter(pass.pass, 0); }
+		~guard_scope()                                   { __pass::scope::leave(pass.pass, 0); }
 		Pass<pass_t> & pass;
+	};
+
+	/// Internal RAII guard for forall substitutions
+	struct guard_forall_subs {
+		guard_forall_subs( Pass<pass_t> & pass, const ParameterizedType * type )
+		: pass( pass ), type( type ) { __pass::forall::enter(pass.pass, 0, type ); }
+		~guard_forall_subs()         { __pass::forall::leave(pass.pass, 0, type ); }
+		Pass<pass_t> & pass;
+		const ParameterizedType * type;
 	};
 
@@ -314,4 +330,10 @@
 	SymbolTable symtab;
 };
+
+/// Use when the templated visitor needs to keep TypeInstType instances properly linked to TypeDecl
+struct WithForallSubstitutor {
+	ForallSubstitutionTable subs;
+};
+
 }
 
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Pass.impl.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -127,8 +127,7 @@
 			, decltype( node->accept(*this) )
 		>::type
-
 	{
 		__pedantic_pass_assert( __visit_children() );
-		__pedantic_pass_assert( expr );
+		__pedantic_pass_assert( node );
 
 		static_assert( !std::is_base_of<ast::Expr, node_t>::value, "ERROR");
@@ -323,4 +322,20 @@
 	}
 
+
+	template< typename pass_t >
+	template< typename node_t >
+	void ast::Pass< pass_t >::mutate_forall( const node_t *& node ) {
+		if ( auto subs = __pass::forall::subs( pass, 0 ) ) {
+			// tracking TypeDecl substitution, full clone
+			if ( node->forall.empty() ) return;
+
+			node_t * mut = mutate( node );
+			mut->forall = subs->clone( node->forall, *this );
+			node = mut;
+		} else {
+			// not tracking TypeDecl substitution, just mutate
+			maybe_accept( node, &node_t::forall );
+		}
+	}
 }
 
@@ -429,12 +444,12 @@
 			guard_symtab guard { *this };
 			// implicit add __func__ identifier as specified in the C manual 6.4.2.2
-			static ast::ObjectDecl func(
-				node->location, "__func__",
-				new ast::ArrayType(
-					new ast::BasicType( ast::BasicType::Char, ast::CV::Qualifiers( ast::CV::Const ) ),
+			static ast::ptr< ast::ObjectDecl > func{ new ast::ObjectDecl{ 
+				CodeLocation{}, "__func__",
+				new ast::ArrayType{
+					new ast::BasicType{ ast::BasicType::Char, ast::CV::Const },
 					nullptr, VariableLen, DynamicDim
-				)
-			);
-			__pass::symtab::addId( pass, 0, &func );
+				}
+			} };
+			__pass::symtab::addId( pass, 0, func );
 			VISIT(
 				maybe_accept( node, &FunctionDecl::type );
@@ -610,8 +625,8 @@
 	VISIT({
 		// do not enter a new scope if inFunction is true - needs to check old state before the assignment
-		auto guard1 = makeFuncGuard( [this, inFunction = this->inFunction]() {
-			if ( ! inFunction ) __pass::symtab::enter(pass, 0);
-		}, [this, inFunction = this->inFunction]() {
-			if ( ! inFunction ) __pass::symtab::leave(pass, 0);
+		auto guard1 = makeFuncGuard( [this, inFunctionCpy = this->inFunction]() {
+			if ( ! inFunctionCpy ) __pass::symtab::enter(pass, 0);
+		}, [this, inFunctionCpy = this->inFunction]() {
+			if ( ! inFunctionCpy ) __pass::symtab::leave(pass, 0);
 		});
 		ValueGuard< bool > guard2( inFunction );
@@ -951,7 +966,7 @@
 	// For now this isn't visited, it is unclear if this causes problem
 	// if all tests are known to pass, remove this code
-	// VISIT(
-	// 	maybe_accept( node, &ImplicitCtorDtorStmt::callStmt );
-	// )
+	VISIT(
+		maybe_accept( node, &ImplicitCtorDtorStmt::callStmt );
+	)
 
 	VISIT_END( Stmt, node );
@@ -1679,9 +1694,10 @@
 	VISIT_START( node );
 
-	VISIT(
-		maybe_accept( node, &FunctionType::forall  );
+	VISIT({
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &FunctionType::returns );
 		maybe_accept( node, &FunctionType::params  );
-	)
+	})
 
 	VISIT_END( Type, node );
@@ -1698,5 +1714,6 @@
 	VISIT({
 		guard_symtab guard { *this };
-		maybe_accept( node, &StructInstType::forall );
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &StructInstType::params );
 	})
@@ -1711,11 +1728,12 @@
 	VISIT_START( node );
 
-	__pass::symtab::addStruct( pass, 0, node->name );
-
-	{
+	__pass::symtab::addUnion( pass, 0, node->name );
+
+	VISIT({
 		guard_symtab guard { *this };
-		maybe_accept( node, &UnionInstType::forall );
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &UnionInstType::params );
-	}
+	})
 
 	VISIT_END( Type, node );
@@ -1728,8 +1746,9 @@
 	VISIT_START( node );
 
-	VISIT(
-		maybe_accept( node, &EnumInstType::forall );
+	VISIT({
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &EnumInstType::params );
-	)
+	})
 
 	VISIT_END( Type, node );
@@ -1742,8 +1761,9 @@
 	VISIT_START( node );
 
-	VISIT(
-		maybe_accept( node, &TraitInstType::forall );
+	VISIT({
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &TraitInstType::params );
-	)
+	})
 
 	VISIT_END( Type, node );
@@ -1757,6 +1777,11 @@
 
 	VISIT(
-		maybe_accept( node, &TypeInstType::forall );
-		maybe_accept( node, &TypeInstType::params );
+		{
+			guard_forall_subs forall_guard { *this, node };
+			mutate_forall( node );
+			maybe_accept( node, &TypeInstType::params );
+		}
+		// ensure that base re-bound if doing substitution
+		__pass::forall::replace( pass, 0, node );
 	)
 
@@ -1907,5 +1932,5 @@
 				guard_symtab guard { *this };
 				auto new_node = p.second->accept( *this );
-				if (new_node != p.second) mutated = false;
+				if (new_node != p.second) mutated = true;
 				new_map.insert({ p.first, new_node });
 			}
@@ -1923,5 +1948,5 @@
 				guard_symtab guard { *this };
 				auto new_node = p.second->accept( *this );
-				if (new_node != p.second) mutated = false;
+				if (new_node != p.second) mutated = true;
 				new_map.insert({ p.first, new_node });
 			}
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Pass.proto.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -263,12 +263,12 @@
 		template<typename pass_t>
 		static inline void leave( pass_t &, long ) {}
-	};
-
-	// Finally certain pass desire an up to date symbol table automatically
+	} // namespace scope
+
+	// Certain passes desire an up to date symbol table automatically
 	// detect the presence of a member name `symtab` and call all the members appropriately
 	namespace symtab {
 		// Some simple scoping rules
 		template<typename pass_t>
-		static inline auto enter( pass_t & pass, int ) -> decltype( pass.symtab.enterScope(), void() ) {
+		static inline auto enter( pass_t & pass, int ) -> decltype( pass.symtab, void() ) {
 			pass.symtab.enterScope();
 		}
@@ -278,5 +278,5 @@
 
 		template<typename pass_t>
-		static inline auto leave( pass_t & pass, int ) -> decltype( pass.symtab.leaveScope(), void() ) {
+		static inline auto leave( pass_t & pass, int ) -> decltype( pass.symtab, void() ) {
 			pass.symtab.leaveScope();
 		}
@@ -311,5 +311,5 @@
 		SYMTAB_FUNC1( addUnion  , const UnionDecl *     );
 		SYMTAB_FUNC1( addTrait  , const TraitDecl *     );
-		SYMTAB_FUNC2( addWith   , const std::vector< ptr<Expr> > &, const Node * );
+		SYMTAB_FUNC2( addWith   , const std::vector< ptr<Expr> > &, const Decl * );
 
 		// A few extra functions have more complicated behaviour, they are hand written
@@ -356,5 +356,49 @@
 		#undef SYMTAB_FUNC1
 		#undef SYMTAB_FUNC2
-	};
-};
-};
+	} // namespace symtab
+
+	// Some passes need to mutate TypeDecl and properly update their pointing TypeInstType.
+	// Detect the presence of a member name `subs` and call all members appropriately
+	namespace forall {
+		// Some simple scoping rules
+		template<typename pass_t>
+		static inline auto enter( pass_t & pass, int, const ast::ParameterizedType * type )
+		-> decltype( pass.subs, void() ) {
+			if ( ! type->forall.empty() ) pass.subs.beginScope();
+		}
+
+		template<typename pass_t>
+		static inline auto enter( pass_t &, long, const ast::ParameterizedType * ) {}
+
+		template<typename pass_t>
+		static inline auto leave( pass_t & pass, int, const ast::ParameterizedType * type )
+		-> decltype( pass.subs, void() ) {
+			if ( ! type->forall.empty() ) { pass.subs.endScope(); }
+		}
+
+		template<typename pass_t>
+		static inline auto leave( pass_t &, long, const ast::ParameterizedType * ) {}
+
+		// Get the substitution table, if present
+		template<typename pass_t>
+		static inline auto subs( pass_t & pass, int ) -> decltype( &pass.subs ) {
+			return &pass.subs;
+		}
+
+		template<typename pass_t>
+		static inline ast::ForallSubstitutionTable * subs( pass_t &, long ) { return nullptr; }
+
+		// Replaces a TypeInstType's base TypeDecl according to the table
+		template<typename pass_t>
+		static inline auto replace( pass_t & pass, int, const ast::TypeInstType *& inst )
+		-> decltype( pass.subs, void() ) {
+			inst = ast::mutate_field(
+				inst, &ast::TypeInstType::base, pass.subs.replace( inst->base ) );
+		}
+
+		template<typename pass_t>
+		static inline auto replace( pass_t &, long, const ast::TypeInstType *& ) {}
+
+	} // namespace forall
+} // namespace __pass
+} // namespace ast
Index: src/AST/Stmt.hpp
===================================================================
--- src/AST/Stmt.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Stmt.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -27,5 +27,7 @@
 
 // Must be included in *all* AST classes; should be #undef'd at the end of the file
-#define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
+#define MUTATE_FRIEND \
+    template<typename node_t> friend node_t * mutate(const node_t * node); \
+	template<typename node_t> friend node_t * shallowCopy(const node_t * node);
 
 namespace ast {
@@ -412,5 +414,5 @@
 class ImplicitCtorDtorStmt final : public Stmt {
 public:
-	readonly<Stmt> callStmt;
+	ptr<Stmt> callStmt;
 
 	ImplicitCtorDtorStmt( const CodeLocation & loc, const Stmt * callStmt,
Index: src/AST/Type.cpp
===================================================================
--- src/AST/Type.cpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Type.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -21,5 +21,7 @@
 
 #include "Decl.hpp"
+#include "ForallSubstitutor.hpp" // for substituteForall
 #include "Init.hpp"
+#include "Common/utility.h"      // for copy, move
 #include "InitTweak/InitTweak.h" // for getPointerBase
 #include "Tuples/Tuples.h"       // for isTtype
@@ -90,5 +92,22 @@
 // GENERATED END
 
+// --- ParameterizedType
+
+void ParameterizedType::initWithSub( 
+	const ParameterizedType & o, Pass< ForallSubstitutor > & sub 
+) {
+	forall = sub.pass( o.forall );
+}
+
 // --- FunctionType
+
+FunctionType::FunctionType( const FunctionType & o )
+: ParameterizedType( o.qualifiers, copy( o.attributes ) ), returns(), params(), 
+  isVarArgs( o.isVarArgs ) {
+	Pass< ForallSubstitutor > sub;
+	initWithSub( o, sub );           // initialize substitution map
+	returns = sub.pass( o.returns ); // apply to return and parameter types
+	params = sub.pass( o.params );
+}
 
 namespace {
@@ -106,4 +125,17 @@
 
 // --- ReferenceToType
+
+void ReferenceToType::initWithSub( const ReferenceToType & o, Pass< ForallSubstitutor > & sub ) {
+	ParameterizedType::initWithSub( o, sub ); // initialize substitution
+	params = sub.pass( o.params );            // apply to parameters
+}
+
+ReferenceToType::ReferenceToType( const ReferenceToType & o )
+: ParameterizedType( o.qualifiers, copy( o.attributes ) ), params(), name( o.name ), 
+  hoistType( o.hoistType ) {
+	Pass< ForallSubstitutor > sub;
+	initWithSub( o, sub );
+}
+
 std::vector<readonly<Decl>> ReferenceToType::lookup( const std::string& name ) const {
 	assertf( aggr(), "Must have aggregate to perform lookup" );
@@ -118,7 +150,7 @@
 // --- StructInstType
 
-StructInstType::StructInstType( const StructDecl * b, CV::Qualifiers q,
-	std::vector<ptr<Attribute>>&& as )
-: ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
+StructInstType::StructInstType( 
+	const StructDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
+: ReferenceToType( b->name, q, move(as) ), base( b ) {}
 
 bool StructInstType::isComplete() const { return base ? base->body : false; }
@@ -126,7 +158,7 @@
 // --- UnionInstType
 
-UnionInstType::UnionInstType( const UnionDecl * b, CV::Qualifiers q,
-	std::vector<ptr<Attribute>>&& as )
-: ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
+UnionInstType::UnionInstType( 
+	const UnionDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
+: ReferenceToType( b->name, q, move(as) ), base( b ) {}
 
 bool UnionInstType::isComplete() const { return base ? base->body : false; }
@@ -134,7 +166,7 @@
 // --- EnumInstType
 
-EnumInstType::EnumInstType( const EnumDecl * b, CV::Qualifiers q,
-	std::vector<ptr<Attribute>>&& as )
-: ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
+EnumInstType::EnumInstType( 
+	const EnumDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
+: ReferenceToType( b->name, q, move(as) ), base( b ) {}
 
 bool EnumInstType::isComplete() const { return base ? base->body : false; }
@@ -142,9 +174,16 @@
 // --- TraitInstType
 
-TraitInstType::TraitInstType( const TraitDecl * b, CV::Qualifiers q,
-	std::vector<ptr<Attribute>>&& as )
-: ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
+TraitInstType::TraitInstType( 
+	const TraitDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
+: ReferenceToType( b->name, q, move(as) ), base( b ) {}
 
 // --- TypeInstType
+
+TypeInstType::TypeInstType( const TypeInstType & o ) 
+: ReferenceToType( o.name, o.qualifiers, copy( o.attributes ) ), base(), kind( o.kind ) {
+	Pass< ForallSubstitutor > sub;
+	initWithSub( o, sub );      // initialize substitution
+	base = sub.pass( o.base );  // apply to base type
+}
 
 void TypeInstType::set_base( const TypeDecl * b ) {
@@ -158,5 +197,5 @@
 
 TupleType::TupleType( std::vector<ptr<Type>> && ts, CV::Qualifiers q )
-: Type( q ), types( std::move(ts) ), members() {
+: Type( q ), types( move(ts) ), members() {
 	// This constructor is awkward. `TupleType` needs to contain objects so that members can be
 	// named, but members without initializer nodes end up getting constructors, which breaks
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/Type.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -29,7 +29,13 @@
 
 // Must be included in *all* AST classes; should be #undef'd at the end of the file
-#define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
+#define MUTATE_FRIEND \
+    template<typename node_t> friend node_t * mutate(const node_t * node); \
+	template<typename node_t> friend node_t * shallowCopy(const node_t * node);
 
 namespace ast {
+
+template< typename T > class Pass;
+
+struct ForallSubstitutor;
 
 class Type : public Node {
@@ -44,5 +50,4 @@
 	bool is_volatile() const { return qualifiers.is_volatile; }
 	bool is_restrict() const { return qualifiers.is_restrict; }
-	bool is_lvalue() const { return qualifiers.is_lvalue; }
 	bool is_mutex() const { return qualifiers.is_mutex; }
 	bool is_atomic() const { return qualifiers.is_atomic; }
@@ -51,5 +56,4 @@
 	Type * set_volatile( bool v ) { qualifiers.is_volatile = v; return this; }
 	Type * set_restrict( bool v ) { qualifiers.is_restrict = v; return this; }
-	Type * set_lvalue( bool v ) { qualifiers.is_lvalue = v; return this; }
 	Type * set_mutex( bool v ) { qualifiers.is_mutex = v; return this; }
 	Type * set_atomic( bool v ) { qualifiers.is_atomic = v; return this; }
@@ -163,5 +167,5 @@
 	static const char *typeNames[];
 
-	BasicType( Kind k, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} ) 
+	BasicType( Kind k, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: Type(q, std::move(as)), kind(k) {}
 
@@ -265,4 +269,7 @@
 /// Base type for potentially forall-qualified types
 class ParameterizedType : public Type {
+protected:
+	/// initializes forall with substitutor
+	void initWithSub( const ParameterizedType & o, Pass< ForallSubstitutor > & sub );
 public:
 	using ForallList = std::vector<ptr<TypeDecl>>;
@@ -276,4 +283,11 @@
 	ParameterizedType( CV::Qualifiers q, std::vector<ptr<Attribute>> && as = {} )
 	: Type(q, std::move(as)), forall() {}
+
+	// enforce use of ForallSubstitutor to copy parameterized type
+	ParameterizedType( const ParameterizedType & ) = delete;
+
+	ParameterizedType( ParameterizedType && ) = default;
+
+	// no need to change destructor, and operator= deleted in Node
 
 private:
@@ -301,4 +315,6 @@
 	: ParameterizedType(q), returns(), params(), isVarArgs(va) {}
 
+	FunctionType( const FunctionType & o );
+
 	/// true if either the parameters or return values contain a tttype
 	bool isTtype() const;
@@ -314,4 +330,7 @@
 /// base class for types that refer to types declared elsewhere (aggregates and typedefs)
 class ReferenceToType : public ParameterizedType {
+protected:
+	/// Initializes forall and parameters based on substitutor
+	void initWithSub( const ReferenceToType & o, Pass< ForallSubstitutor > & sub );
 public:
 	std::vector<ptr<Expr>> params;
@@ -319,7 +338,9 @@
 	bool hoistType = false;
 
-	ReferenceToType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	ReferenceToType(
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ParameterizedType(q, std::move(as)), params(), name(n) {}
+
+	ReferenceToType( const ReferenceToType & o );
 
 	/// Gets aggregate declaration this type refers to
@@ -338,9 +359,10 @@
 	readonly<StructDecl> base;
 
-	StructInstType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	StructInstType(
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base() {}
-	StructInstType( const StructDecl * b, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} );
+
+	StructInstType(
+		const StructDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
 
 	bool isComplete() const override;
@@ -359,9 +381,10 @@
 	readonly<UnionDecl> base;
 
-	UnionInstType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	UnionInstType(
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base() {}
-	UnionInstType( const UnionDecl * b, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} );
+
+	UnionInstType(
+		const UnionDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
 
 	bool isComplete() const override;
@@ -380,9 +403,10 @@
 	readonly<EnumDecl> base;
 
-	EnumInstType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	EnumInstType(
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base() {}
-	EnumInstType( const EnumDecl * b, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} );
+
+	EnumInstType(
+		const EnumDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
 
 	bool isComplete() const override;
@@ -401,9 +425,10 @@
 	readonly<TraitDecl> base;
 
-	TraitInstType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	TraitInstType(
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base() {}
-	TraitInstType( const TraitDecl * b, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} );
+
+	TraitInstType(
+		const TraitDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
 
 	// not meaningful for TraitInstType
@@ -424,5 +449,6 @@
 	TypeDecl::Kind kind;
 
-	TypeInstType( const std::string& n, const TypeDecl * b, CV::Qualifiers q = {},
+	TypeInstType(
+		const std::string& n, const TypeDecl * b, CV::Qualifiers q = {},
 		std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base( b ), kind( b->kind ) {}
@@ -431,4 +457,6 @@
 	: ReferenceToType( n, q, std::move(as) ), base(), kind( k ) {}
 
+	TypeInstType( const TypeInstType & o );
+
 	/// sets `base`, updating `kind` correctly
 	void set_base( const TypeDecl * );
Index: src/AST/TypeEnvironment.hpp
===================================================================
--- src/AST/TypeEnvironment.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/TypeEnvironment.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -37,20 +37,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 {
@@ -86,5 +86,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 {
@@ -95,7 +95,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 ) {}
 
@@ -142,16 +142,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) );
 	}
 
@@ -172,16 +172,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 );
 
@@ -198,10 +198,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 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/TypeSubstitution.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -92,5 +92,5 @@
 namespace {
 	struct EnvTrimmer {
-		ptr<TypeSubstitution> env;
+		const TypeSubstitution * env;
 		TypeSubstitution * newEnv;
 		EnvTrimmer( const TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){}
@@ -108,11 +108,6 @@
 	if ( env ) {
 		TypeSubstitution * newEnv = new TypeSubstitution();
-#if TIME_TO_CONVERT_PASSES
 		Pass<EnvTrimmer> trimmer( env, newEnv );
 		expr->accept( trimmer );
-#else
-		(void)expr;
-		(void)env;
-#endif
 		return newEnv;
 	}
@@ -121,19 +116,15 @@
 
 void TypeSubstitution::normalize() {
-#if TIME_TO_CONVERT_PASSES
-	PassVisitor<Substituter> sub( *this, true );
+	Pass<Substituter> sub( *this, true );
 	do {
 		sub.pass.subCount = 0;
 		sub.pass.freeOnly = true;
 		for ( TypeEnvType::iterator i = typeEnv.begin(); i != typeEnv.end(); ++i ) {
-			i->second = i->second->acceptMutator( sub );
+			i->second = i->second->accept( sub );
 		}
 	} while ( sub.pass.subCount );
-#endif
-}
-
-#if TIME_TO_CONVERT_PASSES
-
-Type * TypeSubstitution::Substituter::postmutate( TypeInstType *inst ) {
+}
+
+const Type * TypeSubstitution::Substituter::postvisit( const TypeInstType *inst ) {
 	BoundVarsType::const_iterator bound = boundVars.find( inst->name );
 	if ( bound != boundVars.end() ) return inst;
@@ -146,5 +137,5 @@
 		// Note: this does not prevent cycles in the general case, so it may be necessary to do something more sophisticated here.
 		// TODO: investigate preventing type variables from being bound to themselves in the first place.
-		if ( TypeInstType * replacement = i->second.as<TypeInstType>() ) {
+		if ( const TypeInstType * replacement = i->second.as<TypeInstType>() ) {
 			if ( inst->name == replacement->name ) {
 				return inst;
@@ -153,13 +144,14 @@
 		// std::cerr << "found " << inst->name << ", replacing with " << i->second << std::endl;
 		subCount++;
-		Type * newtype = i->second->clone();
-		newtype->get_qualifiers() |= inst->get_qualifiers();
-		delete inst;
-		// 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.
-		return newtype->acceptMutator( *visitor );
-	} // if
-}
-
-Expression * TypeSubstitution::Substituter::postmutate( NameExpr * nameExpr ) {
+		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
+		// substitute bound vars, but bound vars must be substituted when not in freeOnly mode.
+		newType = newType->accept( *visitor );
+		return newType.release();
+	} // if
+}
+
+const Expr * TypeSubstitution::Substituter::postvisit( const NameExpr * nameExpr ) {
 	VarEnvType::const_iterator i = sub.varEnv.find( nameExpr->name );
 	if ( i == sub.varEnv.end() ) {
@@ -167,46 +159,43 @@
 	} else {
 		subCount++;
-		delete nameExpr;
-		return i->second->clone();
-	} // if
-}
-
-void TypeSubstitution::Substituter::premutate( Type * type ) {
+		return i->second;
+	} // if
+}
+
+void TypeSubstitution::Substituter::previsit( const ParameterizedType * ptype ) {
 	GuardValue( boundVars );
 	// bind type variables from forall-qualifiers
 	if ( freeOnly ) {
-		for ( Type::ForallList::const_iterator tyvar = type->forall.begin(); tyvar != type->forall.end(); ++tyvar ) {
-			boundVars.insert( (*tyvar)->name );
+		for ( const TypeDecl * tyvar : ptype->forall ) {
+				boundVars.insert( tyvar->name );
 		} // for
 	} // if
 }
 
-template< typename TypeClass >
-void TypeSubstitution::Substituter::handleAggregateType( TypeClass * type ) {
+void TypeSubstitution::Substituter::handleAggregateType( const ReferenceToType * type ) {
 	GuardValue( boundVars );
 	// bind type variables from forall-qualifiers
 	if ( freeOnly ) {
-		for ( Type::ForallList::const_iterator tyvar = type->forall.begin(); tyvar != type->forall.end(); ++tyvar ) {
-			boundVars.insert( (*tyvar)->name );
+		for ( const TypeDecl * tyvar : type->forall ) {
+			boundVars.insert( tyvar->name );
 		} // for
 		// bind type variables from generic type instantiations
-		std::list< TypeDecl* > *baseParameters = type->get_baseParameters();
-		if ( baseParameters && ! type->parameters.empty() ) {
-			for ( std::list< TypeDecl* >::const_iterator tyvar = baseParameters->begin(); tyvar != baseParameters->end(); ++tyvar ) {
-				boundVars.insert( (*tyvar)->name );
-			} // for
-		} // if
-	} // if
-}
-
-void TypeSubstitution::Substituter::premutate( StructInstType * aggregateUseType ) {
+		if ( auto decl = type->aggr() ) {
+			if ( ! type->params.empty() ) {
+				for ( const TypeDecl * tyvar : decl->params ) {
+					boundVars.insert( tyvar->name );
+				} // for
+			} // if
+		}
+	} // if
+}
+
+void TypeSubstitution::Substituter::previsit( const StructInstType * aggregateUseType ) {
 	handleAggregateType( aggregateUseType );
 }
 
-void TypeSubstitution::Substituter::premutate( UnionInstType *aggregateUseType ) {
+void TypeSubstitution::Substituter::previsit( const UnionInstType *aggregateUseType ) {
 	handleAggregateType( aggregateUseType );
 }
-
-#endif
 
 } // namespace ast
Index: src/AST/TypeSubstitution.hpp
===================================================================
--- src/AST/TypeSubstitution.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/TypeSubstitution.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -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;
 	}
 
@@ -155,18 +161,14 @@
 		Substituter( const TypeSubstitution & sub, bool freeOnly ) : sub( sub ), freeOnly( freeOnly ) {}
 
-#if TIME_TO_CONVERT_PASSES
-
-		Type * postmutate( TypeInstType * aggregateUseType );
-		Expression * postmutate( NameExpr * nameExpr );
+		const Type * postvisit( const TypeInstType * aggregateUseType );
+		const Expr * postvisit( const NameExpr * nameExpr );
 
 		/// Records type variable bindings from forall-statements
-		void premutate( Type * type );
+		void previsit( const ParameterizedType * type );
 		/// Records type variable bindings from forall-statements and instantiations of generic types
-		template< typename TypeClass > void handleAggregateType( TypeClass * type );
-
-		void premutate( StructInstType * aggregateUseType );
-		void premutate( UnionInstType * aggregateUseType );
-
-#endif
+		void handleAggregateType( const ReferenceToType * type );
+
+		void previsit( const StructInstType * aggregateUseType );
+		void previsit( const UnionInstType * aggregateUseType );
 
 		const TypeSubstitution & sub;
@@ -179,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/AST/module.mk
===================================================================
--- src/AST/module.mk	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/AST/module.mk	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -22,4 +22,5 @@
 	AST/DeclReplacer.cpp \
 	AST/Expr.cpp \
+	AST/ForallSubstitutionTable.cpp \
 	AST/GenericSubstitution.cpp \
 	AST/Init.cpp \
Index: src/Common/ScopedMap.h
===================================================================
--- src/Common/ScopedMap.h	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/Common/ScopedMap.h	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -249,4 +249,6 @@
 
 	/// Gets the note at the given scope
+	Note& getNote() { return scopes.back().note; }
+	const Note& getNote() const { return scopes.back().note; }
 	Note& getNote( size_type i ) { return scopes[i].note; }
 	const Note& getNote( size_type i ) const { return scopes[i].note; }
Index: src/Makefile.in
===================================================================
--- src/Makefile.in	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/Makefile.in	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -169,4 +169,5 @@
 	AST/Convert.$(OBJEXT) AST/Decl.$(OBJEXT) \
 	AST/DeclReplacer.$(OBJEXT) AST/Expr.$(OBJEXT) \
+	AST/ForallSubstitutionTable.$(OBJEXT) \
 	AST/GenericSubstitution.$(OBJEXT) AST/Init.$(OBJEXT) \
 	AST/LinkageSpec.$(OBJEXT) AST/Node.$(OBJEXT) \
@@ -589,4 +590,5 @@
 	AST/DeclReplacer.cpp \
 	AST/Expr.cpp \
+	AST/ForallSubstitutionTable.cpp \
 	AST/GenericSubstitution.cpp \
 	AST/Init.cpp \
@@ -765,4 +767,6 @@
 	AST/$(DEPDIR)/$(am__dirstamp)
 AST/Expr.$(OBJEXT): AST/$(am__dirstamp) AST/$(DEPDIR)/$(am__dirstamp)
+AST/ForallSubstitutionTable.$(OBJEXT): AST/$(am__dirstamp) \
+	AST/$(DEPDIR)/$(am__dirstamp)
 AST/GenericSubstitution.$(OBJEXT): AST/$(am__dirstamp) \
 	AST/$(DEPDIR)/$(am__dirstamp)
@@ -1218,4 +1222,5 @@
 @AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/DeclReplacer.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/Expr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/ForallSubstitutionTable.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/GenericSubstitution.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/Init.Po@am__quote@
Index: src/ResolvExpr/AdjustExprType.cc
===================================================================
--- src/ResolvExpr/AdjustExprType.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/AdjustExprType.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -100,35 +100,36 @@
 
 namespace {
-	struct AdjustExprType_new final : public ast::WithShortCircuiting {
+	class AdjustExprType_new final : public ast::WithShortCircuiting {
+		const ast::SymbolTable & symtab;
+	public:
 		const ast::TypeEnvironment & tenv;
-		const ast::SymbolTable & symtab;
 
 		AdjustExprType_new( const ast::TypeEnvironment & e, const ast::SymbolTable & syms )
-		: tenv( e ), symtab( syms ) {}
+		: symtab( syms ), tenv( e ) {}
 
-		void premutate( const ast::VoidType * ) { visit_children = false; }
-		void premutate( const ast::BasicType * ) { visit_children = false; }
-		void premutate( const ast::PointerType * ) { visit_children = false; }
-		void premutate( const ast::ArrayType * ) { visit_children = false; }
-		void premutate( const ast::FunctionType * ) { visit_children = false; }
-		void premutate( const ast::StructInstType * ) { visit_children = false; }
-		void premutate( const ast::UnionInstType * ) { visit_children = false; }
-		void premutate( const ast::EnumInstType * ) { visit_children = false; }
-		void premutate( const ast::TraitInstType * ) { visit_children = false; }
-		void premutate( const ast::TypeInstType * ) { visit_children = false; }
-		void premutate( const ast::TupleType * ) { visit_children = false; }
-		void premutate( const ast::VarArgsType * ) { visit_children = false; }
-		void premutate( const ast::ZeroType * ) { visit_children = false; }
-		void premutate( const ast::OneType * ) { visit_children = false; }
+		void previsit( const ast::VoidType * ) { visit_children = false; }
+		void previsit( const ast::BasicType * ) { visit_children = false; }
+		void previsit( const ast::PointerType * ) { visit_children = false; }
+		void previsit( const ast::ArrayType * ) { visit_children = false; }
+		void previsit( const ast::FunctionType * ) { visit_children = false; }
+		void previsit( const ast::StructInstType * ) { visit_children = false; }
+		void previsit( const ast::UnionInstType * ) { visit_children = false; }
+		void previsit( const ast::EnumInstType * ) { visit_children = false; }
+		void previsit( const ast::TraitInstType * ) { visit_children = false; }
+		void previsit( const ast::TypeInstType * ) { visit_children = false; }
+		void previsit( const ast::TupleType * ) { visit_children = false; }
+		void previsit( const ast::VarArgsType * ) { visit_children = false; }
+		void previsit( const ast::ZeroType * ) { visit_children = false; }
+		void previsit( const ast::OneType * ) { visit_children = false; }
 
-		const ast::Type * postmutate( const ast::ArrayType * at ) {
+		const ast::Type * postvisit( const ast::ArrayType * at ) {
 			return new ast::PointerType{ at->base, at->qualifiers };
 		}
 
-		const ast::Type * postmutate( const ast::FunctionType * ft ) {
+		const ast::Type * postvisit( const ast::FunctionType * ft ) {
 			return new ast::PointerType{ ft };
 		}
 
-		const ast::Type * postmutate( const ast::TypeInstType * inst ) {
+		const ast::Type * postvisit( const ast::TypeInstType * inst ) {
 			// replace known function-type-variables with pointer-to-function
 			if ( const ast::EqvClass * eqvClass = tenv.lookup( inst->name ) ) {
Index: src/ResolvExpr/Candidate.hpp
===================================================================
--- src/ResolvExpr/Candidate.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/Candidate.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -51,14 +51,20 @@
 
 	Candidate( const ast::Expr * x, const ast::TypeEnvironment & e )
-	: expr( x ), cost( Cost::zero ), cvtCost( Cost::zero ), env( e ), open(), need() {}
+	: expr( x ), cost( Cost::zero ), cvtCost( Cost::zero ), env( e ), open(), need() {
+		assert(x->result);
+	}
 
 	Candidate( const Candidate & o, const ast::Expr * x, const Cost & addedCost = Cost::zero )
 	: expr( x ), cost( o.cost + addedCost ), cvtCost( Cost::zero ), env( o.env ), open( o.open ),
-	  need( o.need ) {}
+	  need( o.need ) {
+		assert(x->result);
+	}
 
 	Candidate(
-		const ast::Expr * x, const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 
+		const ast::Expr * x, const ast::TypeEnvironment & e, const ast::OpenVarSet & o,
 		const ast::AssertionSet & n, const Cost & c, const Cost & cvt = Cost::zero )
-	: expr( x ), cost( c ), cvtCost( cvt ), env( e ), open( o ), need( n.begin(), n.end() ) {}
+	: expr( x ), cost( c ), cvtCost( cvt ), env( e ), open( o ), need( n.begin(), n.end() ) {
+		assert(x->result);
+	}
 
 	Candidate(
@@ -66,5 +72,7 @@
 		ast::AssertionSet && n, const Cost & c, const Cost & cvt = Cost::zero )
 	: expr( x ), cost( c ), cvtCost( cvt ), env( std::move( e ) ), open( std::move( o ) ),
-	  need( n.begin(), n.end() ) {}
+	  need( n.begin(), n.end() ) {
+		assert(x->result);
+	}
 };
 
Index: src/ResolvExpr/CandidateFinder.cpp
===================================================================
--- src/ResolvExpr/CandidateFinder.cpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -9,7 +9,7 @@
 // Author           : Aaron B. Moss
 // Created On       : Wed Jun 5 14:30:00 2019
-// Last Modified By : Aaron B. Moss
-// Last Modified On : Wed Jun 5 14:30:00 2019
-// Update Count     : 1
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Oct  1 14:55:00 2019
+// Update Count     : 2
 //
 
@@ -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, bool argIsLvalue,
+	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
 ) {
 	PRINT(
@@ -74,5 +74,5 @@
 		std::cerr << std::endl;
 	)
-	Cost convCost = conversionCost( argType, paramType, symtab, env );
+	Cost convCost = conversionCost( argType, paramType, argIsLvalue, symtab, env );
 	PRINT(
 		std::cerr << std::endl << "cost is " << convCost << std::endl;
@@ -107,12 +107,13 @@
 
 	/// 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 );
+		Cost convCost = computeConversionCost(
+				arg->result, paramType, arg->get_lvalue(), 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 +124,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 +144,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 +168,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 +181,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 +189,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 +199,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 +216,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 +255,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 +287,5 @@
 			return args[ nextArg-1 ][ explAlt ];
 		}
-		
+
 		/// Ends a tuple expression, consolidating the appropriate args
 		void endTuple( const std::vector< ArgPack > & packs ) {
@@ -307,8 +308,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 +319,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 +330,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 +343,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 +353,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 );
@@ -370,7 +371,5 @@
 							// push empty tuple expression
 							newResult.parent = i;
-							std::vector< ast::ptr< ast::Expr > > emptyList;
-							newResult.expr = 
-								new ast::TupleExpr{ CodeLocation{}, move( emptyList ) };
+							newResult.expr = new ast::TupleExpr{ CodeLocation{}, {} };
 							argType = newResult.expr->result;
 						} else {
@@ -400,8 +399,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 ) );
@@ -424,7 +423,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;
 						}
@@ -432,6 +431,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 );
 					}
@@ -479,5 +478,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 );
 				}
@@ -495,5 +494,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 );
 					}
@@ -517,7 +516,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;
 				}
@@ -539,5 +538,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 );
 				}
@@ -548,23 +547,23 @@
 		genStart = genEnd;
 
-		return genEnd != results.size();
+		return genEnd != results.size();  // were any new results added?
 	}
 
 	/// 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 };
@@ -574,5 +573,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 ) );
 			}
@@ -594,15 +593,30 @@
 
 	/// Actually visits expressions to find their candidate interpretations
-	struct Finder final : public ast::WithShortCircuiting {
+	class Finder final : public ast::WithShortCircuiting {
+		const ast::SymbolTable & symtab;
+	public:
 		CandidateFinder & selfFinder;
-		const ast::SymbolTable & symtab;
 		CandidateList & candidates;
 		const ast::TypeEnvironment & tenv;
 		ast::ptr< ast::Type > & targetType;
 
+		enum Errors {
+			NotFound,
+			NoMatch,
+			ArgsToFew,
+			ArgsToMany,
+			RetsToFew,
+			RetsToMany,
+			NoReason
+		};
+
+		struct {
+			Errors code = NotFound;
+		} reason;
+
 		Finder( CandidateFinder & f )
-		: selfFinder( f ), symtab( f.symtab ), 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; }
 
@@ -611,4 +625,5 @@
 		void addCandidate( Args &&... args ) {
 			candidates.emplace_back( new Candidate{ std::forward<Args>( args )... } );
+			reason.code = NoReason;
 		}
 
@@ -639,9 +654,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
@@ -657,5 +672,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(
@@ -669,5 +684,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
 		) {
@@ -676,6 +691,6 @@
 			ast::TypeEnvironment funcEnv{ func->env };
 			makeUnifiableVars( funcType, funcOpen, funcNeed );
-			// add all type variables as open variables now so that those not used in the parameter 
-			// list are still considered open
+			// 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 );
 
@@ -683,6 +698,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
@@ -698,7 +713,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;
 			}
@@ -750,6 +765,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 );
 
@@ -760,5 +775,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 );
 						}
@@ -780,11 +795,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() };
@@ -799,13 +814,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 );
@@ -815,15 +830,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 );
 				}
@@ -837,7 +852,9 @@
 			if ( funcFinder.candidates.empty() ) return;
 
-			std::vector< CandidateFinder > argCandidates = 
+			reason.code = NoMatch;
+
+			std::vector< CandidateFinder > argCandidates =
 				selfFinder.findSubExprs( untypedExpr->args );
-			
+
 			// take care of possible tuple assignments
 			// if not tuple assignment, handled as normal function call
@@ -877,15 +894,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 );
@@ -901,5 +918,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 );
 				}
@@ -913,5 +930,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 );
@@ -922,5 +939,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; }
@@ -934,5 +951,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;
@@ -957,6 +974,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 ) {
@@ -966,5 +983,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:
@@ -983,5 +1000,5 @@
 		/// true if expression is an lvalue
 		static bool isLvalue( const ast::Expr * x ) {
-			return x->result && ( x->result->is_lvalue() || x->result.as< ast::ReferenceType >() );
+			return x->result && ( x->get_lvalue() || x->result.as< ast::ReferenceType >() );
 		}
 
@@ -989,4 +1006,9 @@
 			CandidateFinder finder{ symtab, tenv };
 			finder.find( addressExpr->arg );
+
+			if( finder.candidates.empty() ) return;
+
+			reason.code = NoMatch;
+
 			for ( CandidateRef & r : finder.candidates ) {
 				if ( ! isLvalue( r->expr ) ) continue;
@@ -1009,4 +1031,6 @@
 			finder.find( castExpr->arg, ResolvMode::withAdjustment() );
 
+			if( !finder.candidates.empty() ) reason.code = NoMatch;
+
 			CandidateList matches;
 			for ( CandidateRef & cand : finder.candidates ) {
@@ -1016,7 +1040,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();
@@ -1025,5 +1049,6 @@
 				// unification run for side-effects
 				unify( toType, cand->expr->result, cand->env, need, have, open, symtab );
-				Cost thisCost = castCost( cand->expr->result, toType, symtab, cand->env );
+				Cost thisCost = castCost( cand->expr->result, toType, cand->expr->get_lvalue(),
+						symtab, cand->env );
 				PRINT(
 					std::cerr << "working on cast with result: " << toType << std::endl;
@@ -1037,7 +1062,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 );
@@ -1057,6 +1082,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 } );
 			}
@@ -1067,5 +1092,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;
@@ -1074,8 +1099,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 >() ) {
@@ -1092,4 +1117,8 @@
 			std::vector< ast::SymbolTable::IdData > declList = symtab.lookupId( nameExpr->name );
 			PRINT( std::cerr << "nameExpr is " << nameExpr->name << std::endl; )
+			if( declList.empty() ) return;
+
+			reason.code = NoMatch;
+
 			for ( auto & data : declList ) {
 				Cost cost = Cost::zero;
@@ -1097,5 +1126,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(
@@ -1107,8 +1136,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 );
@@ -1120,5 +1149,5 @@
 			// not sufficient to just pass `variableExpr` here, type might have changed since
 			// creation
-			addCandidate( 
+			addCandidate(
 				new ast::VariableExpr{ variableExpr->location, variableExpr->var }, tenv );
 		}
@@ -1130,7 +1159,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 {
@@ -1141,5 +1170,5 @@
 				CandidateList winners = findMinCost( finder.candidates );
 				if ( winners.size() != 1 ) {
-					SemanticError( 
+					SemanticError(
 						sizeofExpr->expr.get(), "Ambiguous expression in sizeof operand: " );
 				}
@@ -1154,7 +1183,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 {
@@ -1165,5 +1194,5 @@
 				CandidateList winners = findMinCost( finder.candidates );
 				if ( winners.size() != 1 ) {
-					SemanticError( 
+					SemanticError(
 						alignofExpr->expr.get(), "Ambiguous expression in alignof operand: " );
 				}
@@ -1172,5 +1201,5 @@
 				choice->expr = referenceToRvalueConversion( choice->expr, choice->cost );
 				choice->cost = Cost::zero;
-				addCandidate( 
+				addCandidate(
 					*choice, new ast::AlignofExpr{ alignofExpr->location, choice->expr } );
 			}
@@ -1185,5 +1214,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 );
 			}
@@ -1206,4 +1235,6 @@
 			finder2.find( logicalExpr->arg2, ResolvMode::withAdjustment() );
 			if ( finder2.candidates.empty() ) return;
+
+			reason.code = NoMatch;
 
 			for ( const CandidateRef & r1 : finder1.candidates ) {
@@ -1218,5 +1249,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 );
@@ -1240,4 +1271,6 @@
 			finder3.find( conditionalExpr->arg3, ResolvMode::withAdjustment() );
 			if ( finder3.candidates.empty() ) return;
+
+			reason.code = NoMatch;
 
 			for ( const CandidateRef & r1 : finder1.candidates ) {
@@ -1256,19 +1289,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(
@@ -1287,5 +1320,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() );
@@ -1317,4 +1350,6 @@
 			finder2.find( rangeExpr->high, ResolvMode::withAdjustment() );
 			if ( finder2.candidates.empty() ) return;
+
+			reason.code = NoMatch;
 
 			for ( const CandidateRef & r1 : finder1.candidates ) {
@@ -1330,16 +1365,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 );
@@ -1350,5 +1385,5 @@
 
 		void postvisit( const ast::UntypedTupleExpr * tupleExpr ) {
-			std::vector< CandidateFinder > subCandidates = 
+			std::vector< CandidateFinder > subCandidates =
 				selfFinder.findSubExprs( tupleExpr->exprs );
 			std::vector< CandidateList > possibilities;
@@ -1370,5 +1405,5 @@
 
 				addCandidate(
-					new ast::TupleExpr{ tupleExpr->location, move( exprs ) }, 
+					new ast::TupleExpr{ tupleExpr->location, move( exprs ) },
 					move( env ), move( open ), move( need ), sumCost( subs ) );
 			}
@@ -1412,10 +1447,12 @@
 				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() );
 				for ( CandidateRef & cand : finder.candidates ) {
+					if(reason.code == NotFound) reason.code = NoMatch;
+
 					ast::TypeEnvironment env{ cand->env };
 					ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have;
@@ -1426,7 +1463,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,13 +1472,14 @@
 					// unification run for side-effects
 					unify( toType, cand->expr->result, env, need, have, open, symtab );
-					Cost thisCost = castCost( cand->expr->result, toType, symtab, env );
-					
+					Cost thisCost = castCost( cand->expr->result, toType, cand->expr->get_lvalue(),
+							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 );
@@ -1469,5 +1507,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 ) {
@@ -1486,4 +1524,5 @@
 			{
 				ast::ptr< ast::Type > newType = candidate->expr->result;
+				assertf(candidate->expr->result, "Result of expression %p for candidate is null", candidate->expr.get());
 				candidate->env.apply( newType );
 				mangleName = Mangle::mangle( newType );
@@ -1494,5 +1533,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;
 					)
@@ -1500,6 +1539,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 ) ) {
@@ -1515,5 +1554,5 @@
 				} else {
 					PRINT(
-						std::cerr << "cost " << candidate->cost << " loses to " 
+						std::cerr << "cost " << candidate->cost << " loses to "
 							<< found->second.candidate->cost << std::endl;
 					)
@@ -1530,10 +1569,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 );
 		}
@@ -1549,5 +1588,17 @@
 
 	if ( mode.failFast && candidates.empty() ) {
-		SemanticError( expr, "No reasonable alternatives for expression " );
+		switch(finder.pass.reason.code) {
+		case Finder::NotFound:
+			{ SemanticError( expr, "No alternatives for expression " ); break; }
+		case Finder::NoMatch:
+			{ SemanticError( expr, "Invalid application of existing declaration(s) in expression " ); break; }
+		case Finder::ArgsToFew:
+		case Finder::ArgsToMany:
+		case Finder::RetsToFew:
+		case Finder::RetsToMany:
+		case Finder::NoReason:
+		default:
+			{ SemanticError( expr->location, "No reasonable alternatives for expression : reasons unkown" ); }
+		}
 	}
 
@@ -1558,5 +1609,5 @@
 		std::vector< std::string > errors;
 		for ( CandidateRef & candidate : candidates ) {
-			satisfyAssertions( candidate, symtab, satisfied, errors );
+			satisfyAssertions( candidate, localSyms, satisfied, errors );
 		}
 
@@ -1583,5 +1634,5 @@
 
 		CandidateList pruned = pruneCandidates( candidates );
-		
+
 		if ( mode.failFast && pruned.empty() ) {
 			std::ostringstream stream;
@@ -1602,16 +1653,16 @@
 		)
 		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, 
-				adjustExprType( r->expr->result, r->env, symtab ) );
+			r->expr = ast::mutate_field(
+				r->expr.get(), &ast::Expr::result,
+				adjustExprType( r->expr->result, r->env, localSyms ) );
 		}
 	}
@@ -1625,13 +1676,13 @@
 }
 
-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;
 
 	for ( const auto & x : xs ) {
-		out.emplace_back( symtab, env );
+		out.emplace_back( localSyms, env );
 		out.back().find( x, ResolvMode::withAdjustment() );
-		
+
 		PRINT(
 			std::cerr << "findSubExprs" << std::endl;
Index: src/ResolvExpr/CandidateFinder.hpp
===================================================================
--- src/ResolvExpr/CandidateFinder.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/CandidateFinder.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -9,7 +9,7 @@
 // Author           : Aaron B. Moss
 // Created On       : Wed Jun 5 14:30:00 2019
-// Last Modified By : Aaron B. Moss
-// Last Modified On : Wed Jun 5 14:30:00 2019
-// Update Count     : 1
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Oct  1  9:51:00 2019
+// Update Count     : 2
 //
 
@@ -28,12 +28,12 @@
 struct CandidateFinder {
 	CandidateList candidates;          ///< List of candidate resolutions
-	const ast::SymbolTable & symtab;   ///< Symbol table to lookup candidates
+	const ast::SymbolTable & localSyms;   ///< Symbol table to lookup candidates
 	const ast::TypeEnvironment & env;  ///< Substitutions performed in this resolution
 	ast::ptr< ast::Type > targetType;  ///< Target type for resolution
 
-	CandidateFinder( 
-		const ast::SymbolTable & symtab, const ast::TypeEnvironment & env, 
+	CandidateFinder(
+		const ast::SymbolTable & syms, const ast::TypeEnvironment & env,
 		const ast::Type * tt = nullptr )
-	: candidates(), symtab( symtab ), env( env ), targetType( tt ) {}
+	: candidates(), localSyms( syms ), env( env ), targetType( tt ) {}
 
 	/// Fill candidates with feasible resolutions for `expr`
@@ -49,5 +49,5 @@
 	iterator begin() { return candidates.begin(); }
 	const_iterator begin() const { return candidates.begin(); }
-	
+
 	iterator end() { return candidates.end(); }
 	const_iterator end() const { return candidates.end(); }
@@ -55,7 +55,7 @@
 
 /// Computes conversion cost between two types
-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, bool argIsLvalue,
+	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env );
 
 } // namespace ResolvExpr
Index: src/ResolvExpr/CastCost.cc
===================================================================
--- src/ResolvExpr/CastCost.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/CastCost.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -10,6 +10,6 @@
 // Created On       : Sun May 17 06:57:43 2015
 // Last Modified By : Andrew Beach
-// Last Modified On : Thu Aug  8 16:12:00 2019
-// Update Count     : 8
+// Last Modified On : Tue Oct  4 15:00:00 2019
+// Update Count     : 9
 //
 
@@ -142,7 +142,7 @@
 
 		CastCost_new(
-			const ast::Type * dst, const ast::SymbolTable & symtab,
+			const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab,
 			const ast::TypeEnvironment & env, CostCalculation costFunc )
-		: ConversionCost_new( dst, symtab, env, costFunc ) {}
+		: ConversionCost_new( dst, srcIsLvalue, symtab, env, costFunc ) {}
 
 		void postvisit( const ast::BasicType * basicType ) {
@@ -152,5 +152,5 @@
 				cost = Cost::unsafe;
 			} else {
-				cost = conversionCost( basicType, dst, symtab, env );
+				cost = conversionCost( basicType, dst, srcIsLvalue, symtab, env );
 			}
 		}
@@ -183,9 +183,21 @@
 		}
 	};
+
+	#warning For overload resolution between the two versions.
+	int localPtrsCastable(const ast::Type * t1, const ast::Type * t2,
+			const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) {
+		return ptrsCastable( t1, t2, symtab, env );
+	}
+	Cost localCastCost(
+		const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+		const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
+	) { return castCost( src, dst, srcIsLvalue, symtab, env ); }
 } // anonymous namespace
 
+
+
 Cost castCost(
-	const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
-	const ast::TypeEnvironment & env
+	const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
 ) {
 	if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
@@ -193,5 +205,5 @@
 			// check cast cost against bound type, if present
 			if ( eqvClass->bound ) {
-				return castCost( src, eqvClass->bound, symtab, env );
+				return castCost( src, eqvClass->bound, srcIsLvalue, symtab, env );
 			} else {
 				return Cost::infinity;
@@ -201,5 +213,5 @@
 			auto type = strict_dynamic_cast< const ast::TypeDecl * >( named );
 			if ( type->base ) {
-				return castCost( src, type->base, symtab, env ) + Cost::safe;
+				return castCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe;
 			}
 		}
@@ -224,17 +236,9 @@
 		#warning cast on ptrsCastable artifact of having two functions, remove when port done
 		return convertToReferenceCost(
-			src, refType, symtab, env,
-			( int (*)(
-				const ast::Type *, const ast::Type *, const ast::SymbolTable &,
-				const ast::TypeEnvironment & )
-			) ptrsCastable );
+			src, refType, srcIsLvalue, symtab, env, localPtrsCastable );
 	} else {
 		#warning cast on castCost artifact of having two functions, remove when port done
-		ast::Pass< CastCost_new > converter{
-			dst, symtab, env,
-			( Cost (*)(
-				const ast::Type *, const ast::Type *, const ast::SymbolTable &,
-				const ast::TypeEnvironment & )
-			) castCost };
+		ast::Pass< CastCost_new > converter(
+			dst, srcIsLvalue, symtab, env, localCastCost );
 		src->accept( converter );
 		return converter.pass.cost;
Index: src/ResolvExpr/CommonType.cc
===================================================================
--- src/ResolvExpr/CommonType.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/CommonType.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -939,5 +939,5 @@
 			ast::ptr< ast::Type > result;
 			const ast::ReferenceType * ref1 = type1.as< ast::ReferenceType >();
-			const ast::ReferenceType * ref2 = type1.as< ast::ReferenceType >();
+			const ast::ReferenceType * ref2 = type2.as< ast::ReferenceType >();
 
 			if ( depth1 > depth2 ) {
Index: src/ResolvExpr/ConversionCost.cc
===================================================================
--- src/ResolvExpr/ConversionCost.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/ConversionCost.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -10,6 +10,6 @@
 // Created On       : Sun May 17 07:06:19 2015
 // Last Modified By : Andrew Beach
-// Last Modified On : Mon Aug 12 10:21:00 2019
-// Update Count     : 27
+// Last Modified On : Fri Oct  4 14:45:00 2019
+// Update Count     : 28
 //
 
@@ -497,24 +497,24 @@
 	}
 
-static int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2,
-		const ast::SymbolTable &, const ast::TypeEnvironment & env ) {
-	return ptrsAssignable( t1, t2, env );
-}
-
-// TODO: This is used for overload resolution. It might be able to be dropped once the old system
-// is removed.
-static Cost localConversionCost(
-	const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
-	const ast::TypeEnvironment & env
-) { return conversionCost( src, dst, symtab, env ); }
+namespace {
+	# warning For overload resolution between the two versions.
+	int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2,
+			const ast::SymbolTable &, const ast::TypeEnvironment & env ) {
+		return ptrsAssignable( t1, t2, env );
+	}
+	Cost localConversionCost(
+		const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+		const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
+	) { return conversionCost( src, dst, srcIsLvalue, symtab, env ); }
+}
 
 Cost conversionCost(
-	const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
-	const ast::TypeEnvironment & env
+	const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
 ) {
 	if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
 		if ( const ast::EqvClass * eqv = env.lookup( inst->name ) ) {
 			if ( eqv->bound ) {
-				return conversionCost(src, eqv->bound, symtab, env );
+				return conversionCost(src, eqv->bound, srcIsLvalue, symtab, env );
 			} else {
 				return Cost::infinity;
@@ -524,5 +524,5 @@
 			assertf( type, "Unexpected typedef." );
 			if ( type->base ) {
-				return conversionCost( src, type->base, symtab, env ) + Cost::safe;
+				return conversionCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe;
 			}
 		}
@@ -534,7 +534,7 @@
 	} else if ( const ast::ReferenceType * refType =
 			 dynamic_cast< const ast::ReferenceType * >( dst ) ) {
-		return convertToReferenceCost( src, refType, symtab, env, localPtrsAssignable );
+		return convertToReferenceCost( src, refType, srcIsLvalue, symtab, env, localPtrsAssignable );
 	} else {
-		ast::Pass<ConversionCost_new> converter( dst, symtab, env, localConversionCost );
+		ast::Pass<ConversionCost_new> converter( dst, srcIsLvalue, symtab, env, localConversionCost );
 		src->accept( converter );
 		return converter.pass.cost;
@@ -542,11 +542,11 @@
 }
 
-static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst,
+static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
 		int diff, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
-		NumCostCalculation func ) {
+		PtrsCalculation func ) {
 	if ( 0 < diff ) {
 		Cost cost = convertToReferenceCost(
-			strict_dynamic_cast< const ast::ReferenceType * >( src )->base,
-			dst, (diff - 1), symtab, env, func );
+			strict_dynamic_cast< const ast::ReferenceType * >( src )->base, dst,
+			srcIsLvalue, (diff - 1), symtab, env, func );
 		cost.incReference();
 		return cost;
@@ -554,5 +554,5 @@
 		Cost cost = convertToReferenceCost(
 			src, strict_dynamic_cast< const ast::ReferenceType * >( dst )->base,
-			(diff + 1), symtab, env, func );
+			srcIsLvalue, (diff + 1), symtab, env, func );
 		cost.incReference();
 		return cost;
@@ -579,5 +579,5 @@
 			}
 		} else {
-			ast::Pass<ConversionCost_new> converter( dst, symtab, env, localConversionCost );
+			ast::Pass<ConversionCost_new> converter( dst, srcIsLvalue, symtab, env, localConversionCost );
 			src->accept( converter );
 			return converter.pass.cost;
@@ -588,5 +588,5 @@
 		assert( dstAsRef );
 		if ( typesCompatibleIgnoreQualifiers( src, dstAsRef->base, symtab, env ) ) {
-			if ( src->is_lvalue() ) {
+			if ( srcIsLvalue ) {
 				if ( src->qualifiers == dstAsRef->base->qualifiers ) {
 					return Cost::reference;
@@ -607,8 +607,8 @@
 
 Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dst,
-	    const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
-		NumCostCalculation func ) {
+		bool srcIsLvalue, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
+		PtrsCalculation func ) {
 	int sdepth = src->referenceDepth(), ddepth = dst->referenceDepth();
-	return convertToReferenceCost( src, dst, sdepth - ddepth, symtab, env, func );
+	return convertToReferenceCost( src, dst, srcIsLvalue, sdepth - ddepth, symtab, env, func );
 }
 
@@ -667,5 +667,5 @@
 	assert( nullptr == dynamic_cast< const ast::ReferenceType * >( dst ) );
 
-	cost = costCalc( refType->base, dst, symtab, env );
+	cost = costCalc( refType->base, dst, srcIsLvalue, symtab, env );
 	if ( refType->base->qualifiers == dst->qualifiers ) {
 		cost.incReference();
@@ -701,6 +701,6 @@
 void ConversionCost_new::postvisit( const ast::EnumInstType * enumInstType ) {
 	(void)enumInstType;
-	static const ast::BasicType integer( ast::BasicType::SignedInt );
-	cost = costCalc( &integer, dst, symtab, env );
+	static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicType::SignedInt ) };
+	cost = costCalc( integer, dst, srcIsLvalue, symtab, env );
 	if ( cost < Cost::unsafe ) {
 		cost.incSafe();
@@ -714,5 +714,5 @@
 void ConversionCost_new::postvisit( const ast::TypeInstType * typeInstType ) {
 	if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) {
-		cost = costCalc( eqv->bound, dst, symtab, env );
+		cost = costCalc( eqv->bound, dst, srcIsLvalue, symtab, env );
 	} else if ( const ast::TypeInstType * dstAsInst =
 			dynamic_cast< const ast::TypeInstType * >( dst ) ) {
@@ -724,5 +724,5 @@
 		assertf( type, "Unexpected typedef.");
 		if ( type->base ) {
-			cost = costCalc( type->base, dst, symtab, env ) + Cost::safe;
+			cost = costCalc( type->base, dst, srcIsLvalue, symtab, env ) + Cost::safe;
 		}
 	}
@@ -737,5 +737,5 @@
 		auto dstEnd = dstAsTuple->types.end();
 		while ( srcIt != srcEnd && dstIt != dstEnd ) {
-			Cost newCost = costCalc( * srcIt++, * dstIt++, symtab, env );
+			Cost newCost = costCalc( * srcIt++, * dstIt++, srcIsLvalue, symtab, env );
 			if ( newCost == Cost::infinity ) {
 				return;
@@ -772,4 +772,8 @@
 			cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
 		}
+	} else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {
+		cost = Cost::zero;
+		// +1 for zero_t ->, +1 for disambiguation
+		cost.incSafe( maxIntCost + 2 );
 	}
 }
@@ -789,7 +793,4 @@
 			cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
 		}
-	} else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {
-		cost = Cost::zero;
-		cost.incSafe( maxIntCost + 2 );
 	}
 }
Index: src/ResolvExpr/ConversionCost.h
===================================================================
--- src/ResolvExpr/ConversionCost.h	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/ConversionCost.h	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -10,6 +10,6 @@
 // Created On       : Sun May 17 09:37:28 2015
 // Last Modified By : Andrew Beach
-// Last Modified On : Thu Aug  8 16:13:00 2019
-// Update Count     : 6
+// Last Modified On : Tue Oct  4 14:59:00 2019
+// Update Count     : 7
 //
 
@@ -74,7 +74,7 @@
 
 // Some function pointer types, differ in return type.
-using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *,
+using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *, bool,
 	const ast::SymbolTable &, const ast::TypeEnvironment &)>;
-using NumCostCalculation = std::function<int(const ast::Type *, const ast::Type *,
+using PtrsCalculation = std::function<int(const ast::Type *, const ast::Type *,
 	const ast::SymbolTable &, const ast::TypeEnvironment &)>;
 
@@ -83,4 +83,5 @@
 protected:
 	const ast::Type * dst;
+	bool srcIsLvalue;
 	const ast::SymbolTable & symtab;
 	const ast::TypeEnvironment & env;
@@ -89,7 +90,8 @@
 	Cost cost;
 
-	ConversionCost_new( const ast::Type * dst, const ast::SymbolTable & symtab,
+	ConversionCost_new( const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab,
 			const ast::TypeEnvironment & env, CostCalculation costCalc ) :
-		dst( dst ), symtab( symtab ), env( env ), costCalc( costCalc ), cost( Cost::infinity )
+		dst( dst ), srcIsLvalue( srcIsLvalue ), symtab( symtab ), env( env ),
+		costCalc( costCalc ), cost( Cost::infinity )
 	{}
 
@@ -114,5 +116,6 @@
 
 Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dest,
-	const ast::SymbolTable & indexer, const ast::TypeEnvironment & env, NumCostCalculation func );
+	bool srcIsLvalue, const ast::SymbolTable & indexer, const ast::TypeEnvironment & env,
+	PtrsCalculation func );
 
 } // namespace ResolvExpr
Index: src/ResolvExpr/CurrentObject.cc
===================================================================
--- src/ResolvExpr/CurrentObject.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/CurrentObject.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -21,8 +21,10 @@
 #include <string>                      // for string, operator<<, allocator
 
+#include "AST/Copy.hpp"                // for shallowCopy
 #include "AST/Expr.hpp"                // for InitAlternative
 #include "AST/GenericSubstitution.hpp" // for genericSubstitution
 #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 +598,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 +639,5 @@
 			auto res = eval(expr);
 			if ( ! res.second ) {
-				SemanticError( location, 
+				SemanticError( location,
 					toString("Array designator must be a constant expression: ", expr ) );
 			}
@@ -644,5 +646,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 +657,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 +664,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 +760,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 +770,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 +788,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() );
 			}
@@ -803,4 +805,5 @@
 						new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
 					// need to substitute for generic types so that casts are to concrete types
+					alt.type = shallowCopy(alt.type.get());
 					PRINT( std::cerr << "  type is: " << alt.type; )
 					sub.apply( alt.type ); // also apply to designation??
@@ -842,5 +845,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 +900,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 +929,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 +952,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 +1016,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/PolyCost.cc
===================================================================
--- src/ResolvExpr/PolyCost.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/PolyCost.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -58,11 +58,12 @@
 
 // TODO: When the old PolyCost is torn out get rid of the _new suffix.
-struct PolyCost_new {
+class PolyCost_new {
+	const ast::SymbolTable &symtab;
+public:
 	int result;
-	const ast::SymbolTable &symtab;
 	const ast::TypeEnvironment &env_;
 
-	PolyCost_new( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) :
-		result( 0 ), symtab( symtab ), env_( env ) {}
+	PolyCost_new( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) 
+	: symtab( symtab ), result( 0 ), env_( env ) {}
 
 	void previsit( const ast::TypeInstType * type ) {
Index: src/ResolvExpr/RenameVars.cc
===================================================================
--- src/ResolvExpr/RenameVars.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/RenameVars.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -19,4 +19,5 @@
 #include <utility>                 // for pair
 
+#include "AST/ForallSubstitutionTable.hpp"
 #include "AST/Pass.hpp"
 #include "AST/Type.hpp"
@@ -30,4 +31,6 @@
 #include "SynTree/Visitor.h"       // for acceptAll, maybeAccept
 
+#include "AST/Copy.hpp"
+
 namespace ResolvExpr {
 
@@ -37,6 +40,7 @@
 		int resetCount = 0;
 		ScopedMap< std::string, std::string > nameMap;
+	public:
+		ast::ForallSubstitutionTable subs;
 
-	public:
 		void reset() {
 			level = 0;
@@ -44,8 +48,6 @@
 		}
 
-		using mapConstIterator = ScopedMap< std::string, std::string >::const_iterator;
-
 		void rename( TypeInstType * type ) {
-			mapConstIterator it = nameMap.find( type->name );
+			auto it = nameMap.find( type->name );
 			if ( it != nameMap.end() ) {
 				type->name = it->second;
@@ -65,7 +67,6 @@
 					// ditto for assertion names, the next level in
 					level++;
-					// acceptAll( td->assertions, *this );
-				} // for
-			} // if
+				}
+			}
 		}
 
@@ -77,10 +78,16 @@
 
 		const ast::TypeInstType * rename( const ast::TypeInstType * type ) {
-			mapConstIterator it = nameMap.find( type->name );
+			// re-linking of base type handled by WithForallSubstitutor
+
+			// rename
+			auto it = nameMap.find( type->name );
 			if ( it != nameMap.end() ) {
-				ast::TypeInstType * mutType = ast::mutate( type );
-				mutType->name = it->second;
-	            type = mutType;
+				// unconditionally mutate because map will *always* have different name,
+				// if this mutates, will *always* have been mutated by ForallSubstitutor above
+				ast::TypeInstType * mut = ast::mutate( type );
+				mut->name = it->second;
+	            type = mut;
 			}
+
 			return type;
 		}
@@ -88,29 +95,32 @@
 		template<typename NodeT>
 		const NodeT * openLevel( const NodeT * type ) {
-			if ( !type->forall.empty() ) {
-				nameMap.beginScope();
-				// Load new names from this forall clause and perform renaming.
-				NodeT * mutType = ast::mutate( type );
-				for ( ast::ptr< ast::TypeDecl > & td : mutType->forall ) {
-					std::ostringstream output;
-					output << "_" << resetCount << "_" << level << "_" << td->name;
-					std::string newname( output.str() );
-					nameMap[ td->name ] = newname;
-					++level;
+			if ( type->forall.empty() ) return type;
 
-					ast::TypeDecl * decl = ast::mutate( td.get() );
-					decl->name = newname;
-					td = decl;
-				}
+			nameMap.beginScope();
+
+			// Load new names from this forall clause and perform renaming.
+			NodeT * mutType = ast::mutate( type );
+			assert( type == mutType && "mutated type must be unique from ForallSubstitutor" );
+			for ( ast::ptr< ast::TypeDecl > & td : mutType->forall ) {
+				std::ostringstream output;
+				output << "_" << resetCount << "_" << level << "_" << td->name;
+				std::string newname =  output.str();
+				nameMap[ td->name ] = newname;
+				++level;
+
+				ast::TypeDecl * mutDecl = ast::mutate( td.get() );
+				assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" );
+				mutDecl->name = newname;
+				// assertion above means `td = mutDecl;` is unnecessary
 			}
+			// assertion above means `type = mutType;` is unnecessary
+
 			return type;
 		}
 
-		template<typename NodeT>
-		const NodeT * closeLevel( const NodeT * type ) {
-			if ( !type->forall.empty() ) {
-				nameMap.endScope();
-			}
-			return type;
+		void closeLevel( const ast::ParameterizedType * type ) {
+			if ( type->forall.empty() ) return;
+
+			nameMap.endScope();
 		}
 	};
@@ -119,5 +129,5 @@
 	RenamingData renaming;
 
-	struct RenameVars {
+	struct RenameVars_old {
 		void previsit( TypeInstType * instType ) {
 			renaming.openLevel( (Type*)instType );
@@ -130,4 +140,9 @@
 			renaming.closeLevel( type );
 		}
+	};
+
+	struct RenameVars_new /*: public ast::WithForallSubstitutor*/ {
+		#warning when old RenameVars goes away, replace hack below with global pass inheriting from WithForallSubstitutor
+		ast::ForallSubstitutionTable & subs = renaming.subs;
 
 		const ast::FunctionType * previsit( const ast::FunctionType * type ) {
@@ -146,6 +161,6 @@
 			return renaming.rename( renaming.openLevel( type ) );
 		}
-		const ast::ParameterizedType * postvisit( const ast::ParameterizedType * type ) {
-			return renaming.closeLevel( type );
+		void postvisit( const ast::ParameterizedType * type ) {
+			renaming.closeLevel( type );
 		}
 	};
@@ -154,11 +169,13 @@
 
 void renameTyVars( Type * t ) {
-	PassVisitor<RenameVars> renamer;
+	PassVisitor<RenameVars_old> renamer;
 	t->accept( renamer );
 }
 
 const ast::Type * renameTyVars( const ast::Type * t ) {
-	ast::Pass<RenameVars> renamer;
-	return t->accept( renamer );
+	ast::Type *tc = ast::deepCopy(t);
+	ast::Pass<RenameVars_new> renamer;
+//	return t->accept( renamer );
+	return tc->accept( renamer );
 }
 
Index: src/ResolvExpr/ResolveTypeof.cc
===================================================================
--- src/ResolvExpr/ResolveTypeof.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/ResolveTypeof.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -99,16 +99,16 @@
 			// replace basetypeof(<enum>) by int
 			if ( dynamic_cast<EnumInstType*>(newType) ) {
-				Type* newerType = 
-					new BasicType{ newType->get_qualifiers(), BasicType::SignedInt, 
+				Type* newerType =
+					new BasicType{ newType->get_qualifiers(), BasicType::SignedInt,
 					newType->attributes };
 				delete newType;
 				newType = newerType;
 			}
-			newType->get_qualifiers().val 
+			newType->get_qualifiers().val
 				= ( newType->get_qualifiers().val & ~Type::Qualifiers::Mask ) | oldQuals;
 		} else {
 			newType->get_qualifiers().val |= oldQuals;
 		}
-		
+
 		return newType;
 	}
@@ -120,7 +120,7 @@
 		ResolveTypeof_new( const ast::SymbolTable & syms ) : localSymtab( syms ) {}
 
-		void premutate( const ast::TypeofType * ) { visit_children = false; }
+		void previsit( const ast::TypeofType * ) { visit_children = false; }
 
-		const ast::Type * postmutate( const ast::TypeofType * typeofType ) {
+		const ast::Type * postvisit( const ast::TypeofType * typeofType ) {
 			// pass on null expression
 			if ( ! typeofType->expr ) return typeofType;
@@ -133,5 +133,5 @@
 				// typeof wrapping expression
 				ast::TypeEnvironment dummy;
-				ast::ptr< ast::Expr > newExpr = 
+				ast::ptr< ast::Expr > newExpr =
 					resolveInVoidContext( typeofType->expr, localSymtab, dummy );
 				assert( newExpr->result && ! newExpr->result->isVoid() );
@@ -143,9 +143,9 @@
 				// replace basetypeof(<enum>) by int
 				if ( newType.as< ast::EnumInstType >() ) {
-					newType = new ast::BasicType{ 
+					newType = new ast::BasicType{
 						ast::BasicType::SignedInt, newType->qualifiers, copy(newType->attributes) };
 				}
-				reset_qualifiers( 
-					newType, 
+				reset_qualifiers(
+					newType,
 					( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers );
 			} else {
@@ -153,5 +153,5 @@
 			}
 
-			return newType;
+			return newType.release();
 		}
 	};
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/Resolver.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -1074,5 +1074,5 @@
 			const ast::Expr * postmutate( const ast::CastExpr * castExpr ) {
 				if (
-					castExpr->isGenerated
+					castExpr->isGenerated == ast::GeneratedCast
 					&& typesCompatible( castExpr->arg->result, castExpr->result )
 				) {
@@ -1128,5 +1128,5 @@
 
 		// set up and resolve expression cast to void
-		ast::CastExpr * untyped = new ast::CastExpr{ expr };
+		ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr };
 		CandidateRef choice = findUnfinishedKindExpression(
 			untyped, symtab, "", anyCandidate, ResolvMode::withAdjustment() );
@@ -1267,5 +1267,5 @@
 	};
 
-	void resolve( std::list< ast::ptr<ast::Decl> >& translationUnit ) {
+	void resolve( std::list< ast::ptr< ast::Decl > >& translationUnit ) {
 		ast::Pass< Resolver_new > resolver;
 		accept_all( translationUnit, resolver );
@@ -1301,5 +1301,5 @@
 		ast::ptr< ast::FunctionDecl > ret = functionDecl;
 		for ( unsigned i = 0; i < functionDecl->type->params.size(); ++i ) {
-			const ast::ptr<ast::DeclWithType> & d = functionDecl->type->params[i];
+			const ast::ptr< ast::DeclWithType > & d = functionDecl->type->params[i];
 
 			if ( const ast::ObjectDecl * obj = d.as< ast::ObjectDecl >() ) {
@@ -1318,5 +1318,5 @@
 			}
 		}
-		return ret.get();
+		return ret.release();
 	}
 
@@ -1341,5 +1341,5 @@
 		// in case we decide to allow nested enums
 		GuardValue( inEnumDecl );
-		inEnumDecl = false;
+		inEnumDecl = true;
 	}
 
Index: src/ResolvExpr/SatisfyAssertions.cpp
===================================================================
--- src/ResolvExpr/SatisfyAssertions.cpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/SatisfyAssertions.cpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -9,7 +9,7 @@
 // Author           : Aaron B. Moss
 // Created On       : Mon Jun 10 17:45:00 2019
-// Last Modified By : Aaron B. Moss
-// Last Modified On : Mon Jun 10 17:45:00 2019
-// Update Count     : 1
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Oct  1 13:56:00 2019
+// Update Count     : 2
 //
 
@@ -299,6 +299,6 @@
 			Cost cost;
 
-			OutType( 
-				const ast::TypeEnvironment & e, const ast::OpenVarSet & o, 
+			OutType(
+				const ast::TypeEnvironment & e, const ast::OpenVarSet & o,
 				const std::vector< DeferRef > & as, const ast::SymbolTable & symtab )
 			: env( e ), open( o ), assns( as ), cost( Cost::zero ) {
@@ -306,9 +306,9 @@
 				for ( const DeferRef & assn : assns ) {
 					// compute conversion cost from satisfying decl to assertion
-					cost += computeConversionCost( 
-						assn.match.adjType, assn.decl->get_type(), symtab, env );
-					
+					cost += computeConversionCost(
+						assn.match.adjType, assn.decl->get_type(), false, symtab, env );
+
 					// mark vars+specialization on function-type assertions
-					const ast::FunctionType * func = 
+					const ast::FunctionType * func =
 						GenPoly::getFunctionType( assn.match.cdata.id->get_type() );
 					if ( ! func ) continue;
@@ -317,7 +317,7 @@
 						cost.decSpec( specCost( param->get_type() ) );
 					}
-					
+
 					cost.incVar( func->forall.size() );
-					
+
 					for ( const ast::TypeDecl * td : func->forall ) {
 						cost.decSpec( td->assertions.size() );
@@ -329,6 +329,6 @@
 		};
 
-		CandidateEnvMerger( 
-			const ast::TypeEnvironment & env, const ast::OpenVarSet & open, 
+		CandidateEnvMerger(
+			const ast::TypeEnvironment & env, const ast::OpenVarSet & open,
 			const ast::SymbolTable & syms )
 		: crnt(), envs{ env }, opens{ open }, symtab( syms ) {}
Index: src/ResolvExpr/SatisfyAssertions.hpp
===================================================================
--- src/ResolvExpr/SatisfyAssertions.hpp	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/SatisfyAssertions.hpp	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -28,6 +28,6 @@
 
 /// Recursively satisfies all assertions provided in a candidate; returns true if succeeds
-void satisfyAssertions( 
-	CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out, 
+void satisfyAssertions(
+	CandidateRef & cand, const ast::SymbolTable & symtab, CandidateList & out,
 	std::vector<std::string> & errors );
 
Index: src/ResolvExpr/SpecCost.cc
===================================================================
--- src/ResolvExpr/SpecCost.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/SpecCost.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -10,8 +10,9 @@
 // Created On       : Tue Oct 02 15:50:00 2018
 // Last Modified By : Andrew Beach
-// Last Modified On : Wed Jun 19 10:43:00 2019
-// Update Count     : 2
-//
-
+// Last Modified On : Wed Jul  3 11:07:00 2019
+// Update Count     : 3
+//
+
+#include <cassert>
 #include <limits>
 #include <list>
@@ -129,4 +130,12 @@
 			typename std::add_pointer<ast::Type const *(typename T::value_type const &)>::type;
 
+		#warning Should use a standard maybe_accept
+		void maybe_accept( ast::Type const * type ) {
+			if ( type ) {
+				auto node = type->accept( *visitor );
+				assert( node == nullptr || node == type );
+			}
+		}
+
 		// Update the minimum to the new lowest non-none value.
 		template<typename T>
@@ -134,5 +143,5 @@
 			for ( const auto & node : list ) {
 				count = -1;
-				mapper( node )->accept( *visitor );
+				maybe_accept( mapper( node ) );
 				if ( count != -1 && count < minimum ) minimum = count;
 			}
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/Unify.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -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,15 @@
 		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 = {};
+		ast::ptr< ast::Type > t1_(newFirst );
+		ast::ptr< ast::Type > t2_(newSecond);
 
 		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 +322,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
@@ -943,9 +933,11 @@
 
 	private:
-		template< typename RefType >
-		const RefType * handleRefType( const RefType * inst, const ast::Type * other ) {
+		// Returns: other, cast as XInstType
+		// Assigns this->result: whether types are compatible (up to generic parameters)
+		template< typename XInstType >
+		const XInstType * handleRefType( const XInstType * inst, const ast::Type * other ) {
 			// check that the other type is compatible and named the same
-			auto otherInst = dynamic_cast< const RefType * >( other );
-			result = otherInst && inst->name == otherInst->name;
+			auto otherInst = dynamic_cast< const XInstType * >( other );
+			this->result = otherInst && inst->name == otherInst->name;
 			return otherInst;
 		}
@@ -968,13 +960,13 @@
 		}
 
-		template< typename RefType >
-		void handleGenericRefType( const RefType * inst, const ast::Type * other ) {
+		template< typename XInstType >
+		void handleGenericRefType( const XInstType * inst, const ast::Type * other ) {
 			// check that other type is compatible and named the same
-			const RefType * inst2 = handleRefType( inst, other );
-			if ( ! inst2 ) return;
+			const XInstType * otherInst = handleRefType( inst, other );
+			if ( ! this->result ) return;
 
 			// check that parameters of types unify, if any
 			const std::vector< ast::ptr< ast::Expr > > & params = inst->params;
-			const std::vector< ast::ptr< ast::Expr > > & params2 = inst2->params;
+			const std::vector< ast::ptr< ast::Expr > > & params2 = otherInst->params;
 
 			auto it = params.begin();
@@ -1202,15 +1194,16 @@
 		// 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 = {};
+		ast::ptr< ast::Type > t1_(t1);
+		ast::ptr< ast::Type > t2_(t2);
 
 		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 +1212,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 {
Index: src/ResolvExpr/typeops.h
===================================================================
--- src/ResolvExpr/typeops.h	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/ResolvExpr/typeops.h	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -10,6 +10,6 @@
 // Created On       : Sun May 17 07:28:22 2015
 // Last Modified By : Andrew Beach
-// Last Modified On : Thu Aug  8 16:36:00 2019
-// Update Count     : 5
+// Last Modified On : Tue Oct  1 09:45:00 2019
+// Update Count     : 6
 //
 
@@ -83,6 +83,6 @@
 		const SymTab::Indexer & indexer, const TypeEnvironment & env );
 	Cost castCost(
-		const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
-		const ast::TypeEnvironment & env );
+		const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+		const ast::SymbolTable & symtab, const ast::TypeEnvironment & env );
 
 	// in ConversionCost.cc
@@ -90,6 +90,6 @@
 		const SymTab::Indexer & indexer, const TypeEnvironment & env );
 	Cost conversionCost(
-		const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
-		const ast::TypeEnvironment & env );
+		const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+		const ast::SymbolTable & symtab, const ast::TypeEnvironment & env );
 
 	// in AlternativeFinder.cc
Index: src/SymTab/Autogen.h
===================================================================
--- src/SymTab/Autogen.h	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/SymTab/Autogen.h	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -21,4 +21,5 @@
 
 #include "AST/Decl.hpp"
+#include "AST/Eval.hpp"
 #include "AST/Expr.hpp"
 #include "AST/Init.hpp"
@@ -265,5 +266,6 @@
 		}
 
-		ast::ptr< ast::Expr > begin, end, cmp, update;
+		ast::ptr< ast::Expr > begin, end;
+		std::string cmp, update;
 
 		if ( forward ) {
@@ -271,14 +273,13 @@
 			begin = ast::ConstantExpr::from_int( loc, 0 );
 			end = array->dimension;
-			cmp = new ast::NameExpr{ loc, "?<?" };
-			update = new ast::NameExpr{ loc, "++?" };
+			cmp = "?<?";
+			update = "++?";
 		} else {
 			// generate: for ( int i = N-1; i >= 0; --i )
-			begin = new ast::UntypedExpr{ 
-				loc, new ast::NameExpr{ loc, "?-?" }, 
-				{ array->dimension, ast::ConstantExpr::from_int( loc, 1 ) } };
+			begin = ast::call( 
+				loc, "?-?", array->dimension, ast::ConstantExpr::from_int( loc, 1 ) );
 			end = ast::ConstantExpr::from_int( loc, 0 );
-			cmp = new ast::NameExpr{ loc, "?>=?" };
-			update = new ast::NameExpr{ loc, "--?" };
+			cmp = "?>=?";
+			update = "--?";
 		}
 
@@ -286,18 +287,15 @@
 			loc, indexName.newName(), new ast::BasicType{ ast::BasicType::SignedInt }, 
 			new ast::SingleInit{ loc, begin } };
-		
-		ast::ptr< ast::Expr > cond = new ast::UntypedExpr{
-			loc, cmp, { new ast::VariableExpr{ loc, index }, end } };
-		
-		ast::ptr< ast::Expr > inc = new ast::UntypedExpr{
-			loc, update, { new ast::VariableExpr{ loc, index } } };
-		
-		ast::ptr< ast::Expr > dstIndex = new ast::UntypedExpr{
-			loc, new ast::NameExpr{ loc, "?[?]" }, 
-			{ dstParam, new ast::VariableExpr{ loc, index } } };
+		ast::ptr< ast::Expr > indexVar = new ast::VariableExpr{ loc, index };
+		
+		ast::ptr< ast::Expr > cond = ast::call( loc, cmp, indexVar, end );
+		
+		ast::ptr< ast::Expr > inc = ast::call( loc, update, indexVar );
+		
+		ast::ptr< ast::Expr > dstIndex = ast::call( loc, "?[?]", dstParam, indexVar );
 		
 		// srcParam must keep track of the array indices to build the source parameter and/or 
 		// array list initializer
-		srcParam.addArrayIndex( new ast::VariableExpr{ loc, index }, array->dimension );
+		srcParam.addArrayIndex( indexVar, array->dimension );
 
 		// for stmt's body, eventually containing call
@@ -385,5 +383,5 @@
 		if ( isUnnamedBitfield( obj ) ) return {};
 
-		ast::ptr< ast::Type > addCast = nullptr;
+		ast::ptr< ast::Type > addCast;
 		if ( (fname == "?{}" || fname == "^?{}") && ( ! obj || ( obj && ! obj->bitfieldWidth ) ) ) {
 			assert( dstParam->result );
Index: src/SynTree/Statement.h
===================================================================
--- src/SynTree/Statement.h	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/SynTree/Statement.h	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -518,5 +518,5 @@
 class ImplicitCtorDtorStmt : public Statement {
   public:
-	// Non-owned pointer to the constructor/destructor statement
+	// the constructor/destructor call statement; owned here for a while, eventually transferred elsewhere
 	Statement * callStmt;
 
Index: src/Tuples/Explode.cc
===================================================================
--- src/Tuples/Explode.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/Tuples/Explode.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -129,5 +129,4 @@
 			for ( const ast::Expr * expr : tupleExpr->exprs ) {
 				exprs.emplace_back( applyCast( expr, false ) );
-				//exprs.emplace_back( ast::ptr< ast::Expr >( applyCast( expr, false ) ) );
 			}
 			if ( first ) {
Index: src/Tuples/Explode.h
===================================================================
--- src/Tuples/Explode.h	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/Tuples/Explode.h	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -210,5 +210,5 @@
 			}
 			// Cast a reference away to a value-type to allow further explosion.
-			if ( dynamic_cast< const ast::ReferenceType *>( local->result.get() ) ) {
+			if ( local->result.as< ast::ReferenceType >() ) {
 				local = new ast::CastExpr{ local, tupleType };
 			}
@@ -220,5 +220,4 @@
 				// delete idx;
 			}
-			// delete local;
 		}
 	} else {
Index: src/Tuples/TupleAssignment.cc
===================================================================
--- src/Tuples/TupleAssignment.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/Tuples/TupleAssignment.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -465,5 +465,5 @@
 					// resolve ctor/dtor for the new object
 					ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
-							InitTweak::genCtorInit( location, ret ), spotter.crntFinder.symtab );
+							InitTweak::genCtorInit( location, ret ), spotter.crntFinder.localSyms );
 					// remove environments from subexpressions of stmtExpr
 					ast::Pass< EnvRemover > rm{ env };
@@ -560,5 +560,5 @@
 					// resolve the cast expression so that rhsCand return type is bound by the cast
 					// type as needed, and transfer the resulting environment
-					ResolvExpr::CandidateFinder finder{ spotter.crntFinder.symtab, env };
+					ResolvExpr::CandidateFinder finder{ spotter.crntFinder.localSyms, env };
 					finder.find( rhsCand->expr, ResolvExpr::ResolvMode::withAdjustment() );
 					assert( finder.candidates.size() == 1 );
@@ -609,5 +609,5 @@
 					// explode the LHS so that each field of a tuple-valued expr is assigned
 					ResolvExpr::CandidateList lhs;
-					explode( *lhsCand, crntFinder.symtab, back_inserter(lhs), true );
+					explode( *lhsCand, crntFinder.localSyms, back_inserter(lhs), true );
 					for ( ResolvExpr::CandidateRef & cand : lhs ) {
 						// each LHS value must be a reference - some come in with a cast, if not
@@ -629,5 +629,5 @@
 							if ( isTuple( rhsCand->expr ) ) {
 								// multiple assignment
-								explode( *rhsCand, crntFinder.symtab, back_inserter(rhs), true );
+								explode( *rhsCand, crntFinder.localSyms, back_inserter(rhs), true );
 								matcher.reset(
 									new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } );
@@ -648,5 +648,5 @@
 							// multiple assignment
 							ResolvExpr::CandidateList rhs;
-							explode( rhsCand, crntFinder.symtab, back_inserter(rhs), true );
+							explode( rhsCand, crntFinder.localSyms, back_inserter(rhs), true );
 							matcher.reset(
 								new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } );
@@ -678,5 +678,5 @@
 				)
 
-				ResolvExpr::CandidateFinder finder{ crntFinder.symtab, matcher->env };
+				ResolvExpr::CandidateFinder finder{ crntFinder.localSyms, matcher->env };
 
 				try {
Index: src/Tuples/TupleExpansion.cc
===================================================================
--- src/Tuples/TupleExpansion.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/Tuples/TupleExpansion.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -323,5 +323,5 @@
 		std::vector<ast::ptr<ast::Type>> types;
 		ast::CV::Qualifiers quals{
-			ast::CV::Const | ast::CV::Volatile | ast::CV::Restrict | ast::CV::Lvalue |
+			ast::CV::Const | ast::CV::Volatile | ast::CV::Restrict |
 			ast::CV::Atomic | ast::CV::Mutex };
 
Index: src/main.cc
===================================================================
--- src/main.cc	(revision 309012709e091dfaf06aaeec56491e73a68b71a1)
+++ src/main.cc	(revision 7030dab75c493bbd89de53cb88e496019c794ff4)
@@ -31,5 +31,5 @@
 using namespace std;
 
-
+#include "AST/Convert.hpp"
 #include "CompilationState.h"
 #include "../config.h"                      // for CFA_LIBDIR
@@ -339,5 +339,11 @@
 		} // if
 
-		PASS( "Resolve", ResolvExpr::resolve( translationUnit ) );
+		// PASS( "Resolve", ResolvExpr::resolve( translationUnit ) );
+		{
+			auto transUnit = convert( move( translationUnit ) );
+			PASS( "Resolve", ResolvExpr::resolve( transUnit ) );
+			translationUnit = convert( move( transUnit ) );
+		}
+
 		if ( exprp ) {
 			dump( translationUnit );
