Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Convert.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -205,5 +205,10 @@
 		ftype->parameters = get<DeclarationWithType>().acceptL(node->params);
 
-		ftype->forall = get<TypeDecl>().acceptL( node->type->forall );
+		ftype->forall = get<TypeDecl>().acceptL( node->type_params );
+		if (!node->assertions.empty()) {
+			assert(!ftype->forall.empty());
+			// find somewhere to place assertions back, for convenience it is the last slot
+			ftype->forall.back()->assertions = get<DeclarationWithType>().acceptL(node->assertions);
+		}
 
 		visitType(node->type, ftype);
@@ -602,11 +607,6 @@
 
 		for (decltype(src->begin()) src_i = src->begin(); src_i != src->end(); src_i++) {
-			rslt->add( src_i->first,
+			rslt->add( src_i->first.typeString(),
 			           get<Type>().accept1(src_i->second) );
-		}
-
-		for (decltype(src->beginVar()) src_i = src->beginVar(); src_i != src->endVar(); src_i++) {
-			rslt->addVar( src_i->first,
-			              get<Expression>().accept1(src_i->second) );
 		}
 
@@ -1212,5 +1212,25 @@
 		// ty->returnVals = get<DeclarationWithType>().acceptL( node->returns );
 		// ty->parameters = get<DeclarationWithType>().acceptL( node->params );
-		ty->forall = get<TypeDecl>().acceptL( node->forall );
+
+		auto types = get<TypeInstType>().acceptL( node->forall );
+		for (auto t : types) {
+			auto newT = new TypeDecl(*t->baseType);
+			newT->name = t->name; // converted by typeString()
+			for (auto asst : newT->assertions) delete asst;
+			newT->assertions.clear();
+			ty->forall.push_back(newT);
+		}
+		auto assts = get<VariableExpr>().acceptL( node->assertions );
+		if (!assts.empty()) {
+			assert(!types.empty());
+			for (auto asst : assts) {
+				auto newDecl = new ObjectDecl(*strict_dynamic_cast<ObjectDecl*>(asst->var));
+				delete newDecl->type;
+				newDecl->type = asst->result->clone();
+				newDecl->storageClasses.is_extern = true; // hack
+				ty->forall.back()->assertions.push_back(newDecl);
+			}
+		}
+
 		return visitType( node, ty );
 	}
@@ -1299,5 +1319,5 @@
 			ty = new TypeInstType{
 				cv( node ),
-				node->name,
+				node->typeString(),
 				get<TypeDecl>().accept1( node->base ),
 				get<Attribute>().acceptL( node->attributes )
@@ -1306,5 +1326,5 @@
 			ty = new TypeInstType{
 				cv( node ),
-				node->name,
+				node->typeString(),
 				node->kind == ast::TypeDecl::Ftype,
 				get<Attribute>().acceptL( node->attributes )
@@ -1431,5 +1451,5 @@
 	/// at conversion stage, all created nodes are guaranteed to be unique, therefore
 	/// const_casting out of smart pointers is permitted.
-	std::unordered_map< const BaseSyntaxNode *, ast::ptr<ast::Node> > cache = {};
+	std::unordered_map< const BaseSyntaxNode *, ast::readonly<ast::Node> > cache = {};
 
 	// Local Utilities:
@@ -1565,4 +1585,16 @@
 		// can function type have attributes? seems not to be the case.
 		// visitType(old->type, ftype);
+
+		// collect assertions and put directly in FunctionDecl
+		std::vector<ast::ptr<ast::DeclWithType>> assertions;
+		for (auto & param: forall) {
+			for (auto & asst: param->assertions) {
+				assertf(asst->unique(), "newly converted decl must be unique");
+				assertions.emplace_back(asst);
+			}
+			auto mut = param.get_and_mutate();
+			assertf(mut == param, "newly converted decl must be unique");
+			mut->assertions.clear();
+		}
 
 		auto decl = new ast::FunctionDecl{
@@ -1584,4 +1616,5 @@
 		cache.emplace( old, decl );
 
+		decl->assertions = std::move(assertions);
 		decl->withExprs = GET_ACCEPT_V(withExprs, Expr);
 		decl->stmts = GET_ACCEPT_1(statements, CompoundStmt);
@@ -2066,8 +2099,12 @@
 	}
 
+	// TypeSubstitution shouldn't exist yet in old.
 	ast::TypeSubstitution * convertTypeSubstitution(const TypeSubstitution * old) {
-
+		
 		if (!old) return nullptr;
-
+		if (old->empty()) return nullptr;
+		assert(false);
+
+		/*
 		ast::TypeSubstitution *rslt = new ast::TypeSubstitution();
 
@@ -2077,10 +2114,6 @@
 		}
 
-		for (decltype(old->beginVar()) old_i = old->beginVar(); old_i != old->endVar(); old_i++) {
-			rslt->addVar( old_i->first,
-			              getAccept1<ast::Expr>(old_i->second) );
-		}
-
 		return rslt;
+		*/
 	}
 
@@ -2610,5 +2643,14 @@
 			ty->params.emplace_back(v->get_type());
 		}
-		ty->forall = GET_ACCEPT_V( forall, TypeDecl );
+		// xxx - when will this be non-null?
+		// will have to create dangling (no-owner) decls to be pointed to
+		auto foralls = GET_ACCEPT_V( forall, TypeDecl );
+
+		for (auto & param : foralls) {
+			ty->forall.emplace_back(new ast::TypeInstType(param->name, param));
+			for (auto asst : param->assertions) {
+				ty->assertions.emplace_back(new ast::VariableExpr({}, asst));
+			}
+		}
 		visitType( old, ty );
 	}
Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Decl.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -50,20 +50,22 @@
 
 FunctionDecl::FunctionDecl( const CodeLocation & loc, const std::string & name, 
-		std::vector<ptr<TypeDecl>>&& forall,
-		std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
-		CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage,
-		std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs)
-	: DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
-	  stmts( stmts ) {
-		  FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs));
-		  for (auto & param : this->params) {
-			  ftype->params.emplace_back(param->get_type());
-		  }
-		  for (auto & ret : this->returns) {
-			  ftype->returns.emplace_back(ret->get_type());
-		  }
-		  ftype->forall = std::move(forall);
-		  this->type = ftype;
-	  }
+	std::vector<ptr<TypeDecl>>&& forall,
+	std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
+	CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage,
+	std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs)
+: DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
+	type_params(std::move(forall)), stmts( stmts ) {
+	FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs));
+	for (auto & param : this->params) {
+		ftype->params.emplace_back(param->get_type());
+	}
+	for (auto & ret : this->returns) {
+		ftype->returns.emplace_back(ret->get_type());
+	}
+	for (auto & tp : this->type_params) {
+		ftype->forall.emplace_back(new TypeInstType(tp->name, tp));
+	}
+	this->type = ftype;
+}
 
 
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Decl.hpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -132,4 +132,7 @@
 	std::vector< ptr<Expr> > withExprs;
 
+	std::vector<ptr<TypeDecl>> type_params;
+	std::vector<ptr<DeclWithType>> assertions;
+
 	FunctionDecl( const CodeLocation & loc, const std::string & name, std::vector<ptr<TypeDecl>>&& forall,
 		std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Expr.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -206,40 +206,5 @@
 	assert( aggregate->result );
 
-	// 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());
+	result = mem->get_type();
 
 	// substitute aggregate generic parameters into member type
Index: src/AST/ForallSubstitutionTable.cpp
===================================================================
--- src/AST/ForallSubstitutionTable.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ 	(revision )
@@ -1,54 +1,0 @@
-//
-// 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 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ 	(revision )
@@ -1,57 +1,0 @@
-//
-// 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 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ 	(revision )
@@ -1,70 +1,0 @@
-//
-// 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
-	FunctionType::ForallList operator() ( const FunctionType::ForallList & o ) {
-		return subs.clone( o, *visitor );
-	}
-
-	template<typename node_t > 
-	std::vector<ptr<node_t>> operator() (const std::vector<ptr<node_t>> & o) {
-		std::vector<ptr<node_t>> n;
-		n.reserve(o.size());
-		for (const node_t * d : o) { n.emplace_back(d->accept(*visitor)); }
-		return n;
-	}
-	
-	/*
-
-	/// 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/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Pass.hpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -34,6 +34,4 @@
 
 #include "AST/SymbolTable.hpp"
-
-#include "AST/ForallSubstitutionTable.hpp"
 
 // Private prelude header, needed for some of the magic tricks this class pulls off
@@ -66,5 +64,4 @@
 // | WithVisitorRef        - provides an pointer to the templated visitor wrapper
 // | WithSymbolTable       - provides symbol table functionality
-// | WithForallSubstitutor - maintains links between TypeInstType and TypeDecl under mutation
 //
 // Other Special Members:
@@ -258,8 +255,4 @@
 	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
@@ -398,9 +391,4 @@
 };
 
-/// 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 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Pass.impl.hpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -367,20 +367,4 @@
 	}
 
-
-	template< typename core_t >
-	template< typename node_t >
-	void ast::Pass< core_t >::mutate_forall( const node_t *& node ) {
-		if ( auto subs = __pass::forall::subs( core, 0 ) ) {
-			// tracking TypeDecl substitution, full clone
-			if ( node->forall.empty() ) return;
-
-			node_t * mut = __pass::mutate<core_t>( node );
-			mut->forall = subs->clone( node->forall, *this );
-			node = mut;
-		} else {
-			// not tracking TypeDecl substitution, just mutate
-			maybe_accept( node, &node_t::forall );
-		}
-	}
 }
 
@@ -504,9 +488,10 @@
 			__pass::symtab::addId( core, 0, func );
 			VISIT(
-				// parameter declarations are now directly here
+				// parameter declarations
 				maybe_accept( node, &FunctionDecl::params );
 				maybe_accept( node, &FunctionDecl::returns );
-				// foralls are still in function type
-				maybe_accept( node, &FunctionDecl::type );
+				// type params and assertions
+				maybe_accept( node, &FunctionDecl::type_params );
+				maybe_accept( node, &FunctionDecl::assertions );
 				// First remember that we are now within a function.
 				ValueGuard< bool > oldInFunction( inFunction );
@@ -1758,6 +1743,7 @@
 
 	VISIT({
-		guard_forall_subs forall_guard { *this, node };
-		mutate_forall( node );
+		// guard_forall_subs forall_guard { *this, node };
+		// mutate_forall( node );
+		maybe_accept( node, &FunctionType::assertions );
 		maybe_accept( node, &FunctionType::returns );
 		maybe_accept( node, &FunctionType::params  );
@@ -1981,5 +1967,5 @@
 		{
 			bool mutated = false;
-			std::unordered_map< std::string, ast::ptr< ast::Type > > new_map;
+			std::unordered_map< ast::TypeInstType::TypeEnvKey, ast::ptr< ast::Type > > new_map;
 			for ( const auto & p : node->typeEnv ) {
 				guard_symtab guard { *this };
@@ -1994,20 +1980,4 @@
 			}
 		}
-
-		{
-			bool mutated = false;
-			std::unordered_map< std::string, ast::ptr< ast::Expr > > new_map;
-			for ( const auto & p : node->varEnv ) {
-				guard_symtab guard { *this };
-				auto new_node = p.second->accept( *this );
-				if (new_node != p.second) mutated = true;
-				new_map.insert({ p.first, new_node });
-			}
-			if (mutated) {
-				auto new_node = __pass::mutate<core_t>( node );
-				new_node->varEnv.swap( new_map );
-				node = new_node;
-			}
-		}
 	)
 
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Pass.proto.hpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -413,13 +413,4 @@
 		static inline auto leave( core_t &, long, const ast::FunctionType * ) {}
 
-		// Get the substitution table, if present
-		template<typename core_t>
-		static inline auto subs( core_t & core, int ) -> decltype( &core.subs ) {
-			return &core.subs;
-		}
-
-		template<typename core_t>
-		static inline ast::ForallSubstitutionTable * subs( core_t &, long ) { return nullptr; }
-
 		// Replaces a TypeInstType's base TypeDecl according to the table
 		template<typename core_t>
Index: src/AST/Print.cpp
===================================================================
--- src/AST/Print.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Print.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -155,4 +155,13 @@
 	}
 
+	void print( const ast::FunctionType::AssertionList & assts ) {
+		if (assts.empty()) return;
+		os << "with assertions" << endl;
+		++indent;
+		printAll(assts);
+		os << indent;
+		--indent;
+	}
+
 	void print( const std::vector<ptr<Attribute>> & attrs ) {
 		if ( attrs.empty() ) return;
@@ -206,6 +215,5 @@
 	void preprint( const ast::NamedTypeDecl * node ) {
 		if ( ! node->name.empty() ) {
-			if( deterministic_output && isUnboundType(node->name) ) os << "[unbound]:";
-			else os << node->name << ": ";
+			os << node->name << ": ";
 		}
 
@@ -261,4 +269,5 @@
 	void preprint( const ast::FunctionType * node ) {
 		print( node->forall );
+		print( node->assertions );
 		print( node->qualifiers );
 	}
@@ -1375,5 +1384,5 @@
 	virtual const ast::Type * visit( const ast::TypeInstType * node ) override final {
 		preprint( node );
-		const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node->name;
+		const auto & _name = deterministic_output && isUnboundType(node) ? "[unbound]" : node->typeString();
 		os << "instance of type " << _name
 		   << " (" << (node->kind == ast::TypeDecl::Ftype ? "" : "not ") << "function type)";
@@ -1502,15 +1511,7 @@
 		os << indent << "Types:" << endl;
 		for ( const auto& i : *node ) {
-			os << indent+1 << i.first << " -> ";
+			os << indent+1 << i.first.typeString() << " -> ";
 			indent += 2;
 			safe_print( i.second );
-			indent -= 2;
-			os << endl;
-		}
-		os << indent << "Non-types:" << endl;
-		for ( auto i = node->beginVar(); i != node->endVar(); ++i ) {
-			os << indent+1 << i->first << " -> ";
-			indent += 2;
-			safe_print( i->second );
 			indent -= 2;
 			os << endl;
Index: src/AST/SymbolTable.cpp
===================================================================
--- src/AST/SymbolTable.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/SymbolTable.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -414,5 +414,11 @@
 
 void SymbolTable::addFunction( const FunctionDecl * func ) {
-	addTypes( func->type->forall );
+	for (auto & td : func->type_params) {
+		addType(td);
+	}
+	for (auto & asst : func->assertions) {
+		addId(asst);
+	}
+	// addTypes( func->type->forall );
 	addIds( func->returns );
 	addIds( func->params );
Index: src/AST/Type.cpp
===================================================================
--- src/AST/Type.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Type.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -21,5 +21,4 @@
 
 #include "Decl.hpp"
-#include "ForallSubstitutor.hpp" // for substituteForall
 #include "Init.hpp"
 #include "Common/utility.h"      // for copy, move
@@ -92,24 +91,5 @@
 // GENERATED END
 
-// --- ParameterizedType
-
-void FunctionType::initWithSub(
-	const FunctionType & o, Pass< ForallSubstitutor > & sub
-) {
-	forall = sub.core( o.forall );
-}
-
 // --- FunctionType
-
-
-FunctionType::FunctionType( const FunctionType & o )
-: Type( o.qualifiers, copy( o.attributes ) ), returns(), params(),
-  isVarArgs( o.isVarArgs ) {
-	Pass< ForallSubstitutor > sub;
-	initWithSub( o, sub );           // initialize substitution map
-	returns = sub.core( o.returns ); // apply to return and parameter types
-	params = sub.core( o.params );
-}
-
 namespace {
 	bool containsTtype( const std::vector<ptr<Type>> & l ) {
@@ -199,16 +179,5 @@
 		// TODO: once TypeInstType representation is updated, it should properly check
 		// if the context id is filled. this is a temporary hack for now
-		return isUnboundType(typeInst->name);
-	}
-	return false;
-}
-
-bool isUnboundType(const std::string & tname) {
-	// xxx - look for a type name produced by renameTyVars.
-
-	// TODO: once TypeInstType representation is updated, it should properly check
-	// if the context id is filled. this is a temporary hack for now
-	if (std::count(tname.begin(), tname.end(), '_') >= 3) {
-		return true;
+		return typeInst->formal_usage > 0;
 	}
 	return false;
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/Type.hpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -36,6 +36,4 @@
 
 template< typename T > class Pass;
-
-struct ForallSubstitutor;
 
 class Type : public Node {
@@ -272,10 +270,9 @@
 /// Type of a function `[R1, R2](*)(P1, P2, P3)`
 class FunctionType final : public Type {
-	protected:
-	/// initializes forall with substitutor
-	void initWithSub( const FunctionType & o, Pass< ForallSubstitutor > & sub );
-public:
-	using ForallList = std::vector<ptr<TypeDecl>>;
+public:
+	using ForallList = std::vector<ptr<TypeInstType>>;
+	using AssertionList = std::vector<ptr<VariableExpr>>;
 	ForallList forall;
+	AssertionList assertions;
 
 	std::vector<ptr<Type>> returns;
@@ -292,5 +289,5 @@
 	: Type(q), returns(), params(), isVarArgs(va) {}
 
-	FunctionType( const FunctionType & o );
+	FunctionType( const FunctionType & o ) = default;
 
 	/// true if either the parameters or return values contain a tttype
@@ -397,5 +394,26 @@
 public:
 	readonly<TypeDecl> base;
+	// previously from renameTyVars; now directly use integer fields instead of synthesized strings
+	// a nonzero value of formal_usage indicates a formal type (only used in function type)
+	// a zero value of formal_usage indicates an actual type (referenced inside body of parametric structs and functions)
 	TypeDecl::Kind kind;
+	int formal_usage;
+	int expr_id;
+
+	// compact representation used for map lookups.
+	struct TypeEnvKey { 
+		const TypeDecl * base;
+		int formal_usage;
+		int expr_id;
+
+		TypeEnvKey() = default;
+		TypeEnvKey(const TypeDecl * base, int formal_usage = 0, int expr_id = 0): base(base), formal_usage(formal_usage), expr_id(expr_id) {}
+		TypeEnvKey(const TypeInstType & inst): base(inst.base), formal_usage(inst.formal_usage), expr_id(inst.expr_id) {}
+		std::string typeString() const { return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + base->name; }
+		bool operator==(const TypeEnvKey & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; }
+
+	};
+
+	bool operator==(const TypeInstType & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; }
 
 	TypeInstType(
@@ -409,4 +427,7 @@
 	TypeInstType( const TypeInstType & o ) = default;
 
+	TypeInstType( const TypeEnvKey & key )
+	: BaseInstType(key.base->name), base(key.base), kind(key.base->kind), formal_usage(key.formal_usage), expr_id(key.expr_id) {}
+
 	/// sets `base`, updating `kind` correctly
 	void set_base( const TypeDecl * );
@@ -418,4 +439,9 @@
 
 	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
+
+	std::string typeString() const { 
+		if (formal_usage > 0) return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + name; 
+		else return name;
+	}
 private:
 	TypeInstType * clone() const override { return new TypeInstType{ *this }; }
@@ -510,6 +536,18 @@
 
 bool isUnboundType(const Type * type);
-bool isUnboundType(const std::string & tname);
-
+
+}
+
+namespace std {
+	template<>
+	struct hash<typename ast::TypeInstType::TypeEnvKey> {
+		size_t operator() (const ast::TypeInstType::TypeEnvKey & x) const {
+			const size_t p = 1000007;
+			size_t res = reinterpret_cast<size_t>(x.base);
+			res = p * res + x.formal_usage;
+			res = p * res + x.expr_id;
+			return res;
+		} 
+	};
 }
 
Index: src/AST/TypeEnvironment.cpp
===================================================================
--- src/AST/TypeEnvironment.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/TypeEnvironment.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -52,5 +52,5 @@
 	for ( const auto & i : open ) {
 		if ( first ) { first = false; } else { out << ' '; }
-		out << i.first << "(" << i.second << ")";
+		out << i.first.typeString() << "(" << i.second << ")";
 	}
 }
@@ -62,6 +62,9 @@
 		if(first) first = false;
 		else out << " ";
-		if( deterministic_output && isUnboundType(var) ) out << "[unbound]";
-		else out << var;
+
+		if( deterministic_output ) out << "[unbound]";
+		else out << "_" << var.formal_usage << "_" << var.expr_id << "_";
+
+		out << var.base->name;
 	}
 	out << ")";
@@ -79,5 +82,5 @@
 }
 
-const EqvClass * TypeEnvironment::lookup( const std::string & var ) const {
+const EqvClass * TypeEnvironment::lookup( const TypeInstType::TypeEnvKey & var ) const {
 	for ( ClassList::const_iterator i = env.begin(); i != env.end(); ++i ) {
 		if ( i->vars.find( var ) != i->vars.end() ) return &*i;
@@ -106,5 +109,5 @@
 
 void TypeEnvironment::add( const FunctionType::ForallList & tyDecls ) {
-	for ( const TypeDecl * tyDecl : tyDecls ) {
+	for ( auto & tyDecl : tyDecls ) {
 		env.emplace_back( tyDecl );
 	}
@@ -119,12 +122,14 @@
 void TypeEnvironment::writeToSubstitution( TypeSubstitution & sub ) const {
 	for ( const auto & clz : env ) {
-		std::string clzRep;
+		TypeInstType::TypeEnvKey clzRep;
+		bool first = true;
 		for ( const auto & var : clz.vars ) {
 			if ( clz.bound ) {
 				sub.add( var, clz.bound );
-			} else if ( clzRep.empty() ) {
+			} else if ( first ) {
 				clzRep = var;
+				first = false;
 			} else {
-				sub.add( var, new TypeInstType{ clzRep, clz.data.kind } );
+				sub.add( var, new TypeInstType{ clzRep } );
 			}
 		}
@@ -141,8 +146,8 @@
 	struct Occurs : public ast::WithVisitorRef<Occurs> {
 		bool result;
-		std::set< std::string > vars;
+		std::unordered_set< TypeInstType::TypeEnvKey > vars;
 		const TypeEnvironment & tenv;
 
-		Occurs( const std::string & var, const TypeEnvironment & env )
+		Occurs( const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env )
 		: result( false ), vars(), tenv( env ) {
 			if ( const EqvClass * clz = tenv.lookup( var ) ) {
@@ -154,7 +159,7 @@
 
 		void previsit( const TypeInstType * typeInst ) {
-			if ( vars.count( typeInst->name ) ) {
+			if ( vars.count( *typeInst ) ) {
 				result = true;
-			} else if ( const EqvClass * clz = tenv.lookup( typeInst->name ) ) {
+			} else if ( const EqvClass * clz = tenv.lookup( *typeInst ) ) {
 				if ( clz->bound ) {
 					clz->bound->accept( *visitor );
@@ -165,5 +170,5 @@
 
 	/// true if `var` occurs in `ty` under `env`
-	bool occurs( const Type * ty, const std::string & var, const TypeEnvironment & env ) {
+	bool occurs( const Type * ty, const TypeInstType::TypeEnvKey & var, const TypeEnvironment & env ) {
 		Pass<Occurs> occur{ var, env };
 		maybe_accept( ty, occur );
@@ -280,10 +285,10 @@
 	// remove references from bound type, so that type variables can only bind to value types
 	ptr<Type> target = bindTo->stripReferences();
-	auto tyvar = open.find( typeInst->name );
+	auto tyvar = open.find( *typeInst );
 	assert( tyvar != open.end() );
 	if ( ! tyVarCompatible( tyvar->second, target ) ) return false;
-	if ( occurs( target, typeInst->name, *this ) ) return false;
-
-	auto it = internal_lookup( typeInst->name );
+	if ( occurs( target, *typeInst, *this ) ) return false;
+
+	auto it = internal_lookup( *typeInst );
 	if ( it != env.end() ) {
 		if ( it->bound ) {
@@ -308,5 +313,5 @@
 	} else {
 		env.emplace_back(
-			typeInst->name, target, widen.first && widen.second, data );
+			*typeInst, target, widen.first && widen.second, data );
 	}
 	return true;
@@ -318,6 +323,6 @@
 		WidenMode widen, const SymbolTable & symtab
 ) {
-	auto c1 = internal_lookup( var1->name );
-	auto c2 = internal_lookup( var2->name );
+	auto c1 = internal_lookup( *var1 );
+	auto c2 = internal_lookup( *var2 );
 
 	// exit early if variables already bound together
@@ -333,5 +338,5 @@
 	if ( c1 != env.end() ) {
 		if ( c1->bound ) {
-			if ( occurs( c1->bound, var2->name, *this ) ) return false;
+			if ( occurs( c1->bound, *var2, *this ) ) return false;
 			type1 = c1->bound;
 		}
@@ -340,5 +345,5 @@
 	if ( c2 != env.end() ) {
 		if ( c2->bound ) {
-			if ( occurs( c2->bound, var1->name, *this ) ) return false;
+			if ( occurs( c2->bound, *var1, *this ) ) return false;
 			type2 = c2->bound;
 		}
@@ -378,15 +383,15 @@
 	} else if ( c1 != env.end() ) {
 		// var2 unbound, add to env[c1]
-		c1->vars.emplace( var2->name );
+		c1->vars.emplace( *var2 );
 		c1->allowWidening = widen1;
 		c1->data.isComplete |= data.isComplete;
 	} else if ( c2 != env.end() ) {
 		// var1 unbound, add to env[c2]
-		c2->vars.emplace( var1->name );
+		c2->vars.emplace( *var1 );
 		c2->allowWidening = widen2;
 		c2->data.isComplete |= data.isComplete;
 	} else {
 		// neither var bound, create new class
-		env.emplace_back( var1->name, var2->name, widen1 && widen2, data );
+		env.emplace_back( *var1, *var2, widen1 && widen2, data );
 	}
 
@@ -452,5 +457,5 @@
 }
 
-TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const std::string & var ) {
+TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const TypeInstType::TypeEnvKey & var ) {
 	for ( ClassList::iterator i = env.begin(); i != env.end(); ++i ) {
 		if ( i->vars.count( var ) ) return i;
Index: src/AST/TypeEnvironment.hpp
===================================================================
--- src/AST/TypeEnvironment.hpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/TypeEnvironment.hpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -55,7 +55,7 @@
 /// recorded. More investigation is needed.
 struct AssertCompare {
-	bool operator()( const DeclWithType * d1, const DeclWithType * d2 ) const {
-		int cmp = d1->name.compare( d2->name );
-		return cmp < 0 || ( cmp == 0 && d1->get_type() < d2->get_type() );
+	bool operator()( const VariableExpr * d1, const VariableExpr * d2 ) const {
+		int cmp = d1->var->name.compare( d2->var->name );
+		return cmp < 0 || ( cmp == 0 && d1->result < d2->result );
 	}
 };
@@ -70,8 +70,8 @@
 
 /// Set of assertions pending satisfaction
-using AssertionSet = std::map< readonly<DeclWithType>, AssertionSetValue, AssertCompare >;
+using AssertionSet = std::map< const VariableExpr *, AssertionSetValue, AssertCompare >;
 
 /// Set of open variables
-using OpenVarSet = std::unordered_map< std::string, TypeDecl::Data >;
+using OpenVarSet = std::unordered_map< TypeInstType::TypeEnvKey, TypeDecl::Data >;
 
 /// Merges one set of open vars into another
@@ -89,5 +89,5 @@
 /// they bind to.
 struct EqvClass {
-	std::set< std::string > vars;
+	std::unordered_set< TypeInstType::TypeEnvKey > vars;
 	ptr<Type> bound;
 	bool allowWidening;
@@ -101,13 +101,13 @@
 
 	/// Singleton class constructor from TypeDecl
-	EqvClass( const TypeDecl * decl )
-	: vars{ decl->name }, bound(), allowWidening( true ), data( decl ) {}
+	EqvClass( const TypeInstType * inst )
+	: vars{ *inst }, bound(), allowWidening( true ), data( inst->base ) {}
 
 	/// Singleton class constructor from substitution
-	EqvClass( const std::string & v, const Type * b )
+	EqvClass( const TypeInstType::TypeEnvKey & v, const Type * b )
 	: vars{ v }, bound( b ), allowWidening( false ), data( TypeDecl::Dtype, false ) {}
 
 	/// Single-var constructor (strips qualifiers from bound type)
-	EqvClass( const std::string & v, const Type * b, bool w, const TypeDecl::Data & d )
+	EqvClass( const TypeInstType::TypeEnvKey & v, const Type * b, bool w, const TypeDecl::Data & d )
 	: vars{ v }, bound( b ), allowWidening( w ), data( d ) {
 		reset_qualifiers( bound );
@@ -115,5 +115,5 @@
 
 	/// Double-var constructor
-	EqvClass( const std::string & v, const std::string & u, bool w, const TypeDecl::Data & d )
+	EqvClass( const TypeInstType::TypeEnvKey & v, const TypeInstType::TypeEnvKey & u, bool w, const TypeDecl::Data & d )
 	: vars{ v, u }, bound(), allowWidening( w ), data( d ) {}
 
@@ -131,5 +131,5 @@
 public:
 	/// Finds the equivalence class containing a variable; nullptr for none such
-	const EqvClass * lookup( const std::string & var ) const;
+	const EqvClass * lookup( const TypeInstType::TypeEnvKey & var ) const;
 
 	/// Add a new equivalence class for each type variable
@@ -207,5 +207,5 @@
 
 	/// Private lookup API; returns array index of string, or env.size() for not found
-	ClassList::iterator internal_lookup( const std::string & );
+	ClassList::iterator internal_lookup( const TypeInstType::TypeEnvKey & );
 };
 
Index: src/AST/TypeSubstitution.cpp
===================================================================
--- src/AST/TypeSubstitution.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/TypeSubstitution.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -39,5 +39,4 @@
 void TypeSubstitution::initialize( const TypeSubstitution &src, TypeSubstitution &dest ) {
 	dest.typeEnv.clear();
-	dest.varEnv.clear();
 	dest.add( src );
 }
@@ -47,26 +46,23 @@
 		typeEnv[ i->first ] = i->second;
 	} // for
-	for ( VarEnvType::const_iterator i = other.varEnv.begin(); i != other.varEnv.end(); ++i ) {
-		varEnv[ i->first ] = i->second;
-	} // for
 }
 
-void TypeSubstitution::add( std::string formalType, const Type *actualType ) {
-	typeEnv[ formalType ] = actualType;
+void TypeSubstitution::add( const TypeInstType * formalType, const Type *actualType ) {
+	typeEnv[ *formalType ] = actualType;
 }
 
-void TypeSubstitution::addVar( std::string formalExpr, const Expr *actualExpr ) {
-	varEnv[ formalExpr ] = actualExpr;
+void TypeSubstitution::add( const TypeInstType::TypeEnvKey & key, const Type * actualType) {
+	typeEnv[ key ] = actualType;
 }
 
-void TypeSubstitution::remove( std::string formalType ) {
-	TypeEnvType::iterator i = typeEnv.find( formalType );
+void TypeSubstitution::remove( const TypeInstType * formalType ) {
+	TypeEnvType::iterator i = typeEnv.find( *formalType );
 	if ( i != typeEnv.end() ) {
-		typeEnv.erase( formalType );
+		typeEnv.erase( *formalType );
 	} // if
 }
 
-const Type *TypeSubstitution::lookup( std::string formalType ) const {
-	TypeEnvType::const_iterator i = typeEnv.find( formalType );
+const Type *TypeSubstitution::lookup( const TypeInstType * formalType ) const {
+	TypeEnvType::const_iterator i = typeEnv.find( *formalType );
 
 	// break on not in substitution set
@@ -75,11 +71,9 @@
 	// attempt to transitively follow TypeInstType links.
 	while ( const TypeInstType *actualType = i->second.as<TypeInstType>()) {
-		const std::string& typeName = actualType->name;
-
 		// break cycles in the transitive follow
-		if ( formalType == typeName ) break;
+		if ( *formalType == *actualType ) break;
 
 		// Look for the type this maps to, returning previous mapping if none-such
-		i = typeEnv.find( typeName );
+		i = typeEnv.find( *actualType );
 		if ( i == typeEnv.end() ) return actualType;
 	}
@@ -90,5 +84,5 @@
 
 bool TypeSubstitution::empty() const {
-	return typeEnv.empty() && varEnv.empty();
+	return typeEnv.empty();
 }
 
@@ -98,8 +92,10 @@
 		TypeSubstitution * newEnv;
 		EnvTrimmer( const TypeSubstitution * env, TypeSubstitution * newEnv ) : env( env ), newEnv( newEnv ){}
-		void previsit( TypeDecl * tyDecl ) {
+		void previsit( FunctionType * ftype ) {
 			// transfer known bindings for seen type variables
-			if ( const Type * t = env->lookup( tyDecl->name ) ) {
-				newEnv->add( tyDecl->name, t );
+			for (auto & formal : ftype->forall) {
+				if ( const Type * t = env->lookup( formal ) ) {
+					newEnv->add( formal, t );
+				}
 			}
 		}
@@ -130,8 +126,8 @@
 
 const Type * TypeSubstitution::Substituter::postvisit( const TypeInstType *inst ) {
-	BoundVarsType::const_iterator bound = boundVars.find( inst->name );
+	BoundVarsType::const_iterator bound = boundVars.find( *inst );
 	if ( bound != boundVars.end() ) return inst;
 
-	TypeEnvType::const_iterator i = sub.typeEnv.find( inst->name );
+	TypeEnvType::const_iterator i = sub.typeEnv.find( *inst );
 	if ( i == sub.typeEnv.end() ) {
 		return inst;
@@ -141,5 +137,5 @@
 		// TODO: investigate preventing type variables from being bound to themselves in the first place.
 		if ( const TypeInstType * replacement = i->second.as<TypeInstType>() ) {
-			if ( inst->name == replacement->name ) {
+			if ( *inst == *replacement ) {
 				return inst;
 			}
@@ -156,24 +152,15 @@
 }
 
-const Expr * TypeSubstitution::Substituter::postvisit( const NameExpr * nameExpr ) {
-	VarEnvType::const_iterator i = sub.varEnv.find( nameExpr->name );
-	if ( i == sub.varEnv.end() ) {
-		return nameExpr;
-	} else {
-		subCount++;
-		return i->second;
-	} // if
-}
-
 void TypeSubstitution::Substituter::previsit( const FunctionType * ptype ) {
 	GuardValue( boundVars );
 	// bind type variables from forall-qualifiers
 	if ( freeOnly ) {
-		for ( const TypeDecl * tyvar : ptype->forall ) {
-				boundVars.insert( tyvar->name );
+		for ( auto & tyvar : ptype->forall ) {
+				boundVars.insert( *tyvar );
 		} // for
 	} // if
 }
 
+/*
 void TypeSubstitution::Substituter::handleAggregateType( const BaseInstType * type ) {
 	GuardValue( boundVars );
@@ -184,5 +171,5 @@
 			if ( ! type->params.empty() ) {
 				for ( const TypeDecl * tyvar : decl->params ) {
-					boundVars.insert( tyvar->name );
+					boundVars.insert( *tyvar );
 				} // for
 			} // if
@@ -198,4 +185,5 @@
 	handleAggregateType( aggregateUseType );
 }
+*/
 
 } // namespace ast
Index: src/AST/TypeSubstitution.hpp
===================================================================
--- src/AST/TypeSubstitution.hpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/TypeSubstitution.hpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -69,11 +69,10 @@
 	}
 
-	void add( std::string formalType, const Type *actualType );
+	void add( const TypeInstType * formalType, const Type *actualType );
+	void add( const TypeInstType::TypeEnvKey & key, const Type *actualType );
 	void add( const TypeSubstitution &other );
-	void remove( std::string formalType );
-	const Type *lookup( std::string formalType ) const;
+	void remove( const TypeInstType * formalType );
+	const Type *lookup( const TypeInstType * formalType ) const;
 	bool empty() const;
-
-	void addVar( std::string formalExpr, const Expr *actualExpr );
 
 	template< typename FormalIterator, typename ActualIterator >
@@ -101,8 +100,6 @@
 	friend class Pass;
 
-	typedef std::unordered_map< std::string, ptr<Type> > TypeEnvType;
-	typedef std::unordered_map< std::string, ptr<Expr> > VarEnvType;
+	typedef std::unordered_map< TypeInstType::TypeEnvKey, ptr<Type> > TypeEnvType;
 	TypeEnvType typeEnv;
-	VarEnvType varEnv;
 
   public:
@@ -113,10 +110,7 @@
 	auto   end() const -> decltype( typeEnv.  end() ) { return typeEnv.  end(); }
 
-	auto beginVar()       -> decltype( varEnv.begin() ) { return varEnv.begin(); }
-	auto   endVar()       -> decltype( varEnv.  end() ) { return varEnv.  end(); }
-	auto beginVar() const -> decltype( varEnv.begin() ) { return varEnv.begin(); }
-	auto   endVar() const -> decltype( varEnv.  end() ) { return varEnv.  end(); }
 };
 
+// this is the only place where type parameters outside a function formal may be substituted.
 template< typename FormalIterator, typename ActualIterator >
 void TypeSubstitution::add( FormalIterator formalBegin, FormalIterator formalEnd, ActualIterator actualBegin ) {
@@ -129,5 +123,5 @@
 			if ( const TypeExpr *actual = actualIt->template as<TypeExpr>() ) {
 				if ( formal->name != "" ) {
-					typeEnv[ formal->name ] = actual->type;
+					typeEnv[ formal ] = actual->type;
 				} // if
 			} else {
@@ -135,11 +129,10 @@
 			} // if
 		} else {
-			// TODO: type check the formal and actual parameters
-			if ( (*formalIt)->name != "" ) {
-				varEnv[ (*formalIt)->name ] = *actualIt;
-			} // if
+			
 		} // if
 	} // for
 }
+
+
 
 template< typename FormalIterator, typename ActualIterator >
@@ -147,4 +140,5 @@
 	add( formalBegin, formalEnd, actualBegin );
 }
+
 
 } // namespace ast
@@ -164,18 +158,17 @@
 
 		const Type * postvisit( const TypeInstType * aggregateUseType );
-		const Expr * postvisit( const NameExpr * nameExpr );
 
 		/// Records type variable bindings from forall-statements
 		void previsit( const FunctionType * type );
 		/// Records type variable bindings from forall-statements and instantiations of generic types
-		void handleAggregateType( const BaseInstType * type );
+		// void handleAggregateType( const BaseInstType * type );
 
-		void previsit( const StructInstType * aggregateUseType );
-		void previsit( const UnionInstType * aggregateUseType );
+		// void previsit( const StructInstType * aggregateUseType );
+		// void previsit( const UnionInstType * aggregateUseType );
 
 		const TypeSubstitution & sub;
 		int subCount = 0;
 		bool freeOnly;
-		typedef std::unordered_set< std::string > BoundVarsType;
+		typedef std::unordered_set< TypeInstType::TypeEnvKey > BoundVarsType;
 		BoundVarsType boundVars;
 
Index: src/AST/module.mk
===================================================================
--- src/AST/module.mk	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/AST/module.mk	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -33,7 +33,4 @@
 	AST/Expr.cpp \
 	AST/Expr.hpp \
-	AST/ForallSubstitutionTable.cpp \
-	AST/ForallSubstitutionTable.hpp \
-	AST/ForallSubstitutor.hpp \
 	AST/FunctionSpec.hpp \
 	AST/Fwd.hpp \
Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/GenPoly/GenPoly.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -115,5 +115,5 @@
 		if (!env) return type;
 		if (auto typeInst = dynamic_cast<const ast::TypeInstType*> (type)) {
-			auto newType = env->lookup(typeInst->name);
+			auto newType = env->lookup(typeInst);
 			if (newType) return newType;
 		}
@@ -172,5 +172,5 @@
 
 		if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( type ) ) {
-			return tyVars.find(typeInst->name) != tyVars.end() ? type : nullptr;
+			return tyVars.find(typeInst->typeString()) != tyVars.end() ? type : nullptr;
 		} else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
 			return isPolyType( arrayType->base, env );
@@ -552,6 +552,6 @@
 	}
 
-	void addToTyVarMap( const ast::TypeDecl * tyVar, TyVarMap & tyVarMap) {
-		tyVarMap.insert(tyVar->name, convData(ast::TypeDecl::Data{tyVar}));
+	void addToTyVarMap( const ast::TypeInstType * tyVar, TyVarMap & tyVarMap) {
+		tyVarMap.insert(tyVar->typeString(), convData(ast::TypeDecl::Data{tyVar->base}));
 	}
 
Index: src/ResolvExpr/AdjustExprType.cc
===================================================================
--- src/ResolvExpr/AdjustExprType.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/AdjustExprType.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -133,5 +133,5 @@
 		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 ) ) {
+			if ( const ast::EqvClass * eqvClass = tenv.lookup( *inst ) ) {
 				if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
 					return new ast::PointerType{ inst };
Index: src/ResolvExpr/CandidateFinder.cpp
===================================================================
--- src/ResolvExpr/CandidateFinder.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -212,7 +212,5 @@
 		// mark type variable and specialization cost of forall clause
 		convCost.incVar( function->forall.size() );
-		for ( const ast::TypeDecl * td : function->forall ) {
-			convCost.decSpec( td->assertions.size() );
-		}
+		convCost.decSpec( function->assertions.size() );
 
 		return convCost;
@@ -223,9 +221,9 @@
 		ast::AssertionSet & need
 	) {
-		for ( const ast::TypeDecl * tyvar : type->forall ) {
-			unifiableVars[ tyvar->name ] = ast::TypeDecl::Data{ tyvar };
-			for ( const ast::DeclWithType * assn : tyvar->assertions ) {
-				need[ assn ].isUsed = true;
-			}
+		for ( auto & tyvar : type->forall ) {
+			unifiableVars[ *tyvar ] = ast::TypeDecl::Data{ tyvar->base };
+		}
+		for ( auto & assn : type->assertions ) {
+			need[ assn ].isUsed = true;
 		}
 	}
@@ -953,5 +951,5 @@
 						auto inst = dynamic_cast< const ast::TypeInstType * >( funcResult )
 					) {
-						if ( const ast::EqvClass * clz = func->env.lookup( inst->name ) ) {
+						if ( const ast::EqvClass * clz = func->env.lookup( *inst ) ) {
 							if ( auto function = clz->bound.as< ast::FunctionType >() ) {
 								CandidateRef newFunc{ new Candidate{ *func } };
@@ -1077,5 +1075,5 @@
 			assert( toType );
 			toType = resolveTypeof( toType, symtab );
-			toType = SymTab::validateType( castExpr->location, toType, symtab );
+			// toType = SymTab::validateType( castExpr->location, toType, symtab );
 			toType = adjustExprType( toType, tenv, symtab );
 
@@ -1162,5 +1160,5 @@
 
 					if(auto insttype = dynamic_cast<const ast::TypeInstType*>(expr)) {
-						auto td = cand->env.lookup(insttype->name);
+						auto td = cand->env.lookup(*insttype);
 						if(!td) { continue; }
 						expr = td->bound.get();
@@ -1568,5 +1566,5 @@
 				// calculate target type
 				const ast::Type * toType = resolveTypeof( initAlt.type, symtab );
-				toType = SymTab::validateType( initExpr->location, toType, symtab );
+				// toType = SymTab::validateType( initExpr->location, toType, symtab );
 				toType = adjustExprType( toType, tenv, symtab );
 				// The call to find must occur inside this loop, otherwise polymorphic return
Index: src/ResolvExpr/CastCost.cc
===================================================================
--- src/ResolvExpr/CastCost.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/CastCost.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -202,5 +202,5 @@
 ) {
 	if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( const ast::EqvClass * eqvClass = env.lookup( typeInst->name ) ) {
+		if ( const ast::EqvClass * eqvClass = env.lookup( *typeInst ) ) {
 			// check cast cost against bound type, if present
 			if ( eqvClass->bound ) {
Index: src/ResolvExpr/CommonType.cc
===================================================================
--- src/ResolvExpr/CommonType.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/CommonType.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -713,5 +713,5 @@
 			const ast::Type * base = oPtr->base;
 			if ( auto var = dynamic_cast< const ast::TypeInstType * >( base ) ) {
-				auto entry = open.find( var->name );
+				auto entry = open.find( *var );
 				if ( entry != open.end() ) {
 					ast::AssertionSet need, have;
Index: src/ResolvExpr/ConversionCost.cc
===================================================================
--- src/ResolvExpr/ConversionCost.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/ConversionCost.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -498,5 +498,5 @@
 ) {
 	if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( const ast::EqvClass * eqv = env.lookup( inst->name ) ) {
+		if ( const ast::EqvClass * eqv = env.lookup( *inst ) ) {
 			if ( eqv->bound ) {
 				return conversionCost(src, eqv->bound, srcIsLvalue, symtab, env );
@@ -675,9 +675,9 @@
 
 void ConversionCost_new::postvisit( const ast::TypeInstType * typeInstType ) {
-	if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) {
+	if ( const ast::EqvClass * eqv = env.lookup( *typeInstType ) ) {
 		cost = costCalc( eqv->bound, dst, srcIsLvalue, symtab, env );
 	} else if ( const ast::TypeInstType * dstAsInst =
 			dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( typeInstType->name == dstAsInst->name ) {
+		if ( *typeInstType == *dstAsInst ) {
 			cost = Cost::zero;
 		}
Index: src/ResolvExpr/FindOpenVars.cc
===================================================================
--- src/ResolvExpr/FindOpenVars.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/FindOpenVars.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -112,16 +112,16 @@
 				// mark open/closed variables
 				if ( nextIsOpen ) {
-					for ( const ast::TypeDecl * decl : type->forall ) {
-						open[ decl->name ] = ast::TypeDecl::Data{ decl };
-						for ( const ast::DeclWithType * assert : decl->assertions ) {
-							need[ assert ].isUsed = false;
-						}
+					for ( auto & decl : type->forall ) {
+						open[ *decl ] = ast::TypeDecl::Data{ decl->base };
+					}
+					for ( auto & assert : type->assertions ) {
+						need[ assert ].isUsed = false;
 					}
 				} else {
-					for ( const ast::TypeDecl * decl : type->forall ) {
-						closed[ decl->name ] = ast::TypeDecl::Data{ decl };
-						for ( const ast::DeclWithType * assert : decl->assertions ) {
-							have[ assert ].isUsed = false;
-						}
+					for ( auto & decl : type->forall ) {
+						closed[ *decl ] = ast::TypeDecl::Data{ decl->base };	
+					}
+					for ( auto & assert : type->assertions ) {
+						have[ assert ].isUsed = false;
 					}
 				}
Index: src/ResolvExpr/PolyCost.cc
===================================================================
--- src/ResolvExpr/PolyCost.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/PolyCost.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -68,5 +68,5 @@
 
 	void previsit( const ast::TypeInstType * type ) {
-		if ( const ast::EqvClass * eqv = env_.lookup( type->name ) ) /* && */ if ( eqv->bound ) {
+		if ( const ast::EqvClass * eqv = env_.lookup( *type ) ) /* && */ if ( eqv->bound ) {
 			if ( const ast::TypeInstType * otherType = eqv->bound.as< ast::TypeInstType >() ) {
 				if ( symtab.lookupType( otherType->name ) ) {
Index: src/ResolvExpr/PtrsAssignable.cc
===================================================================
--- src/ResolvExpr/PtrsAssignable.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/PtrsAssignable.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -134,5 +134,5 @@
 	}
 	void postvisit( const ast::TypeInstType * inst ) {
-		if ( const ast::EqvClass * eqv = typeEnv.lookup( inst->name ) ) {
+		if ( const ast::EqvClass * eqv = typeEnv.lookup( *inst ) ) {
 			if ( eqv->bound ) {
 				// T * = S * for any S depends on the type bound to T
@@ -146,5 +146,5 @@
 		const ast::TypeEnvironment & env ) {
 	if ( const ast::TypeInstType * dstAsInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( const ast::EqvClass * eqv = env.lookup( dstAsInst->name ) ) {
+		if ( const ast::EqvClass * eqv = env.lookup( *dstAsInst ) ) {
 			return ptrsAssignable( src, eqv->bound, env );
 		}
Index: src/ResolvExpr/PtrsCastable.cc
===================================================================
--- src/ResolvExpr/PtrsCastable.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/PtrsCastable.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -180,5 +180,5 @@
 					}
 				}
-			} else if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) {
+			} else if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
 				if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
 					return -1;
@@ -283,5 +283,5 @@
 ) {
 	if ( auto inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( const ast::EqvClass * eqvClass = env.lookup( inst->name ) ) {
+		if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
 			return ptrsAssignable( src, eqvClass->bound, env );
 		}
Index: src/ResolvExpr/RenameVars.cc
===================================================================
--- src/ResolvExpr/RenameVars.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/RenameVars.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -19,5 +19,4 @@
 #include <utility>                 // for pair
 
-#include "AST/ForallSubstitutionTable.hpp"
 #include "AST/Pass.hpp"
 #include "AST/Type.hpp"
@@ -39,8 +38,10 @@
 		int level = 0;
 		int resetCount = 0;
+
+		int next_expr_id = 1;
+		int next_usage_id = 1;
 		ScopedMap< std::string, std::string > nameMap;
+		ScopedMap< std::string, ast::TypeInstType::TypeEnvKey > idMap;
 	public:
-		ast::ForallSubstitutionTable subs;
-
 		void reset() {
 			level = 0;
@@ -53,4 +54,8 @@
 				type->name = it->second;
 			}
+		}
+
+		void nextUsage() {
+			++next_usage_id;
 		}
 
@@ -78,13 +83,13 @@
 
 		const ast::TypeInstType * rename( const ast::TypeInstType * type ) {
-			// re-linking of base type handled by WithForallSubstitutor
-
 			// rename
-			auto it = nameMap.find( type->name );
-			if ( it != nameMap.end() ) {
-				// 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;
+			auto it = idMap.find( type->name );
+			if ( it != idMap.end() ) {
+				// unconditionally mutate because map will *always* have different name
+				ast::TypeInstType * mut = ast::shallowCopy( type );
+				// reconcile base node since some copies might have been made
+				mut->base = it->second.base;
+				mut->formal_usage = it->second.formal_usage;
+				mut->expr_id = it->second.expr_id;
 	            type = mut;
 			}
@@ -93,34 +98,38 @@
 		}
 
-		const ast::FunctionType * openLevel( const ast::FunctionType * type ) {
+		const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) {
 			if ( type->forall.empty() ) return type;
-
-			nameMap.beginScope();
+			idMap.beginScope();
 
 			// Load new names from this forall clause and perform renaming.
-			auto mutType = ast::mutate( type );
-			assert( type == mutType && "mutated type must be unique from ForallSubstitutor" );
-			for ( ast::ptr< ast::TypeDecl > & td : mutType->forall ) {
-				assertf(dynamic_cast<ast::FunctionType *>(mutType), "renaming vars in non-function type");
-				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;
+			auto mutType = ast::shallowCopy( type );
+			// assert( type == mutType && "mutated type must be unique from ForallSubstitutor" );
+			for ( auto & td : mutType->forall ) {
+				auto mut = ast::shallowCopy( td.get() );
+				// assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" );
+
+				if (mode == GEN_EXPR_ID) {
+					mut->expr_id = next_expr_id;
+					mut->formal_usage = -1;
+					++next_expr_id;
+				}
+				else if (mode == GEN_USAGE) {
+					assertf(mut->expr_id, "unfilled expression id in generating candidate type");
+					mut->formal_usage = next_usage_id;
+				}
+				else {
+					assert(false);
+				}
+				idMap[ td->name ] = ast::TypeInstType::TypeEnvKey(*mut);
+				
+				td = mut;
+			}
+
+			return mutType;
 		}
 
 		void closeLevel( const ast::FunctionType * type ) {
 			if ( type->forall.empty() ) return;
-
-			nameMap.endScope();
+			idMap.endScope();
 		}
 	};
@@ -142,10 +151,9 @@
 	};
 
-	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;
+	struct RenameVars_new : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ {
+		RenameMode mode;
 
 		const ast::FunctionType * previsit( const ast::FunctionType * type ) {
-			return renaming.openLevel( type );
+			return renaming.openLevel( type, mode );
 		}
 
@@ -163,4 +171,5 @@
 
 		const ast::TypeInstType * previsit( const ast::TypeInstType * type ) {
+			if (mode == GEN_USAGE && !type->formal_usage) return type; // do not rename an actual type
 			return renaming.rename( type );
 		}
@@ -177,9 +186,12 @@
 }
 
-const ast::Type * renameTyVars( const ast::Type * t ) {
-	ast::Type *tc = ast::deepCopy(t);
+const ast::Type * renameTyVars( const ast::Type * t, RenameMode mode ) {
+	// ast::Type *tc = ast::deepCopy(t);
 	ast::Pass<RenameVars_new> renamer;
-//	return t->accept( renamer );
-	return tc->accept( renamer );
+	renamer.core.mode = mode;
+	if (mode == GEN_USAGE) {
+		renaming.nextUsage();
+	}
+	return t->accept( renamer );
 }
 
Index: src/ResolvExpr/RenameVars.h
===================================================================
--- src/ResolvExpr/RenameVars.h	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/RenameVars.h	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -30,8 +30,15 @@
 	/// Provides a consistent renaming of forall type names in a hierarchy by prefixing them with a unique "level" ID
 	void renameTyVars( Type * );
-	const ast::Type * renameTyVars( const ast::Type * );
+
+	enum RenameMode {
+		GEN_USAGE, // for type in VariableExpr
+		GEN_EXPR_ID // for type in decl
+	};
+	const ast::Type * renameTyVars( const ast::Type *, RenameMode mode = GEN_USAGE );
 
 	/// resets internal state of renamer to avoid overflow
 	void resetTyVarRenaming();
+
+	
 } // namespace ResolvExpr
 
Index: src/ResolvExpr/ResolveTypeof.cc
===================================================================
--- src/ResolvExpr/ResolveTypeof.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/ResolveTypeof.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -15,4 +15,5 @@
 
 #include "ResolveTypeof.h"
+#include "RenameVars.h"
 
 #include <cassert>               // for assert
@@ -218,4 +219,5 @@
 			mutDecl->mangleName = Mangle::mangle(mutDecl); // do not mangle unnamed variables
 		
+		mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
 		mutDecl->isTypeFixed = true;
 		return mutDecl;
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/Resolver.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -986,5 +986,4 @@
 		};
 	} // anonymous namespace
-
 	/// Check if this expression is or includes a deleted expression
 	const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) {
@@ -1375,15 +1374,17 @@
 			}
 
-			// handle assertions. (seems deep)
+			// handle assertions
 
 			symtab.enterScope();
-			for (auto & typeParam : mutType->forall) {
-				auto mutParam = typeParam.get_and_mutate();
-				symtab.addType(mutParam);
-				for (auto & asst : mutParam->assertions) {
-					asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab);
-					symtab.addId(asst);
-				}
-				typeParam = mutParam;
+			mutType->forall.clear();
+			mutType->assertions.clear();
+			for (auto & typeParam : mutDecl->type_params) {
+				symtab.addType(typeParam);
+				mutType->forall.emplace_back(new ast::TypeInstType(typeParam->name, typeParam));
+			}
+			for (auto & asst : mutDecl->assertions) {
+				asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), symtab);
+				symtab.addId(asst);
+				mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst));
 			}
 
@@ -1407,4 +1408,6 @@
 			mutType->returns = std::move(returnTypes);
 
+			auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID));
+
 			std::list<ast::ptr<ast::Stmt>> newStmts;
 			resolveWithExprs (mutDecl->withExprs, newStmts);
@@ -1418,4 +1421,5 @@
 			symtab.leaveScope();
 
+			mutDecl->type = renamedType;
 			mutDecl->mangleName = Mangle::mangle(mutDecl);
 			mutDecl->isTypeFixed = true;
Index: src/ResolvExpr/SatisfyAssertions.cpp
===================================================================
--- src/ResolvExpr/SatisfyAssertions.cpp	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/SatisfyAssertions.cpp	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -69,5 +69,5 @@
 	/// Reference to a single deferred item
 	struct DeferRef {
-		const ast::DeclWithType * decl;
+		const ast::VariableExpr * expr;
 		const ast::AssertionSetValue & info;
 		const AssnCandidate & match;
@@ -77,11 +77,11 @@
 	/// Acts like an indexed list of DeferRef
 	struct DeferItem {
-		const ast::DeclWithType * decl;
+		const ast::VariableExpr * expr;
 		const ast::AssertionSetValue & info;
 		AssnCandidateList matches;
 
 		DeferItem( 
-			const ast::DeclWithType * d, const ast::AssertionSetValue & i, AssnCandidateList && ms )
-		: decl( d ), info( i ), matches( std::move( ms ) ) {}
+			const ast::VariableExpr * d, const ast::AssertionSetValue & i, AssnCandidateList && ms )
+		: expr( d ), info( i ), matches( std::move( ms ) ) {}
 
 		bool empty() const { return matches.empty(); }
@@ -89,5 +89,5 @@
 		AssnCandidateList::size_type size() const { return matches.size(); }
 
-		DeferRef operator[] ( unsigned i ) const { return { decl, info, matches[i] }; }
+		DeferRef operator[] ( unsigned i ) const { return { expr, info, matches[i] }; }
 	};
 
@@ -138,5 +138,5 @@
 	void addToSymbolTable( const ast::AssertionSet & have, ast::SymbolTable & symtab ) {
 		for ( auto & i : have ) {
-			if ( i.second.isUsed ) { symtab.addId( i.first ); }
+			if ( i.second.isUsed ) { symtab.addId( i.first->var ); }
 		}
 	}
@@ -144,5 +144,5 @@
 	/// Binds a single assertion, updating satisfaction state
 	void bindAssertion( 
-		const ast::DeclWithType * decl, const ast::AssertionSetValue & info, CandidateRef & cand, 
+		const ast::VariableExpr * expr, const ast::AssertionSetValue & info, CandidateRef & cand, 
 		AssnCandidate & match, InferCache & inferred
 	) {
@@ -156,6 +156,6 @@
 
 		// place newly-inferred assertion in proper location in cache
-		inferred[ info.resnSlot ][ decl->uniqueId ] = ast::ParamEntry{
-			candidate->uniqueId, candidate, match.adjType, decl->get_type(), varExpr };
+		inferred[ info.resnSlot ][ expr->var->uniqueId ] = ast::ParamEntry{
+			candidate->uniqueId, candidate, match.adjType, expr->result, varExpr };
 	}
 
@@ -169,8 +169,8 @@
 
 		std::vector<ast::SymbolTable::IdData> candidates;
-		auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->name);
+		auto kind = ast::SymbolTable::getSpecialFunctionKind(assn.first->var->name);
 		if (kind != ast::SymbolTable::SpecialFunctionKind::NUMBER_OF_KINDS) {
 			// prefilter special decls by argument type, if already known
-			ast::ptr<ast::Type> thisArgType = strict_dynamic_cast<const ast::PointerType *>(assn.first->get_type())->base
+			ast::ptr<ast::Type> thisArgType = assn.first->result.strict_as<ast::PointerType>()->base
 				.strict_as<ast::FunctionType>()->params[0]
 				.strict_as<ast::ReferenceType>()->base;
@@ -184,5 +184,5 @@
 		}
 		else {
-			candidates = sat.symtab.lookupId(assn.first->name);
+			candidates = sat.symtab.lookupId(assn.first->var->name);
 		}
 		for ( const ast::SymbolTable::IdData & cdata : candidates ) {
@@ -200,5 +200,5 @@
 			ast::TypeEnvironment newEnv{ sat.cand->env };
 			ast::OpenVarSet newOpen{ sat.cand->open };
-			ast::ptr< ast::Type > toType = assn.first->get_type();
+			ast::ptr< ast::Type > toType = assn.first->result;
 			ast::ptr< ast::Type > adjType = 
 				renameTyVars( adjustExprType( candidate->get_type(), newEnv, sat.symtab ) );
@@ -337,5 +337,5 @@
 					// compute conversion cost from satisfying decl to assertion
 					cost += computeConversionCost(
-						assn.match.adjType, assn.decl->get_type(), false, symtab, env );
+						assn.match.adjType, assn.expr->result, false, symtab, env );
 
 					// mark vars+specialization on function-type assertions
@@ -350,7 +350,5 @@
 					cost.incVar( func->forall.size() );
 
-					for ( const ast::TypeDecl * td : func->forall ) {
-						cost.decSpec( td->assertions.size() );
-					}
+					cost.decSpec( func->assertions.size() );
 				}
 			}
@@ -451,5 +449,5 @@
 				ss << (tabs-1) << "Too many non-unique satisfying assignments for assertions:\n";
 				for ( const auto & d : sat.deferred ) {
-					ast::print( ss, d.decl, tabs );
+					ast::print( ss, d.expr, tabs );
 				}
 
@@ -469,5 +467,5 @@
 					ss << (tabs-1) << "No mutually-compatible satisfaction for assertions:\n";
 					for ( const auto& d : sat.deferred ) {
-						ast::print( ss, d.decl, tabs );
+						ast::print( ss, d.expr, tabs );
 					}
 
@@ -501,5 +499,5 @@
 						nextNewNeed.insert( match.need.begin(), match.need.end() );
 
-						bindAssertion( r.decl, r.info, nextCand, match, nextInferred );
+						bindAssertion( r.expr, r.info, nextCand, match, nextInferred );
 					}
 
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/ResolvExpr/Unify.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -773,5 +773,5 @@
 
 			const ast::Type * postvisit( const ast::TypeInstType * typeInst ) {
-				if ( const ast::EqvClass * clz = tenv.lookup( typeInst->name ) ) {
+				if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) {
 					// expand ttype parameter into its actual type
 					if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) {
@@ -888,5 +888,5 @@
 		}
 
-		static void markAssertionSet( ast::AssertionSet & assns, const ast::DeclWithType * assn ) {
+		static void markAssertionSet( ast::AssertionSet & assns, const ast::VariableExpr * assn ) {
 			auto i = assns.find( assn );
 			if ( i != assns.end() ) {
@@ -900,9 +900,7 @@
 			const ast::FunctionType * type
 		) {
-			for ( const auto & tyvar : type->forall ) {
-				for ( const ast::DeclWithType * assert : tyvar->assertions ) {
-					markAssertionSet( assn1, assert );
-					markAssertionSet( assn2, assert );
-				}
+			for ( auto & assert : type->assertions ) {
+				markAssertionSet( assn1, assert );
+				markAssertionSet( assn2, assert );
 			}
 		}
@@ -1030,5 +1028,5 @@
 
 		void postvisit( const ast::TypeInstType * typeInst ) {
-			assert( open.find( typeInst->name ) == open.end() );
+			assert( open.find( *typeInst ) == open.end() );
 			handleRefType( typeInst, type2 );
 		}
@@ -1171,6 +1169,6 @@
 		auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 );
 		ast::OpenVarSet::const_iterator
-			entry1 = var1 ? open.find( var1->name ) : open.end(),
-			entry2 = var2 ? open.find( var2->name ) : open.end();
+			entry1 = var1 ? open.find( *var1 ) : open.end(),
+			entry2 = var2 ? open.find( *var2 ) : open.end();
 		bool isopen1 = entry1 != open.end();
 		bool isopen2 = entry2 != open.end();
Index: src/SymTab/Mangler.cc
===================================================================
--- src/SymTab/Mangler.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/SymTab/Mangler.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -671,5 +671,5 @@
 					int dcount = 0, fcount = 0, vcount = 0, acount = 0;
 					mangleName += Encoding::forall;
-					for ( const ast::TypeDecl * decl : ptype->forall ) {
+					for ( auto & decl : ptype->forall ) {
 						switch ( decl->kind ) {
 						case ast::TypeDecl::Kind::Dtype:
@@ -686,11 +686,11 @@
 						} // switch
 						varNums[ decl->name ] = std::make_pair( nextVarNum, (int)decl->kind );
-						for ( const ast::DeclWithType * assert : decl->assertions ) {
-							ast::Pass<Mangler_new> sub_mangler(
-								mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums );
-							assert->accept( sub_mangler );
-							assertionNames.push_back( sub_mangler.core.get_mangleName() );
-							acount++;
-						} // for
+					} // for
+					for ( auto & assert : ptype->assertions ) {
+						ast::Pass<Mangler_new> sub_mangler(
+							mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums );
+						assert->var->accept( sub_mangler );
+						assertionNames.push_back( sub_mangler.core.get_mangleName() );
+						acount++;
 					} // for
 					mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_";
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/SymTab/Validate.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -1463,4 +1463,6 @@
 	}
 
+	/*
+
 	/// Associates forward declarations of aggregates with their definitions
 	class LinkReferenceToTypes_new final
@@ -1844,6 +1846,8 @@
 		}
 	};
+	*/
 } // anonymous namespace
 
+/*
 const ast::Type * validateType(
 		const CodeLocation & loc, const ast::Type * type, const ast::SymbolTable & symtab ) {
@@ -1854,4 +1858,5 @@
 	return type->accept( lrt )->accept( fpd );
 }
+*/
 
 } // namespace SymTab
Index: src/Tuples/TupleAssignment.cc
===================================================================
--- src/Tuples/TupleAssignment.cc	(revision 2dda05dc482e5dd5d6074e405c5d8c0c5e35654e)
+++ src/Tuples/TupleAssignment.cc	(revision 53449a48dee3324f96932ed8c4befe2756837314)
@@ -504,7 +504,6 @@
 
 			std::vector< ast::ptr< ast::Expr > > match() override {
-				// temporary workaround for new and old ast to coexist and avoid name collision
-				static UniqueName lhsNamer( "__massassign_Ln" );
-				static UniqueName rhsNamer( "__massassign_Rn" );
+				static UniqueName lhsNamer( "__massassign_L" );
+				static UniqueName rhsNamer( "__massassign_R" );
 				// empty tuple case falls into this matcher
 				assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 );
@@ -535,7 +534,6 @@
 
 			std::vector< ast::ptr< ast::Expr > > match() override {
-				// temporary workaround for new and old ast to coexist and avoid name collision
-				static UniqueName lhsNamer( "__multassign_Ln" );
-				static UniqueName rhsNamer( "__multassign_Rn" );
+				static UniqueName lhsNamer( "__multassign_L" );
+				static UniqueName rhsNamer( "__multassign_R" );
 
 				if ( lhs.size() != rhs.size() ) return {};
