Index: src/AST/Bitfield.hpp
===================================================================
--- src/AST/Bitfield.hpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/Bitfield.hpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -9,12 +9,13 @@
 // Author           : Aaron B. Moss
 // Created On       : Thu May 9 10:00:00 2019
-// Last Modified By : Aaron B. Moss
-// Last Modified On : Thu May 9 10:00:00 2019
-// Update Count     : 1
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Jun 5 10:00:00 2019
+// Update Count     : 2
 //
 
 #pragma once
 
-#include <strings.h>  // for ffs
+#include <strings.h>   // for ffs
+#include <type_traits> // for is_unsigned
 
 /// Make a type a bitfield.
@@ -24,7 +25,8 @@
 template<typename T>
 struct bitfield : public T {
-	static_assert(sizeof(T) == sizeof(unsigned int), "Type has incorrect size");
 	using T::val;
 	using val_t = decltype(val);
+	static_assert(sizeof(T) == sizeof(unsigned int), "Type has incorrect size");
+	static_assert(std::is_unsigned<val_t>::value, "Bitfield val field is not unsigned.");
 
 	constexpr bitfield() : T( 0 ) {}
Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/Expr.cpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -65,5 +65,5 @@
 			// base type
 			ret->result = base;
-			add_lvalue( ret->result );
+			add_qualifiers( ret->result, CV::Lvalue );
 		}
 	}
@@ -165,5 +165,5 @@
 	genericSubsitution( aggregate->result ).apply( result );
 	// ensure lvalue and appropriate restrictions from aggregate type
-	result.get_and_mutate()->qualifiers |= aggregate->result->qualifiers | CV::Lvalue;
+	add_qualifiers( result, aggregate->result->qualifiers | CV::Lvalue );
 }
 
@@ -175,5 +175,5 @@
 	assert( var->get_type() );
 	result = var->get_type();
-	add_lvalue( result );
+	add_qualifiers( result, CV::Lvalue );
 }
 
@@ -309,5 +309,5 @@
 	assert( t && i );
 	result = t;
-	add_lvalue( result );
+	add_qualifiers( result, CV::Lvalue );
 }
 
@@ -326,5 +326,5 @@
 	// like MemberExpr, TupleIndexExpr is always an lvalue
 	result = type->types[ index ];
-	add_lvalue( result );
+	add_qualifiers( result, CV::Lvalue );
 }
 
Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/Node.hpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -18,4 +18,6 @@
 #include <cassert>
 #include <iosfwd>
+
+#include "Common/ErrorObjects.h"  // for SemanticErrorException
 
 namespace ast {
@@ -100,4 +102,22 @@
 }
 
+/// Call a visitor on a collection of nodes, throwing any exceptions when completed
+template< typename Container >
+void accept_each( const Container & c, Visitor & v ) {
+	SemanticErrorException errors;
+	for ( const auto & i : c ) {
+		try {
+			if ( i ) {
+				i->accept( v );
+			}
+		} catch ( SemanticErrorException & e ) {
+			errors.append( e );
+		}
+	}
+	if ( ! errors.isEmpty() ) {
+		throw errors;
+	}
+}
+
 /// Base class for the smart pointer types
 /// should never really be used.
@@ -166,4 +186,8 @@
 	const o_node_t * as() const { return dynamic_cast<const o_node_t *>(node); }
 
+	/// wrapper for convenient access to strict_dynamic_cast
+	template<typename o_node_t>
+	const o_node_t * strict_as() const { return strict_dynamic_cast<const o_node_t *>(node); }
+
 	/// Returns a mutable version of the pointer in this node.
 	node_t * get_and_mutate();
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/Pass.hpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -33,5 +33,5 @@
 #include "AST/Visitor.hpp"
 
-#include "SymTab/Indexer.h"
+#include "AST/SymbolTable.hpp"
 
 // Private prelude header, needed for some of the magic tricks this class pulls off
@@ -61,5 +61,5 @@
 //                          postvisit/postmutate teminates.
 // | WithVisitorRef       - provides an pointer to the templated visitor wrapper
-// | WithIndexer          - provides indexer functionality (i.e. up-to-date symbol table)
+// | WithSymbolTable      - provides symbol table functionality
 //-------------------------------------------------------------------------------------------------
 template< typename pass_t >
@@ -206,8 +206,8 @@
 
 private:
-	/// Internal RAII guard for indexer features
-	struct guard_indexer {
-		guard_indexer( Pass<pass_t> & pass ): pass( pass ) { __pass::indexer::enter(pass, 0); }
-		~guard_indexer()                                   { __pass::indexer::leave(pass, 0); }
+	/// 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); }
 		Pass<pass_t> & pass;
 	};
@@ -294,7 +294,7 @@
 };
 
-/// Use when the templated visitor should update the indexer
-struct WithIndexer {
-	SymTab::Indexer indexer;
+/// Use when the templated visitor should update the symbol table
+struct WithSymbolTable {
+	SymbolTable symtab;
 };
 }
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/Pass.impl.hpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -398,5 +398,5 @@
 	VISIT(
 		{
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &ObjectDecl::type );
 		}
@@ -406,5 +406,5 @@
 	)
 
-	__pass::indexer::addId( pass, 0, node );
+	__pass::symtab::addId( pass, 0, node );
 
 	VISIT_END( DeclWithType, node );
@@ -417,15 +417,15 @@
 	VISIT_START( node );
 
-	__pass::indexer::addId( pass, 0, node );
+	__pass::symtab::addId( pass, 0, node );
 
 	VISIT(maybe_accept( node, &FunctionDecl::withExprs );)
 	{
 		// with clause introduces a level of scope (for the with expression members).
-		// with clause exprs are added to the indexer before parameters so that parameters
+		// with clause exprs are added to the symbol table before parameters so that parameters
 		// shadow with exprs and not the other way around.
-		guard_indexer guard { *this };
-		__pass::indexer::addWith( pass, 0, node->withExprs, node );
+		guard_symtab guard { *this };
+		__pass::symtab::addWith( pass, 0, node->withExprs, node );
 		{
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			// implicit add __func__ identifier as specified in the C manual 6.4.2.2
 			static ast::ObjectDecl func(
@@ -436,5 +436,5 @@
 				)
 			);
-			__pass::indexer::addId( pass, 0, &func );
+			__pass::symtab::addId( pass, 0, &func );
 			VISIT(
 				maybe_accept( node, &FunctionDecl::type );
@@ -460,8 +460,8 @@
 	// make up a forward declaration and add it before processing the members
 	// needs to be on the heap because addStruct saves the pointer
-	__pass::indexer::addStructFwd( pass, 0, node );
-
-	VISIT({
-		guard_indexer guard { * this };
+	__pass::symtab::addStructFwd( pass, 0, node );
+
+	VISIT({
+		guard_symtab guard { * this };
 		maybe_accept( node, &StructDecl::params  );
 		maybe_accept( node, &StructDecl::members );
@@ -469,5 +469,5 @@
 
 	// this addition replaces the forward declaration
-	__pass::indexer::addStruct( pass, 0, node );
+	__pass::symtab::addStruct( pass, 0, node );
 
 	VISIT_END( Decl, node );
@@ -481,13 +481,13 @@
 
 	// make up a forward declaration and add it before processing the members
-	__pass::indexer::addUnionFwd( pass, 0, node );
-
-	VISIT({
-		guard_indexer guard { * this };
+	__pass::symtab::addUnionFwd( pass, 0, node );
+
+	VISIT({
+		guard_symtab guard { * this };
 		maybe_accept( node, &UnionDecl::params  );
 		maybe_accept( node, &UnionDecl::members );
 	})
 
-	__pass::indexer::addUnion( pass, 0, node );
+	__pass::symtab::addUnion( pass, 0, node );
 
 	VISIT_END( Decl, node );
@@ -500,5 +500,5 @@
 	VISIT_START( node );
 
-	__pass::indexer::addEnum( pass, 0, node );
+	__pass::symtab::addEnum( pass, 0, node );
 
 	VISIT(
@@ -518,10 +518,10 @@
 
 	VISIT({
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &TraitDecl::params  );
 		maybe_accept( node, &TraitDecl::members );
 	})
 
-	__pass::indexer::addTrait( pass, 0, node );
+	__pass::symtab::addTrait( pass, 0, node );
 
 	VISIT_END( Decl, node );
@@ -535,5 +535,5 @@
 
 	VISIT({
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &TypeDecl::params );
 		maybe_accept( node, &TypeDecl::base   );
@@ -543,5 +543,5 @@
 	// note that assertions come after the type is added to the symtab, since they are not part of the type proper
 	// and may depend on the type itself
-	__pass::indexer::addType( pass, 0, node );
+	__pass::symtab::addType( pass, 0, node );
 
 	VISIT(
@@ -549,5 +549,5 @@
 
 		{
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &TypeDecl::init );
 		}
@@ -564,10 +564,10 @@
 
 	VISIT({
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &TypedefDecl::params );
 		maybe_accept( node, &TypedefDecl::base   );
 	})
 
-	__pass::indexer::addType( pass, 0, node );
+	__pass::symtab::addType( pass, 0, node );
 
 	VISIT( maybe_accept( node, &TypedefDecl::assertions ); )
@@ -611,7 +611,7 @@
 		// 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::indexer::enter(pass, 0);
+			if ( ! inFunction ) __pass::symtab::enter(pass, 0);
 		}, [this, inFunction = this->inFunction]() {
-			if ( ! inFunction ) __pass::indexer::leave(pass, 0);
+			if ( ! inFunction ) __pass::symtab::leave(pass, 0);
 		});
 		ValueGuard< bool > guard2( inFunction );
@@ -669,5 +669,5 @@
 	VISIT({
 		// if statements introduce a level of scope (for the initialization)
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &IfStmt::inits    );
 		maybe_accept( node, &IfStmt::cond     );
@@ -687,5 +687,5 @@
 	VISIT({
 		// while statements introduce a level of scope (for the initialization)
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &WhileStmt::inits );
 		maybe_accept( node, &WhileStmt::cond  );
@@ -704,5 +704,5 @@
 	VISIT({
 		// for statements introduce a level of scope (for the initialization)
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &ForStmt::inits );
 		maybe_accept( node, &ForStmt::cond  );
@@ -800,5 +800,5 @@
 	VISIT({
 		// catch statements introduce a level of scope (for the caught exception)
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &CatchStmt::decl );
 		maybe_accept( node, &CatchStmt::cond );
@@ -901,6 +901,6 @@
 		{
 			// catch statements introduce a level of scope (for the caught exception)
-			guard_indexer guard { *this };
-			__pass::indexer::addWith( pass, 0, node->exprs, node );
+			guard_symtab guard { *this };
+			__pass::symtab::addWith( pass, 0, node->exprs, node );
 			maybe_accept( node, &WithStmt::stmt );
 		}
@@ -953,5 +953,5 @@
 	VISIT(
 		{
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &ApplicationExpr::result );
 		}
@@ -971,5 +971,5 @@
 	VISIT(
 		{
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &UntypedExpr::result );
 		}
@@ -988,5 +988,5 @@
 
 	VISIT({
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &NameExpr::result );
 	})
@@ -1002,5 +1002,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &CastExpr::result );
 		}
@@ -1018,5 +1018,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &KeywordCastExpr::result );
 		}
@@ -1034,5 +1034,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &VirtualCastExpr::result );
 		}
@@ -1050,5 +1050,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &AddressExpr::result );
 		}
@@ -1066,5 +1066,5 @@
 
 	VISIT({
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &LabelAddressExpr::result );
 	})
@@ -1080,5 +1080,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &UntypedMemberExpr::result );
 		}
@@ -1097,5 +1097,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &MemberExpr::result );
 		}
@@ -1113,5 +1113,5 @@
 
 	VISIT({
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &VariableExpr::result );
 	})
@@ -1127,5 +1127,5 @@
 
 	VISIT({
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &ConstantExpr::result );
 	})
@@ -1141,5 +1141,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &SizeofExpr::result );
 		}
@@ -1161,5 +1161,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &AlignofExpr::result );
 		}
@@ -1181,5 +1181,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &UntypedOffsetofExpr::result );
 		}
@@ -1197,5 +1197,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &OffsetofExpr::result );
 		}
@@ -1213,5 +1213,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &OffsetPackExpr::result );
 		}
@@ -1229,5 +1229,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &LogicalExpr::result );
 		}
@@ -1246,5 +1246,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &ConditionalExpr::result );
 		}
@@ -1264,5 +1264,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &CommaExpr::result );
 		}
@@ -1281,5 +1281,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &TypeExpr::result );
 		}
@@ -1297,5 +1297,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &AsmExpr::result );
 		}
@@ -1315,5 +1315,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &ImplicitCopyCtorExpr::result );
 		}
@@ -1331,5 +1331,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &ConstructorExpr::result );
 		}
@@ -1347,5 +1347,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &CompoundLiteralExpr::result );
 		}
@@ -1363,5 +1363,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &RangeExpr::result );
 		}
@@ -1380,5 +1380,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &UntypedTupleExpr::result );
 		}
@@ -1396,5 +1396,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &TupleExpr::result );
 		}
@@ -1412,5 +1412,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &TupleIndexExpr::result );
 		}
@@ -1428,5 +1428,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &TupleAssignExpr::result );
 		}
@@ -1454,5 +1454,5 @@
 
 		{
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &StmtExpr::result );
 		}
@@ -1472,5 +1472,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &UniqueExpr::result );
 		}
@@ -1488,5 +1488,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &UntypedInitExpr::result );
 		}
@@ -1505,5 +1505,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &InitExpr::result );
 		}
@@ -1522,5 +1522,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &DeletedExpr::result );
 		}
@@ -1539,5 +1539,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &DefaultArgExpr::result );
 		}
@@ -1555,5 +1555,5 @@
 
 	VISIT({
-			guard_indexer guard { *this };
+			guard_symtab guard { *this };
 			maybe_accept( node, &GenericExpr::result );
 		}
@@ -1566,5 +1566,5 @@
 			const Type * type = nullptr;
 			if( assoc.type ) {
-				guard_indexer guard { *this };
+				guard_symtab guard { *this };
 				type = assoc.type->accept( *this );
 				if( type != assoc.type ) mutated = true;
@@ -1682,8 +1682,8 @@
 	VISIT_START( node );
 
-	__pass::indexer::addStruct( pass, 0, node->name );
-
-	VISIT({
-		guard_indexer guard { *this };
+	__pass::symtab::addStruct( pass, 0, node->name );
+
+	VISIT({
+		guard_symtab guard { *this };
 		maybe_accept( node, &StructInstType::forall );
 		maybe_accept( node, &StructInstType::params );
@@ -1699,8 +1699,8 @@
 	VISIT_START( node );
 
-	__pass::indexer::addStruct( pass, 0, node->name );
+	__pass::symtab::addStruct( pass, 0, node->name );
 
 	{
-		guard_indexer guard { *this };
+		guard_symtab guard { *this };
 		maybe_accept( node, &UnionInstType::forall );
 		maybe_accept( node, &UnionInstType::params );
@@ -1893,5 +1893,5 @@
 			std::unordered_map< std::string, ast::ptr< ast::Type > > new_map;
 			for ( const auto & p : node->typeEnv ) {
-				guard_indexer guard { *this };
+				guard_symtab guard { *this };
 				auto new_node = p.second->accept( *this );
 				if (new_node != p.second) mutated = false;
@@ -1909,5 +1909,5 @@
 			std::unordered_map< std::string, ast::ptr< ast::Expr > > new_map;
 			for ( const auto & p : node->varEnv ) {
-				guard_indexer guard { *this };
+				guard_symtab guard { *this };
 				auto new_node = p.second->accept( *this );
 				if (new_node != p.second) mutated = false;
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/Pass.proto.hpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -265,11 +265,11 @@
 	};
 
-	// Finally certain pass desire an up to date indexer automatically
-	// detect the presence of a member name indexer and call all the members appropriately
-	namespace indexer {
+	// Finally certain pass 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.indexer.enterScope(), void() ) {
-			pass.indexer.enterScope();
+		static inline auto enter( pass_t & pass, int ) -> decltype( pass.symtab.enterScope(), void() ) {
+			pass.symtab.enterScope();
 		}
 
@@ -278,6 +278,6 @@
 
 		template<typename pass_t>
-		static inline auto leave( pass_t & pass, int ) -> decltype( pass.indexer.leaveScope(), void() ) {
-			pass.indexer.leaveScope();
+		static inline auto leave( pass_t & pass, int ) -> decltype( pass.symtab.leaveScope(), void() ) {
+			pass.symtab.leaveScope();
 		}
 
@@ -285,10 +285,10 @@
 		static inline auto leave( pass_t &, long ) {}
 
-		// The indexer has 2 kind of functions mostly, 1 argument and 2 arguments
+		// The symbol table has 2 kind of functions mostly, 1 argument and 2 arguments
 		// Create macro to condense these common patterns
-		#define INDEXER_FUNC1( func, type ) \
+		#define SYMTAB_FUNC1( func, type ) \
 		template<typename pass_t> \
-		static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.indexer.func( arg ), void() ) {\
-			pass.indexer.func( arg ); \
+		static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.symtab.func( arg ), void() ) {\
+			pass.symtab.func( arg ); \
 		} \
 		\
@@ -296,8 +296,8 @@
 		static inline void func( pass_t &, long, type ) {}
 
-		#define INDEXER_FUNC2( func, type1, type2 ) \
+		#define SYMTAB_FUNC2( func, type1, type2 ) \
 		template<typename pass_t> \
-		static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.indexer.func( arg1, arg2 ), void () ) {\
-			pass.indexer.func( arg1, arg2 ); \
+		static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.symtab.func( arg1, arg2 ), void () ) {\
+			pass.symtab.func( arg1, arg2 ); \
 		} \
 			\
@@ -305,18 +305,18 @@
 		static inline void func( pass_t &, long, type1, type2 ) {}
 
-		INDEXER_FUNC1( addId     , const DeclWithType *  );
-		INDEXER_FUNC1( addType   , const NamedTypeDecl * );
-		INDEXER_FUNC1( addStruct , const StructDecl *    );
-		INDEXER_FUNC1( addEnum   , const EnumDecl *      );
-		INDEXER_FUNC1( addUnion  , const UnionDecl *     );
-		INDEXER_FUNC1( addTrait  , const TraitDecl *     );
-		INDEXER_FUNC2( addWith   , const std::vector< ptr<Expr> > &, const Node * );
+		SYMTAB_FUNC1( addId     , const DeclWithType *  );
+		SYMTAB_FUNC1( addType   , const NamedTypeDecl * );
+		SYMTAB_FUNC1( addStruct , const StructDecl *    );
+		SYMTAB_FUNC1( addEnum   , const EnumDecl *      );
+		SYMTAB_FUNC1( addUnion  , const UnionDecl *     );
+		SYMTAB_FUNC1( addTrait  , const TraitDecl *     );
+		SYMTAB_FUNC2( addWith   , const std::vector< ptr<Expr> > &, const Node * );
 
 		// A few extra functions have more complicated behaviour, they are hand written
 		template<typename pass_t>
-		static inline auto addStructFwd( pass_t & pass, int, const ast::StructDecl * decl ) -> decltype( pass.indexer.addStruct( decl ), void() ) {
+		static inline auto addStructFwd( pass_t & pass, int, const ast::StructDecl * decl ) -> decltype( pass.symtab.addStruct( decl ), void() ) {
 			ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name );
 			fwd->params = decl->params;
-			pass.indexer.addStruct( fwd );
+			pass.symtab.addStruct( fwd );
 		}
 
@@ -325,8 +325,8 @@
 
 		template<typename pass_t>
-		static inline auto addUnionFwd( pass_t & pass, int, const ast::UnionDecl * decl ) -> decltype( pass.indexer.addUnion( decl ), void() ) {
+		static inline auto addUnionFwd( pass_t & pass, int, const ast::UnionDecl * decl ) -> decltype( pass.symtab.addUnion( decl ), void() ) {
 			UnionDecl * fwd = new UnionDecl( decl->location, decl->name );
 			fwd->params = decl->params;
-			pass.indexer.addUnion( fwd );
+			pass.symtab.addUnion( fwd );
 		}
 
@@ -335,7 +335,7 @@
 
 		template<typename pass_t>
-		static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addStruct( str ), void() ) {
-			if ( ! pass.indexer.lookupStruct( str ) ) {
-				pass.indexer.addStruct( str );
+		static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.symtab.addStruct( str ), void() ) {
+			if ( ! pass.symtab.lookupStruct( str ) ) {
+				pass.symtab.addStruct( str );
 			}
 		}
@@ -345,7 +345,7 @@
 
 		template<typename pass_t>
-		static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addUnion( str ), void() ) {
-			if ( ! pass.indexer.lookupUnion( str ) ) {
-				pass.indexer.addUnion( str );
+		static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.symtab.addUnion( str ), void() ) {
+			if ( ! pass.symtab.lookupUnion( str ) ) {
+				pass.symtab.addUnion( str );
 			}
 		}
@@ -354,6 +354,6 @@
 		static inline void addUnion( pass_t &, long, const std::string & ) {}
 
-		#undef INDEXER_FUNC1
-		#undef INDEXER_FUNC2
+		#undef SYMTAB_FUNC1
+		#undef SYMTAB_FUNC2
 	};
 };
Index: src/AST/SymbolTable.hpp
===================================================================
--- src/AST/SymbolTable.hpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/SymbolTable.hpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -123,11 +123,11 @@
 	void addType( const NamedTypeDecl * decl );
 	/// Adds a struct declaration to the symbol table by name
-	void addStruct( const std::string &id );
+	void addStruct( const std::string & id );
 	/// Adds a struct declaration to the symbol table
 	void addStruct( const StructDecl * decl );
 	/// Adds an enum declaration to the symbol table
-	void addEnum( const EnumDecl *decl );
+	void addEnum( const EnumDecl * decl );
 	/// Adds a union declaration to the symbol table by name
-	void addUnion( const std::string &id );
+	void addUnion( const std::string & id );
 	/// Adds a union declaration to the symbol table
 	void addUnion( const UnionDecl * decl );
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/Type.hpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -77,14 +77,20 @@
 };
 
-/// Set the `is_lvalue` qualifier on this type, cloning only if necessary
+/// Clear/reset the qualifiers on this type, cloning only if necessary
 template< enum Node::ref_type ref_t >
-void add_lvalue( ptr_base< Type, ref_t > & p ) {
-	if ( ! p->qualifiers.is_lvalue ) p.get_and_mutate()->qualifiers.is_lvalue = true;
+void reset_qualifiers( ptr_base< Type, ref_t > & p, CV::Qualifiers q = {} ) {
+	if ( p->qualifiers.val != q.val ) p.get_and_mutate()->qualifiers = q;
 }
 
-/// Clear the qualifiers on this type, cloning only if necessary
+/// Add the specified qualifiers to this type, cloning only if necessary
 template< enum Node::ref_type ref_t >
-void clear_qualifiers( ptr_base< Type, ref_t > & p ) {
-	if ( p->qualifiers != CV::Qualifiers{} ) p.get_and_mutate()->qualifiers = CV::Qualifiers{};
+void add_qualifiers( ptr_base< Type, ref_t > & p, CV::Qualifiers q ) {
+	if ( ( p->qualifiers.val & q.val ) != q.val ) p.get_and_mutate()->qualifiers |= q;
+}
+
+/// Remove the specified qualifiers from this type, cloning only if necessary
+template< enum Node::ref_type ref_t >
+void remove_qualifiers( ptr_base< Type, ref_t > & p, CV::Qualifiers q ) {
+	if ( ( p->qualifiers.val & q.val ) != 0 ) p.get_and_mutate()->qualifiers -= q;
 }
 
Index: src/AST/TypeEnvironment.cpp
===================================================================
--- src/AST/TypeEnvironment.cpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/TypeEnvironment.cpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -235,14 +235,14 @@
 }
 
+/// true if a type is a function type
+bool isFtype( const Type * type ) {
+	if ( dynamic_cast< const FunctionType * >( type ) ) {
+		return true;
+	} else if ( auto typeInst = dynamic_cast< const TypeInstType * >( type ) ) {
+		return typeInst->kind == TypeVar::Ftype;
+	} else return false;
+}
+
 namespace {
-	/// true if a type is a function type
-	bool isFtype( const Type * type ) {
-		if ( dynamic_cast< const FunctionType * >( type ) ) {
-			return true;
-		} else if ( auto typeInst = dynamic_cast< const TypeInstType * >( type ) ) {
-			return typeInst->kind == TypeVar::Ftype;
-		} else return false;
-	}
-
 	/// true if the given type can be bound to the given type variable
 	bool tyVarCompatible( const TypeDecl::Data & data, const Type * type ) {
@@ -285,5 +285,5 @@
 			ptr<Type> common;
 			ptr<Type> newType = it->bound;
-			newType.get_and_mutate()->qualifiers = typeInst->qualifiers;
+			reset_qualifiers( newType, typeInst->qualifiers );
 			if ( unifyInexact( 
 					newType, target, *this, need, have, open, 
@@ -291,10 +291,10 @@
 				if ( common ) {
 					it->bound = std::move(common);
-					clear_qualifiers( it->bound );
+					reset_qualifiers( it->bound );
 				}
 			} else return false;
 		} else {
 			it->bound = std::move(target);
-			clear_qualifiers( it->bound );
+			reset_qualifiers( it->bound );
 			it->allowWidening = widen.first && widen.second;
 		}
@@ -351,5 +351,5 @@
 			if ( common ) {
 				c1->bound = std::move(common);
-				clear_qualifiers( c1->bound );
+				reset_qualifiers( c1->bound );
 			}
 			c1->data.isComplete |= data.isComplete;
@@ -411,5 +411,5 @@
 				if ( common ) {
 					to.bound = std::move(common);
-					clear_qualifiers( to.bound );
+					reset_qualifiers( to.bound );
 				}
 			} else return false; // cannot unify
Index: src/AST/TypeEnvironment.hpp
===================================================================
--- src/AST/TypeEnvironment.hpp	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/TypeEnvironment.hpp	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -112,5 +112,5 @@
 	EqvClass( const std::string & v, const Type * b, bool w, const TypeDecl::Data & d )
 	: vars{ v }, bound( b ), allowWidening( w ), data( d ) {
-		clear_qualifiers( bound );
+		reset_qualifiers( bound );
 	}
 
Index: src/AST/porting.md
===================================================================
--- src/AST/porting.md	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/AST/porting.md	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -109,10 +109,10 @@
     * `SymTab::Indexer` => `ast::SymbolTable`
     * `SymTab/Indexer.{h,cc}` => `AST/SymbolTable.{hpp,cpp}`
-    * **TODO** `WithIndexer` => `WithSymbolTable`
+    * `WithIndexer` => `WithSymbolTable`
       * `indexer` => `symTab`
     * `IdData::deleteStmt` => `IdData::deleter`
     * `lookupId()` now returns a vector rather than an out-param list
-    * To avoid name collisions:
-      * `SymTab::Mangler` => `Mangle`
+  * To avoid name collisions:
+    * `SymTab::Mangler` => `Mangle`
   * `ResolvExpr::TypeEnvironment` => `ast::TypeEnvironment`
     * in `AST/TypeEnvironment.hpp`
Index: src/ResolvExpr/CommonType.cc
===================================================================
--- src/ResolvExpr/CommonType.cc	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/ResolvExpr/CommonType.cc	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -18,4 +18,6 @@
 #include <utility>                       // for pair
 
+#include "AST/Decl.hpp"
+#include "AST/Type.hpp"
 #include "Common/PassVisitor.h"
 #include "ResolvExpr/TypeEnvironment.h"  // for OpenVarSet, AssertionSet
@@ -35,6 +37,6 @@
 
 namespace ResolvExpr {
-	struct CommonType : public WithShortCircuiting {
-		CommonType( Type *type2, bool widenFirst, bool widenSecond, const SymTab::Indexer &indexer, TypeEnvironment &env, const OpenVarSet &openVars );
+	struct CommonType_old : public WithShortCircuiting {
+		CommonType_old( Type *type2, bool widenFirst, bool widenSecond, const SymTab::Indexer &indexer, TypeEnvironment &env, const OpenVarSet &openVars );
 		Type *get_result() const { return result; }
 
@@ -94,5 +96,5 @@
 
 	Type *commonType( Type *type1, Type *type2, bool widenFirst, bool widenSecond, const SymTab::Indexer &indexer, TypeEnvironment &env, const OpenVarSet &openVars ) {
-		PassVisitor<CommonType> visitor( type2, widenFirst, widenSecond, indexer, env, openVars );
+		PassVisitor<CommonType_old> visitor( type2, widenFirst, widenSecond, indexer, env, openVars );
 
 		int depth1 = type1->referenceDepth();
@@ -176,14 +178,4 @@
 	}
 
-	const ast::Type * commonType(
-			const ast::Type * type1, const ast::Type * type2, WidenMode widen, 
-			const ast::SymbolTable & symtab, ast::TypeEnvironment & env, 
-			const ast::OpenVarSet & open ) {
-		#warning unimplemented
-		(void)type1; (void)type2; (void)widen; (void)symtab; (void)env; (void)open;
-		assert(false);
-		return nullptr;
-	}
-
 	// GENERATED START, DO NOT EDIT
 	// GENERATED BY BasicTypes-gen.cc
@@ -493,11 +485,11 @@
 	);
 
-	CommonType::CommonType( Type *type2, bool widenFirst, bool widenSecond, const SymTab::Indexer &indexer, TypeEnvironment &env, const OpenVarSet &openVars )
+	CommonType_old::CommonType_old( Type *type2, bool widenFirst, bool widenSecond, const SymTab::Indexer &indexer, TypeEnvironment &env, const OpenVarSet &openVars )
 		: result( 0 ), type2( type2 ), widenFirst( widenFirst ), widenSecond( widenSecond ), indexer( indexer ), env( env ), openVars( openVars ) {
 	}
 
-	void CommonType::postvisit( VoidType * ) {}
-
-	void CommonType::postvisit( BasicType *basicType ) {
+	void CommonType_old::postvisit( VoidType * ) {}
+
+	void CommonType_old::postvisit( BasicType *basicType ) {
 		if ( BasicType *otherBasic = dynamic_cast< BasicType* >( type2 ) ) {
 			BasicType::Kind newType = commonTypes[ basicType->get_kind() ][ otherBasic->get_kind() ];
@@ -515,5 +507,5 @@
 
 	template< typename Pointer >
-	void CommonType::getCommonWithVoidPointer( Pointer* voidPointer, Pointer* otherPointer ) {
+	void CommonType_old::getCommonWithVoidPointer( Pointer* voidPointer, Pointer* otherPointer ) {
 		if ( TypeInstType* var = dynamic_cast< TypeInstType* >( otherPointer->get_base() ) ) {
 			OpenVarSet::const_iterator entry = openVars.find( var->get_name() );
@@ -528,5 +520,5 @@
 	}
 
-	void CommonType::postvisit( PointerType *pointerType ) {
+	void CommonType_old::postvisit( PointerType *pointerType ) {
 		if ( PointerType *otherPointer = dynamic_cast< PointerType* >( type2 ) ) {
 			// std::cerr << "commonType: two pointers: " << pointerType << " / " << otherPointer << std::endl;
@@ -563,7 +555,7 @@
 	}
 
-	void CommonType::postvisit( ArrayType * ) {}
-
-	void CommonType::postvisit( ReferenceType *refType ) {
+	void CommonType_old::postvisit( ArrayType * ) {}
+
+	void CommonType_old::postvisit( ReferenceType *refType ) {
 		if ( ReferenceType *otherRef = dynamic_cast< ReferenceType* >( type2 ) ) {
 			// std::cerr << "commonType: both references: " << refType << " / " << otherRef << std::endl;
@@ -600,9 +592,9 @@
 	}
 
-	void CommonType::postvisit( FunctionType * ) {}
-	void CommonType::postvisit( StructInstType * ) {}
-	void CommonType::postvisit( UnionInstType * ) {}
-
-	void CommonType::postvisit( EnumInstType *enumInstType ) {
+	void CommonType_old::postvisit( FunctionType * ) {}
+	void CommonType_old::postvisit( StructInstType * ) {}
+	void CommonType_old::postvisit( UnionInstType * ) {}
+
+	void CommonType_old::postvisit( EnumInstType *enumInstType ) {
 		if ( dynamic_cast< BasicType * >( type2 ) || dynamic_cast< ZeroType* >( type2 ) || dynamic_cast< OneType* >( type2 ) ) {
 			// reuse BasicType, EnumInstType code by swapping type2 with enumInstType
@@ -611,8 +603,8 @@
 	}
 
-	void CommonType::postvisit( TraitInstType * ) {
-	}
-
-	void CommonType::postvisit( TypeInstType *inst ) {
+	void CommonType_old::postvisit( TraitInstType * ) {
+	}
+
+	void CommonType_old::postvisit( TypeInstType *inst ) {
 		if ( widenFirst ) {
 			NamedTypeDecl *nt = indexer.lookupType( inst->get_name() );
@@ -636,8 +628,8 @@
 	}
 
-	void CommonType::postvisit( TupleType * ) {}
-	void CommonType::postvisit( VarArgsType * ) {}
-
-	void CommonType::postvisit( ZeroType *zeroType ) {
+	void CommonType_old::postvisit( TupleType * ) {}
+	void CommonType_old::postvisit( VarArgsType * ) {}
+
+	void CommonType_old::postvisit( ZeroType *zeroType ) {
 		if ( widenFirst ) {
 			if ( dynamic_cast< BasicType* >( type2 ) || dynamic_cast< PointerType* >( type2 ) || dynamic_cast< EnumInstType* >( type2 ) ) {
@@ -653,5 +645,5 @@
 	}
 
-	void CommonType::postvisit( OneType *oneType ) {
+	void CommonType_old::postvisit( OneType *oneType ) {
 		if ( widenFirst ) {
 			if ( dynamic_cast< BasicType* >( type2 ) || dynamic_cast< EnumInstType* >( type2 ) ) {
@@ -666,4 +658,343 @@
 		}
 	}
+
+	class CommonType_new final : public ast::WithShortCircuiting {
+		const ast::Type * type2;
+		WidenMode widen;
+		const ast::SymbolTable & symtab;
+		ast::TypeEnvironment & tenv;
+		const ast::OpenVarSet & open;
+	public:
+		ast::ptr< ast::Type > result;
+
+		CommonType_new( 
+			const ast::Type * t2, WidenMode w, const ast::SymbolTable & st, 
+			ast::TypeEnvironment & env, const ast::OpenVarSet & o )
+		: type2( t2 ), widen( w ), symtab( st ), tenv( env ), open( o ), result() {}
+
+		void previsit( const ast::Node * ) { visit_children = false; }
+
+		void postvisit( const ast::VoidType * ) {}
+
+		void postvisit( const ast::BasicType * basic ) {
+			if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) {
+				#warning remove casts when `commonTypes` moved to new AST
+				ast::BasicType::Kind kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)basic2->kind ];
+				if ( 
+					( ( kind == basic->kind && basic->qualifiers >= basic2->qualifiers ) 
+						|| widen.first ) 
+					&& ( ( kind == basic2->kind && basic->qualifiers <= basic2->qualifiers ) 
+						|| widen.second ) 
+				) {
+					result = new ast::BasicType{ kind, basic->qualifiers | basic2->qualifiers };
+				}
+			} else if ( 
+				dynamic_cast< const ast::EnumInstType * >( type2 ) 
+				|| dynamic_cast< const ast::ZeroType * >( type2 )
+				|| dynamic_cast< const ast::OneType * >( type2 )
+			) {
+				#warning remove casts when `commonTypes` moved to new AST
+				ast::BasicType::Kind kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)ast::BasicType::SignedInt ];
+				if ( 
+					( ( kind == basic->kind && basic->qualifiers >= type2->qualifiers ) 
+						|| widen.first ) 
+					&& ( ( kind != basic->kind && basic->qualifiers <= type2->qualifiers ) 
+						|| widen.second ) 
+				) {
+					result = new ast::BasicType{ kind, basic->qualifiers | type2->qualifiers };
+				}
+			}
+		}
+
+	private:
+		template< typename Pointer >
+		void getCommonWithVoidPointer( const Pointer * voidPtr, const Pointer * oPtr ) {
+			const ast::Type * base = oPtr->base;
+			if ( auto var = dynamic_cast< const ast::TypeInstType * >( base ) ) {
+				auto entry = open.find( var->name );
+				if ( entry != open.end() ) {
+					ast::AssertionSet need, have;
+					if ( ! tenv.bindVar( 
+						var, voidPtr->base, entry->second, need, have, open, widen, symtab ) 
+					) return;
+				}
+			}
+			result = voidPtr;
+			add_qualifiers( result, oPtr->qualifiers );
+		}
+
+	public:
+		void postvisit( const ast::PointerType * pointer ) {
+			if ( auto pointer2 = dynamic_cast< const ast::PointerType * >( type2 ) ) {
+				if ( 
+					widen.first 
+					&& pointer2->base.as< ast::VoidType >() 
+					&& ! ast::isFtype( pointer->base ) 
+				) {
+					getCommonWithVoidPointer( pointer2, pointer );
+				} else if ( 
+					widen.second 
+					&& pointer->base.as< ast::VoidType >() 
+					&& ! ast::isFtype( pointer2->base ) 
+				) {
+					getCommonWithVoidPointer( pointer, pointer2 );
+				} else if (
+					( pointer->base->qualifiers >= pointer2->base->qualifiers || widen.first )
+					&& ( pointer->base->qualifiers <= pointer2->base->qualifiers || widen.second )
+				) {
+					ast::CV::Qualifiers q1 = pointer->base->qualifiers;
+					ast::CV::Qualifiers q2 = pointer2->base->qualifiers;
+
+					// force t{1,2} to be cloned if their qualifiers must be stripped, so that 
+					// pointer{,2}->base are unchanged
+					ast::ptr< ast::Type > t1{ pointer->base }, t2{ pointer2->base };
+					reset_qualifiers( t1 );
+					reset_qualifiers( t2 );
+					
+					ast::AssertionSet have, need;
+					ast::OpenVarSet newOpen{ open };
+					if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden(), symtab ) ) {
+						result = pointer;
+						if ( q1.val != q2.val ) {
+							// reset result->base->qualifiers to be union of two base qualifiers
+							strict_dynamic_cast< ast::PointerType * >( 
+								result.get_and_mutate() 
+							)->base.get_and_mutate()->qualifiers = q1 | q2;
+						}
+					}
+				}
+			} else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
+				result = pointer;
+				add_qualifiers( result, type2->qualifiers );
+			}
+		}
+
+		void postvisit( const ast::ArrayType * ) {}
+
+		void postvisit( const ast::ReferenceType * ref ) {
+			if ( auto ref2 = dynamic_cast< const ast::ReferenceType * >( type2 ) ) {
+				if (
+					widen.first && ref2->base.as< ast::VoidType >() && ! ast::isFtype( ref->base ) 
+				) {
+					getCommonWithVoidPointer( ref2, ref );
+				} else if ( 
+					widen.second && ref->base.as< ast::VoidType>() && ! ast::isFtype( ref2->base ) 
+				) {
+					getCommonWithVoidPointer( ref, ref2 );
+				} else if (
+					( ref->base->qualifiers >= ref2->base->qualifiers || widen.first )
+					&& ( ref->base->qualifiers <= ref2->base->qualifiers || widen.second )
+				) {
+					ast::CV::Qualifiers q1 = ref->base->qualifiers, q2 = ref2->base->qualifiers;
+
+					// force t{1,2} to be cloned if their qualifiers must be stripped, so that 
+					// ref{,2}->base are unchanged
+					ast::ptr< ast::Type > t1{ ref->base }, t2{ ref2->base };
+					reset_qualifiers( t1 );
+					reset_qualifiers( t2 );
+
+					ast::AssertionSet have, need;
+					ast::OpenVarSet newOpen{ open };
+					if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden(), symtab ) ) {
+						result = ref;
+						if ( q1.val != q2.val ) {
+							// reset result->base->qualifiers to be union of two base qualifiers
+							strict_dynamic_cast< ast::ReferenceType * >( 
+								result.get_and_mutate() 
+							)->base.get_and_mutate()->qualifiers = q1 | q2;
+						}
+					}
+				}
+			} else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
+				result = ref;
+				add_qualifiers( result, type2->qualifiers );
+			}
+		}
+
+		void postvisit( const ast::FunctionType * ) {}
+
+		void postvisit( const ast::StructInstType * ) {}
+
+		void postvisit( const ast::UnionInstType * ) {}
+
+		void postvisit( const ast::EnumInstType * enumInst ) {
+			if ( 
+				dynamic_cast< const ast::BasicType * >( type2 ) 
+				|| dynamic_cast< const ast::ZeroType * >( type2 )
+				|| dynamic_cast< const ast::OneType * >( type2 )
+			) {
+				// reuse BasicType/EnumInstType common type by swapping
+				result = commonType( type2, enumInst, widen, symtab, tenv, open );
+			}
+		}
+
+		void postvisit( const ast::TraitInstType * ) {}
+
+		void postvisit( const ast::TypeInstType * inst ) {
+			if ( ! widen.first ) return;
+			if ( const ast::NamedTypeDecl * nt = symtab.lookupType( inst->name ) ) {
+				if ( const ast::Type * base = 
+						strict_dynamic_cast< const ast::TypeDecl * >( nt )->base 
+				) {
+					ast::CV::Qualifiers q1 = inst->qualifiers, q2 = type2->qualifiers;
+
+					// force t{1,2} to be cloned if their qualifiers must be mutated
+					ast::ptr< ast::Type > t1{ base }, t2{ type2 };
+					reset_qualifiers( t1, q1 );
+					reset_qualifiers( t2 );
+
+					ast::AssertionSet have, need;
+					ast::OpenVarSet newOpen{ open };
+					if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden(), symtab ) ) {
+						result = type2;
+						reset_qualifiers( result, q1 | q2 );
+					}
+				}
+			}
+		}
+
+		void postvisit( const ast::TupleType * ) {}
+
+		void postvisit( const ast::VarArgsType * ) {}
+
+		void postvisit( const ast::ZeroType * zero ) {
+			if ( ! widen.first ) return;
+			if ( 
+				dynamic_cast< const ast::BasicType * >( type2 )
+				|| dynamic_cast< const ast::PointerType * >( type2 )
+				|| dynamic_cast< const ast::EnumInstType * >( type2 )
+			) {
+				if ( widen.second || zero->qualifiers <= type2->qualifiers ) {
+					result = type2;
+					add_qualifiers( result, zero->qualifiers );
+				}
+			} else if ( widen.second && dynamic_cast< const ast::OneType * >( type2 ) ) {
+				result = new ast::BasicType{ 
+					ast::BasicType::SignedInt, zero->qualifiers | type2->qualifiers };
+			}
+		}
+
+		void postvisit( const ast::OneType * one ) {
+			if ( ! widen.first ) return;
+			if ( 
+				dynamic_cast< const ast::BasicType * >( type2 )
+				|| dynamic_cast< const ast::EnumInstType * >( type2 )
+			) {
+				if ( widen.second || one->qualifiers <= type2->qualifiers ) {
+					result = type2;
+					add_qualifiers( result, one->qualifiers );
+				}
+			} else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
+				result = new ast::BasicType{ 
+					ast::BasicType::SignedInt, one->qualifiers | type2->qualifiers };
+			}
+		}
+
+	};
+
+	namespace {
+		ast::ptr< ast::Type > handleReference( 
+			const ast::ptr< ast::Type > & t1, const ast::ptr< ast::Type > & t2, WidenMode widen, 
+			const ast::SymbolTable & symtab, ast::TypeEnvironment & env, 
+			const ast::OpenVarSet & open 
+		) {
+			ast::ptr<ast::Type> common;
+			ast::AssertionSet have, need;
+			ast::OpenVarSet newOpen{ open };
+
+			// need unify to bind type variables
+			if ( unify( t1, t2, env, have, need, newOpen, symtab, common ) ) {
+				ast::CV::Qualifiers q1 = t1->qualifiers, q2 = t2->qualifiers;
+				PRINT(
+					std::cerr << "unify success: " << widenFirst << " " << widenSecond << std::endl;
+				)
+				if ( ( widen.first || q2 <= q1 ) && ( widen.second || q1 <= q2 ) ) {
+					PRINT(
+						std::cerr << "widen okay" << std::endl;
+					)
+					add_qualifiers( common, q1 | q2 );
+					return common;
+				}
+			}
+
+			PRINT(
+				std::cerr << "exact unify failed: " << t1 << " " << t2 << std::endl;
+			)
+			return { nullptr };
+		}
+	}
+
+	ast::ptr< ast::Type > commonType(
+			const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2, 
+			WidenMode widen, const ast::SymbolTable & symtab, ast::TypeEnvironment & env, 
+			const ast::OpenVarSet & open 
+	) {
+		unsigned depth1 = type1->referenceDepth();
+		unsigned depth2 = type2->referenceDepth();
+
+		if ( depth1 != depth2 ) {  // implies depth1 > 0 || depth2 > 0
+			PRINT(
+				std::cerr << "reference depth diff: " << (depth1-depth2) << std::endl;
+			)
+			ast::ptr< ast::Type > result;
+			const ast::ReferenceType * ref1 = type1.as< ast::ReferenceType >();
+			const ast::ReferenceType * ref2 = type1.as< ast::ReferenceType >();
+			
+			if ( depth1 > depth2 ) {
+				assert( ref1 );
+				result = handleReference( ref1->base, type2, widen, symtab, env, open );
+			} else {  // implies depth1 < depth2
+				assert( ref2 );
+				result = handleReference( type1, ref2->base, widen, symtab, env, open );
+			}
+
+			if ( result && ref1 ) {
+				// formal is reference, so result should be reference
+				PRINT(
+					std::cerr << "formal is reference; result should be reference" << std::endl;
+				)
+				result = new ast::ReferenceType{ result, ref1->qualifiers };
+			}
+
+			PRINT(
+				std::cerr << "common type of reference [" << type1 << "] and [" << type2 << "] is "
+				"[" << result << "]" << std::endl;
+			)
+			return result;
+		}
+		// otherwise both are reference types of the same depth and this is handled by the visitor
+		ast::Pass<CommonType_new> visitor{ type2, widen, symtab, env, open };
+		type1->accept( visitor );
+		ast::ptr< ast::Type > result = visitor.pass.result;
+
+		// handling for opaque type declarations (?)
+		if ( ! result && widen.second ) {
+			if ( const ast::TypeInstType * inst = type2.as< ast::TypeInstType >() ) {
+				if ( const ast::NamedTypeDecl * nt = symtab.lookupType( inst->name ) ) {
+					auto type = strict_dynamic_cast< const ast::TypeDecl * >( nt );
+					if ( type->base ) {
+						ast::CV::Qualifiers q1 = type1->qualifiers, q2 = type2->qualifiers;
+						ast::AssertionSet have, need;
+						ast::OpenVarSet newOpen{ open };
+
+						// force t{1,2} to be cloned if its qualifiers must be stripped, so that 
+						// type1 and type->base are left unchanged; calling convention forces 
+						// {type1,type->base}->strong_ref >= 1
+						ast::ptr<ast::Type> t1{ type1 }, t2{ type->base };
+						reset_qualifiers( t1 );
+						reset_qualifiers( t2, q1 );
+						
+						if ( unifyExact( t1, t2, env, have, need, newOpen, noWiden(), symtab ) ) {
+							result = t1;
+							reset_qualifiers( result, q1 | q2 );
+						}
+					}
+				}
+			}
+		}
+
+		return result;
+	}
+
 } // namespace ResolvExpr
 
Index: src/ResolvExpr/FindOpenVars.cc
===================================================================
--- src/ResolvExpr/FindOpenVars.cc	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/ResolvExpr/FindOpenVars.cc	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -19,4 +19,6 @@
 #include <map>                    // for map<>::mapped_type
 
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
 #include "Common/PassVisitor.h"
 #include "SynTree/Declaration.h"  // for TypeDecl, DeclarationWithType (ptr ...
@@ -24,6 +26,6 @@
 
 namespace ResolvExpr {
-	struct FindOpenVars : public WithGuards {
-		FindOpenVars( OpenVarSet &openVars, OpenVarSet &closedVars, AssertionSet &needAssertions, AssertionSet &haveAssertions, bool firstIsOpen );
+	struct FindOpenVars_old : public WithGuards {
+		FindOpenVars_old( OpenVarSet &openVars, OpenVarSet &closedVars, AssertionSet &needAssertions, AssertionSet &haveAssertions, bool firstIsOpen );
 
 		void previsit( PointerType * pointerType );
@@ -40,13 +42,13 @@
 
 	void findOpenVars( Type *type, OpenVarSet &openVars, OpenVarSet &closedVars, AssertionSet &needAssertions, AssertionSet &haveAssertions, bool firstIsOpen ) {
-		PassVisitor<FindOpenVars> finder( openVars, closedVars, needAssertions, haveAssertions, firstIsOpen );
+		PassVisitor<FindOpenVars_old> finder( openVars, closedVars, needAssertions, haveAssertions, firstIsOpen );
 		type->accept( finder );
 	}
 
-	FindOpenVars::FindOpenVars( OpenVarSet &openVars, OpenVarSet &closedVars, AssertionSet &needAssertions, AssertionSet &haveAssertions, bool firstIsOpen )
+	FindOpenVars_old::FindOpenVars_old( OpenVarSet &openVars, OpenVarSet &closedVars, AssertionSet &needAssertions, AssertionSet &haveAssertions, bool firstIsOpen )
 		: openVars( openVars ), closedVars( closedVars ), needAssertions( needAssertions ), haveAssertions( haveAssertions ), nextIsOpen( firstIsOpen )	{
 	}
 
-	void FindOpenVars::common_action( Type *type ) {
+	void FindOpenVars_old::common_action( Type *type ) {
 		if ( nextIsOpen ) {
 			for ( Type::ForallList::const_iterator i = type->get_forall().begin(); i != type->get_forall().end(); ++i ) {
@@ -76,13 +78,13 @@
 	}
 
-	void FindOpenVars::previsit(PointerType *pointerType) {
+	void FindOpenVars_old::previsit(PointerType *pointerType) {
 		common_action( pointerType );
 	}
 
-	void FindOpenVars::previsit(ArrayType *arrayType) {
+	void FindOpenVars_old::previsit(ArrayType *arrayType) {
 		common_action( arrayType );
 	}
 
-	void FindOpenVars::previsit(FunctionType *functionType) {
+	void FindOpenVars_old::previsit(FunctionType *functionType) {
 		common_action( functionType );
 		nextIsOpen = ! nextIsOpen;
@@ -90,6 +92,45 @@
 	}
 
-	void FindOpenVars::previsit(TupleType *tupleType) {
+	void FindOpenVars_old::previsit(TupleType *tupleType) {
 		common_action( tupleType );
+	}
+
+	namespace {
+		struct FindOpenVars_new final : public ast::WithGuards {
+			ast::OpenVarSet & open;
+			ast::OpenVarSet & closed;
+			ast::AssertionSet & need;
+			ast::AssertionSet & have;
+			bool nextIsOpen;
+
+			FindOpenVars_new( 
+				ast::OpenVarSet & o, ast::OpenVarSet & c, ast::AssertionSet & n, 
+				ast::AssertionSet & h, FirstMode firstIsOpen )
+			: open( o ), closed( c ), need( n ), have( h ), nextIsOpen( firstIsOpen ) {}
+
+			void previsit( const ast::FunctionType * type ) {
+				// 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;
+						}
+					}
+				} 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;
+						}
+					}
+				}
+
+				// flip open variables for contained function types
+				nextIsOpen = ! nextIsOpen;
+				GuardAction( [this](){ nextIsOpen = ! nextIsOpen; } );
+			}
+
+		};
 	}
 
@@ -97,7 +138,6 @@
 			const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed, 
 			ast::AssertionSet & need, ast::AssertionSet & have, FirstMode firstIsOpen ) {
-		#warning unimplemented
-		(void)type; (void)open; (void)closed; (void)need; (void)have; (void)firstIsOpen;
-		assert(false);
+		ast::Pass< FindOpenVars_new > finder{ open, closed, need, have, firstIsOpen };
+		type->accept( finder );
 	}
 } // namespace ResolvExpr
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/ResolvExpr/Resolver.cc	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -938,10 +938,11 @@
 
 	class Resolver_new final 
-	: public ast::WithIndexer, public ast::WithGuards, public ast::WithVisitorRef<Resolver_new>, 
-	  public ast::WithShortCircuiting, public ast::WithStmtsToAdd<> {
+	: public ast::WithSymbolTable, public ast::WithGuards, 
+	  public ast::WithVisitorRef<Resolver_new>, public ast::WithShortCircuiting, 
+	  public ast::WithStmtsToAdd<> {
 		  
 	public: 
 		Resolver_new() = default;
-		Resolver_new( const ast::SymbolTable & syms ) { /*symtab = syms;*/ }
+		Resolver_new( const ast::SymbolTable & syms ) { symtab = syms; }
 
 		void previsit( ast::FunctionDecl * functionDecl );
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/ResolvExpr/Unify.cc	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -136,5 +136,5 @@
 
 		return unifyExact( 
-			newFirst, newSecond, newEnv, need, have, open, WidenMode{ false, false }, symtab );
+			newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
 	}
 
@@ -173,9 +173,9 @@
 		env.apply( newFirst );
 		env.apply( newSecond );
-		clear_qualifiers( newFirst );
-		clear_qualifiers( newSecond );
+		reset_qualifiers( newFirst );
+		reset_qualifiers( newSecond );
 
 		return unifyExact( 
-			newFirst, newSecond, newEnv, need, have, open, WidenMode{ false, false }, symtab );
+			newFirst, newSecond, newEnv, need, have, open, noWiden(), symtab );
 	}
 
@@ -700,5 +700,5 @@
 	}
 
-	class Unify_new : public ast::WithShortCircuiting {
+	class Unify_new final : public ast::WithShortCircuiting {
 		const ast::Type * type2;
 		ast::TypeEnvironment & tenv;
@@ -720,11 +720,9 @@
 		void previsit( const ast::Node * ) { visit_children = false; }
 		
-		void previsit( const ast::VoidType * ) {
-			visit_children = false;
+		void postvisit( const ast::VoidType * ) {
 			result = dynamic_cast< const ast::VoidType * >( type2 );
 		}
 
-		void previsit( const ast::BasicType * basic ) {
-			visit_children = false;
+		void postvisit( const ast::BasicType * basic ) {
 			if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) {
 				result = basic->kind == basic2->kind;
@@ -732,15 +730,13 @@
 		}
 
-		void previsit( const ast::PointerType * pointer ) {
-			visit_children = false;
+		void postvisit( const ast::PointerType * pointer ) {
 			if ( auto pointer2 = dynamic_cast< const ast::PointerType * >( type2 ) ) {
 				result = unifyExact( 
 					pointer->base, pointer2->base, tenv, need, have, open, 
-					WidenMode{ false, false }, symtab );
-			}
-		}
-
-		void previsit( const ast::ArrayType * array ) {
-			visit_children = false;
+					noWiden(), symtab );
+			}
+		}
+
+		void postvisit( const ast::ArrayType * array ) {
 			auto array2 = dynamic_cast< const ast::ArrayType * >( type2 );
 			if ( ! array2 ) return;
@@ -761,13 +757,12 @@
 
 			result = unifyExact( 
-				array->base, array2->base, tenv, need, have, open, WidenMode{ false, false }, 
+				array->base, array2->base, tenv, need, have, open, noWiden(), 
 				symtab );
 		}
 
-		void previsit( const ast::ReferenceType * ref ) {
-			visit_children = false;
+		void postvisit( const ast::ReferenceType * ref ) {
 			if ( auto ref2 = dynamic_cast< const ast::ReferenceType * >( type2 ) ) {
 				result = unifyExact( 
-					ref->base, ref2->base, tenv, need, have, open, WidenMode{ false, false }, 
+					ref->base, ref2->base, tenv, need, have, open, noWiden(), 
 					symtab );
 			}
@@ -783,5 +778,5 @@
 			TtypeExpander_new( ast::TypeEnvironment & env ) : tenv( env ) {}
 
-			const ast::Type * postmutate( const ast::TypeInstType * typeInst ) {
+			const ast::Type * postvisit( const ast::TypeInstType * typeInst ) {
 				if ( const ast::EqvClass * clz = tenv.lookup( typeInst->name ) ) {
 					// expand ttype parameter into its actual type
@@ -811,6 +806,5 @@
 					// overloaded on outermost mutex and a mutex function has different 
 					// requirements than a non-mutex function
-					t.get_and_mutate()->qualifiers 
-						-= ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic;
+					remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic );
 					dst.emplace_back( new ast::ObjectDecl{ d->location, "", t } );
 				}
@@ -851,14 +845,14 @@
 					return unifyExact( 
 						t1, tupleFromDecls( crnt2, end2 ), env, need, have, open, 
-						WidenMode{ false, false }, symtab );
+						noWiden(), symtab );
 				} else if ( ! isTuple1 && isTuple2 ) {
 					// combine remainder of list1, then unify
 					return unifyExact( 
 						tupleFromDecls( crnt1, end1 ), t2, env, need, have, open, 
-						WidenMode{ false, false }, symtab );
+						noWiden(), symtab );
 				}
 
 				if ( ! unifyExact( 
-					t1, t2, env, need, have, open, WidenMode{ false, false }, symtab ) 
+					t1, t2, env, need, have, open, noWiden(), symtab ) 
 				) return false;
 
@@ -874,5 +868,5 @@
 				return unifyExact( 
 					t1, tupleFromDecls( crnt2, end2 ), env, need, have, open, 
-					WidenMode{ false, false }, symtab );
+					noWiden(), symtab );
 			} else if ( crnt2 != end2 ) {
 				// try unifying empty tuple with ttype
@@ -881,5 +875,5 @@
 				return unifyExact( 
 					tupleFromDecls( crnt1, end1 ), t2, env, need, have, open, 
-					WidenMode{ false, false }, symtab );
+					noWiden(), symtab );
 			}
 
@@ -919,6 +913,5 @@
 
 	public:
-		void previsit( const ast::FunctionType * func ) {
-			visit_children = false;
+		void postvisit( const ast::FunctionType * func ) {
 			auto func2 = dynamic_cast< const ast::FunctionType * >( type2 );
 			if ( ! func2 ) return;
@@ -952,5 +945,4 @@
 		template< typename RefType >
 		const RefType * handleRefType( const RefType * inst, const ast::Type * other ) {
-			visit_children = false;
 			// check that the other type is compatible and named the same
 			auto otherInst = dynamic_cast< const RefType * >( other );
@@ -1011,5 +1003,5 @@
 
 				if ( ! unifyExact( 
-						pty, pty2, tenv, need, have, open, WidenMode{ false, false }, symtab ) ) {
+						pty, pty2, tenv, need, have, open, noWiden(), symtab ) ) {
 					result = false;
 					return;
@@ -1023,21 +1015,21 @@
 
 	public:
-		void previsit( const ast::StructInstType * aggrType ) {
+		void postvisit( const ast::StructInstType * aggrType ) {
 			handleGenericRefType( aggrType, type2 );
 		}
 
-		void previsit( const ast::UnionInstType * aggrType ) {
+		void postvisit( const ast::UnionInstType * aggrType ) {
 			handleGenericRefType( aggrType, type2 );
 		}
 
-		void previsit( const ast::EnumInstType * aggrType ) {
+		void postvisit( const ast::EnumInstType * aggrType ) {
 			handleRefType( aggrType, type2 );
 		}
 
-		void previsit( const ast::TraitInstType * aggrType ) {
+		void postvisit( const ast::TraitInstType * aggrType ) {
 			handleRefType( aggrType, type2 );
 		}
 
-		void previsit( const ast::TypeInstType * typeInst ) {
+		void postvisit( const ast::TypeInstType * typeInst ) {
 			assert( open.find( typeInst->name ) == open.end() );
 			handleRefType( typeInst, type2 );
@@ -1078,14 +1070,14 @@
 					return unifyExact( 
 						t1, tupleFromTypes( list2 ), env, need, have, open, 
-						WidenMode{ false, false }, symtab );
+						noWiden(), symtab );
 				} else if ( ! isTuple1 && isTuple2 ) {
 					// combine entirety of list1, then unify
 					return unifyExact(
 						tupleFromTypes( list1 ), t2, env, need, have, open, 
-						WidenMode{ false, false }, symtab );
+						noWiden(), symtab );
 				}
 
 				if ( ! unifyExact( 
-					t1, t2, env, need, have, open, WidenMode{ false, false }, symtab ) 
+					t1, t2, env, need, have, open, noWiden(), symtab ) 
 				) return false;
 
@@ -1101,5 +1093,5 @@
 				return unifyExact( 
 						t1, tupleFromTypes( list2 ), env, need, have, open, 
-						WidenMode{ false, false }, symtab );
+						noWiden(), symtab );
 			} else if ( crnt2 != list2.end() ) {
 				// try unifying empty tuple with ttype
@@ -1110,5 +1102,5 @@
 				return unifyExact(
 						tupleFromTypes( list1 ), t2, env, need, have, open, 
-						WidenMode{ false, false }, symtab );
+						noWiden(), symtab );
 			}
 
@@ -1117,6 +1109,5 @@
 
 	public:
-		void previsit( const ast::TupleType * tuple ) {
-			visit_children = false;
+		void postvisit( const ast::TupleType * tuple ) {
 			auto tuple2 = dynamic_cast< const ast::TupleType * >( type2 );
 			if ( ! tuple2 ) return;
@@ -1132,16 +1123,13 @@
 		}
 
-		void previsit( const ast::VarArgsType * ) {
-			visit_children = false;
+		void postvisit( const ast::VarArgsType * ) {
 			result = dynamic_cast< const ast::VarArgsType * >( type2 );
 		}
 
-		void previsit( const ast::ZeroType * ) {
-			visit_children = false;
+		void postvisit( const ast::ZeroType * ) {
 			result = dynamic_cast< const ast::ZeroType * >( type2 );
 		}
 
-		void previsit( const ast::OneType * ) {
-			visit_children = false;
+		void postvisit( const ast::OneType * ) {
 			result = dynamic_cast< const ast::OneType * >( type2 );
 		}	
@@ -1151,4 +1139,16 @@
 		template< typename RefType > void handleGenericRefType( RefType *inst, Type *other );
 	};
+
+	bool unify( 
+			const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 
+			ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 
+			ast::OpenVarSet & open, const ast::SymbolTable & symtab, ast::ptr<ast::Type> & common 
+	) {
+		ast::OpenVarSet closed;
+		findOpenVars( type1, open, closed, need, have, FirstClosed );
+		findOpenVars( type2, open, closed, need, have, FirstOpen );
+		return unifyInexact( 
+			type1, type2, env, need, have, open, WidenMode{ true, true }, symtab, common );
+	}
 
 	bool unifyExact( 
@@ -1184,7 +1184,8 @@
 
 	bool unifyInexact( 
-			ast::ptr<ast::Type> & type1, ast::ptr<ast::Type> & type2, ast::TypeEnvironment & env, 
-			ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 
-			WidenMode widen, const ast::SymbolTable & symtab, ast::ptr<ast::Type> & common 
+			const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 
+			ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 
+			const ast::OpenVarSet & open, WidenMode widen, const ast::SymbolTable & symtab, 
+			ast::ptr<ast::Type> & common 
 	) {
 		ast::CV::Qualifiers q1 = type1->qualifiers, q2 = type2->qualifiers;
@@ -1193,6 +1194,6 @@
 		// type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1
 		ast::ptr<ast::Type> t1{ type1 }, t2{ type2 };
-		clear_qualifiers( t1 );
-		clear_qualifiers( t2 );
+		reset_qualifiers( t1 );
+		reset_qualifiers( t2 );
 		
 		if ( unifyExact( t1, t2, env, need, have, open, widen, symtab ) ) {
@@ -1201,5 +1202,6 @@
 			// if exact unification on unqualified types, try to merge qualifiers
 			if ( q1 == q2 || ( ( q1 > q2 || widen.first ) && ( q2 > q1 || widen.second ) ) ) {
-				common.set_and_mutate( type1 )->qualifiers = q1 | q2;
+				common = type1;
+				reset_qualifiers( common, q1 | q2 );
 				return true;
 			} else {
@@ -1211,5 +1213,5 @@
 
 			// no exact unification, but common type
-			common.get_and_mutate()->qualifiers = q1 | q2;
+			reset_qualifiers( common, q1 | q2 );
 			return true;
 		} else {
Index: src/ResolvExpr/Unify.h
===================================================================
--- src/ResolvExpr/Unify.h	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/ResolvExpr/Unify.h	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -69,13 +69,19 @@
 	}
 
+	bool unify( 
+		const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 
+		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 
+		ast::OpenVarSet & open, const ast::SymbolTable & symtab, ast::ptr<ast::Type> & common );
+
 	bool unifyExact( 
 		const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env, 
-		ast::AssertionSet & need, ast::AssertionSet & have, ast::OpenVarSet & open, 
-		const ast::SymbolTable & symtab );
+		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 
+		WidenMode widen, const ast::SymbolTable & symtab );
 
 	bool unifyInexact( 
-		ast::ptr<ast::Type> & type1, ast::ptr<ast::Type> & type2, ast::TypeEnvironment & env, 
-		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 
-		WidenMode widen, const ast::SymbolTable & symtab, ast::ptr<ast::Type> & common );
+		const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 
+		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 
+		const ast::OpenVarSet & open, WidenMode widen, const ast::SymbolTable & symtab, 
+		ast::ptr<ast::Type> & common );
 
 } // namespace ResolvExpr
Index: src/ResolvExpr/WidenMode.h
===================================================================
--- src/ResolvExpr/WidenMode.h	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/ResolvExpr/WidenMode.h	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -40,4 +40,6 @@
 		bool first : 1, second : 1;
 	};
+
+	static inline WidenMode noWiden() { return { false, false }; }
 } // namespace ResolvExpr
 
Index: src/ResolvExpr/typeops.h
===================================================================
--- src/ResolvExpr/typeops.h	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/ResolvExpr/typeops.h	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -89,5 +89,4 @@
 
 	// in Unify.cc
-	bool isFtype( Type *type );
 	bool typesCompatible( Type *, Type *, const SymTab::Indexer &indexer, const TypeEnvironment &env );
 	bool typesCompatibleIgnoreQualifiers( Type *, Type *, const SymTab::Indexer &indexer, const TypeEnvironment &env );
@@ -118,6 +117,6 @@
 	// in CommonType.cc
 	Type * commonType( Type *type1, Type *type2, bool widenFirst, bool widenSecond, const SymTab::Indexer &indexer, TypeEnvironment &env, const OpenVarSet &openVars );
-	const ast::Type * commonType(
-		const ast::Type * type1, const ast::Type * type2, WidenMode widen, 
+	ast::ptr< ast::Type > commonType(
+		const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2, WidenMode widen, 
 		const ast::SymbolTable & symtab, ast::TypeEnvironment & env, const ast::OpenVarSet & open );
 
@@ -177,5 +176,13 @@
 		return out;
 	}
+
+	// in TypeEnvironment.cc
+	bool isFtype( Type *type );
 } // namespace ResolvExpr
+
+namespace ast {
+	// in TypeEnvironment.cpp
+	bool isFtype( const ast::Type * type );
+} // namespace ast
 
 // Local Variables: //
Index: src/SymTab/Mangler.cc
===================================================================
--- src/SymTab/Mangler.cc	(revision 4741dfe074ccc55bc19d7ab13693524d07c040b1)
+++ src/SymTab/Mangler.cc	(revision d4b6638c939b3e606c9e1fe5bf057ad01dab46a5)
@@ -32,11 +32,13 @@
 #include "SynTree/Type.h"                // for Type, ReferenceToType, Type::Fora...
 
+#include "AST/Pass.hpp"
+
 namespace SymTab {
 	namespace Mangler {
 		namespace {
 			/// Mangles names to a unique C identifier
-			struct Mangler : public WithShortCircuiting, public WithVisitorRef<Mangler>, public WithGuards {
-				Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams );
-				Mangler( const Mangler & ) = delete;
+			struct Mangler_old : public WithShortCircuiting, public WithVisitorRef<Mangler_old>, public WithGuards {
+				Mangler_old( bool mangleOverridable, bool typeMode, bool mangleGenericParams );
+				Mangler_old( const Mangler_old & ) = delete;
 
 				void previsit( BaseSyntaxNode * ) { visit_children = false; }
@@ -77,5 +79,5 @@
 
 			  public:
-				Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams, 
+				Mangler_old( bool mangleOverridable, bool typeMode, bool mangleGenericParams, 
 					int nextVarNum, const VarMapType& varNums );
 
@@ -85,9 +87,9 @@
 
 				void printQualifiers( Type *type );
-			}; // Mangler
+			}; // Mangler_old
 		} // namespace
 
 		std::string mangle( BaseSyntaxNode * decl, bool mangleOverridable, bool typeMode, bool mangleGenericParams ) {
-			PassVisitor<Mangler> mangler( mangleOverridable, typeMode, mangleGenericParams );
+			PassVisitor<Mangler_old> mangler( mangleOverridable, typeMode, mangleGenericParams );
 			maybeAccept( decl, mangler );
 			return mangler.pass.get_mangleName();
@@ -95,5 +97,5 @@
 
 		std::string mangleType( Type * ty ) {
-			PassVisitor<Mangler> mangler( false, true, true );
+			PassVisitor<Mangler_old> mangler( false, true, true );
 			maybeAccept( ty, mangler );
 			return mangler.pass.get_mangleName();
@@ -101,5 +103,5 @@
 
 		std::string mangleConcrete( Type * ty ) {
-			PassVisitor<Mangler> mangler( false, false, false );
+			PassVisitor<Mangler_old> mangler( false, false, false );
 			maybeAccept( ty, mangler );
 			return mangler.pass.get_mangleName();
@@ -107,10 +109,10 @@
 
 		namespace {
-			Mangler::Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams )
+			Mangler_old::Mangler_old( bool mangleOverridable, bool typeMode, bool mangleGenericParams )
 				: nextVarNum( 0 ), isTopLevel( true ), 
 				mangleOverridable( mangleOverridable ), typeMode( typeMode ), 
 				mangleGenericParams( mangleGenericParams ) {}
 			
-			Mangler::Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams, 
+			Mangler_old::Mangler_old( bool mangleOverridable, bool typeMode, bool mangleGenericParams, 
 				int nextVarNum, const VarMapType& varNums )
 				: varNums( varNums ), nextVarNum( nextVarNum ), isTopLevel( false ), 
@@ -118,5 +120,5 @@
 				mangleGenericParams( mangleGenericParams ) {}
 
-			void Mangler::mangleDecl( DeclarationWithType * declaration ) {
+			void Mangler_old::mangleDecl( DeclarationWithType * declaration ) {
 				bool wasTopLevel = isTopLevel;
 				if ( isTopLevel ) {
@@ -148,18 +150,18 @@
 			}
 
-			void Mangler::postvisit( ObjectDecl * declaration ) {
+			void Mangler_old::postvisit( ObjectDecl * declaration ) {
 				mangleDecl( declaration );
 			}
 
-			void Mangler::postvisit( FunctionDecl * declaration ) {
+			void Mangler_old::postvisit( FunctionDecl * declaration ) {
 				mangleDecl( declaration );
 			}
 
-			void Mangler::postvisit( VoidType * voidType ) {
+			void Mangler_old::postvisit( VoidType * voidType ) {
 				printQualifiers( voidType );
 				mangleName << Encoding::void_t;
 			}
 
-			void Mangler::postvisit( BasicType * basicType ) {
+			void Mangler_old::postvisit( BasicType * basicType ) {
 				printQualifiers( basicType );
 				assertf( basicType->get_kind() < BasicType::NUMBER_OF_BASIC_TYPES, "Unhandled basic type: %d", basicType->get_kind() );
@@ -167,5 +169,5 @@
 			}
 
-			void Mangler::postvisit( PointerType * pointerType ) {
+			void Mangler_old::postvisit( PointerType * pointerType ) {
 				printQualifiers( pointerType );
 				// mangle void (*f)() and void f() to the same name to prevent overloading on functions and function pointers
@@ -174,5 +176,5 @@
 			}
 
-			void Mangler::postvisit( ArrayType * arrayType ) {
+			void Mangler_old::postvisit( ArrayType * arrayType ) {
 				// TODO: encode dimension
 				printQualifiers( arrayType );
@@ -181,5 +183,5 @@
 			}
 
-			void Mangler::postvisit( ReferenceType * refType ) {
+			void Mangler_old::postvisit( ReferenceType * refType ) {
 				// don't print prefix (e.g. 'R') for reference types so that references and non-references do not overload.
 				// Further, do not print the qualifiers for a reference type (but do run printQualifers because of TypeDecls, etc.),
@@ -200,5 +202,5 @@
 			}
 
-			void Mangler::postvisit( FunctionType * functionType ) {
+			void Mangler_old::postvisit( FunctionType * functionType ) {
 				printQualifiers( functionType );
 				mangleName << Encoding::function;
@@ -217,5 +219,5 @@
 			}
 
-			void Mangler::mangleRef( ReferenceToType * refType, std::string prefix ) {
+			void Mangler_old::mangleRef( ReferenceToType * refType, std::string prefix ) {
 				printQualifiers( refType );
 
@@ -236,17 +238,17 @@
 			}
 
-			void Mangler::postvisit( StructInstType * aggregateUseType ) {
+			void Mangler_old::postvisit( StructInstType * aggregateUseType ) {
 				mangleRef( aggregateUseType, Encoding::struct_t );
 			}
 
-			void Mangler::postvisit( UnionInstType * aggregateUseType ) {
+			void Mangler_old::postvisit( UnionInstType * aggregateUseType ) {
 				mangleRef( aggregateUseType, Encoding::union_t );
 			}
 
-			void Mangler::postvisit( EnumInstType * aggregateUseType ) {
+			void Mangler_old::postvisit( EnumInstType * aggregateUseType ) {
 				mangleRef( aggregateUseType, Encoding::enum_t );
 			}
 
-			void Mangler::postvisit( TypeInstType * typeInst ) {
+			void Mangler_old::postvisit( TypeInstType * typeInst ) {
 				VarMapType::iterator varNum = varNums.find( typeInst->get_name() );
 				if ( varNum == varNums.end() ) {
@@ -264,10 +266,10 @@
 			}
 
-			void Mangler::postvisit( TraitInstType * inst ) {
+			void Mangler_old::postvisit( TraitInstType * inst ) {
 				printQualifiers( inst );
 				mangleName << inst->name.size() << inst->name;
 			}
 
-			void Mangler::postvisit( TupleType * tupleType ) {
+			void Mangler_old::postvisit( TupleType * tupleType ) {
 				printQualifiers( tupleType );
 				mangleName << Encoding::tuple << tupleType->types.size();
@@ -275,5 +277,5 @@
 			}
 
-			void Mangler::postvisit( VarArgsType * varArgsType ) {
+			void Mangler_old::postvisit( VarArgsType * varArgsType ) {
 				printQualifiers( varArgsType );
 				static const std::string vargs = "__builtin_va_list";
@@ -281,13 +283,13 @@
 			}
 
-			void Mangler::postvisit( ZeroType * ) {
+			void Mangler_old::postvisit( ZeroType * ) {
 				mangleName << Encoding::zero;
 			}
 
-			void Mangler::postvisit( OneType * ) {
+			void Mangler_old::postvisit( OneType * ) {
 				mangleName << Encoding::one;
 			}
 
-			void Mangler::postvisit( QualifiedType * qualType ) {
+			void Mangler_old::postvisit( QualifiedType * qualType ) {
 				bool inqual = inQualifiedType;
 				if (! inqual ) {
@@ -305,5 +307,5 @@
 			}
 
-			void Mangler::postvisit( TypeDecl * decl ) {
+			void Mangler_old::postvisit( TypeDecl * decl ) {
 				// TODO: is there any case where mangling a TypeDecl makes sense? If so, this code needs to be
 				// fixed to ensure that two TypeDecls mangle to the same name when they are the same type and vice versa.
@@ -311,5 +313,5 @@
 				// and the case has not yet come up in practice. Alternatively, if not then this code can be removed
 				// aside from the assert false.
-				assertf(false, "Mangler should not visit typedecl: %s", toCString(decl));
+				assertf(false, "Mangler_old should not visit typedecl: %s", toCString(decl));
 				assertf( decl->get_kind() < TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", decl->get_kind() );
 				mangleName << Encoding::typeVariables[ decl->get_kind() ] << ( decl->name.length() ) << decl->name;
@@ -322,5 +324,5 @@
 			}
 
-			void Mangler::printQualifiers( Type * type ) {
+			void Mangler_old::printQualifiers( Type * type ) {
 				// skip if not including qualifiers
 				if ( typeMode ) return;
@@ -345,5 +347,5 @@
 						varNums[ (*i)->name ] = std::make_pair( nextVarNum, (int)(*i)->get_kind() );
 						for ( std::list< DeclarationWithType* >::iterator assert = (*i)->assertions.begin(); assert != (*i)->assertions.end(); ++assert ) {
-							PassVisitor<Mangler> sub_mangler( 
+							PassVisitor<Mangler_old> sub_mangler( 
 								mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums );
 							(*assert)->accept( sub_mangler );
@@ -391,9 +393,347 @@
 
 namespace Mangle {
+	namespace {
+		/// Mangles names to a unique C identifier
+		struct Mangler_new : public ast::WithShortCircuiting, public ast::WithVisitorRef<Mangler_new>, public ast::WithGuards {
+			Mangler_new( Mangle::Mode mode );
+			Mangler_new( const Mangler_new & ) = delete;
+
+			void previsit( const ast::Node * ) { visit_children = false; }
+
+			void postvisit( const ast::ObjectDecl * declaration );
+			void postvisit( const ast::FunctionDecl * declaration );
+			void postvisit( const ast::TypeDecl * declaration );
+
+			void postvisit( const ast::VoidType * voidType );
+			void postvisit( const ast::BasicType * basicType );
+			void postvisit( const ast::PointerType * pointerType );
+			void postvisit( const ast::ArrayType * arrayType );
+			void postvisit( const ast::ReferenceType * refType );
+			void postvisit( const ast::FunctionType * functionType );
+			void postvisit( const ast::StructInstType * aggregateUseType );
+			void postvisit( const ast::UnionInstType * aggregateUseType );
+			void postvisit( const ast::EnumInstType * aggregateUseType );
+			void postvisit( const ast::TypeInstType * aggregateUseType );
+			void postvisit( const ast::TraitInstType * inst );
+			void postvisit( const ast::TupleType * tupleType );
+			void postvisit( const ast::VarArgsType * varArgsType );
+			void postvisit( const ast::ZeroType * zeroType );
+			void postvisit( const ast::OneType * oneType );
+			void postvisit( const ast::QualifiedType * qualType );
+
+			std::string get_mangleName() { return mangleName.str(); }
+		  private:
+			std::ostringstream mangleName;  ///< Mangled name being constructed
+			typedef std::map< std::string, std::pair< int, int > > VarMapType;
+			VarMapType varNums;             ///< Map of type variables to indices
+			int nextVarNum;                 ///< Next type variable index
+			bool isTopLevel;                ///< Is the Mangler at the top level
+			bool mangleOverridable;         ///< Specially mangle overridable built-in methods
+			bool typeMode;                  ///< Produce a unique mangled name for a type
+			bool mangleGenericParams;       ///< Include generic parameters in name mangling if true
+			bool inFunctionType = false;    ///< Include type qualifiers if false.
+			bool inQualifiedType = false;   ///< Add start/end delimiters around qualified type
+
+		  private:
+			Mangler_new( bool mangleOverridable, bool typeMode, bool mangleGenericParams,  
+				int nextVarNum, const VarMapType& varNums );
+			friend class ast::Pass<Mangler_new>;
+
+		  private:
+			void mangleDecl( const ast::DeclWithType *declaration );
+			void mangleRef( const ast::ReferenceToType *refType, std::string prefix );
+
+			void printQualifiers( const ast::Type *type );
+		}; // Mangler_new
+	} // namespace
+
+
 	std::string mangle( const ast::Node * decl, Mangle::Mode mode ) {
-		#warning unimplemented
-		assert( decl && mode.val && false );
-		return "";
+		ast::Pass<Mangler_new> mangler( mode );
+		maybeAccept( decl, mangler );
+		return mangler.pass.get_mangleName();
 	}
+
+	namespace {
+		Mangler_new::Mangler_new( Mangle::Mode mode )
+			: nextVarNum( 0 ), isTopLevel( true ), 
+			mangleOverridable  ( ! mode.no_overrideable   ),
+			typeMode           (   mode.type              ), 
+			mangleGenericParams( ! mode.no_generic_params ) {}
+		
+		Mangler_new::Mangler_new( bool mangleOverridable, bool typeMode, bool mangleGenericParams, 
+			int nextVarNum, const VarMapType& varNums )
+			: varNums( varNums ), nextVarNum( nextVarNum ), isTopLevel( false ), 
+			mangleOverridable( mangleOverridable ), typeMode( typeMode ), 
+			mangleGenericParams( mangleGenericParams ) {}
+
+		void Mangler_new::mangleDecl( const ast::DeclWithType * decl ) {
+			bool wasTopLevel = isTopLevel;
+			if ( isTopLevel ) {
+				varNums.clear();
+				nextVarNum = 0;
+				isTopLevel = false;
+			} // if
+			mangleName << Encoding::manglePrefix;
+			CodeGen::OperatorInfo opInfo;
+			if ( operatorLookup( decl->name, opInfo ) ) {
+				mangleName << opInfo.outputName.size() << opInfo.outputName;
+			} else {
+				mangleName << decl->name.size() << decl->name;
+			} // if
+			maybeAccept( decl->get_type(), *visitor );
+			if ( mangleOverridable && decl->linkage.is_overrideable ) {
+				// want to be able to override autogenerated and intrinsic routines,
+				// so they need a different name mangling
+				if ( decl->linkage == ast::Linkage::AutoGen ) {
+					mangleName << Encoding::autogen;
+				} else if ( decl->linkage == ast::Linkage::Intrinsic ) {
+					mangleName << Encoding::intrinsic;
+				} else {
+					// if we add another kind of overridable function, this has to change
+					assert( false && "unknown overrideable linkage" );
+				} // if
+			}
+			isTopLevel = wasTopLevel;
+		}
+
+		void Mangler_new::postvisit( const ast::ObjectDecl * decl ) {
+			mangleDecl( decl );
+		}
+
+		void Mangler_new::postvisit( const ast::FunctionDecl * decl ) {
+			mangleDecl( decl );
+		}
+
+		void Mangler_new::postvisit( const ast::VoidType * voidType ) {
+			printQualifiers( voidType );
+			mangleName << Encoding::void_t;
+		}
+
+		void Mangler_new::postvisit( const ast::BasicType * basicType ) {
+			printQualifiers( basicType );
+			assertf( basicType->kind < ast::BasicType::NUMBER_OF_BASIC_TYPES, "Unhandled basic type: %d", basicType->kind );
+			mangleName << Encoding::basicTypes[ basicType->kind ];
+		}
+
+		void Mangler_new::postvisit( const ast::PointerType * pointerType ) {
+			printQualifiers( pointerType );
+			// mangle void (*f)() and void f() to the same name to prevent overloading on functions and function pointers
+			if ( ! pointerType->base.as<ast::FunctionType>() ) mangleName << Encoding::pointer;
+			maybe_accept( pointerType->base.get(), *visitor );
+		}
+
+		void Mangler_new::postvisit( const ast::ArrayType * arrayType ) {
+			// TODO: encode dimension
+			printQualifiers( arrayType );
+			mangleName << Encoding::array << "0";
+			maybeAccept( arrayType->base.get(), *visitor );
+		}
+
+		void Mangler_new::postvisit( const ast::ReferenceType * refType ) {
+			// don't print prefix (e.g. 'R') for reference types so that references and non-references do not overload.
+			// Further, do not print the qualifiers for a reference type (but do run printQualifers because of TypeDecls, etc.),
+			// by pretending every reference type is a function parameter.
+			GuardValue( inFunctionType );
+			inFunctionType = true;
+			printQualifiers( refType );
+			maybeAccept( refType->base.get(), *visitor );
+		}
+
+		inline std::vector< ast::ptr< ast::Type > > getTypes( const std::vector< ast::ptr< ast::DeclWithType > > & decls ) {
+			std::vector< ast::ptr< ast::Type > > ret;
+			std::transform( decls.begin(), decls.end(), std::back_inserter( ret ),
+							std::mem_fun( &ast::DeclWithType::get_type ) );
+			return ret;
+		}
+
+		void Mangler_new::postvisit( const ast::FunctionType * functionType ) {
+			printQualifiers( functionType );
+			mangleName << Encoding::function;
+			// turn on inFunctionType so that printQualifiers does not print most qualifiers for function parameters,
+			// since qualifiers on outermost parameter type do not differentiate function types, e.g.,
+			// void (*)(const int) and void (*)(int) are the same type, but void (*)(const int *) and void (*)(int *) are different
+			GuardValue( inFunctionType );
+			inFunctionType = true;
+			std::vector< ast::ptr< ast::Type > > returnTypes = getTypes( functionType->returns );
+			if (returnTypes.empty()) mangleName << Encoding::void_t;
+			else accept_each( returnTypes, *visitor );
+			mangleName << "_";
+			std::vector< ast::ptr< ast::Type > > paramTypes = getTypes( functionType->params );
+			accept_each( paramTypes, *visitor );
+			mangleName << "_";
+		}
+
+		void Mangler_new::mangleRef( const ast::ReferenceToType * refType, std::string prefix ) {
+			printQualifiers( refType );
+
+			mangleName << prefix << refType->name.length() << refType->name;
+
+			if ( mangleGenericParams ) {
+				if ( ! refType->params.empty() ) {
+					mangleName << "_";
+					for ( const ast::Expr * param : refType->params ) {
+						auto paramType = dynamic_cast< const ast::TypeExpr * >( param );
+						assertf(paramType, "Aggregate parameters should be type expressions: %s", toCString(param));
+						maybeAccept( paramType->type.get(), *visitor );
+					}
+					mangleName << "_";
+				}
+			}
+		}
+
+		void Mangler_new::postvisit( const ast::StructInstType * aggregateUseType ) {
+			mangleRef( aggregateUseType, Encoding::struct_t );
+		}
+
+		void Mangler_new::postvisit( const ast::UnionInstType * aggregateUseType ) {
+			mangleRef( aggregateUseType, Encoding::union_t );
+		}
+
+		void Mangler_new::postvisit( const ast::EnumInstType * aggregateUseType ) {
+			mangleRef( aggregateUseType, Encoding::enum_t );
+		}
+
+		void Mangler_new::postvisit( const ast::TypeInstType * typeInst ) {
+			VarMapType::iterator varNum = varNums.find( typeInst->name );
+			if ( varNum == varNums.end() ) {
+				mangleRef( typeInst, Encoding::type );
+			} else {
+				printQualifiers( typeInst );
+				// Note: Can't use name here, since type variable names do not actually disambiguate a function, e.g.
+				//   forall(dtype T) void f(T);
+				//   forall(dtype S) void f(S);
+				// are equivalent and should mangle the same way. This is accomplished by numbering the type variables when they
+				// are first found and prefixing with the appropriate encoding for the type class.
+				assertf( varNum->second.second < TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", varNum->second.second );
+				mangleName << Encoding::typeVariables[varNum->second.second] << varNum->second.first;
+			} // if
+		}
+
+		void Mangler_new::postvisit( const ast::TraitInstType * inst ) {
+			printQualifiers( inst );
+			mangleName << inst->name.size() << inst->name;
+		}
+
+		void Mangler_new::postvisit( const ast::TupleType * tupleType ) {
+			printQualifiers( tupleType );
+			mangleName << Encoding::tuple << tupleType->types.size();
+			accept_each( tupleType->types, *visitor );
+		}
+
+		void Mangler_new::postvisit( const ast::VarArgsType * varArgsType ) {
+			printQualifiers( varArgsType );
+			static const std::string vargs = "__builtin_va_list";
+			mangleName << Encoding::type << vargs.size() << vargs;
+		}
+
+		void Mangler_new::postvisit( const ast::ZeroType * ) {
+			mangleName << Encoding::zero;
+		}
+
+		void Mangler_new::postvisit( const ast::OneType * ) {
+			mangleName << Encoding::one;
+		}
+
+		void Mangler_new::postvisit( const ast::QualifiedType * qualType ) {
+			bool inqual = inQualifiedType;
+			if (! inqual ) {
+				// N marks the start of a qualified type
+				inQualifiedType = true;
+				mangleName << Encoding::qualifiedTypeStart;
+			}
+			maybeAccept( qualType->parent.get(), *visitor );
+			maybeAccept( qualType->child.get(), *visitor );
+			if ( ! inqual ) {
+				// E marks the end of a qualified type
+				inQualifiedType = false;
+				mangleName << Encoding::qualifiedTypeEnd;
+			}
+		}
+
+		void Mangler_new::postvisit( const ast::TypeDecl * decl ) {
+			// TODO: is there any case where mangling a TypeDecl makes sense? If so, this code needs to be
+			// fixed to ensure that two TypeDecls mangle to the same name when they are the same type and vice versa.
+			// Note: The current scheme may already work correctly for this case, I have not thought about this deeply
+			// and the case has not yet come up in practice. Alternatively, if not then this code can be removed
+			// aside from the assert false.
+			assertf(false, "Mangler_new should not visit typedecl: %s", toCString(decl));
+			assertf( decl->kind < ast::TypeVar::Kind::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", decl->kind );
+			mangleName << Encoding::typeVariables[ decl->kind ] << ( decl->name.length() ) << decl->name;
+		}
+
+		__attribute__((unused)) void printVarMap( const std::map< std::string, std::pair< int, int > > &varMap, std::ostream &os ) {
+			for ( std::map< std::string, std::pair< int, int > >::const_iterator i = varMap.begin(); i != varMap.end(); ++i ) {
+				os << i->first << "(" << i->second.first << "/" << i->second.second << ")" << std::endl;
+			} // for
+		}
+
+		void Mangler_new::printQualifiers( const ast::Type * type ) {
+			// skip if not including qualifiers
+			if ( typeMode ) return;
+			if ( auto ptype = dynamic_cast< const ast::ParameterizedType * >(type) ) {
+				if ( ! ptype->forall.empty() ) {
+					std::list< std::string > assertionNames;
+					int dcount = 0, fcount = 0, vcount = 0, acount = 0;
+					mangleName << Encoding::forall;
+					for ( const ast::TypeDecl * decl : ptype->forall ) {
+						switch ( decl->kind ) {
+						case ast::TypeVar::Kind::Dtype:
+							dcount++;
+							break;
+						case ast::TypeVar::Kind::Ftype:
+							fcount++;
+							break;
+						case ast::TypeVar::Kind::Ttype:
+							vcount++;
+							break;
+						default:
+							assert( false );
+						} // 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.pass.get_mangleName() );
+							acount++;
+						} // for
+					} // for
+					mangleName << dcount << "_" << fcount << "_" << vcount << "_" << acount << "_";
+					std::copy( assertionNames.begin(), assertionNames.end(), std::ostream_iterator< std::string >( mangleName, "" ) );
+					mangleName << "_";
+				} // if
+			} // if
+			if ( ! inFunctionType ) {
+				// these qualifiers do not distinguish the outermost type of a function parameter
+				if ( type->is_const() ) {
+					mangleName << Encoding::qualifiers.at(Type::Const);
+				} // if
+				if ( type->is_volatile() ) {
+					mangleName << Encoding::qualifiers.at(Type::Volatile);
+				} // if
+				// Removed due to restrict not affecting function compatibility in GCC
+				// if ( type->get_isRestrict() ) {
+				// 	mangleName << "E";
+				// } // if
+				if ( type->is_atomic() ) {
+					mangleName << Encoding::qualifiers.at(Type::Atomic);
+				} // if
+			}
+			if ( type->is_mutex() ) {
+				mangleName << Encoding::qualifiers.at(Type::Mutex);
+			} // if
+			if ( type->is_lvalue() ) {
+				// mangle based on whether the type is lvalue, so that the resolver can differentiate lvalues and rvalues
+				mangleName << Encoding::qualifiers.at(Type::Lvalue);
+			}
+
+			if ( inFunctionType ) {
+				// turn off inFunctionType so that types can be differentiated for nested qualifiers
+				GuardValue( inFunctionType );
+				inFunctionType = false;
+			}
+		}
+	}	// namespace
 } // namespace Mangle
 
