Index: src/ResolvExpr/CommonType.cc
===================================================================
--- src/ResolvExpr/CommonType.cc	(revision f474e917671c80fd81b2ffce8b4dac26709b333e)
+++ src/ResolvExpr/CommonType.cc	(revision 67130fe7c6ae00bc7c6da280f8b700e5bc850a3c)
@@ -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
 
