Index: src/CodeGen/CodeGenerator.cpp
===================================================================
--- src/CodeGen/CodeGenerator.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/CodeGen/CodeGenerator.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,1256 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// CodeGenerator.cpp --
+//
+// Author           : Andrew Beach
+// Created On       : Tue Oct 17 15:54:00 2023
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Oct 25 18:28:00 2023
+// Update Count     : 0
+//
+
+#include "CodeGenerator.hpp"
+
+#include "AST/Print.hpp"
+#include "OperatorTable.h"           // for OperatorInfo, operatorLookup
+#include "CodeGen/GenType.h"         // for genType
+#include "Common/ToString.hpp"       // for toString
+#include "Common/UniqueName.h"       // for UniqueName
+
+namespace CodeGen {
+
+int CodeGenerator::tabsize = 4;
+
+// The kinds of statements that should be followed by whitespace.
+static bool wantSpacing( ast::Stmt const * stmt ) {
+	return dynamic_cast<ast::IfStmt const *>( stmt )
+		|| dynamic_cast<ast::CompoundStmt const *>( stmt )
+		|| dynamic_cast<ast::WhileDoStmt const *>( stmt )
+		|| dynamic_cast<ast::ForStmt const *>( stmt )
+		|| dynamic_cast<ast::SwitchStmt const *>( stmt );
+}
+
+void CodeGenerator::extension( ast::Expr const * expr ) {
+	if ( expr->extension ) output << "__extension__ ";
+}
+
+void CodeGenerator::extension( ast::Decl const * decl ) {
+	if ( decl->extension ) output << "__extension__ ";
+}
+
+void CodeGenerator::asmName( ast::DeclWithType const * decl ) {
+	if ( auto asmName = decl->asmName.as<ast::ConstantExpr>() ) {
+		output << " asm ( " << asmName->rep << " )";
+	}
+}
+
+CodeGenerator::LabelPrinter & CodeGenerator::LabelPrinter::operator()(
+		std::vector<ast::Label> const & l ) {
+	labels = &l;
+	return *this;
+}
+
+std::ostream & CodeGenerator::LabelPrinter::operator()( std::ostream & output ) const {
+	const std::vector<ast::Label> & labels = *this->labels;
+	for ( const ast::Label & label : labels ) {
+		output << label.name + ": ";
+		this->cg.genAttributes( label.attributes );
+	}
+	return output;
+}
+
+// Using updateLocation at the beginning of a node and endl within a node
+// should become the method of formating.
+void CodeGenerator::updateLocation( CodeLocation const & to ) {
+	// Skip if linemarks shouldn't appear or if location is unset.
+	if ( !options.lineMarks || to.isUnset() ) return;
+
+	if ( currentLocation.followedBy( to, 0 ) ) {
+		return;
+	} else if ( currentLocation.followedBy( to, 1 ) ) {
+		output << "\n" << indent;
+		currentLocation.first_line += 1;
+	} else if ( currentLocation.followedBy( to, 2 ) ) {
+		output << "\n\n" << indent;
+		currentLocation.first_line += 2;
+	} else {
+		output << "\n# " << to.first_line << " \"" << to.filename
+		       << "\"\n" << indent;
+		currentLocation = to;
+	}
+	output << std::flush;
+}
+
+void CodeGenerator::updateLocation( ast::ParseNode const * to ) {
+	updateLocation( to->location );
+}
+
+std::ostream & CodeGenerator::LineEnder::operator()( std::ostream & os ) const {
+	os << "\n" << std::flush;
+	cg.currentLocation.first_line++;
+	return os;
+}
+
+CodeGenerator::CodeGenerator( std::ostream & os, const Options & options ) :
+		indent( 0, CodeGenerator::tabsize ), output( os ),
+		options( options ), printLabels( *this ), endl( *this )
+{}
+
+std::string CodeGenerator::mangleName( ast::DeclWithType const * decl ) {
+	// GCC builtins should always be printed unmangled.
+	if ( options.pretty || decl->linkage.is_gcc_builtin ) {
+		return decl->name;
+	} else if ( decl->linkage.is_mangled && decl->mangleName != "" ) {
+		return decl->scopedMangleName();
+	} else {
+		return decl->name;
+	}
+}
+
+void CodeGenerator::genAttributes(
+		const std::vector<ast::ptr<ast::Attribute>> & attributes ) {
+	if ( attributes.empty() ) return;
+	output << "__attribute__ ((";
+	for ( auto attr = attributes.begin() ;; ) {
+		output << (*attr)->name;
+		if ( !(*attr)->params.empty() ) {
+			output << "(";
+			genCommaList( (*attr)->params );
+			output << ")";
+		}
+		if ( ++attr == attributes.end() ) break;
+		output << ",";
+	}
+	output << ")) ";
+}
+
+void CodeGenerator::previsit( ast::Node const * ) {
+	// All traversal is manual.
+	// TODO: Which means the ast::Pass is just providing a default no visit?
+	visit_children = false;
+}
+
+void CodeGenerator::previsit( ast::ParseNode const * node ) {
+	previsit( (ast::Node const *)node );
+	updateLocation( node );
+}
+
+void CodeGenerator::postvisit( ast::Node const * node ) {
+	std::stringstream ss;
+	ast::print( ss, node );
+	assertf( false, "Unhandled node reached in CodeGenerator: %s", ss.str().c_str() );
+}
+
+void CodeGenerator::previsit( ast::Expr const * expr ) {
+	previsit( (ast::ParseNode const *)expr );
+	GuardAction( [this, expr](){
+		if ( options.printExprTypes && expr->result ) {
+			output << " /* " << genType( expr->result, "", options ) << " */ ";
+		}
+	} );
+}
+
+void CodeGenerator::postvisit( ast::FunctionDecl const * decl ) {
+	// Deleted decls should never be used, so don't print them in C.
+	if ( decl->isDeleted && options.genC ) return;
+	extension( decl );
+	genAttributes( decl->attributes );
+
+	handleStorageClass( decl );
+	ast::print( output, decl->funcSpec );
+
+	Options subOptions = options;
+	subOptions.anonymousUnused = decl->stmts;
+
+	std::ostringstream acc;
+	ast::Pass<CodeGenerator> subCG( acc, subOptions );
+	// Add the forall clause.
+	// TODO: These probably should be removed by now and the assert used.
+	if ( !decl->type_params.empty() ) {
+		assertf( !options.genC, "FunctionDecl forall should not reach code generation." );
+		acc << "forall(";
+		subCG.core.genCommaList( decl->type_params );
+		acc << ")" << std::endl;
+	}
+
+	acc << mangleName( decl );
+
+	if ( 0 == decl->params.size() ) {
+		if ( decl->type->isVarArgs ) {
+			acc << "()";
+		} else {
+			acc << "(void)";
+		}
+	} else {
+		acc << "(";
+		subCG.core.genCommaList( decl->params );
+		if ( decl->type->isVarArgs ) {
+			acc << ", ...";
+		}
+		acc << ")";
+	}
+
+	assert( decl->returns.size() < 2 );
+	if ( 1 == decl->returns.size() ) {
+		ast::ptr<ast::Type> const & type = decl->returns[0]->get_type();
+		output << genTypeNoAttr( type, acc.str(), subOptions );
+	} else {
+		output << "void " + acc.str();
+	}
+
+	asmName( decl );
+
+	if ( decl->stmts ) {
+		decl->stmts->accept( *visitor );
+	}
+	if ( decl->isDeleted ) {
+		output << " = void";
+	}
+}
+
+ast::ObjectDecl const * CodeGenerator::postvisit(
+		ast::ObjectDecl const * decl ) {
+	// Deleted decls should never be used, so don't print them in C.
+	if ( decl->isDeleted && options.genC ) return decl;
+
+	// GCC allows an empty declarator (no name) for bit-fields and C
+	// states: 6.7.2.1 Structure and union specifiers, point 4, page 113:
+	// If the (bit field) value is zero, the declaration shall have no
+	// declarator. For anything else, the anonymous name refers to the
+	// anonymous object for plan9 inheritance.
+	if ( decl->name.empty() && options.genC && !decl->bitfieldWidth ) {
+		// TODO: Should this be changed in a pervious pass?
+		auto mutDecl = ast::mutate( decl );
+		// Only generate an anonymous name when generating C code,
+		// otherwise it clutters the output too much.
+		static UniqueName name = { "__anonymous_object" };
+		mutDecl->name = name.newName();
+		// Stops unused parameter warnings.
+		if ( options.anonymousUnused ) {
+			mutDecl->attributes.push_back( new ast::Attribute( "unused" ) );
+		}
+		decl = mutDecl;
+	}
+
+	extension( decl );
+	genAttributes( decl->attributes );
+
+	handleStorageClass( decl );
+	output << genType( decl->type, mangleName( decl ),
+		Options( options.pretty, options.genC, false, false ) );
+
+	asmName( decl );
+
+	if ( decl->init ) {
+		output << " = ";
+		decl->init->accept( *visitor );
+	}
+	if ( decl->isDeleted ) {
+		output << " = void";
+	}
+
+	if ( decl->bitfieldWidth ) {
+		output << ":";
+		decl->bitfieldWidth->accept( *visitor );
+	}
+	return decl;
+}
+
+void CodeGenerator::handleStorageClass( ast::DeclWithType const * decl ) {
+	if ( decl->storage.any() ) {
+		ast::print( output, decl->storage );
+	}
+}
+
+void CodeGenerator::handleAggregate(
+		ast::AggregateDecl const * decl, std::string const & kind ) {
+	if ( !decl->params.empty() && !options.genC ) {
+		output << "forall(";
+		genCommaList( decl->params );
+		output << ")\n" << indent << std::flush;
+	}
+
+	output << kind;
+	genAttributes( decl->attributes );
+	output << decl->name;
+
+	if ( decl->body ) {
+		auto & members = decl->members;
+		output << " {" << endl;
+
+		++indent;
+		for ( auto & member : members ) {
+			output << indent;
+			member->accept( *visitor );
+			output << ";" << endl;
+		}
+		--indent;
+
+		output << indent << "}";
+	}
+}
+
+void CodeGenerator::postvisit( ast::StructDecl const * decl ) {
+	extension( decl );
+	handleAggregate( decl, "struct " );
+}
+
+void CodeGenerator::postvisit( ast::UnionDecl const * decl ) {
+	extension( decl );
+	handleAggregate( decl, "union " );
+}
+
+template<typename pass_type>
+inline void genEnumInitializer( ast::Pass<pass_type> * visitor,
+		ast::Type const * baseType, std::ostream & output,
+		ast::Init const * init, long long * curVal, Options options ) {
+	auto baseTypeAsBasic = dynamic_cast<ast::BasicType const *>( baseType );
+	// Value is provided.
+	if ( init ) {
+		output << " = (" << genType( baseType, "", options ) << ")";
+		init->accept( *visitor );
+		// If it is an integral type and initilizer offered,
+		// need to update the curVal.
+		if ( baseTypeAsBasic && baseTypeAsBasic->isInteger() ) {
+			ast::Expr const * expr = ((ast::SingleInit const *)(init))->value;
+			// Unwrap introduced cast.
+			while ( auto temp = dynamic_cast<ast::CastExpr const *>( expr ) ) {
+				expr = temp->arg;
+			}
+			*curVal = ((ast::ConstantExpr const *)(expr))->intValue() + 1;
+		}
+	// Generate next value from previous value.
+	} else if ( baseTypeAsBasic && baseTypeAsBasic->isInteger() ) {
+		output << " = (" << genType( baseType, "", options ) << ")";
+		output << (*curVal)++;
+	}
+}
+
+void CodeGenerator::postvisit( ast::EnumDecl const * decl ) {
+	extension( decl );
+	auto members = decl->members;
+	if ( decl->base && !members.empty() ) {
+		long long curVal = 0;
+		for ( auto member : members ) {
+			auto obj = member.strict_as<ast::ObjectDecl>();
+			output << "static ";
+			output << genType( decl->base, mangleName( obj ), options );
+			genEnumInitializer( visitor, decl->base, output, obj->init, &curVal, options );
+			output << ";" << endl;
+		}
+	} else {
+		output << "enum ";
+		genAttributes( decl->attributes );
+
+		output << decl->name;
+
+		if ( !members.empty() ) {
+			output << " {" << endl;
+
+			++indent;
+			for ( auto member : members ) {
+				auto obj = member.strict_as<ast::ObjectDecl>();
+				output << indent << mangleName( obj );
+				if ( obj->init ) {
+					output << " = ";
+					obj->init->accept( *visitor );
+				}
+				output << "," << endl;
+			}
+			--indent;
+
+			output << indent << "}";
+		}
+	}
+}
+
+void CodeGenerator::postvisit( ast::TraitDecl const * decl ) {
+	assertf( !options.genC, "TraitDecls should not reach code generation." );
+	extension( decl );
+	handleAggregate( decl, "trait " );
+}
+
+void CodeGenerator::postvisit( ast::TypedefDecl const * decl ) {
+	assertf( !options.genC, "Typedefs should not reach code generation." );
+	output << "typedef " << genType( decl->base, decl->name, options ) << endl;
+}
+
+void CodeGenerator::postvisit( ast::TypeDecl const * decl ) {
+	assertf( !options.genC, "TypeDecls should not reach code generation." );
+	output << decl->genTypeString() << " " << decl->name;
+	if ( decl->sized ) {
+		output << " | sized(" << decl->name << ")";
+	}
+	if ( !decl->assertions.empty() ) {
+		output << " | { ";
+		for ( ast::DeclWithType const * assert : decl->assertions ) {
+			assert->accept( *visitor );
+			output << "; ";
+		}
+		output << " }";
+	}
+}
+
+void CodeGenerator::postvisit( ast::StaticAssertDecl const * decl ) {
+	output << "_Static_assert(";
+	decl->cond->accept( *visitor );
+	output << ", ";
+	decl->msg->accept( *visitor );
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::Designation const * designation ) {
+	auto designators = designation->designators;
+	if ( 0 == designators.size() ) return;
+	for ( ast::ptr<ast::Expr> const & des : designators ) {
+		// If the expression is a NameExpr or VariableExpr, then it is a field.
+		if ( des.as<ast::NameExpr>() || des.as<ast::VariableExpr>() ) {
+			output << ".";
+			des->accept( *visitor );
+		// Otherwise, it is a ConstantExpr or CastExpr, then it is an index.
+		} else {
+			output << "[";
+			des->accept( *visitor );
+			output << "]";
+		}
+	}
+	output << " = ";
+}
+
+void CodeGenerator::postvisit( ast::SingleInit const * init ) {
+	init->value->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::ListInit const * init ) {
+	auto initBegin = init->initializers.begin();
+	auto initEnd = init->initializers.end();
+	auto desigBegin = init->designations.begin();
+	auto desigEnd = init->designations.end();
+
+	output << "{ ";
+	if ( initBegin != initEnd ) while (true) {
+		(*desigBegin)->accept( *visitor );
+		(*initBegin)->accept( *visitor );
+		++initBegin, ++desigBegin;
+		if ( initBegin == initEnd ) break;
+		output << ", ";
+	}
+	output << " }";
+	assertf( initBegin == initEnd && desigBegin == desigEnd,
+		"Initializers and designators not the same length. %s", toCString( init ) );
+}
+
+void CodeGenerator::postvisit( ast::ConstructorInit const * init ) {
+	assertf( !options.genC, "ConstructorInit nodes should not reach code generation." );
+	// This isn't actual code, but labels the constructor/destructor pairs.
+	output << "<ctorinit>{" << endl << ++indent << "ctor: ";
+	if ( init->ctor ) init->ctor->accept( *visitor );
+	output << ", " << endl << indent << "dtor: ";
+	if ( init->dtor ) init->dtor->accept( *visitor );
+	output << endl << --indent << "}";
+}
+
+void CodeGenerator::postvisit( ast::ApplicationExpr const * expr ) {
+	extension( expr );
+	if ( auto var = expr->func.as<ast::VariableExpr>() ) {
+		const OperatorInfo * opInfo;
+		if ( var->var->linkage == ast::Linkage::Intrinsic &&
+				( opInfo = operatorLookup( var->var->name ) ) ) {
+			auto arg = expr->args.begin();
+			switch ( opInfo->type ) {
+			case OT_INDEX:
+				assert( 2 == expr->args.size() );
+				(*arg++)->accept( *visitor );
+				output << "[";
+				(*arg)->accept( *visitor );
+				output << "]";
+				break;
+
+			// There are no intrinsic definitions of the function call operator.
+			case OT_CALL:
+				assert( false );
+				break;
+
+			case OT_CTOR:
+			case OT_DTOR:
+				// No-op constructor, but run the internal expression.
+				if ( 1 == expr->args.size() ) {
+					output << "(";
+					(*arg++)->accept( *visitor );
+					output << ") /* " << opInfo->inputName << " */";
+				// These are all implemented as some form of assignment.
+				} else if ( 2 == expr->args.size() ) {
+					output << "(";
+					(*arg++)->accept( *visitor );
+					output << opInfo->symbol;
+					(*arg)->accept( *visitor );
+					output << ") /* " << opInfo->inputName << " */";
+				// No constructors with 0 or more than 2 parameters.
+				} else {
+					assert( false );
+				}
+				break;
+
+			case OT_PREFIX:
+			case OT_PREFIXASSIGN:
+				assert( 1 == expr->args.size() );
+				output << "(" << opInfo->symbol;
+				(*arg)->accept( *visitor );
+				output << ")";
+				break;
+
+			case OT_POSTFIX:
+			case OT_POSTFIXASSIGN:
+				assert( 1 == expr->args.size() );
+				(*arg)->accept( *visitor );
+				output << opInfo->symbol;
+				break;
+
+			case OT_INFIX:
+			case OT_INFIXASSIGN:
+				assert( 2 == expr->args.size() );
+				output << "(";
+				(*arg++)->accept( *visitor );
+				output << opInfo->symbol;
+				(*arg)->accept( *visitor );
+				output << ")";
+				break;
+
+			// There are no intrinsic definitions of 0/1 or label address
+			// as function.
+			case OT_CONSTANT:
+			case OT_LABELADDRESS:
+				assert( false );
+			}
+		// TODO: This is a work-around to make it a constant until a proper
+		// constexpr solution is created.
+		} else if ( var->var->linkage == ast::Linkage::BuiltinCFA &&
+				var->var->name == "intptr" ) {
+			output << "((void*)";
+			auto arg = expr->args.begin();
+			(*arg++)->accept( *visitor );
+			output << ")";
+		} else {
+			var->accept( *visitor );
+			output << "(";
+			genCommaList( expr->args );
+			output << ")";
+		}
+	} else {
+		expr->func->accept( *visitor );
+		output << "(";
+		genCommaList( expr->args );
+		output << ")";
+	}
+}
+
+void CodeGenerator::postvisit( ast::UntypedExpr const * expr ) {
+	extension( expr );
+	if ( auto name = expr->func.as<ast::NameExpr>() ) {
+		if ( const OperatorInfo * opInfo = operatorLookup( name->name ) ) {
+			auto arg = expr->args.begin();
+			switch ( opInfo->type ) {
+			case OT_INDEX:
+				assert( 2 == expr->args.size() );
+				(*arg++)->accept( *visitor );
+				output << "[";
+				(*arg)->accept( *visitor );
+				output << "]";
+				break;
+
+			case OT_CALL:
+				assert( false );
+
+			case OT_CTOR:
+			case OT_DTOR:
+				// No-op constructor, but run the internal expression.
+				if ( 1 == expr->args.size() ) {
+					output << "(";
+					(*arg++)->accept( *visitor );
+					output << ")";
+				// These are all implemented as some form of assignment.
+				} else if ( 2 == expr->args.size() ) {
+					output << "(";
+					(*arg++)->accept( *visitor );
+					output << opInfo->symbol;
+					(*arg)->accept( *visitor );
+					output << ") /* " << opInfo->inputName << " */";
+				// No constructors with 0 or more than 2 parameters.
+				} else {
+					assertf( !options.genC, "UntypedExpr constructor/destructor with 0 or more than 2 parameters." );
+					output << "(";
+					(*arg++)->accept( *visitor );
+					output << opInfo->symbol << "{ ";
+					genCommaList( arg, expr->args.end() );
+					output << "}) /* " << opInfo->inputName << " */";
+				}
+				break;
+
+			case OT_PREFIX:
+			case OT_PREFIXASSIGN:
+			case OT_LABELADDRESS:
+				assert( 1 == expr->args.size() );
+				output << "(" << opInfo->symbol;
+				(*arg)->accept( *visitor );
+				output << ")";
+				break;
+
+			case OT_POSTFIX:
+			case OT_POSTFIXASSIGN:
+				assert( 1 == expr->args.size() );
+				(*arg)->accept( *visitor );
+				output << opInfo->symbol;
+				break;
+
+			case OT_INFIX:
+			case OT_INFIXASSIGN:
+				assert( 2 == expr->args.size() );
+				output << "(";
+				(*arg++)->accept( *visitor );
+				output << opInfo->symbol;
+				(*arg)->accept( *visitor );
+				output << ")";
+				break;
+
+			// There are no intrinsic definitions of 0/1 or label address
+			// as function.
+			case OT_CONSTANT:
+				assert( false );
+			}
+		// builtin routines
+		} else {
+			name->accept( *visitor );
+			output << "(";
+			genCommaList( expr->args );
+			output << ")";
+		}
+	} else {
+		expr->func->accept( *visitor );
+		output << "(";
+		genCommaList( expr->args );
+		output << ")";
+	}
+}
+
+void CodeGenerator::postvisit( ast::RangeExpr const * expr ) {
+	expr->low->accept( *visitor );
+	output << " ... ";
+	expr->high->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::NameExpr const * expr ) {
+	extension( expr );
+	if ( const OperatorInfo * opInfo = operatorLookup( expr->name ) ) {
+		if ( OT_CONSTANT == opInfo->type ) {
+			output << opInfo->symbol;
+		} else {
+			output << opInfo->outputName;
+		}
+	} else {
+		output << expr->name;
+	}
+}
+
+void CodeGenerator::postvisit( ast::DimensionExpr const * expr ) {
+	extension( expr );
+	output << "/*non-type*/" << expr->name;
+}
+
+void CodeGenerator::postvisit( ast::AddressExpr const * expr ) {
+	extension( expr );
+	output << "(&";
+	expr->arg->accept( *visitor );
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::LabelAddressExpr const * expr ) {
+	extension( expr );
+	output << "(&&" << expr->arg << ")";
+}
+
+void CodeGenerator::postvisit( ast::CastExpr const * expr ) {
+	extension( expr );
+	output << "(";
+	if ( expr->result->isVoid() ) {
+		output << "(void)";
+	} else {
+		output << "(";
+		output << genType( expr->result, "", options );
+		output << ")";
+	}
+	expr->arg->accept( *visitor );
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::KeywordCastExpr const * expr ) {
+	assertf( !options.genC, "KeywordCastExpr should not reach code generation." );
+	extension( expr );
+	output << "((" << expr->targetString() << " &)";
+	expr->arg->accept( *visitor );
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::VirtualCastExpr const * expr ) {
+	assertf( !options.genC, "VirtualCastExpr should not reach code generation." );
+	extension( expr );
+	// TODO: Is this busted?
+	output << "(virtual ";
+	expr->arg->accept( *visitor );
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::UntypedMemberExpr const * expr ) {
+	assertf( !options.genC, "UntypedMemberExpr should not reach code generation." );
+	extension( expr );
+	expr->aggregate->accept( *visitor );
+	output << ".";
+	expr->member->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::MemberExpr const * expr ) {
+	extension( expr );
+	expr->aggregate->accept( *visitor );
+	output << "." << mangleName( expr->member );
+}
+
+void CodeGenerator::postvisit( ast::VariableExpr const * expr ) {
+	extension( expr );
+	const OperatorInfo * opInfo;
+	if ( dynamic_cast<ast::ZeroType const *>( expr->var->get_type() ) ) {
+		output << "0";
+	} else if ( expr->var->linkage == ast::Linkage::Intrinsic
+			&& ( opInfo = operatorLookup( expr->var->name ) )
+			&& opInfo->type == OT_CONSTANT ) {
+		output << opInfo->symbol;
+	} else {
+		output << mangleName( expr->var );
+	}
+}
+
+void CodeGenerator::postvisit( ast::ConstantExpr const * expr ) {
+	extension( expr );
+	output << expr->rep;
+}
+
+void CodeGenerator::postvisit( ast::SizeofExpr const * expr ) {
+	extension( expr );
+	output << "sizeof(";
+	if ( expr->type ) {
+		output << genType( expr->type, "", options );
+	} else {
+		expr->expr->accept( *visitor );
+	}
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::AlignofExpr const * expr ) {
+	// Using the GCC extension to avoid changing the std to C11.
+	extension( expr );
+	output << "__alignof__(";
+	if ( expr->type ) {
+		output << genType( expr->type, "", options );
+	} else {
+		expr->expr->accept( *visitor );
+	}
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::UntypedOffsetofExpr const * expr ) {
+	assertf( !options.genC, "UntypedOffsetofExpr should not reach code generation." );
+	output << "offsetof(";
+	output << genType( expr->type, "", options );
+	output << ", " << expr->member;
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::OffsetofExpr const * expr ) {
+	// Use GCC builtin
+	output << "__builtin_offsetof(";
+	output << genType( expr->type, "", options );
+	output << ", " << mangleName( expr->member );
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::OffsetPackExpr const * expr ) {
+	assertf( !options.genC, "OffsetPackExpr should not reach code generation." );
+	output << "__CFA_offsetpack(" << genType( expr->type, "", options ) << ")";
+}
+
+void CodeGenerator::postvisit( ast::LogicalExpr const * expr ) {
+	extension( expr );
+	output << "(";
+	expr->arg1->accept( *visitor );
+	output << ( ( expr->isAnd ) ? " && " : " || " );
+	expr->arg2->accept( *visitor );
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::ConditionalExpr const * expr ) {
+	extension( expr );
+	output << "(";
+	expr->arg1->accept( *visitor );
+	output << " ? ";
+	expr->arg2->accept( *visitor );
+	output << " : ";
+	expr->arg3->accept( *visitor );
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::CommaExpr const * expr ) {
+	extension( expr );
+	output << "(";
+	if ( options.genC ) {
+		// arg1 of a comma expression is never used, so it can be safely cast
+		// to void to reduce gcc warnings.
+		ast::ptr<ast::Expr> arg1 = new ast::CastExpr( expr->location, expr->arg1 );
+		arg1->accept( *visitor );
+	} else {
+		expr->arg1->accept( *visitor );
+	}
+	output << " , ";
+	expr->arg2->accept( *visitor );
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::TupleAssignExpr const * expr ) {
+	assertf( !options.genC, "TupleAssignExpr should not reach code generation." );
+	expr->stmtExpr->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::UntypedTupleExpr const * expr ) {
+	assertf( !options.genC, "UntypedTupleExpr should not reach code generation." );
+	extension( expr );
+	output << "[";
+	genCommaList( expr->exprs );
+	output << "]";
+}
+
+void CodeGenerator::postvisit( ast::TupleExpr const * expr ) {
+	assertf( !options.genC, "TupleExpr should not reach code generation." );
+	extension( expr );
+	output << "[";
+	genCommaList( expr->exprs );
+	output << "]";
+}
+
+void CodeGenerator::postvisit( ast::TupleIndexExpr const * expr ) {
+	assertf( !options.genC, "TupleIndexExpr should not reach code generation." );
+	extension( expr );
+	expr->tuple->accept( *visitor );
+	output << "." << expr->index;
+}
+
+void CodeGenerator::postvisit( ast::TypeExpr const * expr ) {
+	// TODO: Should there be an assertion there?
+	if ( !options.genC ) {
+		output << genType( expr->type, "", options );
+	}
+}
+
+void CodeGenerator::postvisit( ast::AsmExpr const * expr ) {
+	if ( !expr->inout.empty() ) {
+		output << "[ " << expr->inout << " ] ";
+	}
+	expr->constraint->accept( *visitor );
+	output << " ( ";
+	expr->operand->accept( *visitor );
+	output << " )";
+}
+
+void CodeGenerator::postvisit( ast::CompoundLiteralExpr const * expr ) {
+	//assert( expr->result && dynamic_cast<ast::ListInit const *>( expr->init ) );
+	assert( expr->result && expr->init.as<ast::ListInit>() );
+	output << "(" << genType( expr->result, "", options ) << ")";
+	expr->init->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::UniqueExpr const * expr ) {
+	assertf( !options.genC, "UniqueExpr should not reach code generation." );
+	output << "unq<" << expr->id << ">{ ";
+	expr->expr->accept( *visitor );
+	output << " }";
+}
+
+void CodeGenerator::postvisit( ast::StmtExpr const * expr ) {
+	auto stmts = expr->stmts->kids;
+	output << "({" << endl;
+	++indent;
+	unsigned int numStmts = stmts.size();
+	unsigned int i = 0;
+	for ( ast::ptr<ast::Stmt> const & stmt : stmts ) {
+		output << indent << printLabels( stmt->labels );
+		if ( i + 1 == numStmts ) {
+			// Last statement in a statement expression needs to be handled
+			// specially - cannot cast to void, otherwise the expression
+			// statement has no value.
+			if ( ast::ExprStmt const * exprStmt = stmt.as<ast::ExprStmt>() ) {
+				exprStmt->expr->accept( *visitor );
+				output << ";" << endl;
+				++i;
+				break;
+			}
+		}
+		stmt->accept( *visitor );
+		output << endl;
+		if ( wantSpacing( stmt ) ) output << endl;
+		++i;
+	}
+	--indent;
+	output << indent << "})";
+}
+
+void CodeGenerator::postvisit( ast::ConstructorExpr const * expr ) {
+	assertf( !options.genC, "ConstructorExpr should not reach code generation." );
+	expr->callExpr->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::DeletedExpr const * expr ) {
+	assertf( !options.genC, "DeletedExpr should not reach code generation." );
+	expr->expr->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::DefaultArgExpr const * expr ) {
+	assertf( !options.genC, "DefaultArgExpr should not reach code generation." );
+	expr->expr->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::GenericExpr const * expr ) {
+	assertf( !options.genC, "GenericExpr should not reach code generation." );
+	output << "_Generic(";
+	expr->control->accept( *visitor );
+	output << ", ";
+	unsigned int numAssocs = expr->associations.size();
+	unsigned int i = 0;
+	for ( const ast::GenericExpr::Association & assoc : expr->associations ) {
+		if ( nullptr == assoc.type ) {
+			output << "default: ";
+		} else {
+			output << genType( assoc.type, "", options ) << ": ";
+		}
+		assoc.expr->accept( *visitor );
+		++i;
+		if ( i != numAssocs ) output << ", ";
+	}
+	output << ")";
+}
+
+void CodeGenerator::postvisit( ast::CompoundStmt const * stmt ) {
+	output << "{" << endl;
+
+	++indent;
+	for ( auto kid : stmt->kids ) {
+		output << indent << printLabels( kid->labels );
+		kid->accept( *visitor );
+		output << endl;
+		if ( wantSpacing( kid ) ) output << endl;
+	}
+	--indent;
+
+	output << indent << "}";
+}
+
+void CodeGenerator::postvisit( ast::ExprStmt const * stmt ) {
+	assert( stmt );
+	// Cast the top-level expression to void to reduce gcc warnings.
+	if ( options.genC ) {
+		ast::ptr<ast::Expr> expr = new ast::CastExpr( stmt->location, stmt->expr );
+		expr->accept( *visitor );
+	} else {
+		stmt->expr->accept( *visitor );
+	}
+	output << ";";
+}
+
+void CodeGenerator::postvisit( ast::AsmStmt const * stmt ) {
+	output << "asm ";
+	if ( stmt->isVolatile ) output << "volatile ";
+	if ( !stmt->gotoLabels.empty() ) output << "goto ";
+	output << "( ";
+	if ( stmt->instruction ) stmt->instruction->accept( *visitor );
+	output << " : ";
+	genCommaList( stmt->output );
+	output << " : ";
+	genCommaList( stmt->input );
+	output << " : ";
+	genCommaList( stmt->clobber );
+	if ( !stmt->gotoLabels.empty() ) {
+		output << " : ";
+		auto it = stmt->gotoLabels.begin();
+		while (true) {
+			output << *it++;
+			if ( stmt->gotoLabels.end() == it ) break;
+			output << ", ";
+		}
+	}
+	output << " );";
+}
+
+void CodeGenerator::postvisit( ast::AsmDecl const * decl ) {
+	output << "asm ";
+	ast::AsmStmt const * stmt = decl->stmt;
+	output << "( ";
+	if ( stmt->instruction ) stmt->instruction->accept( *visitor );
+	output << " )";
+}
+
+void CodeGenerator::postvisit( ast::DirectiveDecl const * decl ) {
+	// endl prevents spaces before the directive.
+	output << endl << decl->stmt->directive;
+}
+
+void CodeGenerator::postvisit( ast::DirectiveStmt const * stmt ) {
+	// endl prevents spaces before the directive.
+	output << endl << stmt->directive;
+}
+
+void CodeGenerator::postvisit( ast::IfStmt const * stmt ) {
+	output << "if ( ";
+	stmt->cond->accept( *visitor );
+	output << " ) ";
+
+	stmt->then->accept( *visitor );
+
+	if ( nullptr != stmt->else_ ) {
+		output << " else ";
+		stmt->else_->accept( *visitor );
+	}
+}
+
+void CodeGenerator::postvisit( ast::SwitchStmt const * stmt ) {
+	output << "switch ( ";
+	stmt->cond->accept( *visitor );
+	output << " ) ";
+
+	output << "{";
+	++indent;
+	for ( auto node : stmt->cases ) {
+		node->accept( *visitor );
+	}
+	--indent;
+	output << indent << "}";
+}
+
+void CodeGenerator::postvisit( ast::CaseClause const * clause ) {
+	updateLocation( clause );
+	output << indent;
+	if ( clause->isDefault() ) {
+		output << "default";
+	} else {
+		output << "case ";
+		clause->cond->accept( *visitor );
+	}
+	output << ":" << endl;
+
+	++indent;
+	for ( auto stmt : clause->stmts ) {
+		output << indent << printLabels( stmt->labels ) ;
+		stmt->accept( *visitor );
+		output << endl;
+	}
+	--indent;
+}
+
+void CodeGenerator::postvisit( ast::BranchStmt const * stmt ) {
+	switch ( stmt->kind ) {
+	case ast::BranchStmt::Goto:
+		if ( !stmt->target.empty() ) {
+			output << "goto " << stmt->target;
+		} else if ( nullptr != stmt->computedTarget ) {
+			output << "goto *";
+			stmt->computedTarget->accept( *visitor );
+		}
+		break;
+	case ast::BranchStmt::Break:
+		output << "break";
+		break;
+	case ast::BranchStmt::Continue:
+		output << "continue";
+		break;
+	case ast::BranchStmt::FallThrough:
+	case ast::BranchStmt::FallThroughDefault:
+		assertf( !options.genC, "fallthru should not reach code generation." );
+		output << "fallthru";
+		break;
+	default:
+		assertf( false, "Bad BranchStmt value." );
+	}
+	// Print branch target for labelled break/continue/fallthru in debug mode.
+	if ( !options.genC && stmt->kind != ast::BranchStmt::Goto ) {
+		if ( !stmt->target.empty() ) {
+			output << " " << stmt->target;
+		} else if ( stmt->kind == ast::BranchStmt::FallThrough ) {
+			output << " default";
+		}
+	}
+	output << ";";
+}
+
+void CodeGenerator::postvisit( ast::ReturnStmt const * stmt ) {
+	output << "return ";
+	if ( stmt->expr ) stmt->expr->accept( *visitor );
+	output << ";";
+}
+
+void CodeGenerator::postvisit( ast::ThrowStmt const * stmt ) {
+	assertf( !options.genC, "ThrowStmt should not reach code generation." );
+
+	output << ((stmt->kind == ast::Terminate) ? "throw" : "throwResume");
+	if ( stmt->expr ) {
+		output << " ";
+		stmt->expr->accept( *visitor );
+	}
+	if ( stmt->target ) {
+		output << " _At ";
+		stmt->target->accept( *visitor );
+	}
+	output << ";";
+}
+
+void CodeGenerator::postvisit( ast::CatchClause const * stmt ) {
+	assertf( !options.genC, "CatchClause should not reach code generation." );
+
+	output << ((stmt->kind == ast::Terminate) ? "catch" : "catchResume");
+	output << "( ";
+	stmt->decl->accept( *visitor );
+	if ( stmt->cond ) {
+		output << " ; ";
+		stmt->cond->accept( *visitor );
+	}
+	output << " ) ";
+	stmt->body->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::WaitForStmt const * stmt ) {
+	assertf( !options.genC, "WaitforStmt should not reach code generation." );
+
+	bool first = true;
+	for ( ast::ptr<ast::WaitForClause> const & clause : stmt->clauses ) {
+		if (first) { output << "or "; first = false; }
+		if ( clause->when_cond ) {
+			output << "when(";
+			stmt->timeout_cond->accept( *visitor );
+			output << ") ";
+		}
+		output << "waitfor(";
+		clause->target->accept( *visitor );
+		for ( ast::ptr<ast::Expr> const & expr : clause->target_args ) {
+			output << ",";
+			expr->accept( *visitor );
+		}
+		output << ") ";
+		clause->stmt->accept( *visitor );
+	}
+
+	if ( stmt->timeout_stmt ) {
+		output << "or ";
+		if ( stmt->timeout_cond ) {
+			output << "when(";
+			stmt->timeout_cond->accept( *visitor );
+			output << ") ";
+		}
+		output << "timeout(";
+		stmt->timeout_time->accept( *visitor );
+		output << ") ";
+		stmt->timeout_stmt->accept( *visitor );
+	}
+
+	if ( stmt->else_stmt ) {
+		output << "or ";
+		if ( stmt->else_cond ) {
+			output << "when(";
+			stmt->else_cond->accept( *visitor );
+			output << ")";
+		}
+		output << "else ";
+		stmt->else_stmt->accept( *visitor );
+	}
+}
+
+void CodeGenerator::postvisit( ast::WithStmt const * stmt ) {
+	assertf( !options.genC, "WithStmt should not reach code generation." );
+
+	output << "with ( ";
+	genCommaList( stmt->exprs );
+	output << " ) ";
+	stmt->stmt->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::WhileDoStmt const * stmt ) {
+	if ( stmt->isDoWhile ) {
+		output << "do";
+	} else {
+		output << "while (";
+		stmt->cond->accept( *visitor );
+		output << ")";
+	}
+	output << " ";
+
+	output << CodeGenerator::printLabels( stmt->body->labels );
+	stmt->body->accept( *visitor );
+
+	output << indent;
+
+	if ( stmt->isDoWhile ) {
+		output << " while (";
+		stmt->cond->accept( *visitor );
+		output << ");";
+	}
+}
+
+void CodeGenerator::postvisit( ast::ForStmt const * stmt ) {
+	// Initializer is always hoised so don't generate it.
+	// TODO: Do an assertion check?
+	output << "for (;";
+
+	if ( nullptr != stmt->cond ) {
+		stmt->cond->accept( *visitor );
+	}
+	output << ";";
+
+	if ( nullptr != stmt->inc ) {
+		// cast the top-level expression to void to reduce gcc warnings.
+		ast::Expr * expr = new ast::CastExpr( stmt->inc );
+		expr->accept( *visitor );
+	}
+	output << ") ";
+
+	if ( nullptr != stmt->body ) {
+		output << printLabels( stmt->body->labels );
+		stmt->body->accept( *visitor );
+	}
+}
+
+void CodeGenerator::postvisit( ast::NullStmt const * ) {
+	output << "/* null statement */ ;";
+}
+
+void CodeGenerator::postvisit( ast::DeclStmt const * stmt ) {
+	stmt->decl->accept( *visitor );
+
+	if ( doSemicolon( stmt->decl ) ) output << ";";
+}
+
+void CodeGenerator::postvisit( ast::ImplicitCtorDtorStmt const * stmt ) {
+	assertf( !options.genC, "ImplicitCtorCtorStmt should not reach code generation." );
+	stmt->callStmt->accept( *visitor );
+}
+
+void CodeGenerator::postvisit( ast::MutexStmt const * stmt ) {
+	assertf( !options.genC, "MutexStmt should not reach code generation." );
+	// TODO: But this isn't what a mutex statement looks like.
+	stmt->stmt->accept( *visitor );
+}
+
+std::string genName( ast::DeclWithType const * decl ) {
+	if ( const OperatorInfo * opInfo = operatorLookup( decl->name ) ) {
+		return opInfo->outputName;
+	} else {
+		return decl->name;
+	}
+}
+
+} // namespace CodeGen
Index: src/CodeGen/CodeGenerator.hpp
===================================================================
--- src/CodeGen/CodeGenerator.hpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/CodeGen/CodeGenerator.hpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,192 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// CodeGenerator.hpp --
+//
+// Author           : Andrew Beach
+// Created On       : Tue Oct 17 15:54:00 2023
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Oct 25 17:56:00 2023
+// Update Count     : 0
+//
+
+#pragma once
+
+#include <ostream>               // for ostream, operator<<
+
+#include "AST/Fwd.hpp"
+#include "AST/Pass.hpp"          // for WithGuards, WithShortCircuiting, ...
+#include "CodeGen/Options.h"     // for Options
+
+
+namespace CodeGen {
+
+struct CodeGenerator final :
+		public ast::WithGuards,
+		public ast::WithShortCircuiting,
+		public ast::WithVisitorRef<CodeGenerator> {
+	CodeGenerator( std::ostream & out, Options const & options );
+
+	// Turn off visit_children for all nodes.
+	void previsit( ast::Node const * );
+	void previsit( ast::ParseNode const * );
+
+	// Error for unhandled node types.
+	void postvisit( ast::Node const * );
+
+	// Print type for all expressions.
+	void previsit( ast::Expr const * );
+
+	void postvisit( ast::StructDecl const * );
+	void postvisit( ast::FunctionDecl const * );
+	// Yes, there is one visit that does modify the ast.
+	ast::ObjectDecl const * postvisit( ast::ObjectDecl const * );
+	void postvisit( ast::UnionDecl const * );
+	void postvisit( ast::EnumDecl const * );
+	void postvisit( ast::TraitDecl const * );
+	void postvisit( ast::TypedefDecl const * );
+	void postvisit( ast::TypeDecl const * );
+	void postvisit( ast::StaticAssertDecl const * );
+
+	void postvisit( ast::Designation const * );
+	void postvisit( ast::SingleInit const * );
+	void postvisit( ast::ListInit const * );
+	void postvisit( ast::ConstructorInit const * );
+
+	void postvisit( ast::ApplicationExpr const * );
+	void postvisit( ast::UntypedExpr const * );
+	void postvisit( ast::RangeExpr const * );
+	void postvisit( ast::NameExpr const * );
+	void postvisit( ast::AddressExpr const * );
+	void postvisit( ast::LabelAddressExpr const * );
+	void postvisit( ast::CastExpr const * );
+	void postvisit( ast::KeywordCastExpr const * );
+	void postvisit( ast::VirtualCastExpr const * );
+	void postvisit( ast::UntypedMemberExpr const * );
+	void postvisit( ast::MemberExpr const * );
+	void postvisit( ast::VariableExpr const * );
+	void postvisit( ast::ConstantExpr const * );
+	void postvisit( ast::SizeofExpr const * );
+	void postvisit( ast::AlignofExpr const * );
+	void postvisit( ast::UntypedOffsetofExpr const * );
+	void postvisit( ast::OffsetofExpr const * );
+	void postvisit( ast::OffsetPackExpr const * );
+	void postvisit( ast::LogicalExpr const * );
+	void postvisit( ast::ConditionalExpr const * );
+	void postvisit( ast::CommaExpr const * );
+	void postvisit( ast::CompoundLiteralExpr const * );
+	void postvisit( ast::UniqueExpr const * );
+	void postvisit( ast::TupleAssignExpr const * );
+	void postvisit( ast::UntypedTupleExpr const * );
+	void postvisit( ast::TupleExpr const * );
+	void postvisit( ast::TupleIndexExpr const * );
+	void postvisit( ast::TypeExpr const * );
+	void postvisit( ast::DimensionExpr const * );
+	void postvisit( ast::AsmExpr const * );
+	void postvisit( ast::StmtExpr const * );
+	void postvisit( ast::ConstructorExpr const * );
+	void postvisit( ast::DeletedExpr const * );
+	void postvisit( ast::DefaultArgExpr const * );
+	void postvisit( ast::GenericExpr const * );
+
+	void postvisit( ast::CompoundStmt const * );
+	void postvisit( ast::ExprStmt const * );
+	void postvisit( ast::AsmStmt const * );
+	void postvisit( ast::DirectiveStmt const* );
+	void postvisit( ast::AsmDecl const * );
+	void postvisit( ast::DirectiveDecl const * );
+	void postvisit( ast::IfStmt const * );
+	void postvisit( ast::SwitchStmt const * );
+	void postvisit( ast::CaseClause const * );
+	void postvisit( ast::BranchStmt const * );
+	void postvisit( ast::ReturnStmt const * );
+	void postvisit( ast::ThrowStmt const * );
+	void postvisit( ast::CatchClause const * );
+	void postvisit( ast::WaitForStmt const * );
+	void postvisit( ast::WithStmt const * );
+	void postvisit( ast::WhileDoStmt const * );
+	void postvisit( ast::ForStmt const * );
+	void postvisit( ast::NullStmt const * );
+	void postvisit( ast::DeclStmt const * );
+	void postvisit( ast::ImplicitCtorDtorStmt const * );
+	void postvisit( ast::MutexStmt const * stmt );
+
+private:
+	/// Custom local implementation of endl that updates print location.
+	struct LineEnder {
+		CodeGenerator & cg;
+		LineEnder( CodeGenerator & cg ) : cg( cg ) {}
+		std::ostream & operator()( std::ostream & ) const;
+	};
+	friend std::ostream & operator<<( std::ostream & os, const LineEnder & e ) {
+		return e( os );
+	}
+
+	/// Wrapper class to help print vectors of Labels.
+	struct LabelPrinter {
+		LabelPrinter( CodeGenerator & cg ) : cg( cg ), labels( nullptr ) {}
+		LabelPrinter & operator()( std::vector<ast::Label> const & l );
+		std::ostream & operator()( std::ostream & ) const;
+		CodeGenerator & cg;
+		std::vector<ast::Label> const * labels;
+	};
+	friend std::ostream & operator<<( std::ostream & os, const LabelPrinter & p ) {
+		return p( os );
+	}
+
+	static int tabsize;
+
+	Indenter indent;
+	std::ostream & output;
+	Options options;
+	LabelPrinter printLabels;
+
+	CodeLocation currentLocation;
+	void updateLocation( CodeLocation const & to );
+
+	template<typename Iterator>
+	void genCommaList( Iterator begin, Iterator end ) {
+		if ( begin == end ) return;
+		while (true) {
+			(*begin++)->accept( *visitor );
+			if ( begin == end ) break;
+			output << ", ";
+		}
+	}
+
+public:
+	LineEnder endl;
+	void updateLocation( ast::ParseNode const * to );
+
+	template<typename T>
+	void genCommaList( std::vector<ast::ptr<T>> const & range ) {
+		genCommaList( range.begin(), range.end() );
+	}
+
+	void genAttributes( std::vector<ast::ptr<ast::Attribute>> const & );
+
+private:
+	void asmName( ast::DeclWithType const * decl );
+	void extension( ast::Decl const * );
+	void extension( ast::Expr const * );
+
+	void handleStorageClass( ast::DeclWithType const * decl );
+	void handleAggregate( ast::AggregateDecl const *, const std::string & );
+	void handleTypedef( ast::NamedTypeDecl const * type );
+	std::string mangleName( ast::DeclWithType const * decl );
+};
+
+inline bool doSemicolon( ast::Decl const * decl ) {
+	if ( auto func = dynamic_cast<ast::FunctionDecl const *>( decl ) ) {
+		return !func->stmts;
+	}
+	return true;
+}
+
+/// Returns the C-compatible name of the declaration.
+std::string genName( ast::DeclWithType const * decl );
+
+} // namespace CodeGen
Index: c/CodeGen/CodeGeneratorNew.cpp
===================================================================
--- src/CodeGen/CodeGeneratorNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,1256 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// CodeGeneratorNew.cpp --
-//
-// Author           : Andrew Beach
-// Created On       : Tue Oct 17 15:54:00 2023
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Oct 25 18:28:00 2023
-// Update Count     : 0
-//
-
-#include "CodeGeneratorNew.hpp"
-
-#include "AST/Print.hpp"
-#include "OperatorTable.h"           // for OperatorInfo, operatorLookup
-#include "CodeGen/GenType.h"         // for genType
-#include "Common/ToString.hpp"       // for toString
-#include "Common/UniqueName.h"       // for UniqueName
-
-namespace CodeGen {
-
-int CodeGenerator::tabsize = 4;
-
-// The kinds of statements that should be followed by whitespace.
-static bool wantSpacing( ast::Stmt const * stmt ) {
-	return dynamic_cast<ast::IfStmt const *>( stmt )
-		|| dynamic_cast<ast::CompoundStmt const *>( stmt )
-		|| dynamic_cast<ast::WhileDoStmt const *>( stmt )
-		|| dynamic_cast<ast::ForStmt const *>( stmt )
-		|| dynamic_cast<ast::SwitchStmt const *>( stmt );
-}
-
-void CodeGenerator::extension( ast::Expr const * expr ) {
-	if ( expr->extension ) output << "__extension__ ";
-}
-
-void CodeGenerator::extension( ast::Decl const * decl ) {
-	if ( decl->extension ) output << "__extension__ ";
-}
-
-void CodeGenerator::asmName( ast::DeclWithType const * decl ) {
-	if ( auto asmName = decl->asmName.as<ast::ConstantExpr>() ) {
-		output << " asm ( " << asmName->rep << " )";
-	}
-}
-
-CodeGenerator::LabelPrinter & CodeGenerator::LabelPrinter::operator()(
-		std::vector<ast::Label> const & l ) {
-	labels = &l;
-	return *this;
-}
-
-std::ostream & CodeGenerator::LabelPrinter::operator()( std::ostream & output ) const {
-	const std::vector<ast::Label> & labels = *this->labels;
-	for ( const ast::Label & label : labels ) {
-		output << label.name + ": ";
-		this->cg.genAttributes( label.attributes );
-	}
-	return output;
-}
-
-// Using updateLocation at the beginning of a node and endl within a node
-// should become the method of formating.
-void CodeGenerator::updateLocation( CodeLocation const & to ) {
-	// Skip if linemarks shouldn't appear or if location is unset.
-	if ( !options.lineMarks || to.isUnset() ) return;
-
-	if ( currentLocation.followedBy( to, 0 ) ) {
-		return;
-	} else if ( currentLocation.followedBy( to, 1 ) ) {
-		output << "\n" << indent;
-		currentLocation.first_line += 1;
-	} else if ( currentLocation.followedBy( to, 2 ) ) {
-		output << "\n\n" << indent;
-		currentLocation.first_line += 2;
-	} else {
-		output << "\n# " << to.first_line << " \"" << to.filename
-		       << "\"\n" << indent;
-		currentLocation = to;
-	}
-	output << std::flush;
-}
-
-void CodeGenerator::updateLocation( ast::ParseNode const * to ) {
-	updateLocation( to->location );
-}
-
-std::ostream & CodeGenerator::LineEnder::operator()( std::ostream & os ) const {
-	os << "\n" << std::flush;
-	cg.currentLocation.first_line++;
-	return os;
-}
-
-CodeGenerator::CodeGenerator( std::ostream & os, const Options & options ) :
-		indent( 0, CodeGenerator::tabsize ), output( os ),
-		options( options ), printLabels( *this ), endl( *this )
-{}
-
-std::string CodeGenerator::mangleName( ast::DeclWithType const * decl ) {
-	// GCC builtins should always be printed unmangled.
-	if ( options.pretty || decl->linkage.is_gcc_builtin ) {
-		return decl->name;
-	} else if ( decl->linkage.is_mangled && decl->mangleName != "" ) {
-		return decl->scopedMangleName();
-	} else {
-		return decl->name;
-	}
-}
-
-void CodeGenerator::genAttributes(
-		const std::vector<ast::ptr<ast::Attribute>> & attributes ) {
-	if ( attributes.empty() ) return;
-	output << "__attribute__ ((";
-	for ( auto attr = attributes.begin() ;; ) {
-		output << (*attr)->name;
-		if ( !(*attr)->params.empty() ) {
-			output << "(";
-			genCommaList( (*attr)->params );
-			output << ")";
-		}
-		if ( ++attr == attributes.end() ) break;
-		output << ",";
-	}
-	output << ")) ";
-}
-
-void CodeGenerator::previsit( ast::Node const * ) {
-	// All traversal is manual.
-	// TODO: Which means the ast::Pass is just providing a default no visit?
-	visit_children = false;
-}
-
-void CodeGenerator::previsit( ast::ParseNode const * node ) {
-	previsit( (ast::Node const *)node );
-	updateLocation( node );
-}
-
-void CodeGenerator::postvisit( ast::Node const * node ) {
-	std::stringstream ss;
-	ast::print( ss, node );
-	assertf( false, "Unhandled node reached in CodeGenerator: %s", ss.str().c_str() );
-}
-
-void CodeGenerator::previsit( ast::Expr const * expr ) {
-	previsit( (ast::ParseNode const *)expr );
-	GuardAction( [this, expr](){
-		if ( options.printExprTypes && expr->result ) {
-			output << " /* " << genType( expr->result, "", options ) << " */ ";
-		}
-	} );
-}
-
-void CodeGenerator::postvisit( ast::FunctionDecl const * decl ) {
-	// Deleted decls should never be used, so don't print them in C.
-	if ( decl->isDeleted && options.genC ) return;
-	extension( decl );
-	genAttributes( decl->attributes );
-
-	handleStorageClass( decl );
-	ast::print( output, decl->funcSpec );
-
-	Options subOptions = options;
-	subOptions.anonymousUnused = decl->stmts;
-
-	std::ostringstream acc;
-	ast::Pass<CodeGenerator> subCG( acc, subOptions );
-	// Add the forall clause.
-	// TODO: These probably should be removed by now and the assert used.
-	if ( !decl->type_params.empty() ) {
-		assertf( !options.genC, "FunctionDecl forall should not reach code generation." );
-		acc << "forall(";
-		subCG.core.genCommaList( decl->type_params );
-		acc << ")" << std::endl;
-	}
-
-	acc << mangleName( decl );
-
-	if ( 0 == decl->params.size() ) {
-		if ( decl->type->isVarArgs ) {
-			acc << "()";
-		} else {
-			acc << "(void)";
-		}
-	} else {
-		acc << "(";
-		subCG.core.genCommaList( decl->params );
-		if ( decl->type->isVarArgs ) {
-			acc << ", ...";
-		}
-		acc << ")";
-	}
-
-	assert( decl->returns.size() < 2 );
-	if ( 1 == decl->returns.size() ) {
-		ast::ptr<ast::Type> const & type = decl->returns[0]->get_type();
-		output << genTypeNoAttr( type, acc.str(), subOptions );
-	} else {
-		output << "void " + acc.str();
-	}
-
-	asmName( decl );
-
-	if ( decl->stmts ) {
-		decl->stmts->accept( *visitor );
-	}
-	if ( decl->isDeleted ) {
-		output << " = void";
-	}
-}
-
-ast::ObjectDecl const * CodeGenerator::postvisit(
-		ast::ObjectDecl const * decl ) {
-	// Deleted decls should never be used, so don't print them in C.
-	if ( decl->isDeleted && options.genC ) return decl;
-
-	// GCC allows an empty declarator (no name) for bit-fields and C
-	// states: 6.7.2.1 Structure and union specifiers, point 4, page 113:
-	// If the (bit field) value is zero, the declaration shall have no
-	// declarator. For anything else, the anonymous name refers to the
-	// anonymous object for plan9 inheritance.
-	if ( decl->name.empty() && options.genC && !decl->bitfieldWidth ) {
-		// TODO: Should this be changed in a pervious pass?
-		auto mutDecl = ast::mutate( decl );
-		// Only generate an anonymous name when generating C code,
-		// otherwise it clutters the output too much.
-		static UniqueName name = { "__anonymous_object" };
-		mutDecl->name = name.newName();
-		// Stops unused parameter warnings.
-		if ( options.anonymousUnused ) {
-			mutDecl->attributes.push_back( new ast::Attribute( "unused" ) );
-		}
-		decl = mutDecl;
-	}
-
-	extension( decl );
-	genAttributes( decl->attributes );
-
-	handleStorageClass( decl );
-	output << genType( decl->type, mangleName( decl ),
-		Options( options.pretty, options.genC, false, false ) );
-
-	asmName( decl );
-
-	if ( decl->init ) {
-		output << " = ";
-		decl->init->accept( *visitor );
-	}
-	if ( decl->isDeleted ) {
-		output << " = void";
-	}
-
-	if ( decl->bitfieldWidth ) {
-		output << ":";
-		decl->bitfieldWidth->accept( *visitor );
-	}
-	return decl;
-}
-
-void CodeGenerator::handleStorageClass( ast::DeclWithType const * decl ) {
-	if ( decl->storage.any() ) {
-		ast::print( output, decl->storage );
-	}
-}
-
-void CodeGenerator::handleAggregate(
-		ast::AggregateDecl const * decl, std::string const & kind ) {
-	if ( !decl->params.empty() && !options.genC ) {
-		output << "forall(";
-		genCommaList( decl->params );
-		output << ")\n" << indent << std::flush;
-	}
-
-	output << kind;
-	genAttributes( decl->attributes );
-	output << decl->name;
-
-	if ( decl->body ) {
-		auto & members = decl->members;
-		output << " {" << endl;
-
-		++indent;
-		for ( auto & member : members ) {
-			output << indent;
-			member->accept( *visitor );
-			output << ";" << endl;
-		}
-		--indent;
-
-		output << indent << "}";
-	}
-}
-
-void CodeGenerator::postvisit( ast::StructDecl const * decl ) {
-	extension( decl );
-	handleAggregate( decl, "struct " );
-}
-
-void CodeGenerator::postvisit( ast::UnionDecl const * decl ) {
-	extension( decl );
-	handleAggregate( decl, "union " );
-}
-
-template<typename pass_type>
-inline void genEnumInitializer( ast::Pass<pass_type> * visitor,
-		ast::Type const * baseType, std::ostream & output,
-		ast::Init const * init, long long * curVal, Options options ) {
-	auto baseTypeAsBasic = dynamic_cast<ast::BasicType const *>( baseType );
-	// Value is provided.
-	if ( init ) {
-		output << " = (" << genType( baseType, "", options ) << ")";
-		init->accept( *visitor );
-		// If it is an integral type and initilizer offered,
-		// need to update the curVal.
-		if ( baseTypeAsBasic && baseTypeAsBasic->isInteger() ) {
-			ast::Expr const * expr = ((ast::SingleInit const *)(init))->value;
-			// Unwrap introduced cast.
-			while ( auto temp = dynamic_cast<ast::CastExpr const *>( expr ) ) {
-				expr = temp->arg;
-			}
-			*curVal = ((ast::ConstantExpr const *)(expr))->intValue() + 1;
-		}
-	// Generate next value from previous value.
-	} else if ( baseTypeAsBasic && baseTypeAsBasic->isInteger() ) {
-		output << " = (" << genType( baseType, "", options ) << ")";
-		output << (*curVal)++;
-	}
-}
-
-void CodeGenerator::postvisit( ast::EnumDecl const * decl ) {
-	extension( decl );
-	auto members = decl->members;
-	if ( decl->base && !members.empty() ) {
-		long long curVal = 0;
-		for ( auto member : members ) {
-			auto obj = member.strict_as<ast::ObjectDecl>();
-			output << "static ";
-			output << genType( decl->base, mangleName( obj ), options );
-			genEnumInitializer( visitor, decl->base, output, obj->init, &curVal, options );
-			output << ";" << endl;
-		}
-	} else {
-		output << "enum ";
-		genAttributes( decl->attributes );
-
-		output << decl->name;
-
-		if ( !members.empty() ) {
-			output << " {" << endl;
-
-			++indent;
-			for ( auto member : members ) {
-				auto obj = member.strict_as<ast::ObjectDecl>();
-				output << indent << mangleName( obj );
-				if ( obj->init ) {
-					output << " = ";
-					obj->init->accept( *visitor );
-				}
-				output << "," << endl;
-			}
-			--indent;
-
-			output << indent << "}";
-		}
-	}
-}
-
-void CodeGenerator::postvisit( ast::TraitDecl const * decl ) {
-	assertf( !options.genC, "TraitDecls should not reach code generation." );
-	extension( decl );
-	handleAggregate( decl, "trait " );
-}
-
-void CodeGenerator::postvisit( ast::TypedefDecl const * decl ) {
-	assertf( !options.genC, "Typedefs should not reach code generation." );
-	output << "typedef " << genType( decl->base, decl->name, options ) << endl;
-}
-
-void CodeGenerator::postvisit( ast::TypeDecl const * decl ) {
-	assertf( !options.genC, "TypeDecls should not reach code generation." );
-	output << decl->genTypeString() << " " << decl->name;
-	if ( decl->sized ) {
-		output << " | sized(" << decl->name << ")";
-	}
-	if ( !decl->assertions.empty() ) {
-		output << " | { ";
-		for ( ast::DeclWithType const * assert : decl->assertions ) {
-			assert->accept( *visitor );
-			output << "; ";
-		}
-		output << " }";
-	}
-}
-
-void CodeGenerator::postvisit( ast::StaticAssertDecl const * decl ) {
-	output << "_Static_assert(";
-	decl->cond->accept( *visitor );
-	output << ", ";
-	decl->msg->accept( *visitor );
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::Designation const * designation ) {
-	auto designators = designation->designators;
-	if ( 0 == designators.size() ) return;
-	for ( ast::ptr<ast::Expr> const & des : designators ) {
-		// If the expression is a NameExpr or VariableExpr, then it is a field.
-		if ( des.as<ast::NameExpr>() || des.as<ast::VariableExpr>() ) {
-			output << ".";
-			des->accept( *visitor );
-		// Otherwise, it is a ConstantExpr or CastExpr, then it is an index.
-		} else {
-			output << "[";
-			des->accept( *visitor );
-			output << "]";
-		}
-	}
-	output << " = ";
-}
-
-void CodeGenerator::postvisit( ast::SingleInit const * init ) {
-	init->value->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::ListInit const * init ) {
-	auto initBegin = init->initializers.begin();
-	auto initEnd = init->initializers.end();
-	auto desigBegin = init->designations.begin();
-	auto desigEnd = init->designations.end();
-
-	output << "{ ";
-	if ( initBegin != initEnd ) while (true) {
-		(*desigBegin)->accept( *visitor );
-		(*initBegin)->accept( *visitor );
-		++initBegin, ++desigBegin;
-		if ( initBegin == initEnd ) break;
-		output << ", ";
-	}
-	output << " }";
-	assertf( initBegin == initEnd && desigBegin == desigEnd,
-		"Initializers and designators not the same length. %s", toCString( init ) );
-}
-
-void CodeGenerator::postvisit( ast::ConstructorInit const * init ) {
-	assertf( !options.genC, "ConstructorInit nodes should not reach code generation." );
-	// This isn't actual code, but labels the constructor/destructor pairs.
-	output << "<ctorinit>{" << endl << ++indent << "ctor: ";
-	if ( init->ctor ) init->ctor->accept( *visitor );
-	output << ", " << endl << indent << "dtor: ";
-	if ( init->dtor ) init->dtor->accept( *visitor );
-	output << endl << --indent << "}";
-}
-
-void CodeGenerator::postvisit( ast::ApplicationExpr const * expr ) {
-	extension( expr );
-	if ( auto var = expr->func.as<ast::VariableExpr>() ) {
-		const OperatorInfo * opInfo;
-		if ( var->var->linkage == ast::Linkage::Intrinsic &&
-				( opInfo = operatorLookup( var->var->name ) ) ) {
-			auto arg = expr->args.begin();
-			switch ( opInfo->type ) {
-			case OT_INDEX:
-				assert( 2 == expr->args.size() );
-				(*arg++)->accept( *visitor );
-				output << "[";
-				(*arg)->accept( *visitor );
-				output << "]";
-				break;
-
-			// There are no intrinsic definitions of the function call operator.
-			case OT_CALL:
-				assert( false );
-				break;
-
-			case OT_CTOR:
-			case OT_DTOR:
-				// No-op constructor, but run the internal expression.
-				if ( 1 == expr->args.size() ) {
-					output << "(";
-					(*arg++)->accept( *visitor );
-					output << ") /* " << opInfo->inputName << " */";
-				// These are all implemented as some form of assignment.
-				} else if ( 2 == expr->args.size() ) {
-					output << "(";
-					(*arg++)->accept( *visitor );
-					output << opInfo->symbol;
-					(*arg)->accept( *visitor );
-					output << ") /* " << opInfo->inputName << " */";
-				// No constructors with 0 or more than 2 parameters.
-				} else {
-					assert( false );
-				}
-				break;
-
-			case OT_PREFIX:
-			case OT_PREFIXASSIGN:
-				assert( 1 == expr->args.size() );
-				output << "(" << opInfo->symbol;
-				(*arg)->accept( *visitor );
-				output << ")";
-				break;
-
-			case OT_POSTFIX:
-			case OT_POSTFIXASSIGN:
-				assert( 1 == expr->args.size() );
-				(*arg)->accept( *visitor );
-				output << opInfo->symbol;
-				break;
-
-			case OT_INFIX:
-			case OT_INFIXASSIGN:
-				assert( 2 == expr->args.size() );
-				output << "(";
-				(*arg++)->accept( *visitor );
-				output << opInfo->symbol;
-				(*arg)->accept( *visitor );
-				output << ")";
-				break;
-
-			// There are no intrinsic definitions of 0/1 or label address
-			// as function.
-			case OT_CONSTANT:
-			case OT_LABELADDRESS:
-				assert( false );
-			}
-		// TODO: This is a work-around to make it a constant until a proper
-		// constexpr solution is created.
-		} else if ( var->var->linkage == ast::Linkage::BuiltinCFA &&
-				var->var->name == "intptr" ) {
-			output << "((void*)";
-			auto arg = expr->args.begin();
-			(*arg++)->accept( *visitor );
-			output << ")";
-		} else {
-			var->accept( *visitor );
-			output << "(";
-			genCommaList( expr->args );
-			output << ")";
-		}
-	} else {
-		expr->func->accept( *visitor );
-		output << "(";
-		genCommaList( expr->args );
-		output << ")";
-	}
-}
-
-void CodeGenerator::postvisit( ast::UntypedExpr const * expr ) {
-	extension( expr );
-	if ( auto name = expr->func.as<ast::NameExpr>() ) {
-		if ( const OperatorInfo * opInfo = operatorLookup( name->name ) ) {
-			auto arg = expr->args.begin();
-			switch ( opInfo->type ) {
-			case OT_INDEX:
-				assert( 2 == expr->args.size() );
-				(*arg++)->accept( *visitor );
-				output << "[";
-				(*arg)->accept( *visitor );
-				output << "]";
-				break;
-
-			case OT_CALL:
-				assert( false );
-
-			case OT_CTOR:
-			case OT_DTOR:
-				// No-op constructor, but run the internal expression.
-				if ( 1 == expr->args.size() ) {
-					output << "(";
-					(*arg++)->accept( *visitor );
-					output << ")";
-				// These are all implemented as some form of assignment.
-				} else if ( 2 == expr->args.size() ) {
-					output << "(";
-					(*arg++)->accept( *visitor );
-					output << opInfo->symbol;
-					(*arg)->accept( *visitor );
-					output << ") /* " << opInfo->inputName << " */";
-				// No constructors with 0 or more than 2 parameters.
-				} else {
-					assertf( !options.genC, "UntypedExpr constructor/destructor with 0 or more than 2 parameters." );
-					output << "(";
-					(*arg++)->accept( *visitor );
-					output << opInfo->symbol << "{ ";
-					genCommaList( arg, expr->args.end() );
-					output << "}) /* " << opInfo->inputName << " */";
-				}
-				break;
-
-			case OT_PREFIX:
-			case OT_PREFIXASSIGN:
-			case OT_LABELADDRESS:
-				assert( 1 == expr->args.size() );
-				output << "(" << opInfo->symbol;
-				(*arg)->accept( *visitor );
-				output << ")";
-				break;
-
-			case OT_POSTFIX:
-			case OT_POSTFIXASSIGN:
-				assert( 1 == expr->args.size() );
-				(*arg)->accept( *visitor );
-				output << opInfo->symbol;
-				break;
-
-			case OT_INFIX:
-			case OT_INFIXASSIGN:
-				assert( 2 == expr->args.size() );
-				output << "(";
-				(*arg++)->accept( *visitor );
-				output << opInfo->symbol;
-				(*arg)->accept( *visitor );
-				output << ")";
-				break;
-
-			// There are no intrinsic definitions of 0/1 or label address
-			// as function.
-			case OT_CONSTANT:
-				assert( false );
-			}
-		// builtin routines
-		} else {
-			name->accept( *visitor );
-			output << "(";
-			genCommaList( expr->args );
-			output << ")";
-		}
-	} else {
-		expr->func->accept( *visitor );
-		output << "(";
-		genCommaList( expr->args );
-		output << ")";
-	}
-}
-
-void CodeGenerator::postvisit( ast::RangeExpr const * expr ) {
-	expr->low->accept( *visitor );
-	output << " ... ";
-	expr->high->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::NameExpr const * expr ) {
-	extension( expr );
-	if ( const OperatorInfo * opInfo = operatorLookup( expr->name ) ) {
-		if ( OT_CONSTANT == opInfo->type ) {
-			output << opInfo->symbol;
-		} else {
-			output << opInfo->outputName;
-		}
-	} else {
-		output << expr->name;
-	}
-}
-
-void CodeGenerator::postvisit( ast::DimensionExpr const * expr ) {
-	extension( expr );
-	output << "/*non-type*/" << expr->name;
-}
-
-void CodeGenerator::postvisit( ast::AddressExpr const * expr ) {
-	extension( expr );
-	output << "(&";
-	expr->arg->accept( *visitor );
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::LabelAddressExpr const * expr ) {
-	extension( expr );
-	output << "(&&" << expr->arg << ")";
-}
-
-void CodeGenerator::postvisit( ast::CastExpr const * expr ) {
-	extension( expr );
-	output << "(";
-	if ( expr->result->isVoid() ) {
-		output << "(void)";
-	} else {
-		output << "(";
-		output << genType( expr->result, "", options );
-		output << ")";
-	}
-	expr->arg->accept( *visitor );
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::KeywordCastExpr const * expr ) {
-	assertf( !options.genC, "KeywordCastExpr should not reach code generation." );
-	extension( expr );
-	output << "((" << expr->targetString() << " &)";
-	expr->arg->accept( *visitor );
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::VirtualCastExpr const * expr ) {
-	assertf( !options.genC, "VirtualCastExpr should not reach code generation." );
-	extension( expr );
-	// TODO: Is this busted?
-	output << "(virtual ";
-	expr->arg->accept( *visitor );
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::UntypedMemberExpr const * expr ) {
-	assertf( !options.genC, "UntypedMemberExpr should not reach code generation." );
-	extension( expr );
-	expr->aggregate->accept( *visitor );
-	output << ".";
-	expr->member->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::MemberExpr const * expr ) {
-	extension( expr );
-	expr->aggregate->accept( *visitor );
-	output << "." << mangleName( expr->member );
-}
-
-void CodeGenerator::postvisit( ast::VariableExpr const * expr ) {
-	extension( expr );
-	const OperatorInfo * opInfo;
-	if ( dynamic_cast<ast::ZeroType const *>( expr->var->get_type() ) ) {
-		output << "0";
-	} else if ( expr->var->linkage == ast::Linkage::Intrinsic
-			&& ( opInfo = operatorLookup( expr->var->name ) )
-			&& opInfo->type == OT_CONSTANT ) {
-		output << opInfo->symbol;
-	} else {
-		output << mangleName( expr->var );
-	}
-}
-
-void CodeGenerator::postvisit( ast::ConstantExpr const * expr ) {
-	extension( expr );
-	output << expr->rep;
-}
-
-void CodeGenerator::postvisit( ast::SizeofExpr const * expr ) {
-	extension( expr );
-	output << "sizeof(";
-	if ( expr->type ) {
-		output << genType( expr->type, "", options );
-	} else {
-		expr->expr->accept( *visitor );
-	}
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::AlignofExpr const * expr ) {
-	// Using the GCC extension to avoid changing the std to C11.
-	extension( expr );
-	output << "__alignof__(";
-	if ( expr->type ) {
-		output << genType( expr->type, "", options );
-	} else {
-		expr->expr->accept( *visitor );
-	}
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::UntypedOffsetofExpr const * expr ) {
-	assertf( !options.genC, "UntypedOffsetofExpr should not reach code generation." );
-	output << "offsetof(";
-	output << genType( expr->type, "", options );
-	output << ", " << expr->member;
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::OffsetofExpr const * expr ) {
-	// Use GCC builtin
-	output << "__builtin_offsetof(";
-	output << genType( expr->type, "", options );
-	output << ", " << mangleName( expr->member );
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::OffsetPackExpr const * expr ) {
-	assertf( !options.genC, "OffsetPackExpr should not reach code generation." );
-	output << "__CFA_offsetpack(" << genType( expr->type, "", options ) << ")";
-}
-
-void CodeGenerator::postvisit( ast::LogicalExpr const * expr ) {
-	extension( expr );
-	output << "(";
-	expr->arg1->accept( *visitor );
-	output << ( ( expr->isAnd ) ? " && " : " || " );
-	expr->arg2->accept( *visitor );
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::ConditionalExpr const * expr ) {
-	extension( expr );
-	output << "(";
-	expr->arg1->accept( *visitor );
-	output << " ? ";
-	expr->arg2->accept( *visitor );
-	output << " : ";
-	expr->arg3->accept( *visitor );
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::CommaExpr const * expr ) {
-	extension( expr );
-	output << "(";
-	if ( options.genC ) {
-		// arg1 of a comma expression is never used, so it can be safely cast
-		// to void to reduce gcc warnings.
-		ast::ptr<ast::Expr> arg1 = new ast::CastExpr( expr->location, expr->arg1 );
-		arg1->accept( *visitor );
-	} else {
-		expr->arg1->accept( *visitor );
-	}
-	output << " , ";
-	expr->arg2->accept( *visitor );
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::TupleAssignExpr const * expr ) {
-	assertf( !options.genC, "TupleAssignExpr should not reach code generation." );
-	expr->stmtExpr->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::UntypedTupleExpr const * expr ) {
-	assertf( !options.genC, "UntypedTupleExpr should not reach code generation." );
-	extension( expr );
-	output << "[";
-	genCommaList( expr->exprs );
-	output << "]";
-}
-
-void CodeGenerator::postvisit( ast::TupleExpr const * expr ) {
-	assertf( !options.genC, "TupleExpr should not reach code generation." );
-	extension( expr );
-	output << "[";
-	genCommaList( expr->exprs );
-	output << "]";
-}
-
-void CodeGenerator::postvisit( ast::TupleIndexExpr const * expr ) {
-	assertf( !options.genC, "TupleIndexExpr should not reach code generation." );
-	extension( expr );
-	expr->tuple->accept( *visitor );
-	output << "." << expr->index;
-}
-
-void CodeGenerator::postvisit( ast::TypeExpr const * expr ) {
-	// TODO: Should there be an assertion there?
-	if ( !options.genC ) {
-		output << genType( expr->type, "", options );
-	}
-}
-
-void CodeGenerator::postvisit( ast::AsmExpr const * expr ) {
-	if ( !expr->inout.empty() ) {
-		output << "[ " << expr->inout << " ] ";
-	}
-	expr->constraint->accept( *visitor );
-	output << " ( ";
-	expr->operand->accept( *visitor );
-	output << " )";
-}
-
-void CodeGenerator::postvisit( ast::CompoundLiteralExpr const * expr ) {
-	//assert( expr->result && dynamic_cast<ast::ListInit const *>( expr->init ) );
-	assert( expr->result && expr->init.as<ast::ListInit>() );
-	output << "(" << genType( expr->result, "", options ) << ")";
-	expr->init->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::UniqueExpr const * expr ) {
-	assertf( !options.genC, "UniqueExpr should not reach code generation." );
-	output << "unq<" << expr->id << ">{ ";
-	expr->expr->accept( *visitor );
-	output << " }";
-}
-
-void CodeGenerator::postvisit( ast::StmtExpr const * expr ) {
-	auto stmts = expr->stmts->kids;
-	output << "({" << endl;
-	++indent;
-	unsigned int numStmts = stmts.size();
-	unsigned int i = 0;
-	for ( ast::ptr<ast::Stmt> const & stmt : stmts ) {
-		output << indent << printLabels( stmt->labels );
-		if ( i + 1 == numStmts ) {
-			// Last statement in a statement expression needs to be handled
-			// specially - cannot cast to void, otherwise the expression
-			// statement has no value.
-			if ( ast::ExprStmt const * exprStmt = stmt.as<ast::ExprStmt>() ) {
-				exprStmt->expr->accept( *visitor );
-				output << ";" << endl;
-				++i;
-				break;
-			}
-		}
-		stmt->accept( *visitor );
-		output << endl;
-		if ( wantSpacing( stmt ) ) output << endl;
-		++i;
-	}
-	--indent;
-	output << indent << "})";
-}
-
-void CodeGenerator::postvisit( ast::ConstructorExpr const * expr ) {
-	assertf( !options.genC, "ConstructorExpr should not reach code generation." );
-	expr->callExpr->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::DeletedExpr const * expr ) {
-	assertf( !options.genC, "DeletedExpr should not reach code generation." );
-	expr->expr->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::DefaultArgExpr const * expr ) {
-	assertf( !options.genC, "DefaultArgExpr should not reach code generation." );
-	expr->expr->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::GenericExpr const * expr ) {
-	assertf( !options.genC, "GenericExpr should not reach code generation." );
-	output << "_Generic(";
-	expr->control->accept( *visitor );
-	output << ", ";
-	unsigned int numAssocs = expr->associations.size();
-	unsigned int i = 0;
-	for ( const ast::GenericExpr::Association & assoc : expr->associations ) {
-		if ( nullptr == assoc.type ) {
-			output << "default: ";
-		} else {
-			output << genType( assoc.type, "", options ) << ": ";
-		}
-		assoc.expr->accept( *visitor );
-		++i;
-		if ( i != numAssocs ) output << ", ";
-	}
-	output << ")";
-}
-
-void CodeGenerator::postvisit( ast::CompoundStmt const * stmt ) {
-	output << "{" << endl;
-
-	++indent;
-	for ( auto kid : stmt->kids ) {
-		output << indent << printLabels( kid->labels );
-		kid->accept( *visitor );
-		output << endl;
-		if ( wantSpacing( kid ) ) output << endl;
-	}
-	--indent;
-
-	output << indent << "}";
-}
-
-void CodeGenerator::postvisit( ast::ExprStmt const * stmt ) {
-	assert( stmt );
-	// Cast the top-level expression to void to reduce gcc warnings.
-	if ( options.genC ) {
-		ast::ptr<ast::Expr> expr = new ast::CastExpr( stmt->location, stmt->expr );
-		expr->accept( *visitor );
-	} else {
-		stmt->expr->accept( *visitor );
-	}
-	output << ";";
-}
-
-void CodeGenerator::postvisit( ast::AsmStmt const * stmt ) {
-	output << "asm ";
-	if ( stmt->isVolatile ) output << "volatile ";
-	if ( !stmt->gotoLabels.empty() ) output << "goto ";
-	output << "( ";
-	if ( stmt->instruction ) stmt->instruction->accept( *visitor );
-	output << " : ";
-	genCommaList( stmt->output );
-	output << " : ";
-	genCommaList( stmt->input );
-	output << " : ";
-	genCommaList( stmt->clobber );
-	if ( !stmt->gotoLabels.empty() ) {
-		output << " : ";
-		auto it = stmt->gotoLabels.begin();
-		while (true) {
-			output << *it++;
-			if ( stmt->gotoLabels.end() == it ) break;
-			output << ", ";
-		}
-	}
-	output << " );";
-}
-
-void CodeGenerator::postvisit( ast::AsmDecl const * decl ) {
-	output << "asm ";
-	ast::AsmStmt const * stmt = decl->stmt;
-	output << "( ";
-	if ( stmt->instruction ) stmt->instruction->accept( *visitor );
-	output << " )";
-}
-
-void CodeGenerator::postvisit( ast::DirectiveDecl const * decl ) {
-	// endl prevents spaces before the directive.
-	output << endl << decl->stmt->directive;
-}
-
-void CodeGenerator::postvisit( ast::DirectiveStmt const * stmt ) {
-	// endl prevents spaces before the directive.
-	output << endl << stmt->directive;
-}
-
-void CodeGenerator::postvisit( ast::IfStmt const * stmt ) {
-	output << "if ( ";
-	stmt->cond->accept( *visitor );
-	output << " ) ";
-
-	stmt->then->accept( *visitor );
-
-	if ( nullptr != stmt->else_ ) {
-		output << " else ";
-		stmt->else_->accept( *visitor );
-	}
-}
-
-void CodeGenerator::postvisit( ast::SwitchStmt const * stmt ) {
-	output << "switch ( ";
-	stmt->cond->accept( *visitor );
-	output << " ) ";
-
-	output << "{";
-	++indent;
-	for ( auto node : stmt->cases ) {
-		node->accept( *visitor );
-	}
-	--indent;
-	output << indent << "}";
-}
-
-void CodeGenerator::postvisit( ast::CaseClause const * clause ) {
-	updateLocation( clause );
-	output << indent;
-	if ( clause->isDefault() ) {
-		output << "default";
-	} else {
-		output << "case ";
-		clause->cond->accept( *visitor );
-	}
-	output << ":" << endl;
-
-	++indent;
-	for ( auto stmt : clause->stmts ) {
-		output << indent << printLabels( stmt->labels ) ;
-		stmt->accept( *visitor );
-		output << endl;
-	}
-	--indent;
-}
-
-void CodeGenerator::postvisit( ast::BranchStmt const * stmt ) {
-	switch ( stmt->kind ) {
-	case ast::BranchStmt::Goto:
-		if ( !stmt->target.empty() ) {
-			output << "goto " << stmt->target;
-		} else if ( nullptr != stmt->computedTarget ) {
-			output << "goto *";
-			stmt->computedTarget->accept( *visitor );
-		}
-		break;
-	case ast::BranchStmt::Break:
-		output << "break";
-		break;
-	case ast::BranchStmt::Continue:
-		output << "continue";
-		break;
-	case ast::BranchStmt::FallThrough:
-	case ast::BranchStmt::FallThroughDefault:
-		assertf( !options.genC, "fallthru should not reach code generation." );
-		output << "fallthru";
-		break;
-	default:
-		assertf( false, "Bad BranchStmt value." );
-	}
-	// Print branch target for labelled break/continue/fallthru in debug mode.
-	if ( !options.genC && stmt->kind != ast::BranchStmt::Goto ) {
-		if ( !stmt->target.empty() ) {
-			output << " " << stmt->target;
-		} else if ( stmt->kind == ast::BranchStmt::FallThrough ) {
-			output << " default";
-		}
-	}
-	output << ";";
-}
-
-void CodeGenerator::postvisit( ast::ReturnStmt const * stmt ) {
-	output << "return ";
-	if ( stmt->expr ) stmt->expr->accept( *visitor );
-	output << ";";
-}
-
-void CodeGenerator::postvisit( ast::ThrowStmt const * stmt ) {
-	assertf( !options.genC, "ThrowStmt should not reach code generation." );
-
-	output << ((stmt->kind == ast::Terminate) ? "throw" : "throwResume");
-	if ( stmt->expr ) {
-		output << " ";
-		stmt->expr->accept( *visitor );
-	}
-	if ( stmt->target ) {
-		output << " _At ";
-		stmt->target->accept( *visitor );
-	}
-	output << ";";
-}
-
-void CodeGenerator::postvisit( ast::CatchClause const * stmt ) {
-	assertf( !options.genC, "CatchClause should not reach code generation." );
-
-	output << ((stmt->kind == ast::Terminate) ? "catch" : "catchResume");
-	output << "( ";
-	stmt->decl->accept( *visitor );
-	if ( stmt->cond ) {
-		output << " ; ";
-		stmt->cond->accept( *visitor );
-	}
-	output << " ) ";
-	stmt->body->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::WaitForStmt const * stmt ) {
-	assertf( !options.genC, "WaitforStmt should not reach code generation." );
-
-	bool first = true;
-	for ( ast::ptr<ast::WaitForClause> const & clause : stmt->clauses ) {
-		if (first) { output << "or "; first = false; }
-		if ( clause->when_cond ) {
-			output << "when(";
-			stmt->timeout_cond->accept( *visitor );
-			output << ") ";
-		}
-		output << "waitfor(";
-		clause->target->accept( *visitor );
-		for ( ast::ptr<ast::Expr> const & expr : clause->target_args ) {
-			output << ",";
-			expr->accept( *visitor );
-		}
-		output << ") ";
-		clause->stmt->accept( *visitor );
-	}
-
-	if ( stmt->timeout_stmt ) {
-		output << "or ";
-		if ( stmt->timeout_cond ) {
-			output << "when(";
-			stmt->timeout_cond->accept( *visitor );
-			output << ") ";
-		}
-		output << "timeout(";
-		stmt->timeout_time->accept( *visitor );
-		output << ") ";
-		stmt->timeout_stmt->accept( *visitor );
-	}
-
-	if ( stmt->else_stmt ) {
-		output << "or ";
-		if ( stmt->else_cond ) {
-			output << "when(";
-			stmt->else_cond->accept( *visitor );
-			output << ")";
-		}
-		output << "else ";
-		stmt->else_stmt->accept( *visitor );
-	}
-}
-
-void CodeGenerator::postvisit( ast::WithStmt const * stmt ) {
-	assertf( !options.genC, "WithStmt should not reach code generation." );
-
-	output << "with ( ";
-	genCommaList( stmt->exprs );
-	output << " ) ";
-	stmt->stmt->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::WhileDoStmt const * stmt ) {
-	if ( stmt->isDoWhile ) {
-		output << "do";
-	} else {
-		output << "while (";
-		stmt->cond->accept( *visitor );
-		output << ")";
-	}
-	output << " ";
-
-	output << CodeGenerator::printLabels( stmt->body->labels );
-	stmt->body->accept( *visitor );
-
-	output << indent;
-
-	if ( stmt->isDoWhile ) {
-		output << " while (";
-		stmt->cond->accept( *visitor );
-		output << ");";
-	}
-}
-
-void CodeGenerator::postvisit( ast::ForStmt const * stmt ) {
-	// Initializer is always hoised so don't generate it.
-	// TODO: Do an assertion check?
-	output << "for (;";
-
-	if ( nullptr != stmt->cond ) {
-		stmt->cond->accept( *visitor );
-	}
-	output << ";";
-
-	if ( nullptr != stmt->inc ) {
-		// cast the top-level expression to void to reduce gcc warnings.
-		ast::Expr * expr = new ast::CastExpr( stmt->inc );
-		expr->accept( *visitor );
-	}
-	output << ") ";
-
-	if ( nullptr != stmt->body ) {
-		output << printLabels( stmt->body->labels );
-		stmt->body->accept( *visitor );
-	}
-}
-
-void CodeGenerator::postvisit( ast::NullStmt const * ) {
-	output << "/* null statement */ ;";
-}
-
-void CodeGenerator::postvisit( ast::DeclStmt const * stmt ) {
-	stmt->decl->accept( *visitor );
-
-	if ( doSemicolon( stmt->decl ) ) output << ";";
-}
-
-void CodeGenerator::postvisit( ast::ImplicitCtorDtorStmt const * stmt ) {
-	assertf( !options.genC, "ImplicitCtorCtorStmt should not reach code generation." );
-	stmt->callStmt->accept( *visitor );
-}
-
-void CodeGenerator::postvisit( ast::MutexStmt const * stmt ) {
-	assertf( !options.genC, "MutexStmt should not reach code generation." );
-	// TODO: But this isn't what a mutex statement looks like.
-	stmt->stmt->accept( *visitor );
-}
-
-std::string genName( ast::DeclWithType const * decl ) {
-	if ( const OperatorInfo * opInfo = operatorLookup( decl->name ) ) {
-		return opInfo->outputName;
-	} else {
-		return decl->name;
-	}
-}
-
-} // namespace CodeGen
Index: c/CodeGen/CodeGeneratorNew.hpp
===================================================================
--- src/CodeGen/CodeGeneratorNew.hpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,192 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// CodeGeneratorNew.hpp --
-//
-// Author           : Andrew Beach
-// Created On       : Tue Oct 17 15:54:00 2023
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Oct 25 17:56:00 2023
-// Update Count     : 0
-//
-
-#pragma once
-
-#include <ostream>               // for ostream, operator<<
-
-#include "AST/Fwd.hpp"
-#include "AST/Pass.hpp"          // for WithGuards, WithShortCircuiting, ...
-#include "CodeGen/Options.h"     // for Options
-
-
-namespace CodeGen {
-
-struct CodeGenerator final :
-		public ast::WithGuards,
-		public ast::WithShortCircuiting,
-		public ast::WithVisitorRef<CodeGenerator> {
-	CodeGenerator( std::ostream & out, Options const & options );
-
-	// Turn off visit_children for all nodes.
-	void previsit( ast::Node const * );
-	void previsit( ast::ParseNode const * );
-
-	// Error for unhandled node types.
-	void postvisit( ast::Node const * );
-
-	// Print type for all expressions.
-	void previsit( ast::Expr const * );
-
-	void postvisit( ast::StructDecl const * );
-	void postvisit( ast::FunctionDecl const * );
-	// Yes, there is one visit that does modify the ast.
-	ast::ObjectDecl const * postvisit( ast::ObjectDecl const * );
-	void postvisit( ast::UnionDecl const * );
-	void postvisit( ast::EnumDecl const * );
-	void postvisit( ast::TraitDecl const * );
-	void postvisit( ast::TypedefDecl const * );
-	void postvisit( ast::TypeDecl const * );
-	void postvisit( ast::StaticAssertDecl const * );
-
-	void postvisit( ast::Designation const * );
-	void postvisit( ast::SingleInit const * );
-	void postvisit( ast::ListInit const * );
-	void postvisit( ast::ConstructorInit const * );
-
-	void postvisit( ast::ApplicationExpr const * );
-	void postvisit( ast::UntypedExpr const * );
-	void postvisit( ast::RangeExpr const * );
-	void postvisit( ast::NameExpr const * );
-	void postvisit( ast::AddressExpr const * );
-	void postvisit( ast::LabelAddressExpr const * );
-	void postvisit( ast::CastExpr const * );
-	void postvisit( ast::KeywordCastExpr const * );
-	void postvisit( ast::VirtualCastExpr const * );
-	void postvisit( ast::UntypedMemberExpr const * );
-	void postvisit( ast::MemberExpr const * );
-	void postvisit( ast::VariableExpr const * );
-	void postvisit( ast::ConstantExpr const * );
-	void postvisit( ast::SizeofExpr const * );
-	void postvisit( ast::AlignofExpr const * );
-	void postvisit( ast::UntypedOffsetofExpr const * );
-	void postvisit( ast::OffsetofExpr const * );
-	void postvisit( ast::OffsetPackExpr const * );
-	void postvisit( ast::LogicalExpr const * );
-	void postvisit( ast::ConditionalExpr const * );
-	void postvisit( ast::CommaExpr const * );
-	void postvisit( ast::CompoundLiteralExpr const * );
-	void postvisit( ast::UniqueExpr const * );
-	void postvisit( ast::TupleAssignExpr const * );
-	void postvisit( ast::UntypedTupleExpr const * );
-	void postvisit( ast::TupleExpr const * );
-	void postvisit( ast::TupleIndexExpr const * );
-	void postvisit( ast::TypeExpr const * );
-	void postvisit( ast::DimensionExpr const * );
-	void postvisit( ast::AsmExpr const * );
-	void postvisit( ast::StmtExpr const * );
-	void postvisit( ast::ConstructorExpr const * );
-	void postvisit( ast::DeletedExpr const * );
-	void postvisit( ast::DefaultArgExpr const * );
-	void postvisit( ast::GenericExpr const * );
-
-	void postvisit( ast::CompoundStmt const * );
-	void postvisit( ast::ExprStmt const * );
-	void postvisit( ast::AsmStmt const * );
-	void postvisit( ast::DirectiveStmt const* );
-	void postvisit( ast::AsmDecl const * );
-	void postvisit( ast::DirectiveDecl const * );
-	void postvisit( ast::IfStmt const * );
-	void postvisit( ast::SwitchStmt const * );
-	void postvisit( ast::CaseClause const * );
-	void postvisit( ast::BranchStmt const * );
-	void postvisit( ast::ReturnStmt const * );
-	void postvisit( ast::ThrowStmt const * );
-	void postvisit( ast::CatchClause const * );
-	void postvisit( ast::WaitForStmt const * );
-	void postvisit( ast::WithStmt const * );
-	void postvisit( ast::WhileDoStmt const * );
-	void postvisit( ast::ForStmt const * );
-	void postvisit( ast::NullStmt const * );
-	void postvisit( ast::DeclStmt const * );
-	void postvisit( ast::ImplicitCtorDtorStmt const * );
-	void postvisit( ast::MutexStmt const * stmt );
-
-private:
-	/// Custom local implementation of endl that updates print location.
-	struct LineEnder {
-		CodeGenerator & cg;
-		LineEnder( CodeGenerator & cg ) : cg( cg ) {}
-		std::ostream & operator()( std::ostream & ) const;
-	};
-	friend std::ostream & operator<<( std::ostream & os, const LineEnder & e ) {
-		return e( os );
-	}
-
-	/// Wrapper class to help print vectors of Labels.
-	struct LabelPrinter {
-		LabelPrinter( CodeGenerator & cg ) : cg( cg ), labels( nullptr ) {}
-		LabelPrinter & operator()( std::vector<ast::Label> const & l );
-		std::ostream & operator()( std::ostream & ) const;
-		CodeGenerator & cg;
-		std::vector<ast::Label> const * labels;
-	};
-	friend std::ostream & operator<<( std::ostream & os, const LabelPrinter & p ) {
-		return p( os );
-	}
-
-	static int tabsize;
-
-	Indenter indent;
-	std::ostream & output;
-	Options options;
-	LabelPrinter printLabels;
-
-	CodeLocation currentLocation;
-	void updateLocation( CodeLocation const & to );
-
-	template<typename Iterator>
-	void genCommaList( Iterator begin, Iterator end ) {
-		if ( begin == end ) return;
-		while (true) {
-			(*begin++)->accept( *visitor );
-			if ( begin == end ) break;
-			output << ", ";
-		}
-	}
-
-public:
-	LineEnder endl;
-	void updateLocation( ast::ParseNode const * to );
-
-	template<typename T>
-	void genCommaList( std::vector<ast::ptr<T>> const & range ) {
-		genCommaList( range.begin(), range.end() );
-	}
-
-	void genAttributes( std::vector<ast::ptr<ast::Attribute>> const & );
-
-private:
-	void asmName( ast::DeclWithType const * decl );
-	void extension( ast::Decl const * );
-	void extension( ast::Expr const * );
-
-	void handleStorageClass( ast::DeclWithType const * decl );
-	void handleAggregate( ast::AggregateDecl const *, const std::string & );
-	void handleTypedef( ast::NamedTypeDecl const * type );
-	std::string mangleName( ast::DeclWithType const * decl );
-};
-
-inline bool doSemicolon( ast::Decl const * decl ) {
-	if ( auto func = dynamic_cast<ast::FunctionDecl const *>( decl ) ) {
-		return !func->stmts;
-	}
-	return true;
-}
-
-/// Returns the C-compatible name of the declaration.
-std::string genName( ast::DeclWithType const * decl );
-
-} // namespace CodeGen
Index: src/CodeGen/GenType.cc
===================================================================
--- src/CodeGen/GenType.cc	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/CodeGen/GenType.cc	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -21,5 +21,5 @@
 #include "AST/Print.hpp"          // for print
 #include "AST/Vector.hpp"         // for vector
-#include "CodeGeneratorNew.hpp"   // for CodeGenerator
+#include "CodeGenerator.hpp"      // for CodeGenerator
 #include "Common/UniqueName.h"    // for UniqueName
 
Index: src/CodeGen/Generate.cc
===================================================================
--- src/CodeGen/Generate.cc	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/CodeGen/Generate.cc	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -19,5 +19,5 @@
 #include <string>                    // for operator<<
 
-#include "CodeGeneratorNew.hpp"      // for CodeGenerator, doSemicolon, ...
+#include "CodeGenerator.hpp"         // for CodeGenerator, doSemicolon, ...
 #include "GenType.h"                 // for genPrettyType
 
Index: src/CodeGen/module.mk
===================================================================
--- src/CodeGen/module.mk	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/CodeGen/module.mk	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -16,6 +16,6 @@
 
 SRC_CODEGEN = \
-	CodeGen/CodeGeneratorNew.cpp \
-	CodeGen/CodeGeneratorNew.hpp \
+	CodeGen/CodeGenerator.cpp \
+	CodeGen/CodeGenerator.hpp \
 	CodeGen/GenType.cc \
 	CodeGen/GenType.h \
Index: src/Concurrency/Keywords.cpp
===================================================================
--- src/Concurrency/Keywords.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/Concurrency/Keywords.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,1601 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Keywords.cpp -- Implement concurrency constructs from their keywords.
+//
+// Author           : Andrew Beach
+// Created On       : Tue Nov 16  9:53:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Mar 11 10:40:00 2022
+// Update Count     : 2
+//
+
+#include "Concurrency/Keywords.h"
+
+#include <iostream>
+
+#include "AST/Copy.hpp"
+#include "AST/Decl.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Inspect.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/DeclReplacer.hpp"
+#include "AST/TranslationUnit.hpp"
+#include "CodeGen/OperatorTable.h"
+#include "Common/Examine.h"
+#include "Common/utility.h"
+#include "Common/UniqueName.h"
+#include "ControlStruct/LabelGenerator.hpp"
+#include "InitTweak/InitTweak.h"
+#include "Virtual/Tables.h"
+
+namespace Concurrency {
+
+namespace {
+
+// --------------------------------------------------------------------------
+// Loose Helper Functions:
+
+/// Detect threads constructed with the keyword thread.
+bool isThread( const ast::DeclWithType * decl ) {
+	auto baseType = decl->get_type()->stripDeclarator();
+	auto instType = dynamic_cast<const ast::StructInstType *>( baseType );
+	if ( nullptr == instType ) { return false; }
+	return instType->base->is_thread();
+}
+
+/// Get the virtual type id if given a type name.
+std::string typeIdType( std::string const & exception_name ) {
+	return exception_name.empty() ? std::string()
+		: Virtual::typeIdType( exception_name );
+}
+
+/// Get the vtable type name if given a type name.
+std::string vtableTypeName( std::string const & exception_name ) {
+	return exception_name.empty() ? std::string()
+		: Virtual::vtableTypeName( exception_name );
+}
+
+static ast::Type * mutate_under_references( ast::ptr<ast::Type>& type ) {
+	ast::Type * mutType = type.get_and_mutate();
+	for ( ast::ReferenceType * mutRef
+		; (mutRef = dynamic_cast<ast::ReferenceType *>( mutType ))
+		; mutType = mutRef->base.get_and_mutate() );
+	return mutType;
+}
+
+// Describe that it adds the generic parameters and the uses of the generic
+// parameters on the function and first "this" argument.
+ast::FunctionDecl * fixupGenerics(
+		const ast::FunctionDecl * func, const ast::StructDecl * decl ) {
+	const CodeLocation & location = decl->location;
+	// We have to update both the declaration
+	auto mutFunc = ast::mutate( func );
+	auto mutType = mutFunc->type.get_and_mutate();
+
+	if ( decl->params.empty() ) {
+		return mutFunc;
+	}
+
+	assert( 0 != mutFunc->params.size() );
+	assert( 0 != mutType->params.size() );
+
+	// Add the "forall" clause information.
+	for ( const ast::ptr<ast::TypeDecl> & typeParam : decl->params ) {
+		auto typeDecl = ast::deepCopy( typeParam );
+		mutFunc->type_params.push_back( typeDecl );
+		mutType->forall.push_back( new ast::TypeInstType( typeDecl ) );
+		for ( auto & assertion : typeDecl->assertions ) {
+			mutFunc->assertions.push_back( assertion );
+			mutType->assertions.emplace_back(
+				new ast::VariableExpr( location, assertion ) );
+		}
+		typeDecl->assertions.clear();
+	}
+
+	// Even chain_mutate is not powerful enough for this:
+	ast::ptr<ast::Type>& paramType = strict_dynamic_cast<ast::ObjectDecl *>(
+		mutFunc->params[0].get_and_mutate() )->type;
+	auto paramTypeInst = strict_dynamic_cast<ast::StructInstType *>(
+		mutate_under_references( paramType ) );
+	auto typeParamInst = strict_dynamic_cast<ast::StructInstType *>(
+		mutate_under_references( mutType->params[0] ) );
+
+	for ( const ast::ptr<ast::TypeDecl> & typeDecl : mutFunc->type_params ) {
+		paramTypeInst->params.push_back(
+			new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
+		typeParamInst->params.push_back(
+			new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
+	}
+
+	return mutFunc;
+}
+
+// --------------------------------------------------------------------------
+struct ConcurrentSueKeyword : public ast::WithDeclsToAdd<> {
+	ConcurrentSueKeyword(
+		std::string&& type_name, std::string&& field_name,
+		std::string&& getter_name, std::string&& context_error,
+		std::string&& exception_name,
+		bool needs_main, ast::AggregateDecl::Aggregate cast_target
+	) :
+		type_name( type_name ), field_name( field_name ),
+		getter_name( getter_name ), context_error( context_error ),
+		exception_name( exception_name ),
+		typeid_name( typeIdType( exception_name ) ),
+		vtable_name( vtableTypeName( exception_name ) ),
+		needs_main( needs_main ), cast_target( cast_target )
+	{}
+
+	virtual ~ConcurrentSueKeyword() {}
+
+	const ast::Decl * postvisit( const ast::StructDecl * decl );
+	const ast::DeclWithType * postvisit( const ast::FunctionDecl * decl );
+	const ast::Expr * postvisit( const ast::KeywordCastExpr * expr );
+
+	struct StructAndField {
+		const ast::StructDecl * decl;
+		const ast::ObjectDecl * field;
+	};
+
+	const ast::StructDecl * handleStruct( const ast::StructDecl * );
+	void handleMain( const ast::FunctionDecl *, const ast::StructInstType * );
+	void addTypeId( const ast::StructDecl * );
+	void addVtableForward( const ast::StructDecl * );
+	const ast::FunctionDecl * forwardDeclare( const ast::StructDecl * );
+	StructAndField addField( const ast::StructDecl * );
+	void addGetRoutines( const ast::ObjectDecl *, const ast::FunctionDecl * );
+	void addLockUnlockRoutines( const ast::StructDecl * );
+
+private:
+	const std::string type_name;
+	const std::string field_name;
+	const std::string getter_name;
+	const std::string context_error;
+	const std::string exception_name;
+	const std::string typeid_name;
+	const std::string vtable_name;
+	const bool needs_main;
+	const ast::AggregateDecl::Aggregate cast_target;
+
+	const ast::StructDecl   * type_decl = nullptr;
+	const ast::FunctionDecl * dtor_decl = nullptr;
+	const ast::StructDecl * except_decl = nullptr;
+	const ast::StructDecl * typeid_decl = nullptr;
+	const ast::StructDecl * vtable_decl = nullptr;
+
+};
+
+// Handles thread type declarations:
+//
+// thread Mythread {                         struct MyThread {
+//  int data;                                  int data;
+//  a_struct_t more_data;                      a_struct_t more_data;
+//                                =>             thread$ __thrd_d;
+// };                                        };
+//                                           static inline thread$ * get_thread( MyThread * this ) { return &this->__thrd_d; }
+//
+struct ThreadKeyword final : public ConcurrentSueKeyword {
+	ThreadKeyword() : ConcurrentSueKeyword(
+		"thread$",
+		"__thrd",
+		"get_thread",
+		"thread keyword requires threads to be in scope, add #include <thread.hfa>\n",
+		"ThreadCancelled",
+		true,
+		ast::AggregateDecl::Thread )
+	{}
+
+	virtual ~ThreadKeyword() {}
+};
+
+// Handles coroutine type declarations:
+//
+// coroutine MyCoroutine {                   struct MyCoroutine {
+//  int data;                                  int data;
+//  a_struct_t more_data;                      a_struct_t more_data;
+//                                =>             coroutine$ __cor_d;
+// };                                        };
+//                                           static inline coroutine$ * get_coroutine( MyCoroutine * this ) { return &this->__cor_d; }
+//
+struct CoroutineKeyword final : public ConcurrentSueKeyword {
+	CoroutineKeyword() : ConcurrentSueKeyword(
+		"coroutine$",
+		"__cor",
+		"get_coroutine",
+		"coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n",
+		"CoroutineCancelled",
+		true,
+		ast::AggregateDecl::Coroutine )
+	{}
+
+	virtual ~CoroutineKeyword() {}
+};
+
+// Handles monitor type declarations:
+//
+// monitor MyMonitor {                       struct MyMonitor {
+//  int data;                                  int data;
+//  a_struct_t more_data;                      a_struct_t more_data;
+//                                =>             monitor$ __mon_d;
+// };                                        };
+//                                           static inline monitor$ * get_coroutine( MyMonitor * this ) {
+//                                               return &this->__cor_d;
+//                                           }
+//                                           void lock(MyMonitor & this) {
+//                                               lock(get_monitor(this));
+//                                           }
+//                                           void unlock(MyMonitor & this) {
+//                                               unlock(get_monitor(this));
+//                                           }
+//
+struct MonitorKeyword final : public ConcurrentSueKeyword {
+	MonitorKeyword() : ConcurrentSueKeyword(
+		"monitor$",
+		"__mon",
+		"get_monitor",
+		"monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n",
+		"",
+		false,
+		ast::AggregateDecl::Monitor )
+	{}
+
+	virtual ~MonitorKeyword() {}
+};
+
+// Handles generator type declarations:
+//
+// generator MyGenerator {                   struct MyGenerator {
+//  int data;                                  int data;
+//  a_struct_t more_data;                      a_struct_t more_data;
+//                                =>             int __generator_state;
+// };                                        };
+//
+struct GeneratorKeyword final : public ConcurrentSueKeyword {
+	GeneratorKeyword() : ConcurrentSueKeyword(
+		"generator$",
+		"__generator_state",
+		"get_generator",
+		"Unable to find builtin type generator$\n",
+		"",
+		true,
+		ast::AggregateDecl::Generator )
+	{}
+
+	virtual ~GeneratorKeyword() {}
+};
+
+const ast::Decl * ConcurrentSueKeyword::postvisit(
+		const ast::StructDecl * decl ) {
+	if ( !decl->body ) {
+		return decl;
+	} else if ( cast_target == decl->kind ) {
+		return handleStruct( decl );
+	} else if ( type_name == decl->name ) {
+		assert( !type_decl );
+		type_decl = decl;
+	} else if ( exception_name == decl->name ) {
+		assert( !except_decl );
+		except_decl = decl;
+	} else if ( typeid_name == decl->name ) {
+		assert( !typeid_decl );
+		typeid_decl = decl;
+	} else if ( vtable_name == decl->name ) {
+		assert( !vtable_decl );
+		vtable_decl = decl;
+	}
+	return decl;
+}
+
+// Try to get the full definition, but raise an error on conflicts.
+const ast::FunctionDecl * getDefinition(
+		const ast::FunctionDecl * old_decl,
+		const ast::FunctionDecl * new_decl ) {
+	if ( !new_decl->stmts ) {
+		return old_decl;
+	} else if ( !old_decl->stmts ) {
+		return new_decl;
+	} else {
+		assert( !old_decl->stmts || !new_decl->stmts );
+		return nullptr;
+	}
+}
+
+const ast::DeclWithType * ConcurrentSueKeyword::postvisit(
+		const ast::FunctionDecl * decl ) {
+	if ( type_decl && isDestructorFor( decl, type_decl ) ) {
+		// Check for forward declarations, try to get the full definition.
+		dtor_decl = (dtor_decl) ? getDefinition( dtor_decl, decl ) : decl;
+	} else if ( !vtable_name.empty() && decl->has_body() ) {
+		if (const ast::DeclWithType * param = isMainFor( decl, cast_target )) {
+			if ( !vtable_decl ) {
+				SemanticError( decl, context_error );
+			}
+			// Should be safe because of isMainFor.
+			const ast::StructInstType * struct_type =
+				static_cast<const ast::StructInstType *>(
+					static_cast<const ast::ReferenceType *>(
+						param->get_type() )->base.get() );
+
+			handleMain( decl, struct_type );
+		}
+	}
+	return decl;
+}
+
+const ast::Expr * ConcurrentSueKeyword::postvisit(
+		const ast::KeywordCastExpr * expr ) {
+	if ( cast_target == expr->target ) {
+		// Convert `(thread &)ex` to `(thread$ &)*get_thread(ex)`, etc.
+		if ( !type_decl || !dtor_decl ) {
+			SemanticError( expr, context_error );
+		}
+		assert( nullptr == expr->result );
+		auto cast = ast::mutate( expr );
+		cast->result = new ast::ReferenceType( new ast::StructInstType( type_decl ) );
+		cast->concrete_target.field  = field_name;
+		cast->concrete_target.getter = getter_name;
+		return cast;
+	}
+	return expr;
+}
+
+const ast::StructDecl * ConcurrentSueKeyword::handleStruct(
+		const ast::StructDecl * decl ) {
+	assert( decl->body );
+
+	if ( !type_decl || !dtor_decl ) {
+		SemanticError( decl, context_error );
+	}
+
+	if ( !exception_name.empty() ) {
+		if( !typeid_decl || !vtable_decl ) {
+			SemanticError( decl, context_error );
+		}
+		addTypeId( decl );
+		addVtableForward( decl );
+	}
+
+	const ast::FunctionDecl * func = forwardDeclare( decl );
+	StructAndField addFieldRet = addField( decl );
+	decl = addFieldRet.decl;
+	const ast::ObjectDecl * field = addFieldRet.field;
+
+	addGetRoutines( field, func );
+	// Add routines to monitors for use by mutex stmt.
+	if ( ast::AggregateDecl::Monitor == cast_target ) {
+		addLockUnlockRoutines( decl );
+	}
+
+	return decl;
+}
+
+void ConcurrentSueKeyword::handleMain(
+		const ast::FunctionDecl * decl, const ast::StructInstType * type ) {
+	assert( vtable_decl );
+	assert( except_decl );
+
+	const CodeLocation & location = decl->location;
+
+	std::vector<ast::ptr<ast::Expr>> poly_args = {
+		new ast::TypeExpr( location, type ),
+	};
+	ast::ObjectDecl * vtable_object = Virtual::makeVtableInstance(
+		location,
+		"_default_vtable_object_declaration",
+		new ast::StructInstType( vtable_decl, copy( poly_args ) ),
+		type,
+		nullptr
+	);
+	declsToAddAfter.push_back( vtable_object );
+	declsToAddAfter.push_back(
+		new ast::ObjectDecl(
+			location,
+			Virtual::concurrentDefaultVTableName(),
+			new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
+			new ast::SingleInit( location,
+				new ast::VariableExpr( location, vtable_object ) )
+		)
+	);
+	declsToAddAfter.push_back( Virtual::makeGetExceptionFunction(
+		location,
+		vtable_object,
+		new ast::StructInstType( except_decl, copy( poly_args ) )
+	) );
+}
+
+void ConcurrentSueKeyword::addTypeId( const ast::StructDecl * decl ) {
+	assert( typeid_decl );
+	const CodeLocation & location = decl->location;
+
+	ast::StructInstType * typeid_type =
+		new ast::StructInstType( typeid_decl, ast::CV::Const );
+	typeid_type->params.push_back(
+		new ast::TypeExpr( location, new ast::StructInstType( decl ) ) );
+	declsToAddBefore.push_back(
+		Virtual::makeTypeIdInstance( location, typeid_type ) );
+	// If the typeid_type is going to be kept, the other reference will have
+	// been made by now, but we also get to avoid extra mutates.
+	ast::ptr<ast::StructInstType> typeid_cleanup = typeid_type;
+}
+
+void ConcurrentSueKeyword::addVtableForward( const ast::StructDecl * decl ) {
+	assert( vtable_decl );
+	const CodeLocation& location = decl->location;
+
+	std::vector<ast::ptr<ast::Expr>> poly_args = {
+		new ast::TypeExpr( location, new ast::StructInstType( decl ) ),
+	};
+	declsToAddBefore.push_back( Virtual::makeGetExceptionForward(
+		location,
+		new ast::StructInstType( vtable_decl, copy( poly_args ) ),
+		new ast::StructInstType( except_decl, copy( poly_args ) )
+	) );
+	ast::ObjectDecl * vtable_object = Virtual::makeVtableForward(
+		location,
+		"_default_vtable_object_declaration",
+		new ast::StructInstType( vtable_decl, std::move( poly_args ) )
+	);
+	declsToAddBefore.push_back( vtable_object );
+	declsToAddBefore.push_back(
+		new ast::ObjectDecl(
+			location,
+			Virtual::concurrentDefaultVTableName(),
+			new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
+			nullptr,
+			ast::Storage::Extern,
+			ast::Linkage::Cforall
+		)
+	);
+}
+
+const ast::FunctionDecl * ConcurrentSueKeyword::forwardDeclare(
+		const ast::StructDecl * decl ) {
+	const CodeLocation & location = decl->location;
+
+	ast::StructDecl * forward = ast::deepCopy( decl );
+	{
+		// If removing members makes ref-count go to zero, do not free.
+		ast::ptr<ast::StructDecl> forward_ptr = forward;
+		forward->body = false;
+		forward->members.clear();
+		forward_ptr.release();
+	}
+
+	ast::ObjectDecl * this_decl = new ast::ObjectDecl(
+		location,
+		"this",
+		new ast::ReferenceType( new ast::StructInstType( decl ) )
+	);
+
+	ast::ObjectDecl * ret_decl = new ast::ObjectDecl(
+		location,
+		"ret",
+		new ast::PointerType( new ast::StructInstType( type_decl ) )
+	);
+
+	ast::FunctionDecl * get_decl = new ast::FunctionDecl(
+		location,
+		getter_name,
+		{ this_decl }, // params
+		{ ret_decl }, // returns
+		nullptr, // stmts
+		ast::Storage::Static,
+		ast::Linkage::Cforall,
+		{ new ast::Attribute( "const" ) },
+		ast::Function::Inline
+	);
+	get_decl = fixupGenerics( get_decl, decl );
+
+	ast::FunctionDecl * main_decl = nullptr;
+	if ( needs_main ) {
+		// `this_decl` is copied here because the original was used above.
+		main_decl = new ast::FunctionDecl(
+			location,
+			"main",
+			{ ast::deepCopy( this_decl ) },
+			{},
+			nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		);
+		main_decl = fixupGenerics( main_decl, decl );
+	}
+
+	declsToAddBefore.push_back( forward );
+	if ( needs_main ) declsToAddBefore.push_back( main_decl );
+	declsToAddBefore.push_back( get_decl );
+
+	return get_decl;
+}
+
+ConcurrentSueKeyword::StructAndField ConcurrentSueKeyword::addField(
+		const ast::StructDecl * decl ) {
+	const CodeLocation & location = decl->location;
+
+	ast::ObjectDecl * field = new ast::ObjectDecl(
+		location,
+		field_name,
+		new ast::StructInstType( type_decl )
+	);
+
+	auto mutDecl = ast::mutate( decl );
+	mutDecl->members.push_back( field );
+
+	return {mutDecl, field};
+}
+
+void ConcurrentSueKeyword::addGetRoutines(
+		const ast::ObjectDecl * field, const ast::FunctionDecl * forward ) {
+	// Clone the signature and then build the body.
+	ast::FunctionDecl * decl = ast::deepCopy( forward );
+
+	// Say it is generated at the "same" places as the forward declaration.
+	const CodeLocation & location = decl->location;
+
+	const ast::DeclWithType * param = decl->params.front();
+	ast::Stmt * stmt = new ast::ReturnStmt( location,
+		new ast::AddressExpr( location,
+			new ast::MemberExpr( location,
+				field,
+				new ast::CastExpr( location,
+					new ast::VariableExpr( location, param ),
+					ast::deepCopy( param->get_type()->stripReferences() ),
+					ast::ExplicitCast
+				)
+			)
+		)
+	);
+
+	decl->stmts = new ast::CompoundStmt( location, { stmt } );
+	declsToAddAfter.push_back( decl );
+}
+
+void ConcurrentSueKeyword::addLockUnlockRoutines(
+		const ast::StructDecl * decl ) {
+	// This should only be used on monitors.
+	assert( ast::AggregateDecl::Monitor == cast_target );
+
+	const CodeLocation & location = decl->location;
+
+	// The parameter for both routines.
+	ast::ObjectDecl * this_decl = new ast::ObjectDecl(
+		location,
+		"this",
+		new ast::ReferenceType( new ast::StructInstType( decl ) )
+	);
+
+	ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
+		location,
+		"lock",
+		{
+			// Copy the declaration of this.
+			ast::deepCopy( this_decl ),
+		},
+		{ /* returns */ },
+		nullptr,
+		ast::Storage::Static,
+		ast::Linkage::Cforall,
+		{ /* attributes */ },
+		ast::Function::Inline
+	);
+	lock_decl = fixupGenerics( lock_decl, decl );
+
+	lock_decl->stmts = new ast::CompoundStmt( location, {
+		new ast::ExprStmt( location,
+			new ast::UntypedExpr( location,
+				new ast::NameExpr( location, "lock" ),
+				{
+					new ast::UntypedExpr( location,
+						new ast::NameExpr( location, "get_monitor" ),
+						{ new ast::VariableExpr( location,
+							InitTweak::getParamThis( lock_decl ) ) }
+					)
+				}
+			)
+		)
+	} );
+
+	ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
+		location,
+		"unlock",
+		{
+			// Last use, consume the declaration of this.
+			this_decl,
+		},
+		{ /* returns */ },
+		nullptr,
+		ast::Storage::Static,
+		ast::Linkage::Cforall,
+		{ /* attributes */ },
+		ast::Function::Inline
+	);
+	unlock_decl = fixupGenerics( unlock_decl, decl );
+
+	unlock_decl->stmts = new ast::CompoundStmt( location, {
+		new ast::ExprStmt( location,
+			new ast::UntypedExpr( location,
+				new ast::NameExpr( location, "unlock" ),
+				{
+					new ast::UntypedExpr( location,
+						new ast::NameExpr( location, "get_monitor" ),
+						{ new ast::VariableExpr( location,
+							InitTweak::getParamThis( unlock_decl ) ) }
+					)
+				}
+			)
+		)
+	} );
+
+	declsToAddAfter.push_back( lock_decl );
+	declsToAddAfter.push_back( unlock_decl );
+}
+
+
+// --------------------------------------------------------------------------
+struct SuspendKeyword final :
+		public ast::WithStmtsToAdd<>, public ast::WithGuards {
+	SuspendKeyword() = default;
+	virtual ~SuspendKeyword() = default;
+
+	void previsit( const ast::FunctionDecl * );
+	const ast::DeclWithType * postvisit( const ast::FunctionDecl * );
+	const ast::Stmt * postvisit( const ast::SuspendStmt * );
+
+private:
+	bool is_real_suspend( const ast::FunctionDecl * );
+
+	const ast::Stmt * make_generator_suspend( const ast::SuspendStmt * );
+	const ast::Stmt * make_coroutine_suspend( const ast::SuspendStmt * );
+
+	struct LabelPair {
+		ast::Label obj;
+		int idx;
+	};
+
+	LabelPair make_label(const ast::Stmt * stmt ) {
+		labels.push_back( ControlStruct::newLabel( "generator", stmt ) );
+		return { labels.back(), int(labels.size()) };
+	}
+
+	const ast::DeclWithType * in_generator = nullptr;
+	const ast::FunctionDecl * decl_suspend = nullptr;
+	std::vector<ast::Label> labels;
+};
+
+void SuspendKeyword::previsit( const ast::FunctionDecl * decl ) {
+	GuardValue( in_generator ); in_generator = nullptr;
+
+	// If it is the real suspend, grab it if we don't have one already.
+	if ( is_real_suspend( decl ) ) {
+		decl_suspend = decl_suspend ? decl_suspend : decl;
+		return;
+	}
+
+	// Otherwise check if this is a generator main and, if so, handle it.
+	auto param = isMainFor( decl, ast::AggregateDecl::Generator );
+	if ( !param ) return;
+
+	if ( 0 != decl->returns.size() ) {
+		SemanticError( decl->location, "Generator main must return void" );
+	}
+
+	in_generator = param;
+	GuardValue( labels ); labels.clear();
+}
+
+const ast::DeclWithType * SuspendKeyword::postvisit(
+		const ast::FunctionDecl * decl ) {
+	// Only modify a full definition of a generator with states.
+	if ( !decl->stmts || !in_generator || labels.empty() ) return decl;
+
+	const CodeLocation & location = decl->location;
+
+	// Create a new function body:
+	// static void * __generator_labels[] = {&&s0, &&s1, ...};
+	// void * __generator_label = __generator_labels[GEN.__generator_state];
+	// goto * __generator_label;
+	// s0: ;
+	// OLD_BODY
+
+	// This is the null statement inserted right before the body.
+	ast::NullStmt * noop = new ast::NullStmt( location );
+	noop->labels.push_back( ControlStruct::newLabel( "generator", noop ) );
+	const ast::Label & first_label = noop->labels.back();
+
+	// Add each label to the init, starting with the first label.
+	std::vector<ast::ptr<ast::Init>> inits = {
+		new ast::SingleInit( location,
+			new ast::LabelAddressExpr( location, copy( first_label ) ) ) };
+	// Then go through all the stored labels, and clear the store.
+	for ( auto && label : labels ) {
+		inits.push_back( new ast::SingleInit( label.location,
+			new ast::LabelAddressExpr( label.location, std::move( label )
+			) ) );
+	}
+	labels.clear();
+	// Then construct the initializer itself.
+	auto init = new ast::ListInit( location, std::move( inits ) );
+
+	ast::ObjectDecl * generatorLabels = new ast::ObjectDecl(
+		location,
+		"__generator_labels",
+		new ast::ArrayType(
+			new ast::PointerType( new ast::VoidType() ),
+			nullptr,
+			ast::FixedLen,
+			ast::DynamicDim
+		),
+		init,
+		ast::Storage::Classes(),
+		ast::Linkage::AutoGen
+	);
+
+	ast::ObjectDecl * generatorLabel = new ast::ObjectDecl(
+		location,
+		"__generator_label",
+		new ast::PointerType( new ast::VoidType() ),
+		new ast::SingleInit( location,
+			new ast::UntypedExpr( location,
+				new ast::NameExpr( location, "?[?]" ),
+				{
+					// TODO: Could be a variable expr.
+					new ast::NameExpr( location, "__generator_labels" ),
+					new ast::UntypedMemberExpr( location,
+						new ast::NameExpr( location, "__generator_state" ),
+						new ast::VariableExpr( location, in_generator )
+					)
+				}
+			)
+		),
+		ast::Storage::Classes(),
+		ast::Linkage::AutoGen
+	);
+
+	ast::BranchStmt * theGoTo = new ast::BranchStmt(
+		location, new ast::VariableExpr( location, generatorLabel )
+	);
+
+	// The noop goes here in order.
+
+	ast::CompoundStmt * body = new ast::CompoundStmt( location, {
+		{ new ast::DeclStmt( location, generatorLabels ) },
+		{ new ast::DeclStmt( location, generatorLabel ) },
+		{ theGoTo },
+		{ noop },
+		{ decl->stmts },
+	} );
+
+	auto mutDecl = ast::mutate( decl );
+	mutDecl->stmts = body;
+	return mutDecl;
+}
+
+const ast::Stmt * SuspendKeyword::postvisit( const ast::SuspendStmt * stmt ) {
+	switch ( stmt->kind ) {
+	case ast::SuspendStmt::None:
+		// Use the context to determain the implicit target.
+		if ( in_generator ) {
+			return make_generator_suspend( stmt );
+		} else {
+			return make_coroutine_suspend( stmt );
+		}
+	case ast::SuspendStmt::Coroutine:
+		return make_coroutine_suspend( stmt );
+	case ast::SuspendStmt::Generator:
+		// Generator suspends must be directly in a generator.
+		if ( !in_generator ) SemanticError( stmt->location, "'suspend generator' must be used inside main of generator type." );
+		return make_generator_suspend( stmt );
+	}
+	assert( false );
+	return stmt;
+}
+
+/// Find the real/official suspend declaration.
+bool SuspendKeyword::is_real_suspend( const ast::FunctionDecl * decl ) {
+	return ( !decl->linkage.is_mangled
+		&& 0 == decl->params.size()
+		&& 0 == decl->returns.size()
+		&& "__cfactx_suspend" == decl->name );
+}
+
+const ast::Stmt * SuspendKeyword::make_generator_suspend(
+		const ast::SuspendStmt * stmt ) {
+	assert( in_generator );
+	// Target code is:
+	//   GEN.__generator_state = X;
+	//   THEN
+	//   return;
+	//   __gen_X:;
+
+	const CodeLocation & location = stmt->location;
+
+	LabelPair label = make_label( stmt );
+
+	// This is the context saving statement.
+	stmtsToAddBefore.push_back( new ast::ExprStmt( location,
+		new ast::UntypedExpr( location,
+			new ast::NameExpr( location, "?=?" ),
+			{
+				new ast::UntypedMemberExpr( location,
+					new ast::NameExpr( location, "__generator_state" ),
+					new ast::VariableExpr( location, in_generator )
+				),
+				ast::ConstantExpr::from_int( location, label.idx ),
+			}
+		)
+	) );
+
+	// The THEN component is conditional (return is not).
+	if ( stmt->then ) {
+		stmtsToAddBefore.push_back( stmt->then.get() );
+	}
+	stmtsToAddBefore.push_back( new ast::ReturnStmt( location, nullptr ) );
+
+	// The null statement replaces the old suspend statement.
+	return new ast::NullStmt( location, { label.obj } );
+}
+
+const ast::Stmt * SuspendKeyword::make_coroutine_suspend(
+		const ast::SuspendStmt * stmt ) {
+	// The only thing we need from the old statement is the location.
+	const CodeLocation & location = stmt->location;
+
+	if ( !decl_suspend ) {
+		SemanticError( location, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>\n" );
+	}
+	if ( stmt->then ) {
+		SemanticError( location, "Compound statement following coroutines is not implemented." );
+	}
+
+	return new ast::ExprStmt( location,
+		new ast::UntypedExpr( location,
+			ast::VariableExpr::functionPointer( location, decl_suspend ) )
+	);
+}
+
+// --------------------------------------------------------------------------
+struct MutexKeyword final : public ast::WithDeclsToAdd<> {
+	const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
+	void postvisit( const ast::StructDecl * decl );
+	const ast::Stmt * postvisit( const ast::MutexStmt * stmt );
+
+	static std::vector<const ast::DeclWithType *> findMutexArgs(
+			const ast::FunctionDecl * decl, bool & first );
+	static void validate( const ast::DeclWithType * decl );
+
+	ast::CompoundStmt * addDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
+	ast::CompoundStmt * addStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
+	ast::CompoundStmt * addStatements( const ast::CompoundStmt * body, const std::vector<ast::ptr<ast::Expr>> & args );
+	ast::CompoundStmt * addThreadDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt * body, const std::vector<const ast::DeclWithType *> & args );
+	ast::ExprStmt * genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param);
+	ast::IfStmt * genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam );
+private:
+	const ast::StructDecl * monitor_decl = nullptr;
+	const ast::StructDecl * guard_decl = nullptr;
+	const ast::StructDecl * dtor_guard_decl = nullptr;
+	const ast::StructDecl * thread_guard_decl = nullptr;
+	const ast::StructDecl * lock_guard_decl = nullptr;
+
+	static ast::ptr<ast::Type> generic_func;
+
+	UniqueName mutex_func_namer = UniqueName("__lock_unlock_curr");
+};
+
+const ast::FunctionDecl * MutexKeyword::postvisit(
+		const ast::FunctionDecl * decl ) {
+	bool is_first_argument_mutex = false;
+	const std::vector<const ast::DeclWithType *> mutexArgs =
+		findMutexArgs( decl, is_first_argument_mutex );
+	bool const isDtor = CodeGen::isDestructor( decl->name );
+
+	// Does this function have any mutex arguments that connect to monitors?
+	if ( mutexArgs.empty() ) {
+		// If this is the destructor for a monitor it must be mutex.
+		if ( isDtor ) {
+			// This reflects MutexKeyword::validate, but no error messages.
+			const ast::Type * type = decl->type->params.front();
+
+			// If it's a copy, it's not a mutex.
+			const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
+			if ( nullptr == refType ) {
+				return decl;
+			}
+
+			// If it is not pointing directly to a type, it's not a mutex.
+			auto base = refType->base;
+			if ( base.as<ast::ReferenceType>() ) return decl;
+			if ( base.as<ast::PointerType>() ) return decl;
+
+			// If it is not a struct, it's not a mutex.
+			auto baseStruct = base.as<ast::StructInstType>();
+			if ( nullptr == baseStruct ) return decl;
+
+			// If it is a monitor, then it is a monitor.
+			if( baseStruct->base->is_monitor() || baseStruct->base->is_thread() ) {
+				SemanticError( decl, "destructors for structures declared as \"monitor\" must use mutex parameters\n" );
+			}
+		}
+		return decl;
+	}
+
+	// Monitors can't be constructed with mutual exclusion.
+	if ( CodeGen::isConstructor( decl->name ) && is_first_argument_mutex ) {
+		SemanticError( decl, "constructors cannot have mutex parameters\n" );
+	}
+
+	// It makes no sense to have multiple mutex parameters for the destructor.
+	if ( isDtor && mutexArgs.size() != 1 ) {
+		SemanticError( decl, "destructors can only have 1 mutex argument\n" );
+	}
+
+	// Make sure all the mutex arguments are monitors.
+	for ( auto arg : mutexArgs ) {
+		validate( arg );
+	}
+
+	// Check to see if the body needs to be instrument the body.
+	const ast::CompoundStmt * body = decl->stmts;
+	if ( !body ) return decl;
+
+	// Check to if the required headers have been seen.
+	if ( !monitor_decl || !guard_decl || !dtor_guard_decl ) {
+		SemanticError( decl, "mutex keyword requires monitors to be in scope, add #include <monitor.hfa>\n" );
+	}
+
+	// Instrument the body.
+	ast::CompoundStmt * newBody = nullptr;
+	if ( isDtor && isThread( mutexArgs.front() ) ) {
+		if ( !thread_guard_decl ) {
+			SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>\n" );
+		}
+		newBody = addThreadDtorStatements( decl, body, mutexArgs );
+	} else if ( isDtor ) {
+		newBody = addDtorStatements( decl, body, mutexArgs );
+	} else {
+		newBody = addStatements( decl, body, mutexArgs );
+	}
+	assert( newBody );
+	return ast::mutate_field( decl, &ast::FunctionDecl::stmts, newBody );
+}
+
+void MutexKeyword::postvisit( const ast::StructDecl * decl ) {
+	if ( !decl->body ) {
+		return;
+	} else if ( decl->name == "monitor$" ) {
+		assert( !monitor_decl );
+		monitor_decl = decl;
+	} else if ( decl->name == "monitor_guard_t" ) {
+		assert( !guard_decl );
+		guard_decl = decl;
+	} else if ( decl->name == "monitor_dtor_guard_t" ) {
+		assert( !dtor_guard_decl );
+		dtor_guard_decl = decl;
+	} else if ( decl->name == "thread_dtor_guard_t" ) {
+		assert( !thread_guard_decl );
+		thread_guard_decl = decl;
+	} else if ( decl->name == "__mutex_stmt_lock_guard" ) {
+		assert( !lock_guard_decl );
+		lock_guard_decl = decl;
+	}
+}
+
+const ast::Stmt * MutexKeyword::postvisit( const ast::MutexStmt * stmt ) {
+	if ( !lock_guard_decl ) {
+		SemanticError( stmt->location, "mutex stmt requires a header, add #include <mutex_stmt.hfa>\n" );
+	}
+	ast::CompoundStmt * body =
+			new ast::CompoundStmt( stmt->location, { stmt->stmt } );
+	
+	return addStatements( body, stmt->mutexObjs );;
+}
+
+std::vector<const ast::DeclWithType *> MutexKeyword::findMutexArgs(
+		const ast::FunctionDecl * decl, bool & first ) {
+	std::vector<const ast::DeclWithType *> mutexArgs;
+
+	bool once = true;
+	for ( auto arg : decl->params ) {
+		const ast::Type * type = arg->get_type();
+		if ( type->is_mutex() ) {
+			if ( once ) first = true;
+			mutexArgs.push_back( arg.get() );
+		}
+		once = false;
+	}
+	return mutexArgs;
+}
+
+void MutexKeyword::validate( const ast::DeclWithType * decl ) {
+	const ast::Type * type = decl->get_type();
+
+	// If it's a copy, it's not a mutex.
+	const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
+	if ( nullptr == refType ) {
+		SemanticError( decl, "Mutex argument must be of reference type " );
+	}
+
+	// If it is not pointing directly to a type, it's not a mutex.
+	auto base = refType->base;
+	if ( base.as<ast::ReferenceType>() || base.as<ast::PointerType>() ) {
+		SemanticError( decl, "Mutex argument have exactly one level of indirection " );
+	}
+
+	// If it is not a struct, it's not a mutex.
+	auto baseStruct = base.as<ast::StructInstType>();
+	if ( nullptr == baseStruct ) return;
+
+	// Make sure that only the outer reference is mutex.
+	if( baseStruct->is_mutex() ) {
+		SemanticError( decl, "mutex keyword may only appear once per argument " );
+	}
+}
+
+ast::CompoundStmt * MutexKeyword::addDtorStatements(
+		const ast::FunctionDecl* func, const ast::CompoundStmt * body,
+		const std::vector<const ast::DeclWithType *> & args ) {
+	ast::Type * argType = ast::shallowCopy( args.front()->get_type() );
+	argType->set_mutex( false );
+
+	ast::CompoundStmt * mutBody = ast::mutate( body );
+
+	// Generated code goes near the beginning of body:
+	const CodeLocation & location = mutBody->location;
+
+	const ast::ObjectDecl * monitor = new ast::ObjectDecl(
+		location,
+		"__monitor",
+		new ast::PointerType( new ast::StructInstType( monitor_decl ) ),
+		new ast::SingleInit(
+			location,
+			new ast::UntypedExpr(
+				location,
+				new ast::NameExpr( location, "get_monitor" ),
+				{ new ast::CastExpr(
+					location,
+					new ast::VariableExpr( location, args.front() ),
+					argType, ast::ExplicitCast
+				) }
+			)
+		)
+	);
+
+	assert( generic_func );
+
+	// In reverse order:
+	// monitor_dtor_guard_t __guard = { __monitor, func, false };
+	mutBody->push_front(
+		new ast::DeclStmt( location, new ast::ObjectDecl(
+			location,
+			"__guard",
+			new ast::StructInstType( dtor_guard_decl ),
+			new ast::ListInit(
+				location,
+				{
+					new ast::SingleInit( location,
+						new ast::AddressExpr( location,
+							new ast::VariableExpr( location, monitor ) ) ),
+					new ast::SingleInit( location,
+						new ast::CastExpr( location,
+							new ast::VariableExpr( location, func ),
+							generic_func,
+							ast::ExplicitCast ) ),
+					new ast::SingleInit( location,
+						ast::ConstantExpr::from_bool( location, false ) ),
+				},
+				{},
+				ast::MaybeConstruct
+			)
+		))
+	);
+
+	// monitor$ * __monitor = get_monitor(a);
+	mutBody->push_front( new ast::DeclStmt( location, monitor ) );
+
+	return mutBody;
+}
+
+ast::CompoundStmt * MutexKeyword::addStatements(
+		const ast::FunctionDecl* func, const ast::CompoundStmt * body,
+		const std::vector<const ast::DeclWithType * > & args ) {
+	ast::CompoundStmt * mutBody = ast::mutate( body );
+
+	// Code is generated near the beginning of the compound statement.
+	const CodeLocation & location = mutBody->location;
+
+	// Make pointer to the monitors.
+	ast::ObjectDecl * monitors = new ast::ObjectDecl(
+		location,
+		"__monitors",
+		new ast::ArrayType(
+			new ast::PointerType(
+				new ast::StructInstType( monitor_decl )
+			),
+			ast::ConstantExpr::from_ulong( location, args.size() ),
+			ast::FixedLen,
+			ast::DynamicDim
+		),
+		new ast::ListInit(
+			location,
+			map_range<std::vector<ast::ptr<ast::Init>>>(
+				args,
+				[]( const ast::DeclWithType * decl ) {
+					return new ast::SingleInit(
+						decl->location,
+						new ast::UntypedExpr(
+							decl->location,
+							new ast::NameExpr( decl->location, "get_monitor" ),
+							{
+								new ast::CastExpr(
+									decl->location,
+									new ast::VariableExpr( decl->location, decl ),
+									decl->get_type(),
+									ast::ExplicitCast
+								)
+							}
+						)
+					);
+				}
+			)
+		)
+	);
+
+	assert( generic_func );
+
+	// In Reverse Order:
+	mutBody->push_front(
+		new ast::DeclStmt( location, new ast::ObjectDecl(
+			location,
+			"__guard",
+			new ast::StructInstType( guard_decl ),
+			new ast::ListInit(
+				location,
+				{
+					new ast::SingleInit( location,
+						new ast::VariableExpr( location, monitors ) ),
+					new ast::SingleInit( location,
+						ast::ConstantExpr::from_ulong( location, args.size() ) ),
+					new ast::SingleInit( location, new ast::CastExpr(
+						location,
+						new ast::VariableExpr( location, func ),
+						generic_func,
+						ast::ExplicitCast
+					) ),
+				},
+				{},
+				ast::MaybeConstruct
+			)
+		))
+	);
+
+	// monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
+	mutBody->push_front( new ast::DeclStmt( location, monitors ) );
+
+	return mutBody;
+}
+
+// generates a cast to the void ptr to the appropriate lock type and dereferences it before calling lock or unlock on it
+// used to undo the type erasure done by storing all the lock pointers as void 
+ast::ExprStmt * MutexKeyword::genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param ) {
+	return new ast::ExprStmt( location,
+		new ast::UntypedExpr( location,
+			new ast::NameExpr( location, fnName ), {
+				ast::UntypedExpr::createDeref(
+					location,
+					new ast::CastExpr( location, 
+						param,
+						new ast::PointerType( new ast::TypeofType( new ast::UntypedExpr(
+							expr->location,
+							new ast::NameExpr( expr->location, "__get_mutexstmt_lock_type" ),
+							{ expr }
+						) ) ),
+						ast::GeneratedFlag::ExplicitCast
+					)
+				)
+			}
+		)
+	);
+}
+
+ast::IfStmt * MutexKeyword::genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam ) {
+	ast::IfStmt * outerLockIf = nullptr;
+	ast::IfStmt * lastLockIf = nullptr;
+
+	//adds an if/elif clause for each lock to assign type from void ptr based on ptr address
+	for ( long unsigned int i = 0; i < args.size(); i++ ) {
+		
+		ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
+			new ast::NameExpr( location, "?==?" ), {
+				ast::deepCopy( thisParam ),
+				new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
+			}
+		);
+
+		ast::IfStmt * currLockIf = new ast::IfStmt( 
+			location,
+			ifCond,
+			genVirtLockUnlockExpr( fnName, args.at(i), location, ast::deepCopy( thisParam ) )
+		);
+		
+		if ( i == 0 ) {
+			outerLockIf = currLockIf;
+		} else {
+			// add ifstmt to else of previous stmt
+			lastLockIf->else_ = currLockIf;
+		}
+
+		lastLockIf = currLockIf;
+	}
+	return outerLockIf;
+}
+
+void flattenTuple( const ast::UntypedTupleExpr * tuple, std::vector<ast::ptr<ast::Expr>> & output ) {
+    for ( auto & expr : tuple->exprs ) {
+        const ast::UntypedTupleExpr * innerTuple = dynamic_cast<const ast::UntypedTupleExpr *>(expr.get());
+        if ( innerTuple ) flattenTuple( innerTuple, output );
+        else output.emplace_back( ast::deepCopy( expr ));
+    }
+}
+
+ast::CompoundStmt * MutexKeyword::addStatements(
+		const ast::CompoundStmt * body,
+		const std::vector<ast::ptr<ast::Expr>> & args ) {
+
+	// Code is generated near the beginning of the compound statement.
+	const CodeLocation & location = body->location;
+
+		// final body to return
+	ast::CompoundStmt * newBody = new ast::CompoundStmt( location );
+
+	// std::string lockFnName = mutex_func_namer.newName();
+	// std::string unlockFnName = mutex_func_namer.newName();
+
+    // If any arguments to the mutex stmt are tuples, flatten them
+    std::vector<ast::ptr<ast::Expr>> flattenedArgs;
+    for ( auto & arg : args ) {
+        const ast::UntypedTupleExpr * tuple = dynamic_cast<const ast::UntypedTupleExpr *>(args.at(0).get());
+        if ( tuple ) flattenTuple( tuple, flattenedArgs );
+        else flattenedArgs.emplace_back( ast::deepCopy( arg ));
+    }
+
+	// Make pointer to the monitors.
+	ast::ObjectDecl * monitors = new ast::ObjectDecl(
+		location,
+		"__monitors",
+		new ast::ArrayType(
+			new ast::PointerType(
+				new ast::VoidType()
+			),
+			ast::ConstantExpr::from_ulong( location, flattenedArgs.size() ),
+			ast::FixedLen,
+			ast::DynamicDim
+		),
+		new ast::ListInit(
+			location,
+			map_range<std::vector<ast::ptr<ast::Init>>>(
+				flattenedArgs, [](const ast::Expr * expr) {
+					return new ast::SingleInit(
+						expr->location,
+						new ast::UntypedExpr(
+							expr->location,
+							new ast::NameExpr( expr->location, "__get_mutexstmt_lock_ptr" ),
+							{ expr }
+						)
+					);
+				}
+			)
+		)
+	);
+
+	ast::StructInstType * lock_guard_struct =
+			new ast::StructInstType( lock_guard_decl );
+
+	// use try stmts to lock and finally to unlock
+	ast::TryStmt * outerTry = nullptr;
+	ast::TryStmt * currentTry;
+	ast::CompoundStmt * lastBody = nullptr;
+
+	// adds a nested try stmt for each lock we are locking
+	for ( long unsigned int i = 0; i < flattenedArgs.size(); i++ ) {
+		ast::UntypedExpr * innerAccess = new ast::UntypedExpr( 
+			location,
+			new ast::NameExpr( location,"?[?]" ), {
+				new ast::NameExpr( location, "__monitors" ),
+				ast::ConstantExpr::from_int( location, i )
+			}
+		);
+
+		// make the try body
+		ast::CompoundStmt * currTryBody = new ast::CompoundStmt( location );
+		ast::IfStmt * lockCall = genTypeDiscrimLockUnlock( "lock", flattenedArgs, location, innerAccess );
+		currTryBody->push_back( lockCall );
+
+		// make the finally stmt
+		ast::CompoundStmt * currFinallyBody = new ast::CompoundStmt( location );
+		ast::IfStmt * unlockCall = genTypeDiscrimLockUnlock( "unlock", flattenedArgs, location, innerAccess );
+		currFinallyBody->push_back( unlockCall );
+
+		// construct the current try
+		currentTry = new ast::TryStmt(
+			location,
+			currTryBody,
+			{},
+			new ast::FinallyClause( location, currFinallyBody )
+		);
+		if ( i == 0 ) outerTry = currentTry;
+		else {
+			// pushback try into the body of the outer try
+			lastBody->push_back( currentTry );
+		}
+		lastBody = currTryBody;
+	}
+
+	// push body into innermost try body
+	if ( lastBody != nullptr ) {
+		lastBody->push_back( body );
+		newBody->push_front( outerTry );
+	}
+
+	// monitor_guard_t __guard = { __monitors, # };
+	newBody->push_front(
+		new ast::DeclStmt(
+			location,
+			new ast::ObjectDecl(
+				location,
+				"__guard",
+				lock_guard_struct,
+				new ast::ListInit(
+					location,
+					{
+						new ast::SingleInit(
+							location,
+							new ast::VariableExpr( location, monitors ) ),
+						new ast::SingleInit(
+							location,
+							ast::ConstantExpr::from_ulong( location, flattenedArgs.size() ) ),
+					},
+					{},
+					ast::MaybeConstruct
+				)
+			)
+		)
+	);
+
+	// monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
+	newBody->push_front( new ast::DeclStmt( location, monitors ) );
+
+	// // The parameter for both __lock_curr/__unlock_curr routines.
+	// ast::ObjectDecl * this_decl = new ast::ObjectDecl(
+	// 	location,
+	// 	"this",
+	// 	new ast::PointerType( new ast::VoidType() ),
+	// 	nullptr,
+	// 	{},
+	// 	ast::Linkage::Cforall
+	// );
+
+	// ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
+	// 	location,
+	// 	lockFnName,
+	// 	{ /* forall */ },
+	// 	{
+	// 		// Copy the declaration of this.
+	// 		this_decl,
+	// 	},
+	// 	{ /* returns */ },
+	// 	nullptr,
+	// 	0,
+	// 	ast::Linkage::Cforall,
+	// 	{ /* attributes */ },
+	// 	ast::Function::Inline
+	// );
+
+	// ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
+	// 	location,
+	// 	unlockFnName,
+	// 	{ /* forall */ },
+	// 	{
+	// 		// Copy the declaration of this.
+	// 		ast::deepCopy( this_decl ),
+	// 	},
+	// 	{ /* returns */ },
+	// 	nullptr,
+	// 	0,
+	// 	ast::Linkage::Cforall,
+	// 	{ /* attributes */ },
+	// 	ast::Function::Inline
+	// );
+
+	// ast::IfStmt * outerLockIf = nullptr;
+	// ast::IfStmt * outerUnlockIf = nullptr;
+	// ast::IfStmt * lastLockIf = nullptr;
+	// ast::IfStmt * lastUnlockIf = nullptr;
+
+	// //adds an if/elif clause for each lock to assign type from void ptr based on ptr address
+	// for ( long unsigned int i = 0; i < args.size(); i++ ) {
+	// 	ast::VariableExpr * thisParam = new ast::VariableExpr( location, InitTweak::getParamThis( lock_decl ) );
+	// 	ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
+	// 		new ast::NameExpr( location, "?==?" ), {
+	// 			thisParam,
+	// 			new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
+	// 		}
+	// 	);
+
+	// 	ast::IfStmt * currLockIf = new ast::IfStmt( 
+	// 		location,
+	// 		ast::deepCopy( ifCond ),
+	// 		genVirtLockUnlockExpr( "lock", args.at(i), location, ast::deepCopy( thisParam ) )
+	// 	);
+
+	// 	ast::IfStmt * currUnlockIf = new ast::IfStmt( 
+	// 		location,
+	// 		ifCond,
+	// 		genVirtLockUnlockExpr( "unlock", args.at(i), location, ast::deepCopy( thisParam ) )
+	// 	);
+		
+	// 	if ( i == 0 ) {
+	// 		outerLockIf = currLockIf;
+	// 		outerUnlockIf = currUnlockIf;
+	// 	} else {
+	// 		// add ifstmt to else of previous stmt
+	// 		lastLockIf->else_ = currLockIf;
+	// 		lastUnlockIf->else_ = currUnlockIf;
+	// 	}
+
+	// 	lastLockIf = currLockIf;
+	// 	lastUnlockIf = currUnlockIf;
+	// }
+	
+	// // add pointer typing if/elifs to body of routines
+	// lock_decl->stmts = new ast::CompoundStmt( location, { outerLockIf } );
+	// unlock_decl->stmts = new ast::CompoundStmt( location, { outerUnlockIf } );
+
+	// // add routines to scope
+	// declsToAddBefore.push_back( lock_decl );
+	// declsToAddBefore.push_back( unlock_decl );
+
+	// newBody->push_front(new ast::DeclStmt( location, lock_decl ));
+	// newBody->push_front(new ast::DeclStmt( location, unlock_decl ));
+
+	return newBody;
+}
+
+ast::CompoundStmt * MutexKeyword::addThreadDtorStatements(
+		const ast::FunctionDecl*, const ast::CompoundStmt * body,
+		const std::vector<const ast::DeclWithType * > & args ) {
+	assert( args.size() == 1 );
+	const ast::DeclWithType * arg = args.front();
+	const ast::Type * argType = arg->get_type();
+	assert( argType->is_mutex() );
+
+	ast::CompoundStmt * mutBody = ast::mutate( body );
+
+	// The code is generated near the front of the body.
+	const CodeLocation & location = mutBody->location;
+
+	// thread_dtor_guard_t __guard = { this, intptr( 0 ) };
+	mutBody->push_front( new ast::DeclStmt(
+		location,
+		new ast::ObjectDecl(
+			location,
+			"__guard",
+			new ast::StructInstType( thread_guard_decl ),
+			new ast::ListInit(
+				location,
+				{
+					new ast::SingleInit( location,
+						new ast::CastExpr( location,
+							new ast::VariableExpr( location, arg ), argType ) ),
+					new ast::SingleInit(
+						location,
+						new ast::UntypedExpr(
+							location,
+							new ast::NameExpr( location, "intptr" ), {
+								ast::ConstantExpr::from_int( location, 0 ),
+							}
+						) ),
+				},
+				{},
+				ast::MaybeConstruct
+			)
+		)
+	));
+
+	return mutBody;
+}
+
+ast::ptr<ast::Type> MutexKeyword::generic_func =
+	new ast::FunctionType( ast::VariableArgs );
+
+// --------------------------------------------------------------------------
+struct ThreadStarter final {
+	void previsit( const ast::StructDecl * decl );
+	const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
+
+private:
+	bool thread_ctor_seen = false;
+	const ast::StructDecl * thread_decl = nullptr;
+};
+
+void ThreadStarter::previsit( const ast::StructDecl * decl ) {
+	if ( decl->body && decl->name == "thread$" ) {
+		assert( !thread_decl );
+		thread_decl = decl;
+	}
+}
+
+const ast::FunctionDecl * ThreadStarter::postvisit( const ast::FunctionDecl * decl ) {
+	if ( !CodeGen::isConstructor( decl->name ) ) return decl;
+
+	// Seach for the thread constructor.
+	// (Are the "prefixes" of these to blocks the same?)
+	const ast::Type * typeof_this = InitTweak::getTypeofThis( decl->type );
+	auto ctored_type = dynamic_cast<const ast::StructInstType *>( typeof_this );
+	if ( ctored_type && ctored_type->base == thread_decl ) {
+		thread_ctor_seen = true;
+	}
+
+	// Modify this declaration, the extra checks to see if we will are first.
+	const ast::ptr<ast::DeclWithType> & param = decl->params.front();
+	auto type = dynamic_cast<const ast::StructInstType *>(
+		ast::getPointerBase( param->get_type() ) );
+	if ( nullptr == type ) return decl;
+	if ( !type->base->is_thread() ) return decl;
+	if ( !thread_decl || !thread_ctor_seen ) {
+		SemanticError( type->base->location, "thread keyword requires threads to be in scope, add #include <thread.hfa>" );
+	}
+	const ast::CompoundStmt * stmt = decl->stmts;
+	if ( nullptr == stmt ) return decl;
+
+	// Now do the actual modification:
+	ast::CompoundStmt * mutStmt = ast::mutate( stmt );
+	const CodeLocation & location = mutStmt->location;
+	mutStmt->push_back(
+		new ast::ExprStmt(
+			location,
+			new ast::UntypedExpr(
+				location,
+				new ast::NameExpr( location, "__thrd_start" ),
+				{
+					new ast::VariableExpr( location, param ),
+					new ast::NameExpr( location, "main" ),
+				}
+			)
+		)
+	);
+
+	return ast::mutate_field( decl, &ast::FunctionDecl::stmts, mutStmt );
+}
+
+} // namespace
+
+// --------------------------------------------------------------------------
+// Interface Functions:
+
+void implementKeywords( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<ThreadKeyword>::run( translationUnit );
+	ast::Pass<CoroutineKeyword>::run( translationUnit );
+	ast::Pass<MonitorKeyword>::run( translationUnit );
+	ast::Pass<GeneratorKeyword>::run( translationUnit );
+	ast::Pass<SuspendKeyword>::run( translationUnit );
+}
+
+void implementMutex( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<MutexKeyword>::run( translationUnit );
+}
+
+void implementThreadStarter( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<ThreadStarter>::run( translationUnit );
+}
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/Concurrency/KeywordsNew.cpp
===================================================================
--- src/Concurrency/KeywordsNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,1601 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// KeywordsNew.cpp -- Implement concurrency constructs from their keywords.
-//
-// Author           : Andrew Beach
-// Created On       : Tue Nov 16  9:53:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Mar 11 10:40:00 2022
-// Update Count     : 2
-//
-
-#include <iostream>
-
-#include "Concurrency/Keywords.h"
-
-#include "AST/Copy.hpp"
-#include "AST/Decl.hpp"
-#include "AST/Expr.hpp"
-#include "AST/Inspect.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Stmt.hpp"
-#include "AST/DeclReplacer.hpp"
-#include "AST/TranslationUnit.hpp"
-#include "CodeGen/OperatorTable.h"
-#include "Common/Examine.h"
-#include "Common/utility.h"
-#include "Common/UniqueName.h"
-#include "ControlStruct/LabelGeneratorNew.hpp"
-#include "InitTweak/InitTweak.h"
-#include "Virtual/Tables.h"
-
-namespace Concurrency {
-
-namespace {
-
-// --------------------------------------------------------------------------
-// Loose Helper Functions:
-
-/// Detect threads constructed with the keyword thread.
-bool isThread( const ast::DeclWithType * decl ) {
-	auto baseType = decl->get_type()->stripDeclarator();
-	auto instType = dynamic_cast<const ast::StructInstType *>( baseType );
-	if ( nullptr == instType ) { return false; }
-	return instType->base->is_thread();
-}
-
-/// Get the virtual type id if given a type name.
-std::string typeIdType( std::string const & exception_name ) {
-	return exception_name.empty() ? std::string()
-		: Virtual::typeIdType( exception_name );
-}
-
-/// Get the vtable type name if given a type name.
-std::string vtableTypeName( std::string const & exception_name ) {
-	return exception_name.empty() ? std::string()
-		: Virtual::vtableTypeName( exception_name );
-}
-
-static ast::Type * mutate_under_references( ast::ptr<ast::Type>& type ) {
-	ast::Type * mutType = type.get_and_mutate();
-	for ( ast::ReferenceType * mutRef
-		; (mutRef = dynamic_cast<ast::ReferenceType *>( mutType ))
-		; mutType = mutRef->base.get_and_mutate() );
-	return mutType;
-}
-
-// Describe that it adds the generic parameters and the uses of the generic
-// parameters on the function and first "this" argument.
-ast::FunctionDecl * fixupGenerics(
-		const ast::FunctionDecl * func, const ast::StructDecl * decl ) {
-	const CodeLocation & location = decl->location;
-	// We have to update both the declaration
-	auto mutFunc = ast::mutate( func );
-	auto mutType = mutFunc->type.get_and_mutate();
-
-	if ( decl->params.empty() ) {
-		return mutFunc;
-	}
-
-	assert( 0 != mutFunc->params.size() );
-	assert( 0 != mutType->params.size() );
-
-	// Add the "forall" clause information.
-	for ( const ast::ptr<ast::TypeDecl> & typeParam : decl->params ) {
-		auto typeDecl = ast::deepCopy( typeParam );
-		mutFunc->type_params.push_back( typeDecl );
-		mutType->forall.push_back( new ast::TypeInstType( typeDecl ) );
-		for ( auto & assertion : typeDecl->assertions ) {
-			mutFunc->assertions.push_back( assertion );
-			mutType->assertions.emplace_back(
-				new ast::VariableExpr( location, assertion ) );
-		}
-		typeDecl->assertions.clear();
-	}
-
-	// Even chain_mutate is not powerful enough for this:
-	ast::ptr<ast::Type>& paramType = strict_dynamic_cast<ast::ObjectDecl *>(
-		mutFunc->params[0].get_and_mutate() )->type;
-	auto paramTypeInst = strict_dynamic_cast<ast::StructInstType *>(
-		mutate_under_references( paramType ) );
-	auto typeParamInst = strict_dynamic_cast<ast::StructInstType *>(
-		mutate_under_references( mutType->params[0] ) );
-
-	for ( const ast::ptr<ast::TypeDecl> & typeDecl : mutFunc->type_params ) {
-		paramTypeInst->params.push_back(
-			new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
-		typeParamInst->params.push_back(
-			new ast::TypeExpr( location, new ast::TypeInstType( typeDecl ) ) );
-	}
-
-	return mutFunc;
-}
-
-// --------------------------------------------------------------------------
-struct ConcurrentSueKeyword : public ast::WithDeclsToAdd<> {
-	ConcurrentSueKeyword(
-		std::string&& type_name, std::string&& field_name,
-		std::string&& getter_name, std::string&& context_error,
-		std::string&& exception_name,
-		bool needs_main, ast::AggregateDecl::Aggregate cast_target
-	) :
-		type_name( type_name ), field_name( field_name ),
-		getter_name( getter_name ), context_error( context_error ),
-		exception_name( exception_name ),
-		typeid_name( typeIdType( exception_name ) ),
-		vtable_name( vtableTypeName( exception_name ) ),
-		needs_main( needs_main ), cast_target( cast_target )
-	{}
-
-	virtual ~ConcurrentSueKeyword() {}
-
-	const ast::Decl * postvisit( const ast::StructDecl * decl );
-	const ast::DeclWithType * postvisit( const ast::FunctionDecl * decl );
-	const ast::Expr * postvisit( const ast::KeywordCastExpr * expr );
-
-	struct StructAndField {
-		const ast::StructDecl * decl;
-		const ast::ObjectDecl * field;
-	};
-
-	const ast::StructDecl * handleStruct( const ast::StructDecl * );
-	void handleMain( const ast::FunctionDecl *, const ast::StructInstType * );
-	void addTypeId( const ast::StructDecl * );
-	void addVtableForward( const ast::StructDecl * );
-	const ast::FunctionDecl * forwardDeclare( const ast::StructDecl * );
-	StructAndField addField( const ast::StructDecl * );
-	void addGetRoutines( const ast::ObjectDecl *, const ast::FunctionDecl * );
-	void addLockUnlockRoutines( const ast::StructDecl * );
-
-private:
-	const std::string type_name;
-	const std::string field_name;
-	const std::string getter_name;
-	const std::string context_error;
-	const std::string exception_name;
-	const std::string typeid_name;
-	const std::string vtable_name;
-	const bool needs_main;
-	const ast::AggregateDecl::Aggregate cast_target;
-
-	const ast::StructDecl   * type_decl = nullptr;
-	const ast::FunctionDecl * dtor_decl = nullptr;
-	const ast::StructDecl * except_decl = nullptr;
-	const ast::StructDecl * typeid_decl = nullptr;
-	const ast::StructDecl * vtable_decl = nullptr;
-
-};
-
-// Handles thread type declarations:
-//
-// thread Mythread {                         struct MyThread {
-//  int data;                                  int data;
-//  a_struct_t more_data;                      a_struct_t more_data;
-//                                =>             thread$ __thrd_d;
-// };                                        };
-//                                           static inline thread$ * get_thread( MyThread * this ) { return &this->__thrd_d; }
-//
-struct ThreadKeyword final : public ConcurrentSueKeyword {
-	ThreadKeyword() : ConcurrentSueKeyword(
-		"thread$",
-		"__thrd",
-		"get_thread",
-		"thread keyword requires threads to be in scope, add #include <thread.hfa>\n",
-		"ThreadCancelled",
-		true,
-		ast::AggregateDecl::Thread )
-	{}
-
-	virtual ~ThreadKeyword() {}
-};
-
-// Handles coroutine type declarations:
-//
-// coroutine MyCoroutine {                   struct MyCoroutine {
-//  int data;                                  int data;
-//  a_struct_t more_data;                      a_struct_t more_data;
-//                                =>             coroutine$ __cor_d;
-// };                                        };
-//                                           static inline coroutine$ * get_coroutine( MyCoroutine * this ) { return &this->__cor_d; }
-//
-struct CoroutineKeyword final : public ConcurrentSueKeyword {
-	CoroutineKeyword() : ConcurrentSueKeyword(
-		"coroutine$",
-		"__cor",
-		"get_coroutine",
-		"coroutine keyword requires coroutines to be in scope, add #include <coroutine.hfa>\n",
-		"CoroutineCancelled",
-		true,
-		ast::AggregateDecl::Coroutine )
-	{}
-
-	virtual ~CoroutineKeyword() {}
-};
-
-// Handles monitor type declarations:
-//
-// monitor MyMonitor {                       struct MyMonitor {
-//  int data;                                  int data;
-//  a_struct_t more_data;                      a_struct_t more_data;
-//                                =>             monitor$ __mon_d;
-// };                                        };
-//                                           static inline monitor$ * get_coroutine( MyMonitor * this ) {
-//                                               return &this->__cor_d;
-//                                           }
-//                                           void lock(MyMonitor & this) {
-//                                               lock(get_monitor(this));
-//                                           }
-//                                           void unlock(MyMonitor & this) {
-//                                               unlock(get_monitor(this));
-//                                           }
-//
-struct MonitorKeyword final : public ConcurrentSueKeyword {
-	MonitorKeyword() : ConcurrentSueKeyword(
-		"monitor$",
-		"__mon",
-		"get_monitor",
-		"monitor keyword requires monitors to be in scope, add #include <monitor.hfa>\n",
-		"",
-		false,
-		ast::AggregateDecl::Monitor )
-	{}
-
-	virtual ~MonitorKeyword() {}
-};
-
-// Handles generator type declarations:
-//
-// generator MyGenerator {                   struct MyGenerator {
-//  int data;                                  int data;
-//  a_struct_t more_data;                      a_struct_t more_data;
-//                                =>             int __generator_state;
-// };                                        };
-//
-struct GeneratorKeyword final : public ConcurrentSueKeyword {
-	GeneratorKeyword() : ConcurrentSueKeyword(
-		"generator$",
-		"__generator_state",
-		"get_generator",
-		"Unable to find builtin type generator$\n",
-		"",
-		true,
-		ast::AggregateDecl::Generator )
-	{}
-
-	virtual ~GeneratorKeyword() {}
-};
-
-const ast::Decl * ConcurrentSueKeyword::postvisit(
-		const ast::StructDecl * decl ) {
-	if ( !decl->body ) {
-		return decl;
-	} else if ( cast_target == decl->kind ) {
-		return handleStruct( decl );
-	} else if ( type_name == decl->name ) {
-		assert( !type_decl );
-		type_decl = decl;
-	} else if ( exception_name == decl->name ) {
-		assert( !except_decl );
-		except_decl = decl;
-	} else if ( typeid_name == decl->name ) {
-		assert( !typeid_decl );
-		typeid_decl = decl;
-	} else if ( vtable_name == decl->name ) {
-		assert( !vtable_decl );
-		vtable_decl = decl;
-	}
-	return decl;
-}
-
-// Try to get the full definition, but raise an error on conflicts.
-const ast::FunctionDecl * getDefinition(
-		const ast::FunctionDecl * old_decl,
-		const ast::FunctionDecl * new_decl ) {
-	if ( !new_decl->stmts ) {
-		return old_decl;
-	} else if ( !old_decl->stmts ) {
-		return new_decl;
-	} else {
-		assert( !old_decl->stmts || !new_decl->stmts );
-		return nullptr;
-	}
-}
-
-const ast::DeclWithType * ConcurrentSueKeyword::postvisit(
-		const ast::FunctionDecl * decl ) {
-	if ( type_decl && isDestructorFor( decl, type_decl ) ) {
-		// Check for forward declarations, try to get the full definition.
-		dtor_decl = (dtor_decl) ? getDefinition( dtor_decl, decl ) : decl;
-	} else if ( !vtable_name.empty() && decl->has_body() ) {
-		if (const ast::DeclWithType * param = isMainFor( decl, cast_target )) {
-			if ( !vtable_decl ) {
-				SemanticError( decl, context_error );
-			}
-			// Should be safe because of isMainFor.
-			const ast::StructInstType * struct_type =
-				static_cast<const ast::StructInstType *>(
-					static_cast<const ast::ReferenceType *>(
-						param->get_type() )->base.get() );
-
-			handleMain( decl, struct_type );
-		}
-	}
-	return decl;
-}
-
-const ast::Expr * ConcurrentSueKeyword::postvisit(
-		const ast::KeywordCastExpr * expr ) {
-	if ( cast_target == expr->target ) {
-		// Convert `(thread &)ex` to `(thread$ &)*get_thread(ex)`, etc.
-		if ( !type_decl || !dtor_decl ) {
-			SemanticError( expr, context_error );
-		}
-		assert( nullptr == expr->result );
-		auto cast = ast::mutate( expr );
-		cast->result = new ast::ReferenceType( new ast::StructInstType( type_decl ) );
-		cast->concrete_target.field  = field_name;
-		cast->concrete_target.getter = getter_name;
-		return cast;
-	}
-	return expr;
-}
-
-const ast::StructDecl * ConcurrentSueKeyword::handleStruct(
-		const ast::StructDecl * decl ) {
-	assert( decl->body );
-
-	if ( !type_decl || !dtor_decl ) {
-		SemanticError( decl, context_error );
-	}
-
-	if ( !exception_name.empty() ) {
-		if( !typeid_decl || !vtable_decl ) {
-			SemanticError( decl, context_error );
-		}
-		addTypeId( decl );
-		addVtableForward( decl );
-	}
-
-	const ast::FunctionDecl * func = forwardDeclare( decl );
-	StructAndField addFieldRet = addField( decl );
-	decl = addFieldRet.decl;
-	const ast::ObjectDecl * field = addFieldRet.field;
-
-	addGetRoutines( field, func );
-	// Add routines to monitors for use by mutex stmt.
-	if ( ast::AggregateDecl::Monitor == cast_target ) {
-		addLockUnlockRoutines( decl );
-	}
-
-	return decl;
-}
-
-void ConcurrentSueKeyword::handleMain(
-		const ast::FunctionDecl * decl, const ast::StructInstType * type ) {
-	assert( vtable_decl );
-	assert( except_decl );
-
-	const CodeLocation & location = decl->location;
-
-	std::vector<ast::ptr<ast::Expr>> poly_args = {
-		new ast::TypeExpr( location, type ),
-	};
-	ast::ObjectDecl * vtable_object = Virtual::makeVtableInstance(
-		location,
-		"_default_vtable_object_declaration",
-		new ast::StructInstType( vtable_decl, copy( poly_args ) ),
-		type,
-		nullptr
-	);
-	declsToAddAfter.push_back( vtable_object );
-	declsToAddAfter.push_back(
-		new ast::ObjectDecl(
-			location,
-			Virtual::concurrentDefaultVTableName(),
-			new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
-			new ast::SingleInit( location,
-				new ast::VariableExpr( location, vtable_object ) )
-		)
-	);
-	declsToAddAfter.push_back( Virtual::makeGetExceptionFunction(
-		location,
-		vtable_object,
-		new ast::StructInstType( except_decl, copy( poly_args ) )
-	) );
-}
-
-void ConcurrentSueKeyword::addTypeId( const ast::StructDecl * decl ) {
-	assert( typeid_decl );
-	const CodeLocation & location = decl->location;
-
-	ast::StructInstType * typeid_type =
-		new ast::StructInstType( typeid_decl, ast::CV::Const );
-	typeid_type->params.push_back(
-		new ast::TypeExpr( location, new ast::StructInstType( decl ) ) );
-	declsToAddBefore.push_back(
-		Virtual::makeTypeIdInstance( location, typeid_type ) );
-	// If the typeid_type is going to be kept, the other reference will have
-	// been made by now, but we also get to avoid extra mutates.
-	ast::ptr<ast::StructInstType> typeid_cleanup = typeid_type;
-}
-
-void ConcurrentSueKeyword::addVtableForward( const ast::StructDecl * decl ) {
-	assert( vtable_decl );
-	const CodeLocation& location = decl->location;
-
-	std::vector<ast::ptr<ast::Expr>> poly_args = {
-		new ast::TypeExpr( location, new ast::StructInstType( decl ) ),
-	};
-	declsToAddBefore.push_back( Virtual::makeGetExceptionForward(
-		location,
-		new ast::StructInstType( vtable_decl, copy( poly_args ) ),
-		new ast::StructInstType( except_decl, copy( poly_args ) )
-	) );
-	ast::ObjectDecl * vtable_object = Virtual::makeVtableForward(
-		location,
-		"_default_vtable_object_declaration",
-		new ast::StructInstType( vtable_decl, std::move( poly_args ) )
-	);
-	declsToAddBefore.push_back( vtable_object );
-	declsToAddBefore.push_back(
-		new ast::ObjectDecl(
-			location,
-			Virtual::concurrentDefaultVTableName(),
-			new ast::ReferenceType( vtable_object->type, ast::CV::Const ),
-			nullptr,
-			ast::Storage::Extern,
-			ast::Linkage::Cforall
-		)
-	);
-}
-
-const ast::FunctionDecl * ConcurrentSueKeyword::forwardDeclare(
-		const ast::StructDecl * decl ) {
-	const CodeLocation & location = decl->location;
-
-	ast::StructDecl * forward = ast::deepCopy( decl );
-	{
-		// If removing members makes ref-count go to zero, do not free.
-		ast::ptr<ast::StructDecl> forward_ptr = forward;
-		forward->body = false;
-		forward->members.clear();
-		forward_ptr.release();
-	}
-
-	ast::ObjectDecl * this_decl = new ast::ObjectDecl(
-		location,
-		"this",
-		new ast::ReferenceType( new ast::StructInstType( decl ) )
-	);
-
-	ast::ObjectDecl * ret_decl = new ast::ObjectDecl(
-		location,
-		"ret",
-		new ast::PointerType( new ast::StructInstType( type_decl ) )
-	);
-
-	ast::FunctionDecl * get_decl = new ast::FunctionDecl(
-		location,
-		getter_name,
-		{ this_decl }, // params
-		{ ret_decl }, // returns
-		nullptr, // stmts
-		ast::Storage::Static,
-		ast::Linkage::Cforall,
-		{ new ast::Attribute( "const" ) },
-		ast::Function::Inline
-	);
-	get_decl = fixupGenerics( get_decl, decl );
-
-	ast::FunctionDecl * main_decl = nullptr;
-	if ( needs_main ) {
-		// `this_decl` is copied here because the original was used above.
-		main_decl = new ast::FunctionDecl(
-			location,
-			"main",
-			{ ast::deepCopy( this_decl ) },
-			{},
-			nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		);
-		main_decl = fixupGenerics( main_decl, decl );
-	}
-
-	declsToAddBefore.push_back( forward );
-	if ( needs_main ) declsToAddBefore.push_back( main_decl );
-	declsToAddBefore.push_back( get_decl );
-
-	return get_decl;
-}
-
-ConcurrentSueKeyword::StructAndField ConcurrentSueKeyword::addField(
-		const ast::StructDecl * decl ) {
-	const CodeLocation & location = decl->location;
-
-	ast::ObjectDecl * field = new ast::ObjectDecl(
-		location,
-		field_name,
-		new ast::StructInstType( type_decl )
-	);
-
-	auto mutDecl = ast::mutate( decl );
-	mutDecl->members.push_back( field );
-
-	return {mutDecl, field};
-}
-
-void ConcurrentSueKeyword::addGetRoutines(
-		const ast::ObjectDecl * field, const ast::FunctionDecl * forward ) {
-	// Clone the signature and then build the body.
-	ast::FunctionDecl * decl = ast::deepCopy( forward );
-
-	// Say it is generated at the "same" places as the forward declaration.
-	const CodeLocation & location = decl->location;
-
-	const ast::DeclWithType * param = decl->params.front();
-	ast::Stmt * stmt = new ast::ReturnStmt( location,
-		new ast::AddressExpr( location,
-			new ast::MemberExpr( location,
-				field,
-				new ast::CastExpr( location,
-					new ast::VariableExpr( location, param ),
-					ast::deepCopy( param->get_type()->stripReferences() ),
-					ast::ExplicitCast
-				)
-			)
-		)
-	);
-
-	decl->stmts = new ast::CompoundStmt( location, { stmt } );
-	declsToAddAfter.push_back( decl );
-}
-
-void ConcurrentSueKeyword::addLockUnlockRoutines(
-		const ast::StructDecl * decl ) {
-	// This should only be used on monitors.
-	assert( ast::AggregateDecl::Monitor == cast_target );
-
-	const CodeLocation & location = decl->location;
-
-	// The parameter for both routines.
-	ast::ObjectDecl * this_decl = new ast::ObjectDecl(
-		location,
-		"this",
-		new ast::ReferenceType( new ast::StructInstType( decl ) )
-	);
-
-	ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
-		location,
-		"lock",
-		{
-			// Copy the declaration of this.
-			ast::deepCopy( this_decl ),
-		},
-		{ /* returns */ },
-		nullptr,
-		ast::Storage::Static,
-		ast::Linkage::Cforall,
-		{ /* attributes */ },
-		ast::Function::Inline
-	);
-	lock_decl = fixupGenerics( lock_decl, decl );
-
-	lock_decl->stmts = new ast::CompoundStmt( location, {
-		new ast::ExprStmt( location,
-			new ast::UntypedExpr( location,
-				new ast::NameExpr( location, "lock" ),
-				{
-					new ast::UntypedExpr( location,
-						new ast::NameExpr( location, "get_monitor" ),
-						{ new ast::VariableExpr( location,
-							InitTweak::getParamThis( lock_decl ) ) }
-					)
-				}
-			)
-		)
-	} );
-
-	ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
-		location,
-		"unlock",
-		{
-			// Last use, consume the declaration of this.
-			this_decl,
-		},
-		{ /* returns */ },
-		nullptr,
-		ast::Storage::Static,
-		ast::Linkage::Cforall,
-		{ /* attributes */ },
-		ast::Function::Inline
-	);
-	unlock_decl = fixupGenerics( unlock_decl, decl );
-
-	unlock_decl->stmts = new ast::CompoundStmt( location, {
-		new ast::ExprStmt( location,
-			new ast::UntypedExpr( location,
-				new ast::NameExpr( location, "unlock" ),
-				{
-					new ast::UntypedExpr( location,
-						new ast::NameExpr( location, "get_monitor" ),
-						{ new ast::VariableExpr( location,
-							InitTweak::getParamThis( unlock_decl ) ) }
-					)
-				}
-			)
-		)
-	} );
-
-	declsToAddAfter.push_back( lock_decl );
-	declsToAddAfter.push_back( unlock_decl );
-}
-
-
-// --------------------------------------------------------------------------
-struct SuspendKeyword final :
-		public ast::WithStmtsToAdd<>, public ast::WithGuards {
-	SuspendKeyword() = default;
-	virtual ~SuspendKeyword() = default;
-
-	void previsit( const ast::FunctionDecl * );
-	const ast::DeclWithType * postvisit( const ast::FunctionDecl * );
-	const ast::Stmt * postvisit( const ast::SuspendStmt * );
-
-private:
-	bool is_real_suspend( const ast::FunctionDecl * );
-
-	const ast::Stmt * make_generator_suspend( const ast::SuspendStmt * );
-	const ast::Stmt * make_coroutine_suspend( const ast::SuspendStmt * );
-
-	struct LabelPair {
-		ast::Label obj;
-		int idx;
-	};
-
-	LabelPair make_label(const ast::Stmt * stmt ) {
-		labels.push_back( ControlStruct::newLabel( "generator", stmt ) );
-		return { labels.back(), int(labels.size()) };
-	}
-
-	const ast::DeclWithType * in_generator = nullptr;
-	const ast::FunctionDecl * decl_suspend = nullptr;
-	std::vector<ast::Label> labels;
-};
-
-void SuspendKeyword::previsit( const ast::FunctionDecl * decl ) {
-	GuardValue( in_generator ); in_generator = nullptr;
-
-	// If it is the real suspend, grab it if we don't have one already.
-	if ( is_real_suspend( decl ) ) {
-		decl_suspend = decl_suspend ? decl_suspend : decl;
-		return;
-	}
-
-	// Otherwise check if this is a generator main and, if so, handle it.
-	auto param = isMainFor( decl, ast::AggregateDecl::Generator );
-	if ( !param ) return;
-
-	if ( 0 != decl->returns.size() ) {
-		SemanticError( decl->location, "Generator main must return void" );
-	}
-
-	in_generator = param;
-	GuardValue( labels ); labels.clear();
-}
-
-const ast::DeclWithType * SuspendKeyword::postvisit(
-		const ast::FunctionDecl * decl ) {
-	// Only modify a full definition of a generator with states.
-	if ( !decl->stmts || !in_generator || labels.empty() ) return decl;
-
-	const CodeLocation & location = decl->location;
-
-	// Create a new function body:
-	// static void * __generator_labels[] = {&&s0, &&s1, ...};
-	// void * __generator_label = __generator_labels[GEN.__generator_state];
-	// goto * __generator_label;
-	// s0: ;
-	// OLD_BODY
-
-	// This is the null statement inserted right before the body.
-	ast::NullStmt * noop = new ast::NullStmt( location );
-	noop->labels.push_back( ControlStruct::newLabel( "generator", noop ) );
-	const ast::Label & first_label = noop->labels.back();
-
-	// Add each label to the init, starting with the first label.
-	std::vector<ast::ptr<ast::Init>> inits = {
-		new ast::SingleInit( location,
-			new ast::LabelAddressExpr( location, copy( first_label ) ) ) };
-	// Then go through all the stored labels, and clear the store.
-	for ( auto && label : labels ) {
-		inits.push_back( new ast::SingleInit( label.location,
-			new ast::LabelAddressExpr( label.location, std::move( label )
-			) ) );
-	}
-	labels.clear();
-	// Then construct the initializer itself.
-	auto init = new ast::ListInit( location, std::move( inits ) );
-
-	ast::ObjectDecl * generatorLabels = new ast::ObjectDecl(
-		location,
-		"__generator_labels",
-		new ast::ArrayType(
-			new ast::PointerType( new ast::VoidType() ),
-			nullptr,
-			ast::FixedLen,
-			ast::DynamicDim
-		),
-		init,
-		ast::Storage::Classes(),
-		ast::Linkage::AutoGen
-	);
-
-	ast::ObjectDecl * generatorLabel = new ast::ObjectDecl(
-		location,
-		"__generator_label",
-		new ast::PointerType( new ast::VoidType() ),
-		new ast::SingleInit( location,
-			new ast::UntypedExpr( location,
-				new ast::NameExpr( location, "?[?]" ),
-				{
-					// TODO: Could be a variable expr.
-					new ast::NameExpr( location, "__generator_labels" ),
-					new ast::UntypedMemberExpr( location,
-						new ast::NameExpr( location, "__generator_state" ),
-						new ast::VariableExpr( location, in_generator )
-					)
-				}
-			)
-		),
-		ast::Storage::Classes(),
-		ast::Linkage::AutoGen
-	);
-
-	ast::BranchStmt * theGoTo = new ast::BranchStmt(
-		location, new ast::VariableExpr( location, generatorLabel )
-	);
-
-	// The noop goes here in order.
-
-	ast::CompoundStmt * body = new ast::CompoundStmt( location, {
-		{ new ast::DeclStmt( location, generatorLabels ) },
-		{ new ast::DeclStmt( location, generatorLabel ) },
-		{ theGoTo },
-		{ noop },
-		{ decl->stmts },
-	} );
-
-	auto mutDecl = ast::mutate( decl );
-	mutDecl->stmts = body;
-	return mutDecl;
-}
-
-const ast::Stmt * SuspendKeyword::postvisit( const ast::SuspendStmt * stmt ) {
-	switch ( stmt->kind ) {
-	case ast::SuspendStmt::None:
-		// Use the context to determain the implicit target.
-		if ( in_generator ) {
-			return make_generator_suspend( stmt );
-		} else {
-			return make_coroutine_suspend( stmt );
-		}
-	case ast::SuspendStmt::Coroutine:
-		return make_coroutine_suspend( stmt );
-	case ast::SuspendStmt::Generator:
-		// Generator suspends must be directly in a generator.
-		if ( !in_generator ) SemanticError( stmt->location, "'suspend generator' must be used inside main of generator type." );
-		return make_generator_suspend( stmt );
-	}
-	assert( false );
-	return stmt;
-}
-
-/// Find the real/official suspend declaration.
-bool SuspendKeyword::is_real_suspend( const ast::FunctionDecl * decl ) {
-	return ( !decl->linkage.is_mangled
-		&& 0 == decl->params.size()
-		&& 0 == decl->returns.size()
-		&& "__cfactx_suspend" == decl->name );
-}
-
-const ast::Stmt * SuspendKeyword::make_generator_suspend(
-		const ast::SuspendStmt * stmt ) {
-	assert( in_generator );
-	// Target code is:
-	//   GEN.__generator_state = X;
-	//   THEN
-	//   return;
-	//   __gen_X:;
-
-	const CodeLocation & location = stmt->location;
-
-	LabelPair label = make_label( stmt );
-
-	// This is the context saving statement.
-	stmtsToAddBefore.push_back( new ast::ExprStmt( location,
-		new ast::UntypedExpr( location,
-			new ast::NameExpr( location, "?=?" ),
-			{
-				new ast::UntypedMemberExpr( location,
-					new ast::NameExpr( location, "__generator_state" ),
-					new ast::VariableExpr( location, in_generator )
-				),
-				ast::ConstantExpr::from_int( location, label.idx ),
-			}
-		)
-	) );
-
-	// The THEN component is conditional (return is not).
-	if ( stmt->then ) {
-		stmtsToAddBefore.push_back( stmt->then.get() );
-	}
-	stmtsToAddBefore.push_back( new ast::ReturnStmt( location, nullptr ) );
-
-	// The null statement replaces the old suspend statement.
-	return new ast::NullStmt( location, { label.obj } );
-}
-
-const ast::Stmt * SuspendKeyword::make_coroutine_suspend(
-		const ast::SuspendStmt * stmt ) {
-	// The only thing we need from the old statement is the location.
-	const CodeLocation & location = stmt->location;
-
-	if ( !decl_suspend ) {
-		SemanticError( location, "suspend keyword applied to coroutines requires coroutines to be in scope, add #include <coroutine.hfa>\n" );
-	}
-	if ( stmt->then ) {
-		SemanticError( location, "Compound statement following coroutines is not implemented." );
-	}
-
-	return new ast::ExprStmt( location,
-		new ast::UntypedExpr( location,
-			ast::VariableExpr::functionPointer( location, decl_suspend ) )
-	);
-}
-
-// --------------------------------------------------------------------------
-struct MutexKeyword final : public ast::WithDeclsToAdd<> {
-	const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
-	void postvisit( const ast::StructDecl * decl );
-	const ast::Stmt * postvisit( const ast::MutexStmt * stmt );
-
-	static std::vector<const ast::DeclWithType *> findMutexArgs(
-			const ast::FunctionDecl * decl, bool & first );
-	static void validate( const ast::DeclWithType * decl );
-
-	ast::CompoundStmt * addDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
-	ast::CompoundStmt * addStatements( const ast::FunctionDecl* func, const ast::CompoundStmt *, const std::vector<const ast::DeclWithType *> &);
-	ast::CompoundStmt * addStatements( const ast::CompoundStmt * body, const std::vector<ast::ptr<ast::Expr>> & args );
-	ast::CompoundStmt * addThreadDtorStatements( const ast::FunctionDecl* func, const ast::CompoundStmt * body, const std::vector<const ast::DeclWithType *> & args );
-	ast::ExprStmt * genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param);
-	ast::IfStmt * genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam );
-private:
-	const ast::StructDecl * monitor_decl = nullptr;
-	const ast::StructDecl * guard_decl = nullptr;
-	const ast::StructDecl * dtor_guard_decl = nullptr;
-	const ast::StructDecl * thread_guard_decl = nullptr;
-	const ast::StructDecl * lock_guard_decl = nullptr;
-
-	static ast::ptr<ast::Type> generic_func;
-
-	UniqueName mutex_func_namer = UniqueName("__lock_unlock_curr");
-};
-
-const ast::FunctionDecl * MutexKeyword::postvisit(
-		const ast::FunctionDecl * decl ) {
-	bool is_first_argument_mutex = false;
-	const std::vector<const ast::DeclWithType *> mutexArgs =
-		findMutexArgs( decl, is_first_argument_mutex );
-	bool const isDtor = CodeGen::isDestructor( decl->name );
-
-	// Does this function have any mutex arguments that connect to monitors?
-	if ( mutexArgs.empty() ) {
-		// If this is the destructor for a monitor it must be mutex.
-		if ( isDtor ) {
-			// This reflects MutexKeyword::validate, but no error messages.
-			const ast::Type * type = decl->type->params.front();
-
-			// If it's a copy, it's not a mutex.
-			const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
-			if ( nullptr == refType ) {
-				return decl;
-			}
-
-			// If it is not pointing directly to a type, it's not a mutex.
-			auto base = refType->base;
-			if ( base.as<ast::ReferenceType>() ) return decl;
-			if ( base.as<ast::PointerType>() ) return decl;
-
-			// If it is not a struct, it's not a mutex.
-			auto baseStruct = base.as<ast::StructInstType>();
-			if ( nullptr == baseStruct ) return decl;
-
-			// If it is a monitor, then it is a monitor.
-			if( baseStruct->base->is_monitor() || baseStruct->base->is_thread() ) {
-				SemanticError( decl, "destructors for structures declared as \"monitor\" must use mutex parameters\n" );
-			}
-		}
-		return decl;
-	}
-
-	// Monitors can't be constructed with mutual exclusion.
-	if ( CodeGen::isConstructor( decl->name ) && is_first_argument_mutex ) {
-		SemanticError( decl, "constructors cannot have mutex parameters\n" );
-	}
-
-	// It makes no sense to have multiple mutex parameters for the destructor.
-	if ( isDtor && mutexArgs.size() != 1 ) {
-		SemanticError( decl, "destructors can only have 1 mutex argument\n" );
-	}
-
-	// Make sure all the mutex arguments are monitors.
-	for ( auto arg : mutexArgs ) {
-		validate( arg );
-	}
-
-	// Check to see if the body needs to be instrument the body.
-	const ast::CompoundStmt * body = decl->stmts;
-	if ( !body ) return decl;
-
-	// Check to if the required headers have been seen.
-	if ( !monitor_decl || !guard_decl || !dtor_guard_decl ) {
-		SemanticError( decl, "mutex keyword requires monitors to be in scope, add #include <monitor.hfa>\n" );
-	}
-
-	// Instrument the body.
-	ast::CompoundStmt * newBody = nullptr;
-	if ( isDtor && isThread( mutexArgs.front() ) ) {
-		if ( !thread_guard_decl ) {
-			SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>\n" );
-		}
-		newBody = addThreadDtorStatements( decl, body, mutexArgs );
-	} else if ( isDtor ) {
-		newBody = addDtorStatements( decl, body, mutexArgs );
-	} else {
-		newBody = addStatements( decl, body, mutexArgs );
-	}
-	assert( newBody );
-	return ast::mutate_field( decl, &ast::FunctionDecl::stmts, newBody );
-}
-
-void MutexKeyword::postvisit( const ast::StructDecl * decl ) {
-	if ( !decl->body ) {
-		return;
-	} else if ( decl->name == "monitor$" ) {
-		assert( !monitor_decl );
-		monitor_decl = decl;
-	} else if ( decl->name == "monitor_guard_t" ) {
-		assert( !guard_decl );
-		guard_decl = decl;
-	} else if ( decl->name == "monitor_dtor_guard_t" ) {
-		assert( !dtor_guard_decl );
-		dtor_guard_decl = decl;
-	} else if ( decl->name == "thread_dtor_guard_t" ) {
-		assert( !thread_guard_decl );
-		thread_guard_decl = decl;
-	} else if ( decl->name == "__mutex_stmt_lock_guard" ) {
-		assert( !lock_guard_decl );
-		lock_guard_decl = decl;
-	}
-}
-
-const ast::Stmt * MutexKeyword::postvisit( const ast::MutexStmt * stmt ) {
-	if ( !lock_guard_decl ) {
-		SemanticError( stmt->location, "mutex stmt requires a header, add #include <mutex_stmt.hfa>\n" );
-	}
-	ast::CompoundStmt * body =
-			new ast::CompoundStmt( stmt->location, { stmt->stmt } );
-	
-	return addStatements( body, stmt->mutexObjs );;
-}
-
-std::vector<const ast::DeclWithType *> MutexKeyword::findMutexArgs(
-		const ast::FunctionDecl * decl, bool & first ) {
-	std::vector<const ast::DeclWithType *> mutexArgs;
-
-	bool once = true;
-	for ( auto arg : decl->params ) {
-		const ast::Type * type = arg->get_type();
-		if ( type->is_mutex() ) {
-			if ( once ) first = true;
-			mutexArgs.push_back( arg.get() );
-		}
-		once = false;
-	}
-	return mutexArgs;
-}
-
-void MutexKeyword::validate( const ast::DeclWithType * decl ) {
-	const ast::Type * type = decl->get_type();
-
-	// If it's a copy, it's not a mutex.
-	const ast::ReferenceType * refType = dynamic_cast<const ast::ReferenceType *>( type );
-	if ( nullptr == refType ) {
-		SemanticError( decl, "Mutex argument must be of reference type " );
-	}
-
-	// If it is not pointing directly to a type, it's not a mutex.
-	auto base = refType->base;
-	if ( base.as<ast::ReferenceType>() || base.as<ast::PointerType>() ) {
-		SemanticError( decl, "Mutex argument have exactly one level of indirection " );
-	}
-
-	// If it is not a struct, it's not a mutex.
-	auto baseStruct = base.as<ast::StructInstType>();
-	if ( nullptr == baseStruct ) return;
-
-	// Make sure that only the outer reference is mutex.
-	if( baseStruct->is_mutex() ) {
-		SemanticError( decl, "mutex keyword may only appear once per argument " );
-	}
-}
-
-ast::CompoundStmt * MutexKeyword::addDtorStatements(
-		const ast::FunctionDecl* func, const ast::CompoundStmt * body,
-		const std::vector<const ast::DeclWithType *> & args ) {
-	ast::Type * argType = ast::shallowCopy( args.front()->get_type() );
-	argType->set_mutex( false );
-
-	ast::CompoundStmt * mutBody = ast::mutate( body );
-
-	// Generated code goes near the beginning of body:
-	const CodeLocation & location = mutBody->location;
-
-	const ast::ObjectDecl * monitor = new ast::ObjectDecl(
-		location,
-		"__monitor",
-		new ast::PointerType( new ast::StructInstType( monitor_decl ) ),
-		new ast::SingleInit(
-			location,
-			new ast::UntypedExpr(
-				location,
-				new ast::NameExpr( location, "get_monitor" ),
-				{ new ast::CastExpr(
-					location,
-					new ast::VariableExpr( location, args.front() ),
-					argType, ast::ExplicitCast
-				) }
-			)
-		)
-	);
-
-	assert( generic_func );
-
-	// In reverse order:
-	// monitor_dtor_guard_t __guard = { __monitor, func, false };
-	mutBody->push_front(
-		new ast::DeclStmt( location, new ast::ObjectDecl(
-			location,
-			"__guard",
-			new ast::StructInstType( dtor_guard_decl ),
-			new ast::ListInit(
-				location,
-				{
-					new ast::SingleInit( location,
-						new ast::AddressExpr( location,
-							new ast::VariableExpr( location, monitor ) ) ),
-					new ast::SingleInit( location,
-						new ast::CastExpr( location,
-							new ast::VariableExpr( location, func ),
-							generic_func,
-							ast::ExplicitCast ) ),
-					new ast::SingleInit( location,
-						ast::ConstantExpr::from_bool( location, false ) ),
-				},
-				{},
-				ast::MaybeConstruct
-			)
-		))
-	);
-
-	// monitor$ * __monitor = get_monitor(a);
-	mutBody->push_front( new ast::DeclStmt( location, monitor ) );
-
-	return mutBody;
-}
-
-ast::CompoundStmt * MutexKeyword::addStatements(
-		const ast::FunctionDecl* func, const ast::CompoundStmt * body,
-		const std::vector<const ast::DeclWithType * > & args ) {
-	ast::CompoundStmt * mutBody = ast::mutate( body );
-
-	// Code is generated near the beginning of the compound statement.
-	const CodeLocation & location = mutBody->location;
-
-	// Make pointer to the monitors.
-	ast::ObjectDecl * monitors = new ast::ObjectDecl(
-		location,
-		"__monitors",
-		new ast::ArrayType(
-			new ast::PointerType(
-				new ast::StructInstType( monitor_decl )
-			),
-			ast::ConstantExpr::from_ulong( location, args.size() ),
-			ast::FixedLen,
-			ast::DynamicDim
-		),
-		new ast::ListInit(
-			location,
-			map_range<std::vector<ast::ptr<ast::Init>>>(
-				args,
-				[]( const ast::DeclWithType * decl ) {
-					return new ast::SingleInit(
-						decl->location,
-						new ast::UntypedExpr(
-							decl->location,
-							new ast::NameExpr( decl->location, "get_monitor" ),
-							{
-								new ast::CastExpr(
-									decl->location,
-									new ast::VariableExpr( decl->location, decl ),
-									decl->get_type(),
-									ast::ExplicitCast
-								)
-							}
-						)
-					);
-				}
-			)
-		)
-	);
-
-	assert( generic_func );
-
-	// In Reverse Order:
-	mutBody->push_front(
-		new ast::DeclStmt( location, new ast::ObjectDecl(
-			location,
-			"__guard",
-			new ast::StructInstType( guard_decl ),
-			new ast::ListInit(
-				location,
-				{
-					new ast::SingleInit( location,
-						new ast::VariableExpr( location, monitors ) ),
-					new ast::SingleInit( location,
-						ast::ConstantExpr::from_ulong( location, args.size() ) ),
-					new ast::SingleInit( location, new ast::CastExpr(
-						location,
-						new ast::VariableExpr( location, func ),
-						generic_func,
-						ast::ExplicitCast
-					) ),
-				},
-				{},
-				ast::MaybeConstruct
-			)
-		))
-	);
-
-	// monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
-	mutBody->push_front( new ast::DeclStmt( location, monitors ) );
-
-	return mutBody;
-}
-
-// generates a cast to the void ptr to the appropriate lock type and dereferences it before calling lock or unlock on it
-// used to undo the type erasure done by storing all the lock pointers as void 
-ast::ExprStmt * MutexKeyword::genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param ) {
-	return new ast::ExprStmt( location,
-		new ast::UntypedExpr( location,
-			new ast::NameExpr( location, fnName ), {
-				ast::UntypedExpr::createDeref(
-					location,
-					new ast::CastExpr( location, 
-						param,
-						new ast::PointerType( new ast::TypeofType( new ast::UntypedExpr(
-							expr->location,
-							new ast::NameExpr( expr->location, "__get_mutexstmt_lock_type" ),
-							{ expr }
-						) ) ),
-						ast::GeneratedFlag::ExplicitCast
-					)
-				)
-			}
-		)
-	);
-}
-
-ast::IfStmt * MutexKeyword::genTypeDiscrimLockUnlock( const std::string & fnName, const std::vector<ast::ptr<ast::Expr>> & args, const CodeLocation & location, ast::UntypedExpr * thisParam ) {
-	ast::IfStmt * outerLockIf = nullptr;
-	ast::IfStmt * lastLockIf = nullptr;
-
-	//adds an if/elif clause for each lock to assign type from void ptr based on ptr address
-	for ( long unsigned int i = 0; i < args.size(); i++ ) {
-		
-		ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
-			new ast::NameExpr( location, "?==?" ), {
-				ast::deepCopy( thisParam ),
-				new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
-			}
-		);
-
-		ast::IfStmt * currLockIf = new ast::IfStmt( 
-			location,
-			ifCond,
-			genVirtLockUnlockExpr( fnName, args.at(i), location, ast::deepCopy( thisParam ) )
-		);
-		
-		if ( i == 0 ) {
-			outerLockIf = currLockIf;
-		} else {
-			// add ifstmt to else of previous stmt
-			lastLockIf->else_ = currLockIf;
-		}
-
-		lastLockIf = currLockIf;
-	}
-	return outerLockIf;
-}
-
-void flattenTuple( const ast::UntypedTupleExpr * tuple, std::vector<ast::ptr<ast::Expr>> & output ) {
-    for ( auto & expr : tuple->exprs ) {
-        const ast::UntypedTupleExpr * innerTuple = dynamic_cast<const ast::UntypedTupleExpr *>(expr.get());
-        if ( innerTuple ) flattenTuple( innerTuple, output );
-        else output.emplace_back( ast::deepCopy( expr ));
-    }
-}
-
-ast::CompoundStmt * MutexKeyword::addStatements(
-		const ast::CompoundStmt * body,
-		const std::vector<ast::ptr<ast::Expr>> & args ) {
-
-	// Code is generated near the beginning of the compound statement.
-	const CodeLocation & location = body->location;
-
-		// final body to return
-	ast::CompoundStmt * newBody = new ast::CompoundStmt( location );
-
-	// std::string lockFnName = mutex_func_namer.newName();
-	// std::string unlockFnName = mutex_func_namer.newName();
-
-    // If any arguments to the mutex stmt are tuples, flatten them
-    std::vector<ast::ptr<ast::Expr>> flattenedArgs;
-    for ( auto & arg : args ) {
-        const ast::UntypedTupleExpr * tuple = dynamic_cast<const ast::UntypedTupleExpr *>(args.at(0).get());
-        if ( tuple ) flattenTuple( tuple, flattenedArgs );
-        else flattenedArgs.emplace_back( ast::deepCopy( arg ));
-    }
-
-	// Make pointer to the monitors.
-	ast::ObjectDecl * monitors = new ast::ObjectDecl(
-		location,
-		"__monitors",
-		new ast::ArrayType(
-			new ast::PointerType(
-				new ast::VoidType()
-			),
-			ast::ConstantExpr::from_ulong( location, flattenedArgs.size() ),
-			ast::FixedLen,
-			ast::DynamicDim
-		),
-		new ast::ListInit(
-			location,
-			map_range<std::vector<ast::ptr<ast::Init>>>(
-				flattenedArgs, [](const ast::Expr * expr) {
-					return new ast::SingleInit(
-						expr->location,
-						new ast::UntypedExpr(
-							expr->location,
-							new ast::NameExpr( expr->location, "__get_mutexstmt_lock_ptr" ),
-							{ expr }
-						)
-					);
-				}
-			)
-		)
-	);
-
-	ast::StructInstType * lock_guard_struct =
-			new ast::StructInstType( lock_guard_decl );
-
-	// use try stmts to lock and finally to unlock
-	ast::TryStmt * outerTry = nullptr;
-	ast::TryStmt * currentTry;
-	ast::CompoundStmt * lastBody = nullptr;
-
-	// adds a nested try stmt for each lock we are locking
-	for ( long unsigned int i = 0; i < flattenedArgs.size(); i++ ) {
-		ast::UntypedExpr * innerAccess = new ast::UntypedExpr( 
-			location,
-			new ast::NameExpr( location,"?[?]" ), {
-				new ast::NameExpr( location, "__monitors" ),
-				ast::ConstantExpr::from_int( location, i )
-			}
-		);
-
-		// make the try body
-		ast::CompoundStmt * currTryBody = new ast::CompoundStmt( location );
-		ast::IfStmt * lockCall = genTypeDiscrimLockUnlock( "lock", flattenedArgs, location, innerAccess );
-		currTryBody->push_back( lockCall );
-
-		// make the finally stmt
-		ast::CompoundStmt * currFinallyBody = new ast::CompoundStmt( location );
-		ast::IfStmt * unlockCall = genTypeDiscrimLockUnlock( "unlock", flattenedArgs, location, innerAccess );
-		currFinallyBody->push_back( unlockCall );
-
-		// construct the current try
-		currentTry = new ast::TryStmt(
-			location,
-			currTryBody,
-			{},
-			new ast::FinallyClause( location, currFinallyBody )
-		);
-		if ( i == 0 ) outerTry = currentTry;
-		else {
-			// pushback try into the body of the outer try
-			lastBody->push_back( currentTry );
-		}
-		lastBody = currTryBody;
-	}
-
-	// push body into innermost try body
-	if ( lastBody != nullptr ) {
-		lastBody->push_back( body );
-		newBody->push_front( outerTry );
-	}
-
-	// monitor_guard_t __guard = { __monitors, # };
-	newBody->push_front(
-		new ast::DeclStmt(
-			location,
-			new ast::ObjectDecl(
-				location,
-				"__guard",
-				lock_guard_struct,
-				new ast::ListInit(
-					location,
-					{
-						new ast::SingleInit(
-							location,
-							new ast::VariableExpr( location, monitors ) ),
-						new ast::SingleInit(
-							location,
-							ast::ConstantExpr::from_ulong( location, flattenedArgs.size() ) ),
-					},
-					{},
-					ast::MaybeConstruct
-				)
-			)
-		)
-	);
-
-	// monitor$ * __monitors[] = { get_monitor(a), get_monitor(b) };
-	newBody->push_front( new ast::DeclStmt( location, monitors ) );
-
-	// // The parameter for both __lock_curr/__unlock_curr routines.
-	// ast::ObjectDecl * this_decl = new ast::ObjectDecl(
-	// 	location,
-	// 	"this",
-	// 	new ast::PointerType( new ast::VoidType() ),
-	// 	nullptr,
-	// 	{},
-	// 	ast::Linkage::Cforall
-	// );
-
-	// ast::FunctionDecl * lock_decl = new ast::FunctionDecl(
-	// 	location,
-	// 	lockFnName,
-	// 	{ /* forall */ },
-	// 	{
-	// 		// Copy the declaration of this.
-	// 		this_decl,
-	// 	},
-	// 	{ /* returns */ },
-	// 	nullptr,
-	// 	0,
-	// 	ast::Linkage::Cforall,
-	// 	{ /* attributes */ },
-	// 	ast::Function::Inline
-	// );
-
-	// ast::FunctionDecl * unlock_decl = new ast::FunctionDecl(
-	// 	location,
-	// 	unlockFnName,
-	// 	{ /* forall */ },
-	// 	{
-	// 		// Copy the declaration of this.
-	// 		ast::deepCopy( this_decl ),
-	// 	},
-	// 	{ /* returns */ },
-	// 	nullptr,
-	// 	0,
-	// 	ast::Linkage::Cforall,
-	// 	{ /* attributes */ },
-	// 	ast::Function::Inline
-	// );
-
-	// ast::IfStmt * outerLockIf = nullptr;
-	// ast::IfStmt * outerUnlockIf = nullptr;
-	// ast::IfStmt * lastLockIf = nullptr;
-	// ast::IfStmt * lastUnlockIf = nullptr;
-
-	// //adds an if/elif clause for each lock to assign type from void ptr based on ptr address
-	// for ( long unsigned int i = 0; i < args.size(); i++ ) {
-	// 	ast::VariableExpr * thisParam = new ast::VariableExpr( location, InitTweak::getParamThis( lock_decl ) );
-	// 	ast::UntypedExpr * ifCond = new ast::UntypedExpr( location,
-	// 		new ast::NameExpr( location, "?==?" ), {
-	// 			thisParam,
-	// 			new ast::CastExpr( location, new ast::AddressExpr( location, args.at(i) ), new ast::PointerType( new ast::VoidType() ))
-	// 		}
-	// 	);
-
-	// 	ast::IfStmt * currLockIf = new ast::IfStmt( 
-	// 		location,
-	// 		ast::deepCopy( ifCond ),
-	// 		genVirtLockUnlockExpr( "lock", args.at(i), location, ast::deepCopy( thisParam ) )
-	// 	);
-
-	// 	ast::IfStmt * currUnlockIf = new ast::IfStmt( 
-	// 		location,
-	// 		ifCond,
-	// 		genVirtLockUnlockExpr( "unlock", args.at(i), location, ast::deepCopy( thisParam ) )
-	// 	);
-		
-	// 	if ( i == 0 ) {
-	// 		outerLockIf = currLockIf;
-	// 		outerUnlockIf = currUnlockIf;
-	// 	} else {
-	// 		// add ifstmt to else of previous stmt
-	// 		lastLockIf->else_ = currLockIf;
-	// 		lastUnlockIf->else_ = currUnlockIf;
-	// 	}
-
-	// 	lastLockIf = currLockIf;
-	// 	lastUnlockIf = currUnlockIf;
-	// }
-	
-	// // add pointer typing if/elifs to body of routines
-	// lock_decl->stmts = new ast::CompoundStmt( location, { outerLockIf } );
-	// unlock_decl->stmts = new ast::CompoundStmt( location, { outerUnlockIf } );
-
-	// // add routines to scope
-	// declsToAddBefore.push_back( lock_decl );
-	// declsToAddBefore.push_back( unlock_decl );
-
-	// newBody->push_front(new ast::DeclStmt( location, lock_decl ));
-	// newBody->push_front(new ast::DeclStmt( location, unlock_decl ));
-
-	return newBody;
-}
-
-ast::CompoundStmt * MutexKeyword::addThreadDtorStatements(
-		const ast::FunctionDecl*, const ast::CompoundStmt * body,
-		const std::vector<const ast::DeclWithType * > & args ) {
-	assert( args.size() == 1 );
-	const ast::DeclWithType * arg = args.front();
-	const ast::Type * argType = arg->get_type();
-	assert( argType->is_mutex() );
-
-	ast::CompoundStmt * mutBody = ast::mutate( body );
-
-	// The code is generated near the front of the body.
-	const CodeLocation & location = mutBody->location;
-
-	// thread_dtor_guard_t __guard = { this, intptr( 0 ) };
-	mutBody->push_front( new ast::DeclStmt(
-		location,
-		new ast::ObjectDecl(
-			location,
-			"__guard",
-			new ast::StructInstType( thread_guard_decl ),
-			new ast::ListInit(
-				location,
-				{
-					new ast::SingleInit( location,
-						new ast::CastExpr( location,
-							new ast::VariableExpr( location, arg ), argType ) ),
-					new ast::SingleInit(
-						location,
-						new ast::UntypedExpr(
-							location,
-							new ast::NameExpr( location, "intptr" ), {
-								ast::ConstantExpr::from_int( location, 0 ),
-							}
-						) ),
-				},
-				{},
-				ast::MaybeConstruct
-			)
-		)
-	));
-
-	return mutBody;
-}
-
-ast::ptr<ast::Type> MutexKeyword::generic_func =
-	new ast::FunctionType( ast::VariableArgs );
-
-// --------------------------------------------------------------------------
-struct ThreadStarter final {
-	void previsit( const ast::StructDecl * decl );
-	const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
-
-private:
-	bool thread_ctor_seen = false;
-	const ast::StructDecl * thread_decl = nullptr;
-};
-
-void ThreadStarter::previsit( const ast::StructDecl * decl ) {
-	if ( decl->body && decl->name == "thread$" ) {
-		assert( !thread_decl );
-		thread_decl = decl;
-	}
-}
-
-const ast::FunctionDecl * ThreadStarter::postvisit( const ast::FunctionDecl * decl ) {
-	if ( !CodeGen::isConstructor( decl->name ) ) return decl;
-
-	// Seach for the thread constructor.
-	// (Are the "prefixes" of these to blocks the same?)
-	const ast::Type * typeof_this = InitTweak::getTypeofThis( decl->type );
-	auto ctored_type = dynamic_cast<const ast::StructInstType *>( typeof_this );
-	if ( ctored_type && ctored_type->base == thread_decl ) {
-		thread_ctor_seen = true;
-	}
-
-	// Modify this declaration, the extra checks to see if we will are first.
-	const ast::ptr<ast::DeclWithType> & param = decl->params.front();
-	auto type = dynamic_cast<const ast::StructInstType *>(
-		ast::getPointerBase( param->get_type() ) );
-	if ( nullptr == type ) return decl;
-	if ( !type->base->is_thread() ) return decl;
-	if ( !thread_decl || !thread_ctor_seen ) {
-		SemanticError( type->base->location, "thread keyword requires threads to be in scope, add #include <thread.hfa>" );
-	}
-	const ast::CompoundStmt * stmt = decl->stmts;
-	if ( nullptr == stmt ) return decl;
-
-	// Now do the actual modification:
-	ast::CompoundStmt * mutStmt = ast::mutate( stmt );
-	const CodeLocation & location = mutStmt->location;
-	mutStmt->push_back(
-		new ast::ExprStmt(
-			location,
-			new ast::UntypedExpr(
-				location,
-				new ast::NameExpr( location, "__thrd_start" ),
-				{
-					new ast::VariableExpr( location, param ),
-					new ast::NameExpr( location, "main" ),
-				}
-			)
-		)
-	);
-
-	return ast::mutate_field( decl, &ast::FunctionDecl::stmts, mutStmt );
-}
-
-} // namespace
-
-// --------------------------------------------------------------------------
-// Interface Functions:
-
-void implementKeywords( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<ThreadKeyword>::run( translationUnit );
-	ast::Pass<CoroutineKeyword>::run( translationUnit );
-	ast::Pass<MonitorKeyword>::run( translationUnit );
-	ast::Pass<GeneratorKeyword>::run( translationUnit );
-	ast::Pass<SuspendKeyword>::run( translationUnit );
-}
-
-void implementMutex( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<MutexKeyword>::run( translationUnit );
-}
-
-void implementThreadStarter( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<ThreadStarter>::run( translationUnit );
-}
-
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Concurrency/Waitfor.cpp
===================================================================
--- src/Concurrency/Waitfor.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/Concurrency/Waitfor.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,578 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Waitfor.cpp -- Expand waitfor clauses into code.
+//
+// Author           : Andrew Beach
+// Created On       : Fri May 27 10:31:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Jun 13 13:30:00 2022
+// Update Count     : 0
+//
+
+#include "Waitfor.h"
+
+#include <string>
+
+#include "AST/Pass.hpp"
+#include "Common/UniqueName.h"
+#include "InitTweak/InitTweak.h"
+#include "ResolvExpr/Resolver.h"
+
+#include "AST/Print.hpp"
+
+using namespace std::string_literals;
+using ResolvExpr::ResolveContext;
+
+/* So this is what this file dones:
+
+void f(int i, float f, A & mutex b, struct foo *  );
+void f(int );
+
+...{
+	when ( a < 1 ) waitfor( f : a ) { fee(); }
+	or timeout( getWaitTime() ) { fy(); }
+	or waitfor( g : a ) { foe(); }
+	or waitfor( ^?{} : a ) { break; }
+	or waitfor( ^?{} ) { break; }
+	or when ( a < 1 ) else { fum(); }
+}...
+
+		 ||
+		 ||
+		\||/
+		 \/
+
+...{
+	{
+		__acceptable_t __acceptables_#[4 <num-clauses>];
+		bool __do_run_# = false;
+
+		monitor$ * __monitors_#[1 <num-monitors>] = { a };
+		if ( a < 1) {
+			void (*__function_#)() = <casts> f;
+			__acceptables_#[0].is_dtor = false;
+			__acceptables_#[0].func = __function_#;
+			__acceptables_#[0].data = __monitors_#;
+			__acceptables_#[0].size = 1;
+			__do_run_# = true;
+		}
+
+		// Remaining waitfor clauses go here.
+
+		long long unsigned int __timeout_# = -1;
+		if ( true ) {
+			__timeout_# = getWaitTime();
+			__do_run_# = true;
+		}
+
+		if ( a < 1 ) {
+			__timeout_# = 0
+			__do_run_# = true;
+		}
+
+		short int __index_# = -1;
+		__waitfor_mask_t __mask_# = {&__index_#, {__acceptables_#, ?}};
+		__waitfor_internal((__waitfor_mask_t&)__mask_#, __timeout_#);
+
+		switch (__index_#) {
+		case 0:
+			{ { fee(); } break; }
+		case 1:
+			{ { foe(); } break; }
+		case 2:
+			{ <modified-break> break; }
+		case 3:
+			{ <modified-break> break; }
+		case -2:
+			{ { fy(); } break; }
+		case -1:
+			{ { foe(); } break; }
+		}
+	}
+}...
+*/
+
+namespace Concurrency {
+
+namespace {
+
+class GenerateWaitForCore final :
+		public ast::WithSymbolTable, public ast::WithConstTranslationUnit {
+	const ast::FunctionDecl * decl_waitfor    = nullptr;
+	const ast::StructDecl   * decl_mask       = nullptr;
+	const ast::StructDecl   * decl_acceptable = nullptr;
+	const ast::StructDecl   * decl_monitor    = nullptr;
+
+	UniqueName namer_acc = "__acceptables_"s;
+	UniqueName namer_idx = "__index_"s;
+	UniqueName namer_flg = "__do_run_"s;
+	UniqueName namer_msk = "__mask_"s;
+	UniqueName namer_mon = "__monitors_"s;
+	UniqueName namer_tim = "__timeout_"s;
+	UniqueName namer_fun = "__function_"s;
+
+	ast::ObjectDecl * declareAcceptables( ast::CompoundStmt * out,
+		const CodeLocation & location, unsigned long numClauses );
+	ast::ObjectDecl * declareFlag(
+		ast::CompoundStmt * out, const CodeLocation & location );
+	ast::ExprStmt * makeSetter(
+		const CodeLocation & location, ast::ObjectDecl * flag );
+	ast::ObjectDecl * declMonitors(
+		ast::CompoundStmt * out, const ast::WaitForClause * clause );
+	void init_clause( ast::CompoundStmt * out, ast::ObjectDecl * acceptables,
+		int index, const ast::WaitForClause * clause, ast::Stmt * setter );
+	ast::Expr * init_timeout(
+		ast::CompoundStmt * out, const CodeLocation & topLocation,
+		const ast::Expr * timeout_time, const ast::Expr * timeout_cond,
+		const ast::Stmt * else_stmt, const ast::Expr * else_cond,
+		const ast::Stmt * setter );
+	ast::Expr * call(
+		ast::CompoundStmt * out, const CodeLocation & location,
+		size_t numClauses, ast::ObjectDecl * acceptables,
+		ast::Expr * timeout );
+public:
+	void previsit( const ast::FunctionDecl * decl );
+	void previsit( const ast::StructDecl * decl );
+	ast::Stmt * postvisit( const ast::WaitForStmt * stmt );
+};
+
+ast::Expr * makeOpIndex( const CodeLocation & location,
+		const ast::DeclWithType * array, unsigned long index ) {
+	return new ast::UntypedExpr( location,
+		new ast::NameExpr( location, "?[?]" ),
+		{
+			new ast::VariableExpr( location, array ),
+			ast::ConstantExpr::from_ulong( location, index ),
+		}
+	);
+}
+
+ast::Expr * makeOpAssign( const CodeLocation & location,
+		const ast::Expr * lhs, const ast::Expr * rhs ) {
+	return new ast::UntypedExpr( location,
+		new ast::NameExpr( location, "?=?" ),
+		{ lhs, rhs }
+	);
+}
+
+ast::Expr * makeOpMember( const CodeLocation & location,
+		const std::string & mem, const ast::Expr * sue ) {
+	return new ast::UntypedMemberExpr( location,
+		new ast::NameExpr( location, mem ),
+		sue
+	);
+}
+
+ast::Stmt * makeAccStmt(
+		const CodeLocation & location, ast::DeclWithType * object,
+		unsigned long index, const std::string & member,
+		const ast::Expr * value, const ResolveContext & context
+) {
+	ast::Expr * expr = makeOpAssign( location,
+		makeOpMember( location,
+			member,
+			makeOpIndex( location,
+				object,
+				index
+			)
+		),
+		value
+	);
+
+	auto result = ResolvExpr::findVoidExpression( expr, context );
+	return new ast::ExprStmt( location, result.get() );
+}
+
+const ast::Stmt * maybeCond( const CodeLocation & location,
+		const ast::Expr * cond, std::list<ast::ptr<ast::Stmt>> && stmts ) {
+	ast::Stmt * block = new ast::CompoundStmt( location, std::move( stmts ) );
+	return (cond) ? new ast::IfStmt( location, cond, block ) : block;
+}
+
+const ast::VariableExpr * extractVariable( const ast::Expr * func ) {
+	if ( auto var = dynamic_cast<const ast::VariableExpr *>( func ) ) {
+		return var;
+	}
+	auto cast = strict_dynamic_cast<const ast::CastExpr *>( func );
+	return cast->arg.strict_as<ast::VariableExpr>();
+}
+
+const ast::Expr * detectIsDtor(
+		const CodeLocation & location, const ast::Expr * func ) {
+	const ast::VariableExpr * typed_func = extractVariable( func );
+	bool is_dtor = InitTweak::isDestructor(
+		typed_func->var.strict_as<ast::FunctionDecl>() );
+	return ast::ConstantExpr::from_bool( location, is_dtor );
+}
+
+ast::ObjectDecl * GenerateWaitForCore::declareAcceptables(
+		ast::CompoundStmt * out,
+		const CodeLocation & location, unsigned long numClauses ) {
+	ast::ObjectDecl * acceptables = new ast::ObjectDecl( location,
+		namer_acc.newName(),
+		new ast::ArrayType(
+			new ast::StructInstType( decl_acceptable ),
+			ast::ConstantExpr::from_ulong( location, numClauses ),
+			ast::FixedLen,
+			ast::DynamicDim
+		)
+	);
+	out->push_back( new ast::DeclStmt( location, acceptables ) );
+
+	ast::Expr * set = new ast::UntypedExpr( location,
+		new ast::NameExpr( location, "__builtin_memset" ),
+		{
+			new ast::VariableExpr( location, acceptables ),
+			ast::ConstantExpr::from_int( location, 0 ),
+			new ast::SizeofExpr( location,
+				new ast::VariableExpr( location, acceptables ) ),
+		}
+	);
+	ResolveContext context{ symtab, transUnit().global };
+	auto result = ResolvExpr::findVoidExpression( set, context );
+	out->push_back( new ast::ExprStmt( location, result.get() ) );
+
+	return acceptables;
+}
+
+ast::ObjectDecl * GenerateWaitForCore::declareFlag(
+		ast::CompoundStmt * out, const CodeLocation & location ) {
+	ast::ObjectDecl * flag = new ast::ObjectDecl( location,
+		namer_flg.newName(),
+		new ast::BasicType( ast::BasicType::Bool ),
+		new ast::SingleInit( location,
+			ast::ConstantExpr::from_ulong( location, 0 )
+		)
+	);
+	out->push_back( new ast::DeclStmt( location, flag ) );
+	return flag;
+}
+
+ast::ExprStmt * GenerateWaitForCore::makeSetter(
+		const CodeLocation & location, ast::ObjectDecl * flag ) {
+	ast::Expr * expr = new ast::UntypedExpr( location,
+		new ast::NameExpr( location, "?=?" ),
+		{
+			new ast::VariableExpr( location, flag ),
+			ast::ConstantExpr::from_ulong( location, 1 ),
+		}
+	);
+	ResolveContext context{ symtab, transUnit().global };
+	auto result = ResolvExpr::findVoidExpression( expr, context );
+	return new ast::ExprStmt( location, result.get() );
+}
+
+ast::ObjectDecl * GenerateWaitForCore::declMonitors(
+		ast::CompoundStmt * out,
+		const ast::WaitForClause * clause ) {
+	const CodeLocation & location = clause->location;
+	ast::ObjectDecl * monitor = new ast::ObjectDecl( location,
+		namer_mon.newName(),
+		new ast::ArrayType(
+			new ast::PointerType(
+				new ast::StructInstType( decl_monitor )
+			),
+			ast::ConstantExpr::from_ulong( location, clause->target_args.size() ),
+			ast::FixedLen,
+			ast::DynamicDim
+		),
+		new ast::ListInit( location,
+			map_range<std::vector<ast::ptr<ast::Init>>>(
+				clause->target_args,
+				[]( const ast::Expr * expr ){
+					return new ast::SingleInit( expr->location, expr ); }
+			)
+		)
+	);
+	out->push_back( new ast::DeclStmt( location, monitor ) );
+	return monitor;
+}
+
+void GenerateWaitForCore::init_clause(
+		ast::CompoundStmt * out,
+		ast::ObjectDecl * acceptables,
+		int index,
+		const ast::WaitForClause * clause,
+		ast::Stmt * setter ) {
+	const CodeLocation & location = clause->location;
+	const ast::ObjectDecl * monitors = declMonitors( out, clause );
+	ast::Type * fptr_t = new ast::PointerType(
+			new ast::FunctionType( ast::VariableArgs ) );
+
+	const ast::VariableExpr * variableExpr =
+		clause->target.as<ast::VariableExpr>();
+	ast::Expr * castExpr = new ast::CastExpr(
+		location,
+		new ast::CastExpr(
+			location,
+			clause->target,
+			ast::deepCopy( variableExpr->result.get() ),
+			ast::GeneratedCast ),
+		fptr_t,
+		ast::GeneratedCast );
+
+	ast::ObjectDecl * funcDecl = new ast::ObjectDecl( location,
+		namer_fun.newName(),
+		ast::deepCopy( fptr_t ),
+		new ast::SingleInit( location, castExpr )
+		);
+	ast::Expr * funcExpr = new ast::VariableExpr( location, funcDecl );
+	out->push_back( new ast::DeclStmt( location, funcDecl ) );
+
+	ResolveContext context{ symtab, transUnit().global };
+	out->push_back( maybeCond( location, clause->when_cond.get(), {
+		makeAccStmt( location, acceptables, index, "is_dtor",
+			detectIsDtor( location, clause->target ), context ),
+		makeAccStmt( location, acceptables, index, "func",
+			funcExpr, context ),
+		makeAccStmt( location, acceptables, index, "data",
+			new ast::VariableExpr( location, monitors ), context ),
+		makeAccStmt( location, acceptables, index, "size",
+			ast::ConstantExpr::from_ulong( location,
+				clause->target_args.size() ), context ),
+		ast::deepCopy( setter ),
+	} ) );
+}
+
+ast::Expr * GenerateWaitForCore::init_timeout(
+		ast::CompoundStmt * out,
+		const CodeLocation & topLocation,
+		const ast::Expr * timeout_time,
+		const ast::Expr * timeout_cond,
+		const ast::Stmt * else_stmt,
+		const ast::Expr * else_cond,
+		const ast::Stmt * setter ) {
+	ast::ObjectDecl * timeout = new ast::ObjectDecl( topLocation,
+		namer_tim.newName(),
+		new ast::BasicType( ast::BasicType::LongLongUnsignedInt ),
+		new ast::SingleInit( topLocation,
+			ast::ConstantExpr::from_int( topLocation, -1 )
+		)
+	);
+	out->push_back( new ast::DeclStmt( topLocation, timeout ) );
+
+	if ( timeout_time ) {
+		const CodeLocation & location = timeout_time->location;
+		out->push_back( maybeCond( location, timeout_cond, {
+			new ast::ExprStmt( location,
+				makeOpAssign(
+					location,
+					new ast::VariableExpr( location, timeout ),
+					timeout_time
+				)
+			),
+			ast::deepCopy( setter ),
+		} ) );
+	}
+
+	// We only care about the else_stmt's presence and location.
+	if ( else_stmt ) {
+		const CodeLocation & location = else_stmt->location;
+		out->push_back( maybeCond( location, else_cond, {
+			new ast::ExprStmt( location,
+				makeOpAssign(
+					location,
+					new ast::VariableExpr( location, timeout ),
+					ast::ConstantExpr::from_ulong( location, 0 )
+				)
+			),
+			ast::deepCopy( setter ),
+		} ) );
+	}
+
+	return new ast::VariableExpr( topLocation, timeout );
+}
+
+ast::Expr * GenerateWaitForCore::call(
+	ast::CompoundStmt * out,
+	const CodeLocation & location,
+	size_t numClauses,
+	ast::ObjectDecl * acceptables,
+	ast::Expr * timeout
+) {
+	ast::ObjectDecl * index = new ast::ObjectDecl( location,
+		namer_idx.newName(),
+		new ast::BasicType( ast::BasicType::ShortSignedInt ),
+		new ast::SingleInit( location,
+			ast::ConstantExpr::from_int( location, -1 )
+		)
+	);
+	out->push_back( new ast::DeclStmt( location, index ) );
+
+	ast::ObjectDecl * mask = new ast::ObjectDecl( location,
+		namer_msk.newName(),
+		new ast::StructInstType( decl_mask ),
+		new ast::ListInit( location, {
+			new ast::SingleInit( location,
+				new ast::AddressExpr( location,
+					new ast::VariableExpr( location, index )
+				)
+			),
+			new ast::ListInit( location, {
+				new ast::SingleInit( location,
+					new ast::VariableExpr( location, acceptables )
+				),
+				new ast::SingleInit( location,
+					ast::ConstantExpr::from_ulong( location, numClauses )
+				),
+			}),
+		})
+	);
+	out->push_back( new ast::DeclStmt( location, mask ) );
+
+	ast::ApplicationExpr * waitforMask = new ast::ApplicationExpr( location,
+		ast::VariableExpr::functionPointer( location, decl_waitfor ),
+		{
+			new ast::CastExpr(
+				new ast::VariableExpr( location, mask ),
+				new ast::ReferenceType(
+					new ast::StructInstType( decl_mask )
+				)
+			),
+			timeout
+		}
+	);
+	out->push_back( new ast::ExprStmt( location, waitforMask ) );
+
+	return new ast::VariableExpr( location, index );
+}
+
+ast::Stmt * choose( const ast::WaitForStmt * waitfor, ast::Expr * result ) {
+	const CodeLocation & location = waitfor->location;
+
+	ast::SwitchStmt * theSwitch = new ast::SwitchStmt( location,
+		result,
+		std::vector<ast::ptr<ast::CaseClause>>()
+	);
+
+	for ( const auto & [i, clause] : enumerate( waitfor->clauses ) ) {
+		theSwitch->cases.push_back(
+			new ast::CaseClause( location,
+				ast::ConstantExpr::from_ulong( location, i ),
+				{
+					new ast::CompoundStmt( location, {
+						clause->stmt,
+						new ast::BranchStmt( location,
+							ast::BranchStmt::Break,
+							ast::Label( location )
+						)
+					})
+				}
+			)
+		);
+	}
+
+	if ( waitfor->timeout_stmt ) {
+		theSwitch->cases.push_back(
+			new ast::CaseClause( location,
+				ast::ConstantExpr::from_int( location, -2 ),
+				{
+					new ast::CompoundStmt( location, {
+						waitfor->timeout_stmt,
+						new ast::BranchStmt( location,
+							ast::BranchStmt::Break,
+							ast::Label( location )
+						)
+					})
+				}
+			)
+		);
+	}
+
+	if ( waitfor->else_stmt ) {
+		theSwitch->cases.push_back(
+			new ast::CaseClause( location,
+				ast::ConstantExpr::from_int( location, -1 ),
+				{
+					new ast::CompoundStmt( location, {
+						waitfor->else_stmt,
+						new ast::BranchStmt( location,
+							ast::BranchStmt::Break,
+							ast::Label( location )
+						)
+					})
+				}
+			)
+		);
+	}
+
+	return theSwitch;
+}
+
+void GenerateWaitForCore::previsit( const ast::FunctionDecl * decl ) {
+	if ( "__waitfor_internal" == decl->name ) {
+		decl_waitfor = decl;
+	}
+}
+
+void GenerateWaitForCore::previsit( const ast::StructDecl * decl ) {
+	if ( !decl->body ) {
+		return;
+	} else if ( "__acceptable_t" == decl->name ) {
+		assert( !decl_acceptable );
+		decl_acceptable = decl;
+	} else if ( "__waitfor_mask_t" == decl->name ) {
+		assert( !decl_mask );
+		decl_mask = decl;
+	} else if ( "monitor$" == decl->name ) {
+		assert( !decl_monitor );
+		decl_monitor = decl;
+	}
+}
+
+ast::Stmt * GenerateWaitForCore::postvisit( const ast::WaitForStmt * stmt ) {
+	if ( !decl_monitor || !decl_acceptable || !decl_mask ) {
+		SemanticError( stmt, "waitfor keyword requires monitors to be in scope, add #include <monitor.hfa>" );
+	}
+
+	const CodeLocation & location = stmt->location;
+	ast::CompoundStmt * comp = new ast::CompoundStmt( location );
+
+	ast::ObjectDecl * acceptables = declareAcceptables( comp, location, stmt->clauses.size() );
+	ast::ObjectDecl * flag        = declareFlag( comp, location );
+	ast::Stmt       * setter      = makeSetter( location, flag );
+
+	for ( const auto & [i, clause] : enumerate( stmt->clauses ) ) {
+		init_clause( comp, acceptables, i, clause, setter );
+	}
+
+	ast::Expr * timeout = init_timeout(
+		comp,
+		location,
+		stmt->timeout_time,
+		stmt->timeout_cond,
+		stmt->else_stmt,
+		stmt->else_cond,
+		setter
+	);
+
+	ast::CompoundStmt * compound = new ast::CompoundStmt( location );
+	comp->push_back( new ast::IfStmt( location,
+		new ast::VariableExpr( location, flag ),
+		compound,
+		nullptr
+	));
+
+	ast::Expr * result = call(
+		compound, location, stmt->clauses.size(), acceptables, timeout );
+	compound->push_back( choose( stmt, result ) );
+	return comp;
+}
+
+} // namespace
+
+void generateWaitFor( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<GenerateWaitForCore>::run( translationUnit );
+}
+
+} // namespace Concurrency
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/Concurrency/WaitforNew.cpp
===================================================================
--- src/Concurrency/WaitforNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,578 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// WaitforNew.cpp -- Expand waitfor clauses into code.
-//
-// Author           : Andrew Beach
-// Created On       : Fri May 27 10:31:00 2022
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Jun 13 13:30:00 2022
-// Update Count     : 0
-//
-
-#include "Waitfor.h"
-
-#include <string>
-
-#include "AST/Pass.hpp"
-#include "Common/UniqueName.h"
-#include "InitTweak/InitTweak.h"
-#include "ResolvExpr/Resolver.h"
-
-#include "AST/Print.hpp"
-
-using namespace std::string_literals;
-using ResolvExpr::ResolveContext;
-
-/* So this is what this file dones:
-
-void f(int i, float f, A & mutex b, struct foo *  );
-void f(int );
-
-...{
-	when ( a < 1 ) waitfor( f : a ) { fee(); }
-	or timeout( getWaitTime() ) { fy(); }
-	or waitfor( g : a ) { foe(); }
-	or waitfor( ^?{} : a ) { break; }
-	or waitfor( ^?{} ) { break; }
-	or when ( a < 1 ) else { fum(); }
-}...
-
-		 ||
-		 ||
-		\||/
-		 \/
-
-...{
-	{
-		__acceptable_t __acceptables_#[4 <num-clauses>];
-		bool __do_run_# = false;
-
-		monitor$ * __monitors_#[1 <num-monitors>] = { a };
-		if ( a < 1) {
-			void (*__function_#)() = <casts> f;
-			__acceptables_#[0].is_dtor = false;
-			__acceptables_#[0].func = __function_#;
-			__acceptables_#[0].data = __monitors_#;
-			__acceptables_#[0].size = 1;
-			__do_run_# = true;
-		}
-
-		// Remaining waitfor clauses go here.
-
-		long long unsigned int __timeout_# = -1;
-		if ( true ) {
-			__timeout_# = getWaitTime();
-			__do_run_# = true;
-		}
-
-		if ( a < 1 ) {
-			__timeout_# = 0
-			__do_run_# = true;
-		}
-
-		short int __index_# = -1;
-		__waitfor_mask_t __mask_# = {&__index_#, {__acceptables_#, ?}};
-		__waitfor_internal((__waitfor_mask_t&)__mask_#, __timeout_#);
-
-		switch (__index_#) {
-		case 0:
-			{ { fee(); } break; }
-		case 1:
-			{ { foe(); } break; }
-		case 2:
-			{ <modified-break> break; }
-		case 3:
-			{ <modified-break> break; }
-		case -2:
-			{ { fy(); } break; }
-		case -1:
-			{ { foe(); } break; }
-		}
-	}
-}...
-*/
-
-namespace Concurrency {
-
-namespace {
-
-class GenerateWaitForCore final :
-		public ast::WithSymbolTable, public ast::WithConstTranslationUnit {
-	const ast::FunctionDecl * decl_waitfor    = nullptr;
-	const ast::StructDecl   * decl_mask       = nullptr;
-	const ast::StructDecl   * decl_acceptable = nullptr;
-	const ast::StructDecl   * decl_monitor    = nullptr;
-
-	UniqueName namer_acc = "__acceptables_"s;
-	UniqueName namer_idx = "__index_"s;
-	UniqueName namer_flg = "__do_run_"s;
-	UniqueName namer_msk = "__mask_"s;
-	UniqueName namer_mon = "__monitors_"s;
-	UniqueName namer_tim = "__timeout_"s;
-	UniqueName namer_fun = "__function_"s;
-
-	ast::ObjectDecl * declareAcceptables( ast::CompoundStmt * out,
-		const CodeLocation & location, unsigned long numClauses );
-	ast::ObjectDecl * declareFlag(
-		ast::CompoundStmt * out, const CodeLocation & location );
-	ast::ExprStmt * makeSetter(
-		const CodeLocation & location, ast::ObjectDecl * flag );
-	ast::ObjectDecl * declMonitors(
-		ast::CompoundStmt * out, const ast::WaitForClause * clause );
-	void init_clause( ast::CompoundStmt * out, ast::ObjectDecl * acceptables,
-		int index, const ast::WaitForClause * clause, ast::Stmt * setter );
-	ast::Expr * init_timeout(
-		ast::CompoundStmt * out, const CodeLocation & topLocation,
-		const ast::Expr * timeout_time, const ast::Expr * timeout_cond,
-		const ast::Stmt * else_stmt, const ast::Expr * else_cond,
-		const ast::Stmt * setter );
-	ast::Expr * call(
-		ast::CompoundStmt * out, const CodeLocation & location,
-		size_t numClauses, ast::ObjectDecl * acceptables,
-		ast::Expr * timeout );
-public:
-	void previsit( const ast::FunctionDecl * decl );
-	void previsit( const ast::StructDecl * decl );
-	ast::Stmt * postvisit( const ast::WaitForStmt * stmt );
-};
-
-ast::Expr * makeOpIndex( const CodeLocation & location,
-		const ast::DeclWithType * array, unsigned long index ) {
-	return new ast::UntypedExpr( location,
-		new ast::NameExpr( location, "?[?]" ),
-		{
-			new ast::VariableExpr( location, array ),
-			ast::ConstantExpr::from_ulong( location, index ),
-		}
-	);
-}
-
-ast::Expr * makeOpAssign( const CodeLocation & location,
-		const ast::Expr * lhs, const ast::Expr * rhs ) {
-	return new ast::UntypedExpr( location,
-		new ast::NameExpr( location, "?=?" ),
-		{ lhs, rhs }
-	);
-}
-
-ast::Expr * makeOpMember( const CodeLocation & location,
-		const std::string & mem, const ast::Expr * sue ) {
-	return new ast::UntypedMemberExpr( location,
-		new ast::NameExpr( location, mem ),
-		sue
-	);
-}
-
-ast::Stmt * makeAccStmt(
-		const CodeLocation & location, ast::DeclWithType * object,
-		unsigned long index, const std::string & member,
-		const ast::Expr * value, const ResolveContext & context
-) {
-	ast::Expr * expr = makeOpAssign( location,
-		makeOpMember( location,
-			member,
-			makeOpIndex( location,
-				object,
-				index
-			)
-		),
-		value
-	);
-
-	auto result = ResolvExpr::findVoidExpression( expr, context );
-	return new ast::ExprStmt( location, result.get() );
-}
-
-const ast::Stmt * maybeCond( const CodeLocation & location,
-		const ast::Expr * cond, std::list<ast::ptr<ast::Stmt>> && stmts ) {
-	ast::Stmt * block = new ast::CompoundStmt( location, std::move( stmts ) );
-	return (cond) ? new ast::IfStmt( location, cond, block ) : block;
-}
-
-const ast::VariableExpr * extractVariable( const ast::Expr * func ) {
-	if ( auto var = dynamic_cast<const ast::VariableExpr *>( func ) ) {
-		return var;
-	}
-	auto cast = strict_dynamic_cast<const ast::CastExpr *>( func );
-	return cast->arg.strict_as<ast::VariableExpr>();
-}
-
-const ast::Expr * detectIsDtor(
-		const CodeLocation & location, const ast::Expr * func ) {
-	const ast::VariableExpr * typed_func = extractVariable( func );
-	bool is_dtor = InitTweak::isDestructor(
-		typed_func->var.strict_as<ast::FunctionDecl>() );
-	return ast::ConstantExpr::from_bool( location, is_dtor );
-}
-
-ast::ObjectDecl * GenerateWaitForCore::declareAcceptables(
-		ast::CompoundStmt * out,
-		const CodeLocation & location, unsigned long numClauses ) {
-	ast::ObjectDecl * acceptables = new ast::ObjectDecl( location,
-		namer_acc.newName(),
-		new ast::ArrayType(
-			new ast::StructInstType( decl_acceptable ),
-			ast::ConstantExpr::from_ulong( location, numClauses ),
-			ast::FixedLen,
-			ast::DynamicDim
-		)
-	);
-	out->push_back( new ast::DeclStmt( location, acceptables ) );
-
-	ast::Expr * set = new ast::UntypedExpr( location,
-		new ast::NameExpr( location, "__builtin_memset" ),
-		{
-			new ast::VariableExpr( location, acceptables ),
-			ast::ConstantExpr::from_int( location, 0 ),
-			new ast::SizeofExpr( location,
-				new ast::VariableExpr( location, acceptables ) ),
-		}
-	);
-	ResolveContext context{ symtab, transUnit().global };
-	auto result = ResolvExpr::findVoidExpression( set, context );
-	out->push_back( new ast::ExprStmt( location, result.get() ) );
-
-	return acceptables;
-}
-
-ast::ObjectDecl * GenerateWaitForCore::declareFlag(
-		ast::CompoundStmt * out, const CodeLocation & location ) {
-	ast::ObjectDecl * flag = new ast::ObjectDecl( location,
-		namer_flg.newName(),
-		new ast::BasicType( ast::BasicType::Bool ),
-		new ast::SingleInit( location,
-			ast::ConstantExpr::from_ulong( location, 0 )
-		)
-	);
-	out->push_back( new ast::DeclStmt( location, flag ) );
-	return flag;
-}
-
-ast::ExprStmt * GenerateWaitForCore::makeSetter(
-		const CodeLocation & location, ast::ObjectDecl * flag ) {
-	ast::Expr * expr = new ast::UntypedExpr( location,
-		new ast::NameExpr( location, "?=?" ),
-		{
-			new ast::VariableExpr( location, flag ),
-			ast::ConstantExpr::from_ulong( location, 1 ),
-		}
-	);
-	ResolveContext context{ symtab, transUnit().global };
-	auto result = ResolvExpr::findVoidExpression( expr, context );
-	return new ast::ExprStmt( location, result.get() );
-}
-
-ast::ObjectDecl * GenerateWaitForCore::declMonitors(
-		ast::CompoundStmt * out,
-		const ast::WaitForClause * clause ) {
-	const CodeLocation & location = clause->location;
-	ast::ObjectDecl * monitor = new ast::ObjectDecl( location,
-		namer_mon.newName(),
-		new ast::ArrayType(
-			new ast::PointerType(
-				new ast::StructInstType( decl_monitor )
-			),
-			ast::ConstantExpr::from_ulong( location, clause->target_args.size() ),
-			ast::FixedLen,
-			ast::DynamicDim
-		),
-		new ast::ListInit( location,
-			map_range<std::vector<ast::ptr<ast::Init>>>(
-				clause->target_args,
-				[]( const ast::Expr * expr ){
-					return new ast::SingleInit( expr->location, expr ); }
-			)
-		)
-	);
-	out->push_back( new ast::DeclStmt( location, monitor ) );
-	return monitor;
-}
-
-void GenerateWaitForCore::init_clause(
-		ast::CompoundStmt * out,
-		ast::ObjectDecl * acceptables,
-		int index,
-		const ast::WaitForClause * clause,
-		ast::Stmt * setter ) {
-	const CodeLocation & location = clause->location;
-	const ast::ObjectDecl * monitors = declMonitors( out, clause );
-	ast::Type * fptr_t = new ast::PointerType(
-			new ast::FunctionType( ast::VariableArgs ) );
-
-	const ast::VariableExpr * variableExpr =
-		clause->target.as<ast::VariableExpr>();
-	ast::Expr * castExpr = new ast::CastExpr(
-		location,
-		new ast::CastExpr(
-			location,
-			clause->target,
-			ast::deepCopy( variableExpr->result.get() ),
-			ast::GeneratedCast ),
-		fptr_t,
-		ast::GeneratedCast );
-
-	ast::ObjectDecl * funcDecl = new ast::ObjectDecl( location,
-		namer_fun.newName(),
-		ast::deepCopy( fptr_t ),
-		new ast::SingleInit( location, castExpr )
-		);
-	ast::Expr * funcExpr = new ast::VariableExpr( location, funcDecl );
-	out->push_back( new ast::DeclStmt( location, funcDecl ) );
-
-	ResolveContext context{ symtab, transUnit().global };
-	out->push_back( maybeCond( location, clause->when_cond.get(), {
-		makeAccStmt( location, acceptables, index, "is_dtor",
-			detectIsDtor( location, clause->target ), context ),
-		makeAccStmt( location, acceptables, index, "func",
-			funcExpr, context ),
-		makeAccStmt( location, acceptables, index, "data",
-			new ast::VariableExpr( location, monitors ), context ),
-		makeAccStmt( location, acceptables, index, "size",
-			ast::ConstantExpr::from_ulong( location,
-				clause->target_args.size() ), context ),
-		ast::deepCopy( setter ),
-	} ) );
-}
-
-ast::Expr * GenerateWaitForCore::init_timeout(
-		ast::CompoundStmt * out,
-		const CodeLocation & topLocation,
-		const ast::Expr * timeout_time,
-		const ast::Expr * timeout_cond,
-		const ast::Stmt * else_stmt,
-		const ast::Expr * else_cond,
-		const ast::Stmt * setter ) {
-	ast::ObjectDecl * timeout = new ast::ObjectDecl( topLocation,
-		namer_tim.newName(),
-		new ast::BasicType( ast::BasicType::LongLongUnsignedInt ),
-		new ast::SingleInit( topLocation,
-			ast::ConstantExpr::from_int( topLocation, -1 )
-		)
-	);
-	out->push_back( new ast::DeclStmt( topLocation, timeout ) );
-
-	if ( timeout_time ) {
-		const CodeLocation & location = timeout_time->location;
-		out->push_back( maybeCond( location, timeout_cond, {
-			new ast::ExprStmt( location,
-				makeOpAssign(
-					location,
-					new ast::VariableExpr( location, timeout ),
-					timeout_time
-				)
-			),
-			ast::deepCopy( setter ),
-		} ) );
-	}
-
-	// We only care about the else_stmt's presence and location.
-	if ( else_stmt ) {
-		const CodeLocation & location = else_stmt->location;
-		out->push_back( maybeCond( location, else_cond, {
-			new ast::ExprStmt( location,
-				makeOpAssign(
-					location,
-					new ast::VariableExpr( location, timeout ),
-					ast::ConstantExpr::from_ulong( location, 0 )
-				)
-			),
-			ast::deepCopy( setter ),
-		} ) );
-	}
-
-	return new ast::VariableExpr( topLocation, timeout );
-}
-
-ast::Expr * GenerateWaitForCore::call(
-	ast::CompoundStmt * out,
-	const CodeLocation & location,
-	size_t numClauses,
-	ast::ObjectDecl * acceptables,
-	ast::Expr * timeout
-) {
-	ast::ObjectDecl * index = new ast::ObjectDecl( location,
-		namer_idx.newName(),
-		new ast::BasicType( ast::BasicType::ShortSignedInt ),
-		new ast::SingleInit( location,
-			ast::ConstantExpr::from_int( location, -1 )
-		)
-	);
-	out->push_back( new ast::DeclStmt( location, index ) );
-
-	ast::ObjectDecl * mask = new ast::ObjectDecl( location,
-		namer_msk.newName(),
-		new ast::StructInstType( decl_mask ),
-		new ast::ListInit( location, {
-			new ast::SingleInit( location,
-				new ast::AddressExpr( location,
-					new ast::VariableExpr( location, index )
-				)
-			),
-			new ast::ListInit( location, {
-				new ast::SingleInit( location,
-					new ast::VariableExpr( location, acceptables )
-				),
-				new ast::SingleInit( location,
-					ast::ConstantExpr::from_ulong( location, numClauses )
-				),
-			}),
-		})
-	);
-	out->push_back( new ast::DeclStmt( location, mask ) );
-
-	ast::ApplicationExpr * waitforMask = new ast::ApplicationExpr( location,
-		ast::VariableExpr::functionPointer( location, decl_waitfor ),
-		{
-			new ast::CastExpr(
-				new ast::VariableExpr( location, mask ),
-				new ast::ReferenceType(
-					new ast::StructInstType( decl_mask )
-				)
-			),
-			timeout
-		}
-	);
-	out->push_back( new ast::ExprStmt( location, waitforMask ) );
-
-	return new ast::VariableExpr( location, index );
-}
-
-ast::Stmt * choose( const ast::WaitForStmt * waitfor, ast::Expr * result ) {
-	const CodeLocation & location = waitfor->location;
-
-	ast::SwitchStmt * theSwitch = new ast::SwitchStmt( location,
-		result,
-		std::vector<ast::ptr<ast::CaseClause>>()
-	);
-
-	for ( const auto & [i, clause] : enumerate( waitfor->clauses ) ) {
-		theSwitch->cases.push_back(
-			new ast::CaseClause( location,
-				ast::ConstantExpr::from_ulong( location, i ),
-				{
-					new ast::CompoundStmt( location, {
-						clause->stmt,
-						new ast::BranchStmt( location,
-							ast::BranchStmt::Break,
-							ast::Label( location )
-						)
-					})
-				}
-			)
-		);
-	}
-
-	if ( waitfor->timeout_stmt ) {
-		theSwitch->cases.push_back(
-			new ast::CaseClause( location,
-				ast::ConstantExpr::from_int( location, -2 ),
-				{
-					new ast::CompoundStmt( location, {
-						waitfor->timeout_stmt,
-						new ast::BranchStmt( location,
-							ast::BranchStmt::Break,
-							ast::Label( location )
-						)
-					})
-				}
-			)
-		);
-	}
-
-	if ( waitfor->else_stmt ) {
-		theSwitch->cases.push_back(
-			new ast::CaseClause( location,
-				ast::ConstantExpr::from_int( location, -1 ),
-				{
-					new ast::CompoundStmt( location, {
-						waitfor->else_stmt,
-						new ast::BranchStmt( location,
-							ast::BranchStmt::Break,
-							ast::Label( location )
-						)
-					})
-				}
-			)
-		);
-	}
-
-	return theSwitch;
-}
-
-void GenerateWaitForCore::previsit( const ast::FunctionDecl * decl ) {
-	if ( "__waitfor_internal" == decl->name ) {
-		decl_waitfor = decl;
-	}
-}
-
-void GenerateWaitForCore::previsit( const ast::StructDecl * decl ) {
-	if ( !decl->body ) {
-		return;
-	} else if ( "__acceptable_t" == decl->name ) {
-		assert( !decl_acceptable );
-		decl_acceptable = decl;
-	} else if ( "__waitfor_mask_t" == decl->name ) {
-		assert( !decl_mask );
-		decl_mask = decl;
-	} else if ( "monitor$" == decl->name ) {
-		assert( !decl_monitor );
-		decl_monitor = decl;
-	}
-}
-
-ast::Stmt * GenerateWaitForCore::postvisit( const ast::WaitForStmt * stmt ) {
-	if ( !decl_monitor || !decl_acceptable || !decl_mask ) {
-		SemanticError( stmt, "waitfor keyword requires monitors to be in scope, add #include <monitor.hfa>" );
-	}
-
-	const CodeLocation & location = stmt->location;
-	ast::CompoundStmt * comp = new ast::CompoundStmt( location );
-
-	ast::ObjectDecl * acceptables = declareAcceptables( comp, location, stmt->clauses.size() );
-	ast::ObjectDecl * flag        = declareFlag( comp, location );
-	ast::Stmt       * setter      = makeSetter( location, flag );
-
-	for ( const auto & [i, clause] : enumerate( stmt->clauses ) ) {
-		init_clause( comp, acceptables, i, clause, setter );
-	}
-
-	ast::Expr * timeout = init_timeout(
-		comp,
-		location,
-		stmt->timeout_time,
-		stmt->timeout_cond,
-		stmt->else_stmt,
-		stmt->else_cond,
-		setter
-	);
-
-	ast::CompoundStmt * compound = new ast::CompoundStmt( location );
-	comp->push_back( new ast::IfStmt( location,
-		new ast::VariableExpr( location, flag ),
-		compound,
-		nullptr
-	));
-
-	ast::Expr * result = call(
-		compound, location, stmt->clauses.size(), acceptables, timeout );
-	compound->push_back( choose( stmt, result ) );
-	return comp;
-}
-
-} // namespace
-
-void generateWaitFor( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<GenerateWaitForCore>::run( translationUnit );
-}
-
-} // namespace Concurrency
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Concurrency/module.mk
===================================================================
--- src/Concurrency/module.mk	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/Concurrency/module.mk	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -20,7 +20,7 @@
 	Concurrency/Corun.cpp \
 	Concurrency/Corun.hpp \
-	Concurrency/KeywordsNew.cpp \
+	Concurrency/Keywords.cpp \
 	Concurrency/Keywords.h \
-	Concurrency/WaitforNew.cpp \
+	Concurrency/Waitfor.cpp \
 	Concurrency/Waitfor.h \
 	Concurrency/Waituntil.cpp \
Index: src/ControlStruct/ExceptDecl.cpp
===================================================================
--- src/ControlStruct/ExceptDecl.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/ControlStruct/ExceptDecl.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,515 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ExceptDecl.cpp --
+//
+// Author           : Andrew Beach
+// Created On       : Tue Jul 12 15:50:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Jul 18 11:01:00 2022
+// Update Count     : 0
+//
+
+#include "ExceptDecl.h"
+
+#include <sstream>
+
+#include "AST/Copy.hpp"
+#include "AST/Decl.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Print.hpp"
+#include "AST/Type.hpp"
+#include "Virtual/Tables.h"
+
+namespace ControlStruct {
+
+namespace {
+
+std::vector<ast::ptr<ast::Expr>> forallToParams(
+		std::vector<ast::ptr<ast::TypeDecl>> const & forall ) {
+	return map_range<std::vector<ast::ptr<ast::Expr>>>( forall,
+		[]( ast::ptr<ast::TypeDecl> const & decl ) {
+			return new ast::TypeExpr( decl->location,
+				new ast::TypeInstType( decl->name, decl->kind ) );
+		}
+	);
+}
+
+// A slightly argumented extra constructor, adds a deepCopy.
+ast::StructInstType * namedStructInstType(
+		std::string const & name, ast::CV::Qualifiers qualifiers,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	ast::StructInstType * type = new ast::StructInstType( name, qualifiers );
+	for ( ast::ptr<ast::Expr> const & param : params ) {
+		type->params.push_back( ast::deepCopy( param ) );
+	}
+	return type;
+}
+
+ast::StructInstType * createExceptionInstType(
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	return namedStructInstType( exceptionName, ast::CV::Qualifiers(), params );
+}
+
+ast::StructInstType * createVTableInstType(
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	std::string name = Virtual::vtableTypeName( exceptionName );
+	return namedStructInstType( name, ast::CV::Const, params );
+}
+
+ast::StructInstType * createTypeIdInstType(
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	std::string name = Virtual::typeIdType( exceptionName );
+	return namedStructInstType( name, ast::CV::Const, params );
+}
+
+ast::FunctionType const * createCopyFuncType(
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	ast::FunctionType * type = new ast::FunctionType( ast::FixedArgs );
+	type->params.push_back( new ast::PointerType(
+		createExceptionInstType( exceptionName, params ) ) );
+	type->params.push_back( new ast::PointerType(
+		createExceptionInstType( exceptionName, params ) ) );
+	type->returns.push_back( new ast::VoidType() );
+	return type;
+}
+
+ast::FunctionType const * createDtorFuncType(
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	ast::FunctionType * type = new ast::FunctionType( ast::FixedArgs );
+	type->params.push_back( new ast::ReferenceType(
+		createExceptionInstType( exceptionName, params ) ) );
+	type->returns.push_back( new ast::VoidType() );
+	return type;
+}
+
+ast::FunctionType const * createMsgFuncType(
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	ast::FunctionType * type = new ast::FunctionType( ast::FixedArgs );
+	type->params.push_back( new ast::PointerType(
+		createExceptionInstType( exceptionName, params ) ) );
+	type->returns.push_back( new ast::PointerType(
+		new ast::BasicType( ast::BasicType::Char, ast::CV::Const ) ) );
+	return type;
+}
+
+ast::StructDecl const * createTypeIdStruct(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::TypeDecl>> const & forallClause ) {
+	ast::StructDecl * decl = new ast::StructDecl( location,
+			Virtual::typeIdType( exceptionName ) );
+	decl->members.push_back( new ast::ObjectDecl(
+		location,
+		"parent",
+		new ast::PointerType(
+			new ast::StructInstType( "__cfavir_type_info", ast::CV::Const ) )
+	) );
+	decl->body = true;
+	for ( ast::ptr<ast::TypeDecl> const & param : forallClause ) {
+		decl->params.push_back( ast::deepCopy( param ) );
+	}
+	return decl;
+}
+
+ast::ObjectDecl const * createTypeIdValue(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	ast::StructInstType * typeIdType =
+		createTypeIdInstType( exceptionName, params );
+	return new ast::ObjectDecl(
+		location,
+		Virtual::typeIdName( exceptionName ),
+		typeIdType,
+		new ast::ListInit( location, {
+			new ast::SingleInit( location,
+				new ast::AddressExpr( location,
+					new ast::NameExpr( location, "__cfatid_exception_t" ) ),
+				ast::MaybeConstruct ),
+		}, {}, ast::MaybeConstruct ),
+		ast::Storage::Classes(),
+		ast::Linkage::Cforall,
+		nullptr,
+		{ new ast::Attribute( "cfa_linkonce" ) }
+	);
+}
+
+ast::StructDecl const * createExceptionStructForward(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::TypeDecl>> const & forall ) {
+	ast::StructDecl * decl = new ast::StructDecl( location, exceptionName );
+	for ( ast::ptr<ast::TypeDecl> const & param : forall ) {
+		decl->params.push_back( ast::deepCopy( param ) );
+	}
+	return decl;
+}
+
+ast::StructDecl const * createVirtualTableStruct(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::TypeDecl>> const & forall,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	ast::StructInstType * typeIdType =
+		createTypeIdInstType( exceptionName, params );
+	ast::ObjectDecl * typeId = new ast::ObjectDecl(
+		location,
+		"__cfavir_typeid",
+		new ast::PointerType( typeIdType )
+	);
+	ast::ObjectDecl * size = new ast::ObjectDecl(
+		location,
+		"size",
+		new ast::TypeInstType( "size_t", ast::TypeDecl::Dtype )
+	);
+	ast::ObjectDecl * copy = new ast::ObjectDecl(
+		location,
+		"copy",
+		new ast::PointerType( createCopyFuncType( exceptionName, params ) )
+	);
+	ast::ObjectDecl * dtor = new ast::ObjectDecl(
+		location,
+		"^?{}",
+		new ast::PointerType( createDtorFuncType( exceptionName, params ) )
+	);
+	ast::ObjectDecl * msg = new ast::ObjectDecl(
+		location,
+		"msg",
+		new ast::PointerType( createMsgFuncType( exceptionName, params ) )
+	);
+	ast::StructDecl * decl = new ast::StructDecl(
+		location,
+		Virtual::vtableTypeName( exceptionName ) );
+	decl->members.push_back( typeId );
+	decl->members.push_back( size );
+	decl->members.push_back( copy );
+	decl->members.push_back( dtor );
+	decl->members.push_back( msg );
+	decl->body = true;
+	for ( ast::ptr<ast::TypeDecl> const & param : forall ) {
+		decl->params.push_back( param );
+	}
+	return decl;
+}
+
+ast::StructDecl const * createExceptionStruct(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::TypeDecl>> const & forallClause,
+		std::vector<ast::ptr<ast::Expr>> const & params,
+		std::vector<ast::ptr<ast::Decl>> const & members ) {
+	ast::StructDecl * decl = new ast::StructDecl( location, exceptionName );
+	decl->members.push_back( new ast::ObjectDecl(
+		location,
+		"virtual_table",
+		new ast::PointerType(
+			createVTableInstType( exceptionName, params ) )
+	) );
+	for ( ast::ptr<ast::Decl> const & member : members ) {
+		decl->members.push_back( ast::deepCopy( member ) );
+	}
+	decl->body = true;
+	for ( ast::ptr<ast::TypeDecl> const & param : forallClause ) {
+		decl->params.push_back( ast::deepCopy( param ) );
+	}
+	return decl;
+}
+
+ast::ObjectDecl const * createExternTypeId(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	return new ast::ObjectDecl(
+		location,
+		Virtual::typeIdName( exceptionName ),
+		createVTableInstType( exceptionName, params ),
+		nullptr,
+		ast::Storage::Extern,
+		ast::Linkage::Cforall,
+		nullptr,
+		{ new ast::Attribute( "cfa_linkonce" ) }
+	);
+}
+
+ast::ObjectDecl * createExternVTable(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params,
+		std::string const & tableName ) {
+	return new ast::ObjectDecl(
+		location,
+		tableName,
+		createVTableInstType( exceptionName, params ),
+		nullptr,
+		ast::Storage::Extern,
+		ast::Linkage::Cforall
+	);
+}
+
+ast::FunctionDecl const * createCopy(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	return new ast::FunctionDecl(
+		location,
+		"copy",
+		{/* forall */},
+		{/* assertions */},
+		{
+			new ast::ObjectDecl(
+				location,
+				"this",
+				new ast::PointerType(
+					createExceptionInstType( exceptionName, params ) )
+			),
+			new ast::ObjectDecl(
+				location,
+				"that",
+				new ast::PointerType(
+					createExceptionInstType( exceptionName, params ) )
+			),
+		},
+		{
+			new ast::ObjectDecl( location, "", new ast::VoidType() ),
+		},
+		new ast::CompoundStmt( location, {
+			new ast::ExprStmt( location,
+				new ast::UntypedExpr( location,
+					new ast::NameExpr( location, "?=?" ),
+					{
+						new ast::UntypedExpr( location,
+							new ast::NameExpr( location, "*?" ),
+							{ new ast::NameExpr( location, "this" ) } ),
+						new ast::UntypedExpr( location,
+							new ast::NameExpr( location, "*?" ),
+							{ new ast::NameExpr( location, "that" ) } ),
+					}
+				)
+			),
+		} ),
+		ast::Storage::Classes(),
+		ast::Linkage::Cforall,
+		{ new ast::Attribute( "cfa_linkonce" ) }
+	);
+}
+
+ast::FunctionDecl const * createMsg(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params ) {
+	std::stringstream msg;
+	msg << exceptionName;
+	// The forall variant, add parameters to the string.
+	if ( !params.empty() ) {
+		msg << "(";
+		bool first = true;
+		for ( auto & param : params ) {
+			// Seperator Logic: A comma proceeds all but the first object.
+			if ( first ) {
+				first = false;
+			} else {
+				msg << ", ";
+			}
+
+			ast::print( msg, param.get() );
+		}
+		msg << ")";
+	}
+	return new ast::FunctionDecl(
+		location,
+		"msg",
+		{/* forall */},
+		{/* assertions */},
+		{
+			new ast::ObjectDecl(
+				location,
+				"this",
+				new ast::PointerType(
+					createExceptionInstType( exceptionName, params ) )
+			),
+		},
+		{
+			new ast::ObjectDecl(
+				location,
+				"",
+				new ast::PointerType(
+					new ast::BasicType( ast::BasicType::Char, ast::CV::Const ) )
+			),
+		},
+		new ast::CompoundStmt( location, {
+			new ast::ReturnStmt( location,
+				ast::ConstantExpr::from_string( location, msg.str() )
+			),
+		} ),
+		ast::Storage::Classes(),
+		ast::Linkage::Cforall,
+		{ new ast::Attribute( "cfa_linkonce" ) }
+	);
+}
+
+ast::ObjectDecl * createVirtualTable(
+		CodeLocation const & location,
+		std::string const & exceptionName,
+		std::vector<ast::ptr<ast::Expr>> const & params,
+		std::string const & tableName ) {
+	ast::StructInstType * sizeType = new ast::StructInstType( exceptionName );
+	for ( ast::ptr<ast::Expr> const & param : params ) {
+		sizeType->params.push_back( ast::deepCopy( param ) );
+	}
+	std::vector<ast::ptr<ast::Init>> inits {
+		new ast::SingleInit( location,
+			new ast::AddressExpr( location,
+				new ast::NameExpr( location,
+					Virtual::typeIdName( exceptionName ) ) ) ),
+		new ast::SingleInit( location,
+			new ast::SizeofExpr( location, sizeType )  ),
+		new ast::SingleInit( location,
+			new ast::NameExpr( location, "copy" ) ),
+		new ast::SingleInit( location,
+			new ast::NameExpr( location, "^?{}" ) ),
+		new ast::SingleInit( location,
+			new ast::NameExpr( location, "msg" ) ),
+	};
+	std::vector<ast::ptr<ast::Designation>> dsigs {
+		new ast::Designation( location, {
+			new ast::NameExpr( location, "__cfavir_typeid" ) } ),
+		new ast::Designation( location, {
+			new ast::NameExpr( location, "size" ) } ),
+		new ast::Designation( location, {
+			new ast::NameExpr( location, "copy" ) } ),
+		new ast::Designation( location, {
+			new ast::NameExpr( location, "^?{}" ) } ),
+		new ast::Designation( location, {
+			new ast::NameExpr( location, "msg" ) } ),
+	};
+	return new ast::ObjectDecl(
+		location,
+		tableName,
+		createVTableInstType( exceptionName, params ),
+		new ast::ListInit( location, std::move( inits ), std::move( dsigs ) )
+	);
+}
+
+struct ExceptDeclCore : public ast::WithDeclsToAdd<> {
+	ast::StructDecl const * transformExcept( ast::StructDecl const * decl );
+	ast::ObjectDecl const * transformVTable(
+		ast::ObjectDecl const * decl, ast::VTableType const * type );
+
+	ast::StructDecl const * postvisit( ast::StructDecl const * decl ) {
+		// Exceptions don't get their own node type, so filter that.
+		if ( ast::AggregateDecl::Exception == decl->kind ) {
+			return transformExcept( decl );
+		}
+		return decl;
+	}
+
+	ast::ObjectDecl const * postvisit( ast::ObjectDecl const * decl ) {
+		// Modify remaining objects that have a vtable type.
+		if ( auto * type = decl->type.as<ast::VTableType>() ) {
+			return transformVTable( decl, type );
+		}
+		return decl;
+	}
+};
+
+ast::StructDecl const * ExceptDeclCore::transformExcept(
+		ast::StructDecl const * decl ) {
+	CodeLocation const & location = decl->location;
+	std::string const & exceptionName = decl->name;
+	std::vector<ast::ptr<ast::TypeDecl>> const & forall = decl->params;
+	std::vector<ast::ptr<ast::Expr>> params = forallToParams( forall );
+	std::vector<ast::ptr<ast::Decl>> const & members = decl->members;
+
+	declsToAddBefore.push_back(
+		createTypeIdStruct( location, exceptionName, forall ) );
+	if ( forall.empty() ) {
+		// Non-forall variant.
+		declsToAddBefore.push_back(
+			createTypeIdValue( location, exceptionName, params ) );
+	}
+	declsToAddBefore.push_back(
+		createExceptionStructForward( location, exceptionName, forall ) );
+	declsToAddBefore.push_back(
+		createVirtualTableStruct( location, exceptionName, forall, params ) );
+	return createExceptionStruct( location, exceptionName, forall, params, members );
+}
+
+ast::ObjectDecl const * ExceptDeclCore::transformVTable(
+		ast::ObjectDecl const * decl, ast::VTableType const * type ) {
+	CodeLocation const & location = decl->location;
+	auto base = type->base.strict_as<ast::TypeInstType>();
+	std::string const & exceptionName = base->name;
+	std::vector<ast::ptr<ast::Expr>> const & params = base->params;
+	std::string const & tableName = decl->name;
+
+	ast::ObjectDecl * retDecl;
+	if ( decl->storage.is_extern ) {
+		// Unique type-ids are only needed for polymorphic instances.
+		if ( !params.empty() ) {
+			declsToAddBefore.push_back(
+				createExternTypeId( location, exceptionName, params ) );
+		}
+		retDecl = createExternVTable( location, exceptionName, params, tableName );
+	} else {
+		// Unique type-ids are only needed for polymorphic instances.
+		if ( !params.empty() ) {
+			declsToAddBefore.push_back(
+				createTypeIdValue( location, exceptionName, params ) );
+		}
+		declsToAddBefore.push_back(
+			createCopy( location, exceptionName, params ) );
+		declsToAddBefore.push_back(
+			createMsg( location, exceptionName, params ) );
+		retDecl = createVirtualTable(
+			location, exceptionName, params, tableName );
+	}
+
+	for ( ast::ptr<ast::Attribute> const & attr : decl->attributes ) {
+		retDecl->attributes.push_back( attr );
+	}
+
+	return retDecl;
+}
+
+struct VTableCore {
+	ast::StructInstType const * postvisit( ast::VTableType const * type ) {
+		auto inst = type->base.as<ast::BaseInstType>();
+
+		std::string vtableName = Virtual::vtableTypeName( inst->name );
+
+		auto newType = new ast::StructInstType( vtableName );
+		for ( ast::ptr<ast::Expr> const & param : inst->params ) {
+			newType->params.push_back( param );
+		}
+
+		return newType;
+	}
+};
+
+} // namespace
+
+void translateExcept( ast::TranslationUnit & translationUnit ) {
+	// Can I combine these?
+	// Second pass really only covers what the first has missed.
+	// Maybe if the first one is all previsits and the second all postvisit.
+	ast::Pass<ExceptDeclCore>::run( translationUnit );
+	ast::Pass<VTableCore>::run( translationUnit );
+}
+
+} // namespace ControlStruct
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/ControlStruct/ExceptDeclNew.cpp
===================================================================
--- src/ControlStruct/ExceptDeclNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,515 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ExceptDeclNew.cpp --
-//
-// Author           : Andrew Beach
-// Created On       : Tue Jul 12 15:50:00 2022
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Jul 18 11:01:00 2022
-// Update Count     : 0
-//
-
-#include "ExceptDecl.h"
-
-#include <sstream>
-
-#include "AST/Copy.hpp"
-#include "AST/Decl.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Print.hpp"
-#include "AST/Type.hpp"
-#include "Virtual/Tables.h"
-
-namespace ControlStruct {
-
-namespace {
-
-std::vector<ast::ptr<ast::Expr>> forallToParams(
-		std::vector<ast::ptr<ast::TypeDecl>> const & forall ) {
-	return map_range<std::vector<ast::ptr<ast::Expr>>>( forall,
-		[]( ast::ptr<ast::TypeDecl> const & decl ) {
-			return new ast::TypeExpr( decl->location,
-				new ast::TypeInstType( decl->name, decl->kind ) );
-		}
-	);
-}
-
-// A slightly argumented extra constructor, adds a deepCopy.
-ast::StructInstType * namedStructInstType(
-		std::string const & name, ast::CV::Qualifiers qualifiers,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	ast::StructInstType * type = new ast::StructInstType( name, qualifiers );
-	for ( ast::ptr<ast::Expr> const & param : params ) {
-		type->params.push_back( ast::deepCopy( param ) );
-	}
-	return type;
-}
-
-ast::StructInstType * createExceptionInstType(
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	return namedStructInstType( exceptionName, ast::CV::Qualifiers(), params );
-}
-
-ast::StructInstType * createVTableInstType(
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	std::string name = Virtual::vtableTypeName( exceptionName );
-	return namedStructInstType( name, ast::CV::Const, params );
-}
-
-ast::StructInstType * createTypeIdInstType(
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	std::string name = Virtual::typeIdType( exceptionName );
-	return namedStructInstType( name, ast::CV::Const, params );
-}
-
-ast::FunctionType const * createCopyFuncType(
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	ast::FunctionType * type = new ast::FunctionType( ast::FixedArgs );
-	type->params.push_back( new ast::PointerType(
-		createExceptionInstType( exceptionName, params ) ) );
-	type->params.push_back( new ast::PointerType(
-		createExceptionInstType( exceptionName, params ) ) );
-	type->returns.push_back( new ast::VoidType() );
-	return type;
-}
-
-ast::FunctionType const * createDtorFuncType(
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	ast::FunctionType * type = new ast::FunctionType( ast::FixedArgs );
-	type->params.push_back( new ast::ReferenceType(
-		createExceptionInstType( exceptionName, params ) ) );
-	type->returns.push_back( new ast::VoidType() );
-	return type;
-}
-
-ast::FunctionType const * createMsgFuncType(
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	ast::FunctionType * type = new ast::FunctionType( ast::FixedArgs );
-	type->params.push_back( new ast::PointerType(
-		createExceptionInstType( exceptionName, params ) ) );
-	type->returns.push_back( new ast::PointerType(
-		new ast::BasicType( ast::BasicType::Char, ast::CV::Const ) ) );
-	return type;
-}
-
-ast::StructDecl const * createTypeIdStruct(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::TypeDecl>> const & forallClause ) {
-	ast::StructDecl * decl = new ast::StructDecl( location,
-			Virtual::typeIdType( exceptionName ) );
-	decl->members.push_back( new ast::ObjectDecl(
-		location,
-		"parent",
-		new ast::PointerType(
-			new ast::StructInstType( "__cfavir_type_info", ast::CV::Const ) )
-	) );
-	decl->body = true;
-	for ( ast::ptr<ast::TypeDecl> const & param : forallClause ) {
-		decl->params.push_back( ast::deepCopy( param ) );
-	}
-	return decl;
-}
-
-ast::ObjectDecl const * createTypeIdValue(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	ast::StructInstType * typeIdType =
-		createTypeIdInstType( exceptionName, params );
-	return new ast::ObjectDecl(
-		location,
-		Virtual::typeIdName( exceptionName ),
-		typeIdType,
-		new ast::ListInit( location, {
-			new ast::SingleInit( location,
-				new ast::AddressExpr( location,
-					new ast::NameExpr( location, "__cfatid_exception_t" ) ),
-				ast::MaybeConstruct ),
-		}, {}, ast::MaybeConstruct ),
-		ast::Storage::Classes(),
-		ast::Linkage::Cforall,
-		nullptr,
-		{ new ast::Attribute( "cfa_linkonce" ) }
-	);
-}
-
-ast::StructDecl const * createExceptionStructForward(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::TypeDecl>> const & forall ) {
-	ast::StructDecl * decl = new ast::StructDecl( location, exceptionName );
-	for ( ast::ptr<ast::TypeDecl> const & param : forall ) {
-		decl->params.push_back( ast::deepCopy( param ) );
-	}
-	return decl;
-}
-
-ast::StructDecl const * createVirtualTableStruct(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::TypeDecl>> const & forall,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	ast::StructInstType * typeIdType =
-		createTypeIdInstType( exceptionName, params );
-	ast::ObjectDecl * typeId = new ast::ObjectDecl(
-		location,
-		"__cfavir_typeid",
-		new ast::PointerType( typeIdType )
-	);
-	ast::ObjectDecl * size = new ast::ObjectDecl(
-		location,
-		"size",
-		new ast::TypeInstType( "size_t", ast::TypeDecl::Dtype )
-	);
-	ast::ObjectDecl * copy = new ast::ObjectDecl(
-		location,
-		"copy",
-		new ast::PointerType( createCopyFuncType( exceptionName, params ) )
-	);
-	ast::ObjectDecl * dtor = new ast::ObjectDecl(
-		location,
-		"^?{}",
-		new ast::PointerType( createDtorFuncType( exceptionName, params ) )
-	);
-	ast::ObjectDecl * msg = new ast::ObjectDecl(
-		location,
-		"msg",
-		new ast::PointerType( createMsgFuncType( exceptionName, params ) )
-	);
-	ast::StructDecl * decl = new ast::StructDecl(
-		location,
-		Virtual::vtableTypeName( exceptionName ) );
-	decl->members.push_back( typeId );
-	decl->members.push_back( size );
-	decl->members.push_back( copy );
-	decl->members.push_back( dtor );
-	decl->members.push_back( msg );
-	decl->body = true;
-	for ( ast::ptr<ast::TypeDecl> const & param : forall ) {
-		decl->params.push_back( param );
-	}
-	return decl;
-}
-
-ast::StructDecl const * createExceptionStruct(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::TypeDecl>> const & forallClause,
-		std::vector<ast::ptr<ast::Expr>> const & params,
-		std::vector<ast::ptr<ast::Decl>> const & members ) {
-	ast::StructDecl * decl = new ast::StructDecl( location, exceptionName );
-	decl->members.push_back( new ast::ObjectDecl(
-		location,
-		"virtual_table",
-		new ast::PointerType(
-			createVTableInstType( exceptionName, params ) )
-	) );
-	for ( ast::ptr<ast::Decl> const & member : members ) {
-		decl->members.push_back( ast::deepCopy( member ) );
-	}
-	decl->body = true;
-	for ( ast::ptr<ast::TypeDecl> const & param : forallClause ) {
-		decl->params.push_back( ast::deepCopy( param ) );
-	}
-	return decl;
-}
-
-ast::ObjectDecl const * createExternTypeId(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	return new ast::ObjectDecl(
-		location,
-		Virtual::typeIdName( exceptionName ),
-		createVTableInstType( exceptionName, params ),
-		nullptr,
-		ast::Storage::Extern,
-		ast::Linkage::Cforall,
-		nullptr,
-		{ new ast::Attribute( "cfa_linkonce" ) }
-	);
-}
-
-ast::ObjectDecl * createExternVTable(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params,
-		std::string const & tableName ) {
-	return new ast::ObjectDecl(
-		location,
-		tableName,
-		createVTableInstType( exceptionName, params ),
-		nullptr,
-		ast::Storage::Extern,
-		ast::Linkage::Cforall
-	);
-}
-
-ast::FunctionDecl const * createCopy(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	return new ast::FunctionDecl(
-		location,
-		"copy",
-		{/* forall */},
-		{/* assertions */},
-		{
-			new ast::ObjectDecl(
-				location,
-				"this",
-				new ast::PointerType(
-					createExceptionInstType( exceptionName, params ) )
-			),
-			new ast::ObjectDecl(
-				location,
-				"that",
-				new ast::PointerType(
-					createExceptionInstType( exceptionName, params ) )
-			),
-		},
-		{
-			new ast::ObjectDecl( location, "", new ast::VoidType() ),
-		},
-		new ast::CompoundStmt( location, {
-			new ast::ExprStmt( location,
-				new ast::UntypedExpr( location,
-					new ast::NameExpr( location, "?=?" ),
-					{
-						new ast::UntypedExpr( location,
-							new ast::NameExpr( location, "*?" ),
-							{ new ast::NameExpr( location, "this" ) } ),
-						new ast::UntypedExpr( location,
-							new ast::NameExpr( location, "*?" ),
-							{ new ast::NameExpr( location, "that" ) } ),
-					}
-				)
-			),
-		} ),
-		ast::Storage::Classes(),
-		ast::Linkage::Cforall,
-		{ new ast::Attribute( "cfa_linkonce" ) }
-	);
-}
-
-ast::FunctionDecl const * createMsg(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params ) {
-	std::stringstream msg;
-	msg << exceptionName;
-	// The forall variant, add parameters to the string.
-	if ( !params.empty() ) {
-		msg << "(";
-		bool first = true;
-		for ( auto & param : params ) {
-			// Seperator Logic: A comma proceeds all but the first object.
-			if ( first ) {
-				first = false;
-			} else {
-				msg << ", ";
-			}
-
-			ast::print( msg, param.get() );
-		}
-		msg << ")";
-	}
-	return new ast::FunctionDecl(
-		location,
-		"msg",
-		{/* forall */},
-		{/* assertions */},
-		{
-			new ast::ObjectDecl(
-				location,
-				"this",
-				new ast::PointerType(
-					createExceptionInstType( exceptionName, params ) )
-			),
-		},
-		{
-			new ast::ObjectDecl(
-				location,
-				"",
-				new ast::PointerType(
-					new ast::BasicType( ast::BasicType::Char, ast::CV::Const ) )
-			),
-		},
-		new ast::CompoundStmt( location, {
-			new ast::ReturnStmt( location,
-				ast::ConstantExpr::from_string( location, msg.str() )
-			),
-		} ),
-		ast::Storage::Classes(),
-		ast::Linkage::Cforall,
-		{ new ast::Attribute( "cfa_linkonce" ) }
-	);
-}
-
-ast::ObjectDecl * createVirtualTable(
-		CodeLocation const & location,
-		std::string const & exceptionName,
-		std::vector<ast::ptr<ast::Expr>> const & params,
-		std::string const & tableName ) {
-	ast::StructInstType * sizeType = new ast::StructInstType( exceptionName );
-	for ( ast::ptr<ast::Expr> const & param : params ) {
-		sizeType->params.push_back( ast::deepCopy( param ) );
-	}
-	std::vector<ast::ptr<ast::Init>> inits {
-		new ast::SingleInit( location,
-			new ast::AddressExpr( location,
-				new ast::NameExpr( location,
-					Virtual::typeIdName( exceptionName ) ) ) ),
-		new ast::SingleInit( location,
-			new ast::SizeofExpr( location, sizeType )  ),
-		new ast::SingleInit( location,
-			new ast::NameExpr( location, "copy" ) ),
-		new ast::SingleInit( location,
-			new ast::NameExpr( location, "^?{}" ) ),
-		new ast::SingleInit( location,
-			new ast::NameExpr( location, "msg" ) ),
-	};
-	std::vector<ast::ptr<ast::Designation>> dsigs {
-		new ast::Designation( location, {
-			new ast::NameExpr( location, "__cfavir_typeid" ) } ),
-		new ast::Designation( location, {
-			new ast::NameExpr( location, "size" ) } ),
-		new ast::Designation( location, {
-			new ast::NameExpr( location, "copy" ) } ),
-		new ast::Designation( location, {
-			new ast::NameExpr( location, "^?{}" ) } ),
-		new ast::Designation( location, {
-			new ast::NameExpr( location, "msg" ) } ),
-	};
-	return new ast::ObjectDecl(
-		location,
-		tableName,
-		createVTableInstType( exceptionName, params ),
-		new ast::ListInit( location, std::move( inits ), std::move( dsigs ) )
-	);
-}
-
-struct ExceptDeclCore : public ast::WithDeclsToAdd<> {
-	ast::StructDecl const * transformExcept( ast::StructDecl const * decl );
-	ast::ObjectDecl const * transformVTable(
-		ast::ObjectDecl const * decl, ast::VTableType const * type );
-
-	ast::StructDecl const * postvisit( ast::StructDecl const * decl ) {
-		// Exceptions don't get their own node type, so filter that.
-		if ( ast::AggregateDecl::Exception == decl->kind ) {
-			return transformExcept( decl );
-		}
-		return decl;
-	}
-
-	ast::ObjectDecl const * postvisit( ast::ObjectDecl const * decl ) {
-		// Modify remaining objects that have a vtable type.
-		if ( auto * type = decl->type.as<ast::VTableType>() ) {
-			return transformVTable( decl, type );
-		}
-		return decl;
-	}
-};
-
-ast::StructDecl const * ExceptDeclCore::transformExcept(
-		ast::StructDecl const * decl ) {
-	CodeLocation const & location = decl->location;
-	std::string const & exceptionName = decl->name;
-	std::vector<ast::ptr<ast::TypeDecl>> const & forall = decl->params;
-	std::vector<ast::ptr<ast::Expr>> params = forallToParams( forall );
-	std::vector<ast::ptr<ast::Decl>> const & members = decl->members;
-
-	declsToAddBefore.push_back(
-		createTypeIdStruct( location, exceptionName, forall ) );
-	if ( forall.empty() ) {
-		// Non-forall variant.
-		declsToAddBefore.push_back(
-			createTypeIdValue( location, exceptionName, params ) );
-	}
-	declsToAddBefore.push_back(
-		createExceptionStructForward( location, exceptionName, forall ) );
-	declsToAddBefore.push_back(
-		createVirtualTableStruct( location, exceptionName, forall, params ) );
-	return createExceptionStruct( location, exceptionName, forall, params, members );
-}
-
-ast::ObjectDecl const * ExceptDeclCore::transformVTable(
-		ast::ObjectDecl const * decl, ast::VTableType const * type ) {
-	CodeLocation const & location = decl->location;
-	auto base = type->base.strict_as<ast::TypeInstType>();
-	std::string const & exceptionName = base->name;
-	std::vector<ast::ptr<ast::Expr>> const & params = base->params;
-	std::string const & tableName = decl->name;
-
-	ast::ObjectDecl * retDecl;
-	if ( decl->storage.is_extern ) {
-		// Unique type-ids are only needed for polymorphic instances.
-		if ( !params.empty() ) {
-			declsToAddBefore.push_back(
-				createExternTypeId( location, exceptionName, params ) );
-		}
-		retDecl = createExternVTable( location, exceptionName, params, tableName );
-	} else {
-		// Unique type-ids are only needed for polymorphic instances.
-		if ( !params.empty() ) {
-			declsToAddBefore.push_back(
-				createTypeIdValue( location, exceptionName, params ) );
-		}
-		declsToAddBefore.push_back(
-			createCopy( location, exceptionName, params ) );
-		declsToAddBefore.push_back(
-			createMsg( location, exceptionName, params ) );
-		retDecl = createVirtualTable(
-			location, exceptionName, params, tableName );
-	}
-
-	for ( ast::ptr<ast::Attribute> const & attr : decl->attributes ) {
-		retDecl->attributes.push_back( attr );
-	}
-
-	return retDecl;
-}
-
-struct VTableCore {
-	ast::StructInstType const * postvisit( ast::VTableType const * type ) {
-		auto inst = type->base.as<ast::BaseInstType>();
-
-		std::string vtableName = Virtual::vtableTypeName( inst->name );
-
-		auto newType = new ast::StructInstType( vtableName );
-		for ( ast::ptr<ast::Expr> const & param : inst->params ) {
-			newType->params.push_back( param );
-		}
-
-		return newType;
-	}
-};
-
-} // namespace
-
-void translateExcept( ast::TranslationUnit & translationUnit ) {
-	// Can I combine these?
-	// Second pass really only covers what the first has missed.
-	// Maybe if the first one is all previsits and the second all postvisit.
-	ast::Pass<ExceptDeclCore>::run( translationUnit );
-	ast::Pass<VTableCore>::run( translationUnit );
-}
-
-} // namespace ControlStruct
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ControlStruct/ExceptTranslate.cpp
===================================================================
--- src/ControlStruct/ExceptTranslate.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/ControlStruct/ExceptTranslate.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,725 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ExceptTranslate.cpp -- Conversion of exception control flow structures.
+//
+// Author           : Andrew Beach
+// Created On       : Mon Nov  8 11:53:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Mar 11 17:51:00 2022
+// Update Count     : 2
+//
+
+#include "ExceptTranslate.h"
+
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/TranslationUnit.hpp"
+#include "AST/DeclReplacer.hpp"
+
+namespace ControlStruct {
+
+namespace {
+
+	typedef std::list<ast::CatchClause*> CatchList;
+
+	void appendDeclStmt( ast::CompoundStmt * block, ast::DeclWithType * item ) {
+		block->push_back(new ast::DeclStmt(block->location, item));
+	}
+
+class TranslateThrowsCore final : public ast::WithGuards {
+	const ast::ObjectDecl * terminateHandlerExcept;
+	enum Context { NoHandler, TerHandler, ResHandler } currentContext;
+
+	const ast::Stmt * createEitherThrow(
+		const ast::ThrowStmt * throwStmt, const char * funcName );
+	const ast::Stmt * createTerminateRethrow( const ast::ThrowStmt * );
+
+public:
+	TranslateThrowsCore() :
+		terminateHandlerExcept( nullptr ), currentContext( NoHandler )
+	{}
+
+	void previsit( const ast::CatchClause * stmt );
+	const ast::Stmt * postvisit( const ast::ThrowStmt * stmt );
+};
+
+const ast::Stmt * TranslateThrowsCore::createEitherThrow(
+		const ast::ThrowStmt * throwStmt, const char * funcName ) {
+	// `throwFunc`( `throwStmt->name` );
+	ast::UntypedExpr * call = new ast::UntypedExpr( throwStmt->location,
+		new ast::NameExpr( throwStmt->location, funcName )
+	);
+	call->args.push_back( throwStmt->expr );
+	return new ast::ExprStmt( throwStmt->location, call );
+}
+
+ast::VariableExpr * varOf( const ast::DeclWithType * decl ) {
+	return new ast::VariableExpr( decl->location, decl );
+}
+
+const ast::Stmt * TranslateThrowsCore::createTerminateRethrow(
+		const ast::ThrowStmt * stmt ) {
+	// { `terminate_handler_except` = 0p; __rethrow_terminate(); }
+	assert( nullptr == stmt->expr );
+	assert( terminateHandlerExcept );
+
+	ast::CompoundStmt * result = new ast::CompoundStmt(
+		stmt->location, {}, std::vector<ast::Label>( stmt->labels ) );
+	result->push_back( new ast::ExprStmt( stmt->location,
+		ast::UntypedExpr::createAssign(
+			stmt->location,
+			varOf( terminateHandlerExcept ),
+			ast::ConstantExpr::null(
+				stmt->location,
+				terminateHandlerExcept->type
+			)
+		)
+	) );
+	result->push_back( new ast::ExprStmt( stmt->location, new ast::UntypedExpr(
+		stmt->location,
+		new ast::NameExpr( stmt->location, "__cfaehm_rethrow_terminate" )
+	) ) );
+	return result;
+}
+
+void TranslateThrowsCore::previsit( const ast::CatchClause * stmt ) {
+	// Validate the statement's form.
+	const ast::ObjectDecl * decl = stmt->decl.as<ast::ObjectDecl>();
+	// Also checking the type would be nice.
+	if ( !decl || !decl->type.as<ast::PointerType>() ) {
+		std::string kind = (ast::Terminate == stmt->kind) ? "catch" : "catchResume";
+		SemanticError( stmt->location, kind + " must have pointer to an exception type" );
+	}
+
+	// Track the handler context.
+	if ( ast::Terminate == stmt->kind ) {
+		GuardValue( currentContext ) = TerHandler;
+		GuardValue( terminateHandlerExcept ) = decl;
+	} else {
+		GuardValue( currentContext ) = ResHandler;
+	}
+}
+
+const ast::Stmt * TranslateThrowsCore::postvisit(
+		const ast::ThrowStmt * stmt ) {
+	// Ignoring ThrowStmt::target for now.
+	// Handle Termination (Raise, Reraise, Error):
+	if ( ast::Terminate == stmt->kind ) {
+		if ( stmt->expr ) {
+			return createEitherThrow( stmt, "$throw" );
+		} else if ( TerHandler == currentContext ) {
+			return createTerminateRethrow( stmt );
+		} else {
+			abort( "Invalid throw in %s at %i\n",
+				stmt->location.filename.c_str(),
+				stmt->location.first_line);
+		}
+	// Handle Resumption (Raise, Reraise, Error):
+	} else {
+		if ( stmt->expr ) {
+			return createEitherThrow( stmt, "$throwResume" );
+		} else if ( ResHandler == currentContext ) {
+			// This has to be handled later.
+			return stmt;
+		} else {
+			abort( "Invalid throwResume in %s at %i\n",
+				stmt->location.filename.c_str(),
+				stmt->location.first_line);
+		}
+	}
+}
+
+
+class TryMutatorCore final {
+	// The built in types used in translation.
+	const ast::StructDecl * except_decl;
+	const ast::StructDecl * node_decl;
+	const ast::StructDecl * hook_decl;
+
+	// The many helper functions for code/syntree generation.
+	ast::CompoundStmt * take_try_block( ast::TryStmt * tryStmt );
+	ast::FunctionDecl * create_try_wrapper( const ast::CompoundStmt * body );
+	ast::FunctionDecl * create_terminate_catch( CatchList &handlers );
+	ast::CompoundStmt * create_single_matcher(
+		const ast::DeclWithType * except_obj, ast::CatchClause * modded_handler );
+	ast::FunctionDecl * create_terminate_match( CatchList &handlers );
+	ast::CompoundStmt * create_terminate_caller( CodeLocation loc, ast::FunctionDecl * try_wrapper,
+		ast::FunctionDecl * terminate_catch, ast::FunctionDecl * terminate_match );
+	ast::FunctionDecl * create_resume_handler( CatchList &handlers );
+	ast::CompoundStmt * create_resume_wrapper(
+		const ast::Stmt * wraps, const ast::FunctionDecl * resume_handler );
+	ast::FunctionDecl * create_finally_wrapper( ast::TryStmt * tryStmt );
+	ast::ObjectDecl * create_finally_hook( ast::FunctionDecl * finally_wrapper );
+	ast::Stmt * create_resume_rethrow( const ast::ThrowStmt * throwStmt );
+
+	// Types used in translation, first group are internal.
+	ast::ObjectDecl * make_index_object( CodeLocation const & ) const;
+	ast::ObjectDecl * make_exception_object( CodeLocation const & ) const;
+	ast::ObjectDecl * make_bool_object( CodeLocation const & ) const;
+	ast::ObjectDecl * make_voidptr_object( CodeLocation const & ) const;
+	ast::ObjectDecl * make_unused_index_object( CodeLocation const & ) const;
+	// void (*function)();
+	ast::FunctionDecl * make_try_function( CodeLocation const & ) const;
+	// void (*function)(int, exception);
+	ast::FunctionDecl * make_catch_function( CodeLocation const & ) const;
+	// int (*function)(exception);
+	ast::FunctionDecl * make_match_function( CodeLocation const & ) const;
+	// bool (*function)(exception);
+	ast::FunctionDecl * make_handle_function( CodeLocation const & ) const;
+	// void (*function)(__attribute__((unused)) void *);
+	ast::FunctionDecl * make_finally_function( CodeLocation const & ) const;
+
+public:
+	TryMutatorCore() :
+		except_decl( nullptr ), node_decl( nullptr ), hook_decl( nullptr )
+	{}
+
+	void previsit( const ast::StructDecl *structDecl );
+	ast::Stmt * postvisit( const ast::TryStmt *tryStmt );
+	ast::Stmt * postvisit( const ast::ThrowStmt *throwStmt );
+};
+
+ast::ObjectDecl * TryMutatorCore::make_index_object(
+		CodeLocation const & location ) const {
+	return new ast::ObjectDecl(
+		location,
+		"__handler_index",
+		new ast::BasicType( ast::BasicType::SignedInt )
+		);
+}
+
+ast::ObjectDecl * TryMutatorCore::make_exception_object(
+		CodeLocation const & location ) const {
+	assert( except_decl );
+	return new ast::ObjectDecl(
+		location,
+		"__exception_inst",
+		new ast::PointerType( new ast::StructInstType( except_decl ) )
+		);
+}
+
+ast::ObjectDecl * TryMutatorCore::make_bool_object(
+		CodeLocation const & location ) const {
+	return new ast::ObjectDecl(
+		location,
+		"__ret_bool",
+		new ast::BasicType( ast::BasicType::Bool ),
+		nullptr, //init
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall,
+		nullptr, //width
+		std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }
+		);
+}
+
+ast::ObjectDecl * TryMutatorCore::make_voidptr_object(
+		CodeLocation const & location ) const {
+	return new ast::ObjectDecl(
+		location,
+		"__hook",
+		new ast::PointerType(
+			new ast::VoidType()
+		),
+		nullptr, //init
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall,
+		nullptr, //width
+		std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }
+		);
+}
+
+ast::ObjectDecl * TryMutatorCore::make_unused_index_object(
+		CodeLocation const & location ) const {
+	return new ast::ObjectDecl(
+		location,
+		"__handler_index",
+		new ast::BasicType(ast::BasicType::SignedInt),
+		nullptr,
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall,
+		nullptr, //width
+		std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }
+	);
+}
+
+ast::FunctionDecl * TryMutatorCore::make_try_function(
+		CodeLocation const & location ) const {
+	return new ast::FunctionDecl(
+		location,
+		"try",
+		{}, //no param
+		{}, //no return
+		nullptr,
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall
+	);
+}
+
+ast::FunctionDecl * TryMutatorCore::make_catch_function(
+		CodeLocation const & location ) const {
+	return new ast::FunctionDecl(
+		location,
+		"catch",
+		{ make_index_object( location ), make_exception_object( location ) },
+		{}, //return void
+		nullptr,
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall
+	);
+}
+
+ast::FunctionDecl * TryMutatorCore::make_match_function(
+		CodeLocation const & location ) const {
+	return new ast::FunctionDecl(
+		location,
+		"match",
+		{ make_exception_object( location ) },
+		{ make_unused_index_object( location ) },
+		nullptr,
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall
+	);
+}
+
+ast::FunctionDecl * TryMutatorCore::make_handle_function(
+		CodeLocation const & location ) const {
+	return new ast::FunctionDecl(
+		location,
+		"handle",
+		{ make_exception_object( location ) },
+		{ make_bool_object( location ) },
+		nullptr,
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall
+	);
+}
+
+ast::FunctionDecl * TryMutatorCore::make_finally_function(
+		CodeLocation const & location ) const {
+	return new ast::FunctionDecl(
+		location,
+		"finally",
+		{ make_voidptr_object( location ) },
+		{}, //return void
+		nullptr,
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall,
+		{},
+		{ ast::Function::Inline }
+	);
+}
+
+// TryStmt Mutation Helpers
+
+ast::FunctionDecl * TryMutatorCore::create_try_wrapper(
+		const ast::CompoundStmt *body ) {
+
+	ast::FunctionDecl * ret = make_try_function( body->location );
+	ret->stmts = body;
+	return ret;
+}
+
+ast::FunctionDecl * TryMutatorCore::create_terminate_catch(
+		CatchList &handlers ) {
+	std::vector<ast::ptr<ast::CaseClause>> handler_wrappers;
+
+	assert (!handlers.empty());
+	const CodeLocation loc = handlers.front()->location;
+
+	ast::FunctionDecl * func_t = make_catch_function( loc );
+	const ast::DeclWithType * index_obj = func_t->params.front();
+	const ast::DeclWithType * except_obj = func_t->params.back();
+
+	// Index 1..{number of handlers}
+	int index = 0;
+	CatchList::iterator it = handlers.begin();
+	for ( ; it != handlers.end() ; ++it ) {
+		++index;
+		ast::CatchClause * handler = *it;
+		const CodeLocation loc = handler->location;
+
+		// case `index`:
+		// {
+		//     `handler.decl` = { (virtual `decl.type`)`except` };
+		//     `handler.body`;
+		// }
+		// return;
+		ast::CompoundStmt * block = new ast::CompoundStmt(loc);
+
+		// Just copy the exception value. (Post Validation)
+		const ast::ObjectDecl * handler_decl =
+			handler->decl.strict_as<ast::ObjectDecl>();
+		ast::ObjectDecl * local_except = ast::deepCopy(handler_decl);
+		ast::VirtualCastExpr * vcex = new ast::VirtualCastExpr(loc,
+			new ast::VariableExpr( loc, except_obj ),
+			local_except->get_type()
+			);
+		vcex->location = handler->location;
+		local_except->init = new ast::ListInit(loc, { new ast::SingleInit( loc, vcex ) });
+		block->push_back( new ast::DeclStmt( loc, local_except ) );
+
+		// Add the cleanup attribute.
+		local_except->attributes.push_back( new ast::Attribute(
+			"cleanup",
+			{ new ast::NameExpr( loc, "__cfaehm_cleanup_terminate" ) }
+			) );
+
+		ast::DeclReplacer::DeclMap mapping;
+		mapping[handler_decl] = local_except;
+		const ast::Stmt * mutBody = strict_dynamic_cast<const ast::Stmt *>(
+			ast::DeclReplacer::replace(handler->body, mapping));
+
+
+		block->push_back( mutBody );
+		// handler->body = nullptr;
+
+		handler_wrappers.push_back( new ast::CaseClause(loc,
+			ast::ConstantExpr::from_int(loc, index) ,
+			{ block, new ast::ReturnStmt( loc, nullptr ) }
+			));
+	}
+	// TODO: Some sort of meaningful error on default perhaps?
+
+	ast::SwitchStmt * handler_lookup = new ast::SwitchStmt( loc,
+		new ast::VariableExpr( loc, index_obj ),
+		std::move(handler_wrappers)
+		);
+	ast::CompoundStmt * body = new ast::CompoundStmt( loc, {handler_lookup} );
+
+	func_t->stmts = body;
+	return func_t;
+}
+
+// Create a single check from a moddified handler.
+// except_obj is referenced, modded_handler will be freed.
+ast::CompoundStmt * TryMutatorCore::create_single_matcher(
+		const ast::DeclWithType * except_obj, ast::CatchClause * modded_handler ) {
+	// {
+	//     `modded_handler.decl`
+	//     if ( `decl.name = (virtual `decl.type`)`except`
+	//             [&& `modded_handler.cond`] ) {
+	//         `modded_handler.body`
+	//     }
+	// }
+
+	const CodeLocation loc = modded_handler->location;
+	ast::CompoundStmt * block = new ast::CompoundStmt(loc);
+
+	// Local Declaration
+	const ast::ObjectDecl * local_except =
+		modded_handler->decl.strict_as<ast::ObjectDecl>();
+	block->push_back( new ast::DeclStmt( loc,  local_except ) );
+
+	// Check for type match.
+	ast::VirtualCastExpr * vcex = new ast::VirtualCastExpr(loc,
+		new ast::VariableExpr(loc, except_obj ),
+		local_except->get_type()
+		);
+	ast::Expr * cond = ast::UntypedExpr::createAssign(loc,
+		new ast::VariableExpr(loc, local_except ), vcex );
+
+	// Add the check on the conditional if it is provided.
+	if ( modded_handler->cond ) {
+		cond = new ast::LogicalExpr( loc, cond, modded_handler->cond, ast::LogicalFlag::AndExpr );
+	}
+	// Construct the match condition.
+	block->push_back( new ast::IfStmt(loc,
+		cond, modded_handler->body, nullptr ) );
+
+	return block;
+}
+
+ast::FunctionDecl * TryMutatorCore::create_terminate_match(
+		CatchList &handlers ) {
+	// int match(exception * except) {
+	//     HANDLER WRAPPERS { return `index`; }
+	// }
+
+	assert (!handlers.empty());
+	const CodeLocation loc = handlers.front()->location;
+
+	ast::CompoundStmt * body = new ast::CompoundStmt(loc);
+
+	ast::FunctionDecl * func_t = make_match_function( loc );
+	const ast::DeclWithType * except_obj = func_t->params.back();
+
+	// Index 1..{number of handlers}
+	int index = 0;
+	CatchList::iterator it;
+	for ( it = handlers.begin() ; it != handlers.end() ; ++it ) {
+		++index;
+		ast::CatchClause * handler = *it;
+
+		// Body should have been taken by create_terminate_catch.
+		// xxx - just ignore it?
+		// assert( nullptr == handler->get_body() );
+
+		// Create new body.
+		handler->body = new ast::ReturnStmt( handler->location,
+			ast::ConstantExpr::from_int( handler->location, index ) );
+
+		// Create the handler.
+		body->push_back( create_single_matcher( except_obj, handler ) );
+		*it = nullptr;
+	}
+
+	body->push_back( new ast::ReturnStmt(loc,
+		ast::ConstantExpr::from_int( loc, 0 ) ));
+
+	func_t->stmts = body;
+
+	return func_t;
+}
+
+ast::CompoundStmt * TryMutatorCore::create_terminate_caller(
+		CodeLocation loc,
+		ast::FunctionDecl * try_wrapper,
+		ast::FunctionDecl * terminate_catch,
+		ast::FunctionDecl * terminate_match ) {
+	// { __cfaehm_try_terminate(`try`, `catch`, `match`); }
+
+	ast::UntypedExpr * caller = new ast::UntypedExpr(loc, new ast::NameExpr(loc,
+		"__cfaehm_try_terminate" ) );
+	caller->args.push_back( new ast::VariableExpr(loc, try_wrapper ) );
+	caller->args.push_back( new ast::VariableExpr(loc, terminate_catch ) );
+	caller->args.push_back( new ast::VariableExpr(loc, terminate_match ) );
+
+	ast::CompoundStmt * callStmt = new ast::CompoundStmt(loc);
+	callStmt->push_back( new ast::ExprStmt( loc, caller ) );
+	return callStmt;
+}
+
+ast::FunctionDecl * TryMutatorCore::create_resume_handler(
+		CatchList &handlers ) {
+	// bool handle(exception * except) {
+	//     HANDLER WRAPPERS { `hander->body`; return true; }
+	// }
+	assert (!handlers.empty());
+	const CodeLocation loc = handlers.front()->location;
+	ast::CompoundStmt * body = new ast::CompoundStmt(loc);
+
+	ast::FunctionDecl * func_t = make_handle_function( loc );
+	const ast::DeclWithType * except_obj = func_t->params.back();
+
+	CatchList::iterator it;
+	for ( it = handlers.begin() ; it != handlers.end() ; ++it ) {
+		ast::CatchClause * handler = *it;
+		const CodeLocation loc = handler->location;
+		// Modifiy body.
+		ast::CompoundStmt * handling_code;
+		if (handler->body.as<ast::CompoundStmt>()) {
+			handling_code = strict_dynamic_cast<ast::CompoundStmt*>(
+				handler->body.get_and_mutate() );
+		} else {
+			handling_code = new ast::CompoundStmt(loc);
+			handling_code->push_back( handler->body );
+		}
+		handling_code->push_back( new ast::ReturnStmt(loc,
+			ast::ConstantExpr::from_bool(loc, true ) ) );
+		handler->body = handling_code;
+
+		// Create the handler.
+		body->push_back( create_single_matcher( except_obj, handler ) );
+		*it = nullptr;
+	}
+
+	body->push_back( new ast::ReturnStmt(loc,
+		ast::ConstantExpr::from_bool(loc, false ) ) );
+	func_t->stmts = body;
+
+	return func_t;
+}
+
+ast::CompoundStmt * TryMutatorCore::create_resume_wrapper(
+		const ast::Stmt * wraps,
+		const ast::FunctionDecl * resume_handler ) {
+	const CodeLocation loc = wraps->location;
+	ast::CompoundStmt * body = new ast::CompoundStmt(loc);
+
+	// struct __try_resume_node __resume_node
+	//  	__attribute__((cleanup( __cfaehm_try_resume_cleanup )));
+	// ** unwinding of the stack here could cause problems **
+	// ** however I don't think that can happen currently **
+	// __cfaehm_try_resume_setup( &__resume_node, resume_handler );
+
+	ast::ObjectDecl * obj = new ast::ObjectDecl(
+		loc,
+		"__resume_node",
+		new ast::StructInstType(
+			node_decl
+			),
+		nullptr,
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall,
+		nullptr,
+		{new ast::Attribute("cleanup", {new ast::NameExpr(loc, "__cfaehm_try_resume_cleanup")})}
+		);
+	appendDeclStmt( body, obj );
+
+	ast::UntypedExpr *setup = new ast::UntypedExpr(loc, new ast::NameExpr(loc,
+		"__cfaehm_try_resume_setup" ) );
+	setup->args.push_back( new ast::AddressExpr( loc, new ast::VariableExpr(loc, obj ) ) );
+	setup->args.push_back( new ast::VariableExpr( loc, resume_handler ) );
+
+	body->push_back( new ast::ExprStmt(loc, setup ) );
+
+	body->push_back( wraps );
+	return body;
+}
+
+ast::FunctionDecl * TryMutatorCore::create_finally_wrapper(
+		ast::TryStmt * tryStmt ) {
+	// void finally() { `finally->block` }
+	const ast::FinallyClause * finally = tryStmt->finally;
+	const ast::CompoundStmt * body = finally->body;
+
+	ast::FunctionDecl * func_t = make_finally_function( tryStmt->location );
+	func_t->stmts = body;
+
+	tryStmt->finally = nullptr;
+
+	return func_t;
+}
+
+ast::ObjectDecl * TryMutatorCore::create_finally_hook(
+		ast::FunctionDecl * finally_wrapper ) {
+	// struct __cfaehm_cleanup_hook __finally_hook
+	//   	__attribute__((cleanup( `finally_wrapper` )));
+
+	const CodeLocation loc = finally_wrapper->location;
+	return new ast::ObjectDecl(
+		loc,
+		"__finally_hook",
+		new ast::StructInstType(
+			hook_decl
+			),
+		nullptr,
+		ast::Storage::Classes{},
+		ast::Linkage::Cforall,
+		nullptr,
+		{new ast::Attribute("cleanup", {new ast::VariableExpr{loc, finally_wrapper}})}
+		);
+}
+
+ast::Stmt * TryMutatorCore::create_resume_rethrow( const ast::ThrowStmt *throwStmt ) {
+	// return false;
+	const CodeLocation loc = throwStmt->location;
+	ast::Stmt * result = new ast::ReturnStmt(loc,
+		ast::ConstantExpr::from_bool( loc, false )
+		);
+	result->labels = throwStmt->labels;
+	return result;
+}
+
+// Visiting/Mutating Functions
+void TryMutatorCore::previsit( const ast::StructDecl *structDecl ) {
+	if ( !structDecl->body ) {
+		// Skip children?
+		return;
+	} else if ( structDecl->name == "__cfaehm_base_exception_t" ) {
+		assert( nullptr == except_decl );
+		except_decl = structDecl;
+	} else if ( structDecl->name == "__cfaehm_try_resume_node" ) {
+		assert( nullptr == node_decl );
+		node_decl = structDecl;
+	} else if ( structDecl->name == "__cfaehm_cleanup_hook" ) {
+		assert( nullptr == hook_decl );
+		hook_decl = structDecl;
+	}
+}
+
+ast::Stmt * TryMutatorCore::postvisit( const ast::TryStmt *tryStmt ) {
+	assert( except_decl );
+	assert( node_decl );
+	assert( hook_decl );
+
+	const CodeLocation loc = tryStmt->location;
+	ast::TryStmt * mutStmt = mutate(tryStmt);
+	// Generate a prefix for the function names?
+
+	ast::CompoundStmt * block = new ast::CompoundStmt( loc );
+	// ast::CompoundStmt * inner = take_try_block( mutStmt );
+	// this is never mutated so let node deletion do its job?
+	const ast::CompoundStmt * inner = mutStmt->body;
+
+	if ( mutStmt->finally ) {
+		// Define the helper function.
+		ast::FunctionDecl * finally_block =
+			create_finally_wrapper( mutStmt );
+		appendDeclStmt( block, finally_block );
+		// Create and add the finally cleanup hook.
+		appendDeclStmt( block, create_finally_hook( finally_block ) );
+	}
+
+	CatchList termination_handlers;
+	CatchList resumption_handlers;
+
+	for (auto & handler: mutStmt->handlers) {
+		// xxx - should always be unique? mutate as safe const-cast
+		assert(handler->unique());
+		if (handler->kind == ast::ExceptionKind::Resume) {
+			resumption_handlers.push_back(handler.get_and_mutate());
+		}
+		else {
+			termination_handlers.push_back(handler.get_and_mutate());
+		}
+	}
+
+	if ( resumption_handlers.size() ) {
+		// Define the helper function.
+		ast::FunctionDecl * resume_handler =
+			create_resume_handler( resumption_handlers );
+		appendDeclStmt( block, resume_handler );
+		// Prepare hooks
+		inner = create_resume_wrapper( inner, resume_handler );
+	}
+
+	if ( termination_handlers.size() ) {
+		// Define the three helper functions.
+		ast::FunctionDecl * try_wrapper = create_try_wrapper( inner );
+		appendDeclStmt( block, try_wrapper );
+		ast::FunctionDecl * terminate_catch =
+			create_terminate_catch( termination_handlers );
+		appendDeclStmt( block, terminate_catch );
+		ast::FunctionDecl * terminate_match =
+			create_terminate_match( termination_handlers );
+		appendDeclStmt( block, terminate_match );
+		// Build the call to the try wrapper.
+		inner = create_terminate_caller(inner->location,
+			try_wrapper, terminate_catch, terminate_match );
+	}
+
+	// Embed the try block.
+	block->push_back( inner );
+
+	return block;
+}
+
+ast::Stmt * TryMutatorCore::postvisit( const ast::ThrowStmt *throwStmt ) {
+	// Only valid `throwResume;` statements should remain. (2/3 checks)
+	assert( ast::ExceptionKind::Resume == throwStmt->kind && ! throwStmt->expr );
+	return create_resume_rethrow( throwStmt );
+}
+
+} // namespace
+
+void translateThrows( ast::TranslationUnit & transUnit ) {
+	ast::Pass<TranslateThrowsCore>::run( transUnit );
+}
+
+void translateTries( ast::TranslationUnit & transUnit ) {
+	ast::Pass<TryMutatorCore>::run(transUnit);
+}
+
+} // namespace ControlStruct
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/ControlStruct/ExceptTranslateNew.cpp
===================================================================
--- src/ControlStruct/ExceptTranslateNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,725 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ExceptTranslateNew.cpp -- Conversion of exception control flow structures.
-//
-// Author           : Andrew Beach
-// Created On       : Mon Nov  8 11:53:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Mar 11 17:51:00 2022
-// Update Count     : 2
-//
-
-#include "ExceptTranslate.h"
-
-#include "AST/Expr.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Stmt.hpp"
-#include "AST/TranslationUnit.hpp"
-#include "AST/DeclReplacer.hpp"
-
-namespace ControlStruct {
-
-namespace {
-
-	typedef std::list<ast::CatchClause*> CatchList;
-
-	void appendDeclStmt( ast::CompoundStmt * block, ast::DeclWithType * item ) {
-		block->push_back(new ast::DeclStmt(block->location, item));
-	}
-
-class TranslateThrowsCore final : public ast::WithGuards {
-	const ast::ObjectDecl * terminateHandlerExcept;
-	enum Context { NoHandler, TerHandler, ResHandler } currentContext;
-
-	const ast::Stmt * createEitherThrow(
-		const ast::ThrowStmt * throwStmt, const char * funcName );
-	const ast::Stmt * createTerminateRethrow( const ast::ThrowStmt * );
-
-public:
-	TranslateThrowsCore() :
-		terminateHandlerExcept( nullptr ), currentContext( NoHandler )
-	{}
-
-	void previsit( const ast::CatchClause * stmt );
-	const ast::Stmt * postvisit( const ast::ThrowStmt * stmt );
-};
-
-const ast::Stmt * TranslateThrowsCore::createEitherThrow(
-		const ast::ThrowStmt * throwStmt, const char * funcName ) {
-	// `throwFunc`( `throwStmt->name` );
-	ast::UntypedExpr * call = new ast::UntypedExpr( throwStmt->location,
-		new ast::NameExpr( throwStmt->location, funcName )
-	);
-	call->args.push_back( throwStmt->expr );
-	return new ast::ExprStmt( throwStmt->location, call );
-}
-
-ast::VariableExpr * varOf( const ast::DeclWithType * decl ) {
-	return new ast::VariableExpr( decl->location, decl );
-}
-
-const ast::Stmt * TranslateThrowsCore::createTerminateRethrow(
-		const ast::ThrowStmt * stmt ) {
-	// { `terminate_handler_except` = 0p; __rethrow_terminate(); }
-	assert( nullptr == stmt->expr );
-	assert( terminateHandlerExcept );
-
-	ast::CompoundStmt * result = new ast::CompoundStmt(
-		stmt->location, {}, std::vector<ast::Label>( stmt->labels ) );
-	result->push_back( new ast::ExprStmt( stmt->location,
-		ast::UntypedExpr::createAssign(
-			stmt->location,
-			varOf( terminateHandlerExcept ),
-			ast::ConstantExpr::null(
-				stmt->location,
-				terminateHandlerExcept->type
-			)
-		)
-	) );
-	result->push_back( new ast::ExprStmt( stmt->location, new ast::UntypedExpr(
-		stmt->location,
-		new ast::NameExpr( stmt->location, "__cfaehm_rethrow_terminate" )
-	) ) );
-	return result;
-}
-
-void TranslateThrowsCore::previsit( const ast::CatchClause * stmt ) {
-	// Validate the statement's form.
-	const ast::ObjectDecl * decl = stmt->decl.as<ast::ObjectDecl>();
-	// Also checking the type would be nice.
-	if ( !decl || !decl->type.as<ast::PointerType>() ) {
-		std::string kind = (ast::Terminate == stmt->kind) ? "catch" : "catchResume";
-		SemanticError( stmt->location, kind + " must have pointer to an exception type" );
-	}
-
-	// Track the handler context.
-	if ( ast::Terminate == stmt->kind ) {
-		GuardValue( currentContext ) = TerHandler;
-		GuardValue( terminateHandlerExcept ) = decl;
-	} else {
-		GuardValue( currentContext ) = ResHandler;
-	}
-}
-
-const ast::Stmt * TranslateThrowsCore::postvisit(
-		const ast::ThrowStmt * stmt ) {
-	// Ignoring ThrowStmt::target for now.
-	// Handle Termination (Raise, Reraise, Error):
-	if ( ast::Terminate == stmt->kind ) {
-		if ( stmt->expr ) {
-			return createEitherThrow( stmt, "$throw" );
-		} else if ( TerHandler == currentContext ) {
-			return createTerminateRethrow( stmt );
-		} else {
-			abort( "Invalid throw in %s at %i\n",
-				stmt->location.filename.c_str(),
-				stmt->location.first_line);
-		}
-	// Handle Resumption (Raise, Reraise, Error):
-	} else {
-		if ( stmt->expr ) {
-			return createEitherThrow( stmt, "$throwResume" );
-		} else if ( ResHandler == currentContext ) {
-			// This has to be handled later.
-			return stmt;
-		} else {
-			abort( "Invalid throwResume in %s at %i\n",
-				stmt->location.filename.c_str(),
-				stmt->location.first_line);
-		}
-	}
-}
-
-
-class TryMutatorCore final {
-	// The built in types used in translation.
-	const ast::StructDecl * except_decl;
-	const ast::StructDecl * node_decl;
-	const ast::StructDecl * hook_decl;
-
-	// The many helper functions for code/syntree generation.
-	ast::CompoundStmt * take_try_block( ast::TryStmt * tryStmt );
-	ast::FunctionDecl * create_try_wrapper( const ast::CompoundStmt * body );
-	ast::FunctionDecl * create_terminate_catch( CatchList &handlers );
-	ast::CompoundStmt * create_single_matcher(
-		const ast::DeclWithType * except_obj, ast::CatchClause * modded_handler );
-	ast::FunctionDecl * create_terminate_match( CatchList &handlers );
-	ast::CompoundStmt * create_terminate_caller( CodeLocation loc, ast::FunctionDecl * try_wrapper,
-		ast::FunctionDecl * terminate_catch, ast::FunctionDecl * terminate_match );
-	ast::FunctionDecl * create_resume_handler( CatchList &handlers );
-	ast::CompoundStmt * create_resume_wrapper(
-		const ast::Stmt * wraps, const ast::FunctionDecl * resume_handler );
-	ast::FunctionDecl * create_finally_wrapper( ast::TryStmt * tryStmt );
-	ast::ObjectDecl * create_finally_hook( ast::FunctionDecl * finally_wrapper );
-	ast::Stmt * create_resume_rethrow( const ast::ThrowStmt * throwStmt );
-
-	// Types used in translation, first group are internal.
-	ast::ObjectDecl * make_index_object( CodeLocation const & ) const;
-	ast::ObjectDecl * make_exception_object( CodeLocation const & ) const;
-	ast::ObjectDecl * make_bool_object( CodeLocation const & ) const;
-	ast::ObjectDecl * make_voidptr_object( CodeLocation const & ) const;
-	ast::ObjectDecl * make_unused_index_object( CodeLocation const & ) const;
-	// void (*function)();
-	ast::FunctionDecl * make_try_function( CodeLocation const & ) const;
-	// void (*function)(int, exception);
-	ast::FunctionDecl * make_catch_function( CodeLocation const & ) const;
-	// int (*function)(exception);
-	ast::FunctionDecl * make_match_function( CodeLocation const & ) const;
-	// bool (*function)(exception);
-	ast::FunctionDecl * make_handle_function( CodeLocation const & ) const;
-	// void (*function)(__attribute__((unused)) void *);
-	ast::FunctionDecl * make_finally_function( CodeLocation const & ) const;
-
-public:
-	TryMutatorCore() :
-		except_decl( nullptr ), node_decl( nullptr ), hook_decl( nullptr )
-	{}
-
-	void previsit( const ast::StructDecl *structDecl );
-	ast::Stmt * postvisit( const ast::TryStmt *tryStmt );
-	ast::Stmt * postvisit( const ast::ThrowStmt *throwStmt );
-};
-
-ast::ObjectDecl * TryMutatorCore::make_index_object(
-		CodeLocation const & location ) const {
-	return new ast::ObjectDecl(
-		location,
-		"__handler_index",
-		new ast::BasicType( ast::BasicType::SignedInt )
-		);
-}
-
-ast::ObjectDecl * TryMutatorCore::make_exception_object(
-		CodeLocation const & location ) const {
-	assert( except_decl );
-	return new ast::ObjectDecl(
-		location,
-		"__exception_inst",
-		new ast::PointerType( new ast::StructInstType( except_decl ) )
-		);
-}
-
-ast::ObjectDecl * TryMutatorCore::make_bool_object(
-		CodeLocation const & location ) const {
-	return new ast::ObjectDecl(
-		location,
-		"__ret_bool",
-		new ast::BasicType( ast::BasicType::Bool ),
-		nullptr, //init
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall,
-		nullptr, //width
-		std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }
-		);
-}
-
-ast::ObjectDecl * TryMutatorCore::make_voidptr_object(
-		CodeLocation const & location ) const {
-	return new ast::ObjectDecl(
-		location,
-		"__hook",
-		new ast::PointerType(
-			new ast::VoidType()
-		),
-		nullptr, //init
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall,
-		nullptr, //width
-		std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }
-		);
-}
-
-ast::ObjectDecl * TryMutatorCore::make_unused_index_object(
-		CodeLocation const & location ) const {
-	return new ast::ObjectDecl(
-		location,
-		"__handler_index",
-		new ast::BasicType(ast::BasicType::SignedInt),
-		nullptr,
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall,
-		nullptr, //width
-		std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }
-	);
-}
-
-ast::FunctionDecl * TryMutatorCore::make_try_function(
-		CodeLocation const & location ) const {
-	return new ast::FunctionDecl(
-		location,
-		"try",
-		{}, //no param
-		{}, //no return
-		nullptr,
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall
-	);
-}
-
-ast::FunctionDecl * TryMutatorCore::make_catch_function(
-		CodeLocation const & location ) const {
-	return new ast::FunctionDecl(
-		location,
-		"catch",
-		{ make_index_object( location ), make_exception_object( location ) },
-		{}, //return void
-		nullptr,
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall
-	);
-}
-
-ast::FunctionDecl * TryMutatorCore::make_match_function(
-		CodeLocation const & location ) const {
-	return new ast::FunctionDecl(
-		location,
-		"match",
-		{ make_exception_object( location ) },
-		{ make_unused_index_object( location ) },
-		nullptr,
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall
-	);
-}
-
-ast::FunctionDecl * TryMutatorCore::make_handle_function(
-		CodeLocation const & location ) const {
-	return new ast::FunctionDecl(
-		location,
-		"handle",
-		{ make_exception_object( location ) },
-		{ make_bool_object( location ) },
-		nullptr,
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall
-	);
-}
-
-ast::FunctionDecl * TryMutatorCore::make_finally_function(
-		CodeLocation const & location ) const {
-	return new ast::FunctionDecl(
-		location,
-		"finally",
-		{ make_voidptr_object( location ) },
-		{}, //return void
-		nullptr,
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall,
-		{},
-		{ ast::Function::Inline }
-	);
-}
-
-// TryStmt Mutation Helpers
-
-ast::FunctionDecl * TryMutatorCore::create_try_wrapper(
-		const ast::CompoundStmt *body ) {
-
-	ast::FunctionDecl * ret = make_try_function( body->location );
-	ret->stmts = body;
-	return ret;
-}
-
-ast::FunctionDecl * TryMutatorCore::create_terminate_catch(
-		CatchList &handlers ) {
-	std::vector<ast::ptr<ast::CaseClause>> handler_wrappers;
-
-	assert (!handlers.empty());
-	const CodeLocation loc = handlers.front()->location;
-
-	ast::FunctionDecl * func_t = make_catch_function( loc );
-	const ast::DeclWithType * index_obj = func_t->params.front();
-	const ast::DeclWithType * except_obj = func_t->params.back();
-
-	// Index 1..{number of handlers}
-	int index = 0;
-	CatchList::iterator it = handlers.begin();
-	for ( ; it != handlers.end() ; ++it ) {
-		++index;
-		ast::CatchClause * handler = *it;
-		const CodeLocation loc = handler->location;
-
-		// case `index`:
-		// {
-		//     `handler.decl` = { (virtual `decl.type`)`except` };
-		//     `handler.body`;
-		// }
-		// return;
-		ast::CompoundStmt * block = new ast::CompoundStmt(loc);
-
-		// Just copy the exception value. (Post Validation)
-		const ast::ObjectDecl * handler_decl =
-			handler->decl.strict_as<ast::ObjectDecl>();
-		ast::ObjectDecl * local_except = ast::deepCopy(handler_decl);
-		ast::VirtualCastExpr * vcex = new ast::VirtualCastExpr(loc,
-			new ast::VariableExpr( loc, except_obj ),
-			local_except->get_type()
-			);
-		vcex->location = handler->location;
-		local_except->init = new ast::ListInit(loc, { new ast::SingleInit( loc, vcex ) });
-		block->push_back( new ast::DeclStmt( loc, local_except ) );
-
-		// Add the cleanup attribute.
-		local_except->attributes.push_back( new ast::Attribute(
-			"cleanup",
-			{ new ast::NameExpr( loc, "__cfaehm_cleanup_terminate" ) }
-			) );
-
-		ast::DeclReplacer::DeclMap mapping;
-		mapping[handler_decl] = local_except;
-		const ast::Stmt * mutBody = strict_dynamic_cast<const ast::Stmt *>(
-			ast::DeclReplacer::replace(handler->body, mapping));
-
-
-		block->push_back( mutBody );
-		// handler->body = nullptr;
-
-		handler_wrappers.push_back( new ast::CaseClause(loc,
-			ast::ConstantExpr::from_int(loc, index) ,
-			{ block, new ast::ReturnStmt( loc, nullptr ) }
-			));
-	}
-	// TODO: Some sort of meaningful error on default perhaps?
-
-	ast::SwitchStmt * handler_lookup = new ast::SwitchStmt( loc,
-		new ast::VariableExpr( loc, index_obj ),
-		std::move(handler_wrappers)
-		);
-	ast::CompoundStmt * body = new ast::CompoundStmt( loc, {handler_lookup} );
-
-	func_t->stmts = body;
-	return func_t;
-}
-
-// Create a single check from a moddified handler.
-// except_obj is referenced, modded_handler will be freed.
-ast::CompoundStmt * TryMutatorCore::create_single_matcher(
-		const ast::DeclWithType * except_obj, ast::CatchClause * modded_handler ) {
-	// {
-	//     `modded_handler.decl`
-	//     if ( `decl.name = (virtual `decl.type`)`except`
-	//             [&& `modded_handler.cond`] ) {
-	//         `modded_handler.body`
-	//     }
-	// }
-
-	const CodeLocation loc = modded_handler->location;
-	ast::CompoundStmt * block = new ast::CompoundStmt(loc);
-
-	// Local Declaration
-	const ast::ObjectDecl * local_except =
-		modded_handler->decl.strict_as<ast::ObjectDecl>();
-	block->push_back( new ast::DeclStmt( loc,  local_except ) );
-
-	// Check for type match.
-	ast::VirtualCastExpr * vcex = new ast::VirtualCastExpr(loc,
-		new ast::VariableExpr(loc, except_obj ),
-		local_except->get_type()
-		);
-	ast::Expr * cond = ast::UntypedExpr::createAssign(loc,
-		new ast::VariableExpr(loc, local_except ), vcex );
-
-	// Add the check on the conditional if it is provided.
-	if ( modded_handler->cond ) {
-		cond = new ast::LogicalExpr( loc, cond, modded_handler->cond, ast::LogicalFlag::AndExpr );
-	}
-	// Construct the match condition.
-	block->push_back( new ast::IfStmt(loc,
-		cond, modded_handler->body, nullptr ) );
-
-	return block;
-}
-
-ast::FunctionDecl * TryMutatorCore::create_terminate_match(
-		CatchList &handlers ) {
-	// int match(exception * except) {
-	//     HANDLER WRAPPERS { return `index`; }
-	// }
-
-	assert (!handlers.empty());
-	const CodeLocation loc = handlers.front()->location;
-
-	ast::CompoundStmt * body = new ast::CompoundStmt(loc);
-
-	ast::FunctionDecl * func_t = make_match_function( loc );
-	const ast::DeclWithType * except_obj = func_t->params.back();
-
-	// Index 1..{number of handlers}
-	int index = 0;
-	CatchList::iterator it;
-	for ( it = handlers.begin() ; it != handlers.end() ; ++it ) {
-		++index;
-		ast::CatchClause * handler = *it;
-
-		// Body should have been taken by create_terminate_catch.
-		// xxx - just ignore it?
-		// assert( nullptr == handler->get_body() );
-
-		// Create new body.
-		handler->body = new ast::ReturnStmt( handler->location,
-			ast::ConstantExpr::from_int( handler->location, index ) );
-
-		// Create the handler.
-		body->push_back( create_single_matcher( except_obj, handler ) );
-		*it = nullptr;
-	}
-
-	body->push_back( new ast::ReturnStmt(loc,
-		ast::ConstantExpr::from_int( loc, 0 ) ));
-
-	func_t->stmts = body;
-
-	return func_t;
-}
-
-ast::CompoundStmt * TryMutatorCore::create_terminate_caller(
-		CodeLocation loc,
-		ast::FunctionDecl * try_wrapper,
-		ast::FunctionDecl * terminate_catch,
-		ast::FunctionDecl * terminate_match ) {
-	// { __cfaehm_try_terminate(`try`, `catch`, `match`); }
-
-	ast::UntypedExpr * caller = new ast::UntypedExpr(loc, new ast::NameExpr(loc,
-		"__cfaehm_try_terminate" ) );
-	caller->args.push_back( new ast::VariableExpr(loc, try_wrapper ) );
-	caller->args.push_back( new ast::VariableExpr(loc, terminate_catch ) );
-	caller->args.push_back( new ast::VariableExpr(loc, terminate_match ) );
-
-	ast::CompoundStmt * callStmt = new ast::CompoundStmt(loc);
-	callStmt->push_back( new ast::ExprStmt( loc, caller ) );
-	return callStmt;
-}
-
-ast::FunctionDecl * TryMutatorCore::create_resume_handler(
-		CatchList &handlers ) {
-	// bool handle(exception * except) {
-	//     HANDLER WRAPPERS { `hander->body`; return true; }
-	// }
-	assert (!handlers.empty());
-	const CodeLocation loc = handlers.front()->location;
-	ast::CompoundStmt * body = new ast::CompoundStmt(loc);
-
-	ast::FunctionDecl * func_t = make_handle_function( loc );
-	const ast::DeclWithType * except_obj = func_t->params.back();
-
-	CatchList::iterator it;
-	for ( it = handlers.begin() ; it != handlers.end() ; ++it ) {
-		ast::CatchClause * handler = *it;
-		const CodeLocation loc = handler->location;
-		// Modifiy body.
-		ast::CompoundStmt * handling_code;
-		if (handler->body.as<ast::CompoundStmt>()) {
-			handling_code = strict_dynamic_cast<ast::CompoundStmt*>(
-				handler->body.get_and_mutate() );
-		} else {
-			handling_code = new ast::CompoundStmt(loc);
-			handling_code->push_back( handler->body );
-		}
-		handling_code->push_back( new ast::ReturnStmt(loc,
-			ast::ConstantExpr::from_bool(loc, true ) ) );
-		handler->body = handling_code;
-
-		// Create the handler.
-		body->push_back( create_single_matcher( except_obj, handler ) );
-		*it = nullptr;
-	}
-
-	body->push_back( new ast::ReturnStmt(loc,
-		ast::ConstantExpr::from_bool(loc, false ) ) );
-	func_t->stmts = body;
-
-	return func_t;
-}
-
-ast::CompoundStmt * TryMutatorCore::create_resume_wrapper(
-		const ast::Stmt * wraps,
-		const ast::FunctionDecl * resume_handler ) {
-	const CodeLocation loc = wraps->location;
-	ast::CompoundStmt * body = new ast::CompoundStmt(loc);
-
-	// struct __try_resume_node __resume_node
-	//  	__attribute__((cleanup( __cfaehm_try_resume_cleanup )));
-	// ** unwinding of the stack here could cause problems **
-	// ** however I don't think that can happen currently **
-	// __cfaehm_try_resume_setup( &__resume_node, resume_handler );
-
-	ast::ObjectDecl * obj = new ast::ObjectDecl(
-		loc,
-		"__resume_node",
-		new ast::StructInstType(
-			node_decl
-			),
-		nullptr,
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall,
-		nullptr,
-		{new ast::Attribute("cleanup", {new ast::NameExpr(loc, "__cfaehm_try_resume_cleanup")})}
-		);
-	appendDeclStmt( body, obj );
-
-	ast::UntypedExpr *setup = new ast::UntypedExpr(loc, new ast::NameExpr(loc,
-		"__cfaehm_try_resume_setup" ) );
-	setup->args.push_back( new ast::AddressExpr( loc, new ast::VariableExpr(loc, obj ) ) );
-	setup->args.push_back( new ast::VariableExpr( loc, resume_handler ) );
-
-	body->push_back( new ast::ExprStmt(loc, setup ) );
-
-	body->push_back( wraps );
-	return body;
-}
-
-ast::FunctionDecl * TryMutatorCore::create_finally_wrapper(
-		ast::TryStmt * tryStmt ) {
-	// void finally() { `finally->block` }
-	const ast::FinallyClause * finally = tryStmt->finally;
-	const ast::CompoundStmt * body = finally->body;
-
-	ast::FunctionDecl * func_t = make_finally_function( tryStmt->location );
-	func_t->stmts = body;
-
-	tryStmt->finally = nullptr;
-
-	return func_t;
-}
-
-ast::ObjectDecl * TryMutatorCore::create_finally_hook(
-		ast::FunctionDecl * finally_wrapper ) {
-	// struct __cfaehm_cleanup_hook __finally_hook
-	//   	__attribute__((cleanup( `finally_wrapper` )));
-
-	const CodeLocation loc = finally_wrapper->location;
-	return new ast::ObjectDecl(
-		loc,
-		"__finally_hook",
-		new ast::StructInstType(
-			hook_decl
-			),
-		nullptr,
-		ast::Storage::Classes{},
-		ast::Linkage::Cforall,
-		nullptr,
-		{new ast::Attribute("cleanup", {new ast::VariableExpr{loc, finally_wrapper}})}
-		);
-}
-
-ast::Stmt * TryMutatorCore::create_resume_rethrow( const ast::ThrowStmt *throwStmt ) {
-	// return false;
-	const CodeLocation loc = throwStmt->location;
-	ast::Stmt * result = new ast::ReturnStmt(loc,
-		ast::ConstantExpr::from_bool( loc, false )
-		);
-	result->labels = throwStmt->labels;
-	return result;
-}
-
-// Visiting/Mutating Functions
-void TryMutatorCore::previsit( const ast::StructDecl *structDecl ) {
-	if ( !structDecl->body ) {
-		// Skip children?
-		return;
-	} else if ( structDecl->name == "__cfaehm_base_exception_t" ) {
-		assert( nullptr == except_decl );
-		except_decl = structDecl;
-	} else if ( structDecl->name == "__cfaehm_try_resume_node" ) {
-		assert( nullptr == node_decl );
-		node_decl = structDecl;
-	} else if ( structDecl->name == "__cfaehm_cleanup_hook" ) {
-		assert( nullptr == hook_decl );
-		hook_decl = structDecl;
-	}
-}
-
-ast::Stmt * TryMutatorCore::postvisit( const ast::TryStmt *tryStmt ) {
-	assert( except_decl );
-	assert( node_decl );
-	assert( hook_decl );
-
-	const CodeLocation loc = tryStmt->location;
-	ast::TryStmt * mutStmt = mutate(tryStmt);
-	// Generate a prefix for the function names?
-
-	ast::CompoundStmt * block = new ast::CompoundStmt( loc );
-	// ast::CompoundStmt * inner = take_try_block( mutStmt );
-	// this is never mutated so let node deletion do its job?
-	const ast::CompoundStmt * inner = mutStmt->body;
-
-	if ( mutStmt->finally ) {
-		// Define the helper function.
-		ast::FunctionDecl * finally_block =
-			create_finally_wrapper( mutStmt );
-		appendDeclStmt( block, finally_block );
-		// Create and add the finally cleanup hook.
-		appendDeclStmt( block, create_finally_hook( finally_block ) );
-	}
-
-	CatchList termination_handlers;
-	CatchList resumption_handlers;
-
-	for (auto & handler: mutStmt->handlers) {
-		// xxx - should always be unique? mutate as safe const-cast
-		assert(handler->unique());
-		if (handler->kind == ast::ExceptionKind::Resume) {
-			resumption_handlers.push_back(handler.get_and_mutate());
-		}
-		else {
-			termination_handlers.push_back(handler.get_and_mutate());
-		}
-	}
-
-	if ( resumption_handlers.size() ) {
-		// Define the helper function.
-		ast::FunctionDecl * resume_handler =
-			create_resume_handler( resumption_handlers );
-		appendDeclStmt( block, resume_handler );
-		// Prepare hooks
-		inner = create_resume_wrapper( inner, resume_handler );
-	}
-
-	if ( termination_handlers.size() ) {
-		// Define the three helper functions.
-		ast::FunctionDecl * try_wrapper = create_try_wrapper( inner );
-		appendDeclStmt( block, try_wrapper );
-		ast::FunctionDecl * terminate_catch =
-			create_terminate_catch( termination_handlers );
-		appendDeclStmt( block, terminate_catch );
-		ast::FunctionDecl * terminate_match =
-			create_terminate_match( termination_handlers );
-		appendDeclStmt( block, terminate_match );
-		// Build the call to the try wrapper.
-		inner = create_terminate_caller(inner->location,
-			try_wrapper, terminate_catch, terminate_match );
-	}
-
-	// Embed the try block.
-	block->push_back( inner );
-
-	return block;
-}
-
-ast::Stmt * TryMutatorCore::postvisit( const ast::ThrowStmt *throwStmt ) {
-	// Only valid `throwResume;` statements should remain. (2/3 checks)
-	assert( ast::ExceptionKind::Resume == throwStmt->kind && ! throwStmt->expr );
-	return create_resume_rethrow( throwStmt );
-}
-
-} // namespace
-
-void translateThrows( ast::TranslationUnit & transUnit ) {
-	ast::Pass<TranslateThrowsCore>::run( transUnit );
-}
-
-void translateTries( ast::TranslationUnit & transUnit ) {
-	ast::Pass<TryMutatorCore>::run(transUnit);
-}
-
-} // namespace ControlStruct
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ControlStruct/LabelGenerator.cpp
===================================================================
--- src/ControlStruct/LabelGenerator.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/ControlStruct/LabelGenerator.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,71 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// LabelGenerator.cpp --
+//
+// Author           : Peter A. Buhr
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Mar 28 10:03:00 2022
+// Update Count     : 73
+//
+
+#include "LabelGenerator.hpp"
+
+#include "AST/Attribute.hpp"
+#include "AST/Label.hpp"
+#include "AST/Stmt.hpp"
+
+using namespace std;
+using namespace ast;
+
+namespace ControlStruct {
+
+enum { size = 128 };
+
+static int newLabelPre( char buf[size], const string & suffix ) {
+	static int current = 0;
+
+	int len = snprintf( buf, size, "__L%d__%s", current++, suffix.c_str() );
+	assertf( len < size, "CFA Internal error: buffer overflow creating label" );
+	return len;
+}
+
+static Label newLabelPost( char buf[size], const CodeLocation & location ) {
+	Label ret_label( location, buf );
+	ret_label.attributes.push_back( new Attribute( "unused" ) );
+	return ret_label;
+}
+
+Label newLabel( const string & suffix, const Stmt * stmt ) {
+	// Buffer for string manipulation.
+	char buf[size];
+
+	assertf( stmt, "CFA internal error: parameter statement cannot be null pointer" );
+	int len = newLabelPre( buf, suffix );
+
+	// What does this do?
+	if ( ! stmt->labels.empty() ) {
+		len = snprintf( buf + len, size - len, "_%s__", stmt->labels.front().name.c_str() );
+		assertf( len < size - len, "CFA Internal error: buffer overflow creating label" );
+	} // if
+
+	return newLabelPost( buf, stmt->location );
+}
+
+Label newLabel( const string & suffix, const CodeLocation & location ) {
+	// Buffer for string manipulation.
+	char buf[size];
+
+	newLabelPre( buf, suffix );
+	return newLabelPost( buf, location );
+}
+
+} // namespace ControlStruct
+
+// Local Variables: //
+// mode: c++ //
+// End: //
Index: src/ControlStruct/LabelGenerator.hpp
===================================================================
--- src/ControlStruct/LabelGenerator.hpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/ControlStruct/LabelGenerator.hpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,38 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// LabelGenerator.hpp --
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Fir Mar 25 15:40:00 2022
+// Update Count     : 28
+//
+
+#pragma once
+
+#include <string>										// for string
+
+struct CodeLocation;
+
+namespace ast {
+	class Label;
+	class Stmt;
+} // namespace ast
+
+namespace ControlStruct {
+
+ast::Label newLabel( const std::string &, const ast::Stmt * );
+ast::Label newLabel( const std::string &, const CodeLocation & );
+
+} // namespace ControlStruct
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/ControlStruct/LabelGeneratorNew.cpp
===================================================================
--- src/ControlStruct/LabelGeneratorNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,71 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// LabelGeneratorNew.cpp --
-//
-// Author           : Peter A. Buhr
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Mar 28 10:03:00 2022
-// Update Count     : 73
-//
-
-#include "LabelGeneratorNew.hpp"
-
-#include "AST/Attribute.hpp"
-#include "AST/Label.hpp"
-#include "AST/Stmt.hpp"
-
-using namespace std;
-using namespace ast;
-
-namespace ControlStruct {
-
-enum { size = 128 };
-
-static int newLabelPre( char buf[size], const string & suffix ) {
-	static int current = 0;
-
-	int len = snprintf( buf, size, "__L%d__%s", current++, suffix.c_str() );
-	assertf( len < size, "CFA Internal error: buffer overflow creating label" );
-	return len;
-}
-
-static Label newLabelPost( char buf[size], const CodeLocation & location ) {
-	Label ret_label( location, buf );
-	ret_label.attributes.push_back( new Attribute( "unused" ) );
-	return ret_label;
-}
-
-Label newLabel( const string & suffix, const Stmt * stmt ) {
-	// Buffer for string manipulation.
-	char buf[size];
-
-	assertf( stmt, "CFA internal error: parameter statement cannot be null pointer" );
-	int len = newLabelPre( buf, suffix );
-
-	// What does this do?
-	if ( ! stmt->labels.empty() ) {
-		len = snprintf( buf + len, size - len, "_%s__", stmt->labels.front().name.c_str() );
-		assertf( len < size - len, "CFA Internal error: buffer overflow creating label" );
-	} // if
-
-	return newLabelPost( buf, stmt->location );
-}
-
-Label newLabel( const string & suffix, const CodeLocation & location ) {
-	// Buffer for string manipulation.
-	char buf[size];
-
-	newLabelPre( buf, suffix );
-	return newLabelPost( buf, location );
-}
-
-} // namespace ControlStruct
-
-// Local Variables: //
-// mode: c++ //
-// End: //
Index: c/ControlStruct/LabelGeneratorNew.hpp
===================================================================
--- src/ControlStruct/LabelGeneratorNew.hpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,36 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// LabelGenerator.h --
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Fir Mar 25 15:40:00 2022
-// Update Count     : 28
-//
-
-#pragma once
-
-#include <string>										// for string
-
-struct CodeLocation;
-
-namespace ast {
-	class Label;
-	class Stmt;
-} // namespace ast
-
-namespace ControlStruct {
-	ast::Label newLabel( const std::string &, const ast::Stmt * );
-	ast::Label newLabel( const std::string &, const CodeLocation & );
-} // namespace ControlStruct
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ControlStruct/MultiLevelExit.cpp
===================================================================
--- src/ControlStruct/MultiLevelExit.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/ControlStruct/MultiLevelExit.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -16,9 +16,10 @@
 #include "MultiLevelExit.hpp"
 
+#include <set>
+
 #include "AST/Pass.hpp"
 #include "AST/Stmt.hpp"
-#include "LabelGeneratorNew.hpp"
-
-#include <set>
+#include "LabelGenerator.hpp"
+
 using namespace std;
 using namespace ast;
Index: src/ControlStruct/module.mk
===================================================================
--- src/ControlStruct/module.mk	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/ControlStruct/module.mk	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -16,7 +16,7 @@
 
 SRC += \
-	ControlStruct/ExceptDeclNew.cpp \
+	ControlStruct/ExceptDecl.cpp \
 	ControlStruct/ExceptDecl.h \
-	ControlStruct/ExceptTranslateNew.cpp \
+	ControlStruct/ExceptTranslate.cpp \
 	ControlStruct/ExceptTranslate.h \
 	ControlStruct/FixLabels.cpp \
@@ -24,6 +24,6 @@
 	ControlStruct/HoistControlDecls.cpp \
 	ControlStruct/HoistControlDecls.hpp \
-	ControlStruct/LabelGeneratorNew.cpp \
-	ControlStruct/LabelGeneratorNew.hpp \
+	ControlStruct/LabelGenerator.cpp \
+	ControlStruct/LabelGenerator.hpp \
 	ControlStruct/MultiLevelExit.cpp \
 	ControlStruct/MultiLevelExit.hpp
Index: src/GenPoly/Box.cpp
===================================================================
--- src/GenPoly/Box.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/GenPoly/Box.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,2270 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Box.cpp -- Implement polymorphic function calls and types.
+//
+// Author           : Andrew Beach
+// Created On       : Thr Oct  6 13:39:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Oct  2 17:00:00 2023
+// Update Count     : 0
+//
+
+#include "Box.h"
+
+#include "AST/Decl.hpp"                // for Decl, FunctionDecl, ...
+#include "AST/Expr.hpp"                // for AlignofExpr, ConstantExpr, ...
+#include "AST/Init.hpp"                // for Init, SingleInit
+#include "AST/Inspect.hpp"             // for getFunctionName
+#include "AST/Pass.hpp"                // for Pass, WithDeclsToAdd, ...
+#include "AST/Stmt.hpp"                // for CompoundStmt, ExprStmt, ...
+#include "AST/Vector.hpp"              // for vector
+#include "AST/GenericSubstitution.hpp" // for genericSubstitution
+#include "CodeGen/OperatorTable.h"     // for isAssignment
+#include "Common/ScopedMap.h"          // for ScopedMap
+#include "Common/UniqueName.h"         // for UniqueName
+#include "Common/utility.h"            // for toCString, group_iterate
+#include "GenPoly/FindFunction.h"      // for findFunction
+#include "GenPoly/GenPoly.h"           // for getFunctionType, ...
+#include "GenPoly/Lvalue.h"            // for generalizedLvalue
+#include "GenPoly/ScopedSet.h"         // for ScopedSet
+#include "GenPoly/ScrubTyVars.h"       // for scrubTypeVars, scrubAllTypeVars
+#include "ResolvExpr/Unify.h"          // for typesCompatible
+#include "SymTab/Mangler.h"            // for mangle, mangleType
+
+namespace GenPoly {
+
+namespace {
+
+// --------------------------------------------------------------------------
+/// Adds layout-generation functions to polymorphic types.
+struct LayoutFunctionBuilder final :
+		public ast::WithDeclsToAdd<>,
+		public ast::WithShortCircuiting,
+		public ast::WithVisitorRef<LayoutFunctionBuilder> {
+	void previsit( ast::StructDecl const * decl );
+	void previsit( ast::UnionDecl const * decl );
+};
+
+/// Get all sized type declarations; those that affect a layout function.
+ast::vector<ast::TypeDecl> takeSizedParams(
+		ast::vector<ast::TypeDecl> const & decls ) {
+	ast::vector<ast::TypeDecl> sizedParams;
+	for ( ast::ptr<ast::TypeDecl> const & decl : decls ) {
+		if ( decl->isComplete() ) {
+			sizedParams.emplace_back( decl );
+		}
+	}
+	return sizedParams;
+}
+
+ast::BasicType * makeSizeAlignType() {
+	return new ast::BasicType( ast::BasicType::LongUnsignedInt );
+}
+
+/// Adds parameters for otype size and alignment to a function type.
+void addSTypeParams(
+		ast::vector<ast::DeclWithType> & params,
+		ast::vector<ast::TypeDecl> const & sizedParams ) {
+	for ( ast::ptr<ast::TypeDecl> const & sizedParam : sizedParams ) {
+		ast::TypeInstType inst( sizedParam );
+		std::string paramName = Mangle::mangleType( &inst );
+		params.emplace_back( new ast::ObjectDecl(
+			sizedParam->location,
+			sizeofName( paramName ),
+			makeSizeAlignType()
+		) );
+		params.emplace_back( new ast::ObjectDecl(
+			sizedParam->location,
+			alignofName( paramName ),
+			makeSizeAlignType()
+		) );
+	}
+}
+
+ast::Type * makeSizeAlignOutType() {
+	return new ast::PointerType( makeSizeAlignType() );
+}
+
+struct LayoutData {
+	ast::FunctionDecl * function;
+	ast::ObjectDecl * sizeofParam;
+	ast::ObjectDecl * alignofParam;
+	ast::ObjectDecl * offsetofParam;
+};
+
+LayoutData buildLayoutFunction(
+		CodeLocation const & location, ast::AggregateDecl const * aggr,
+		ast::vector<ast::TypeDecl> const & sizedParams,
+		bool isInFunction, bool isStruct ) {
+	ast::ObjectDecl * sizeParam = new ast::ObjectDecl(
+		location,
+		sizeofName( aggr->name ),
+		makeSizeAlignOutType()
+	);
+	ast::ObjectDecl * alignParam = new ast::ObjectDecl(
+		location,
+		alignofName( aggr->name ),
+		makeSizeAlignOutType()
+	);
+	ast::ObjectDecl * offsetParam = nullptr;
+	ast::vector<ast::DeclWithType> params = { sizeParam, alignParam };
+	if ( isStruct ) {
+		offsetParam = new ast::ObjectDecl(
+			location,
+			offsetofName( aggr->name ),
+			makeSizeAlignOutType()
+		);
+		params.push_back( offsetParam );
+	}
+	addSTypeParams( params, sizedParams );
+
+	// Routines at global scope marked "static" to prevent multiple
+	// definitions is separate translation units because each unit generates
+	// copies of the default routines for each aggregate.
+	ast::FunctionDecl * layoutDecl = new ast::FunctionDecl(
+		location,
+		layoutofName( aggr ),
+		{}, // forall
+		{}, // assertions
+		std::move( params ),
+		{}, // returns
+		new ast::CompoundStmt( location ),
+		isInFunction ? ast::Storage::Classes() : ast::Storage::Static,
+		ast::Linkage::AutoGen,
+		{}, // attrs
+		ast::Function::Inline,
+		ast::FixedArgs
+	);
+	layoutDecl->fixUniqueId();
+	return LayoutData{ layoutDecl, sizeParam, alignParam, offsetParam };
+}
+
+/// Makes a binary operation.
+ast::Expr * makeOp( CodeLocation const & location, std::string const & name,
+		ast::Expr const * lhs, ast::Expr const * rhs ) {
+	return new ast::UntypedExpr( location,
+		new ast::NameExpr( location, name ), { lhs, rhs } );
+}
+
+/// Make a binary operation and wrap it in a statement.
+ast::Stmt * makeOpStmt( CodeLocation const & location, std::string const & name,
+		ast::Expr const * lhs, ast::Expr const * rhs ) {
+	return new ast::ExprStmt( location, makeOp( location, name, lhs, rhs ) );
+}
+
+/// Returns the dereference of a local pointer variable.
+ast::Expr * derefVar(
+		CodeLocation const & location, ast::ObjectDecl const * var ) {
+	return ast::UntypedExpr::createDeref( location,
+		new ast::VariableExpr( location, var ) );
+}
+
+/// Makes an if-statement with a single-expression then and no else.
+ast::Stmt * makeCond( CodeLocation const & location,
+		ast::Expr const * cond, ast::Expr const * thenPart ) {
+	return new ast::IfStmt( location,
+		cond, new ast::ExprStmt( location, thenPart ), nullptr );
+}
+
+/// Makes a statement that aligns lhs to rhs (rhs should be an integer
+/// power of two).
+ast::Stmt * makeAlignTo( CodeLocation const & location,
+		ast::Expr const * lhs, ast::Expr const * rhs ) {
+	// Check that the lhs is zeroed out to the level of rhs.
+	ast::Expr * ifCond = makeOp( location, "?&?", lhs,
+		makeOp( location, "?-?", rhs,
+				ast::ConstantExpr::from_ulong( location, 1 ) ) );
+	// If not aligned, increment to alignment.
+	ast::Expr * ifExpr = makeOp( location, "?+=?", ast::deepCopy( lhs ),
+		makeOp( location, "?-?", ast::deepCopy( rhs ),
+				ast::deepCopy( ifCond ) ) );
+	return makeCond( location, ifCond, ifExpr );
+}
+
+/// Makes a statement that assigns rhs to lhs if lhs < rhs.
+ast::Stmt * makeAssignMax( CodeLocation const & location,
+		ast::Expr const * lhs, ast::Expr const * rhs ) {
+	return makeCond( location,
+		makeOp( location, "?<?", ast::deepCopy( lhs ), ast::deepCopy( rhs ) ),
+		makeOp( location, "?=?", lhs, rhs ) );
+}
+
+void LayoutFunctionBuilder::previsit( ast::StructDecl const * decl ) {
+	// Do not generate layout function for empty tag structures.
+	visit_children = false;
+	if ( decl->members.empty() ) return;
+
+	// Get parameters that can change layout, exiting early if none.
+	ast::vector<ast::TypeDecl> sizedParams =
+		takeSizedParams( decl->params );
+	if ( sizedParams.empty() ) return;
+
+	CodeLocation const & location = decl->location;
+
+	// Build layout function signature.
+	LayoutData layout = buildLayoutFunction(
+		location, decl, sizedParams, isInFunction(), true );
+	ast::FunctionDecl * layoutDecl = layout.function;
+	// Also return these or extract them from the parameter list?
+	ast::ObjectDecl const * sizeofParam = layout.sizeofParam;
+	ast::ObjectDecl const * alignofParam = layout.alignofParam;
+	ast::ObjectDecl const * offsetofParam = layout.offsetofParam;
+	assert( nullptr != layout.offsetofParam );
+
+	// Calculate structure layout in function body.
+	// Initialize size and alignment to 0 and 1
+	// (Will have at least one member to update size).
+	auto & kids = layoutDecl->stmts.get_and_mutate()->kids;
+	kids.emplace_back( makeOpStmt( location, "?=?",
+		derefVar( location, sizeofParam ),
+		ast::ConstantExpr::from_ulong( location, 0 )
+	) );
+	kids.emplace_back( makeOpStmt( location, "?=?",
+		derefVar( location, alignofParam ),
+		ast::ConstantExpr::from_ulong( location, 1 )
+	) );
+	// TODO: Polymorphic types will be out of the struct declaration scope.
+	// Should be removed by PolyGenericCalculator.
+	for ( auto const & member : enumerate( decl->members ) ) {
+		auto dwt = member.val.strict_as<ast::DeclWithType>();
+		ast::Type const * memberType = dwt->get_type();
+
+		if ( 0 < member.idx ) {
+			// Make sure all later members have padding to align them.
+			kids.emplace_back( makeAlignTo( location,
+				derefVar( location, sizeofParam ),
+				new ast::AlignofExpr( location, ast::deepCopy( memberType ) )
+			) );
+		}
+
+		// Place current size in the current offset index.
+		kids.emplace_back( makeOpStmt( location, "?=?",
+			makeOp( location, "?[?]",
+				new ast::VariableExpr( location, offsetofParam ),
+				ast::ConstantExpr::from_ulong( location, member.idx ) ),
+			derefVar( location, sizeofParam ) ) );
+
+		// Add member size to current size.
+		kids.emplace_back( makeOpStmt( location, "?+=?",
+			derefVar( location, sizeofParam ),
+			new ast::SizeofExpr( location, ast::deepCopy( memberType ) ) ) );
+
+		// Take max of member alignment and global alignment.
+		// (As align is always 2^n, this will always be a multiple of both.)
+		kids.emplace_back( makeAssignMax( location,
+			derefVar( location, alignofParam ),
+			new ast::AlignofExpr( location, ast::deepCopy( memberType ) ) ) );
+	}
+	// Make sure the type is end-padded to a multiple of its alignment.
+	kids.emplace_back( makeAlignTo( location,
+		derefVar( location, sizeofParam ),
+		derefVar( location, alignofParam ) ) );
+
+	declsToAddAfter.emplace_back( layoutDecl );
+}
+
+void LayoutFunctionBuilder::previsit( ast::UnionDecl const * decl ) {
+	visit_children = false;
+	// Do not generate layout function for empty tag unions.
+	if ( decl->members.empty() ) return;
+
+	// Get parameters that can change layout, exiting early if none.
+	ast::vector<ast::TypeDecl> sizedParams =
+		takeSizedParams( decl->params );
+	if ( sizedParams.empty() ) return;
+
+	CodeLocation const & location = decl->location;
+
+	// Build layout function signature.
+	LayoutData layout = buildLayoutFunction(
+		location, decl, sizedParams, isInFunction(), false );
+	ast::FunctionDecl * layoutDecl = layout.function;
+	// Also return these or extract them from the parameter list?
+	ast::ObjectDecl const * sizeofParam = layout.sizeofParam;
+	ast::ObjectDecl const * alignofParam = layout.alignofParam;
+	assert( nullptr == layout.offsetofParam );
+
+	// Calculate union layout in function body.
+	// Both are simply the maximum for union (actually align is always the
+	// LCM, but with powers of two that is also the maximum).
+	auto & kids = layoutDecl->stmts.get_and_mutate()->kids;
+	kids.emplace_back( makeOpStmt( location,
+		"?=?", derefVar( location, sizeofParam ),
+		ast::ConstantExpr::from_ulong( location, 1 )
+	) );
+	kids.emplace_back( makeOpStmt( location,
+		"?=?", derefVar( location, alignofParam ),
+		ast::ConstantExpr::from_ulong( location, 1 )
+	) );
+	// TODO: Polymorphic types will be out of the union declaration scope.
+	for ( auto const & member : decl->members ) {
+		auto dwt = member.strict_as<ast::DeclWithType>();
+		ast::Type const * memberType = dwt->get_type();
+
+		// Take max member size and global size.
+		kids.emplace_back( makeAssignMax( location,
+			derefVar( location, sizeofParam ),
+			new ast::SizeofExpr( location, ast::deepCopy( memberType ) )
+		) );
+
+		// Take max of member alignment and global alignment.
+		kids.emplace_back( makeAssignMax( location,
+			derefVar( location, alignofParam ),
+			new ast::AlignofExpr( location, ast::deepCopy( memberType ) )
+		) );
+	}
+	kids.emplace_back( makeAlignTo( location,
+		derefVar( location, sizeofParam ),
+		derefVar( location, alignofParam ) ) );
+
+	declsToAddAfter.emplace_back( layoutDecl );
+}
+
+// --------------------------------------------------------------------------
+/// Application expression transformer.
+/// * Replaces polymorphic return types with out-parameters.
+/// * Replaces call to polymorphic functions with adapter calls which handles
+///   dynamic arguments and return values.
+/// * Adds appropriate type variables to the function calls.
+struct CallAdapter final :
+		public ast::WithConstTypeSubstitution,
+		public ast::WithGuards,
+		public ast::WithShortCircuiting,
+		public ast::WithStmtsToAdd<>,
+		public ast::WithVisitorRef<CallAdapter> {
+	CallAdapter();
+
+	void previsit( ast::Decl const * decl );
+	ast::FunctionDecl const * previsit( ast::FunctionDecl const * decl );
+	void previsit( ast::TypeDecl const * decl );
+	void previsit( ast::CommaExpr const * expr );
+	ast::Expr const * postvisit( ast::ApplicationExpr const * expr );
+	ast::Expr const * postvisit( ast::UntypedExpr const * expr );
+	void previsit( ast::AddressExpr const * expr );
+	ast::Expr const * postvisit( ast::AddressExpr const * expr );
+	ast::ReturnStmt const * previsit( ast::ReturnStmt const * stmt );
+
+	void beginScope();
+	void endScope();
+private:
+	// Many helpers here use a mutable ApplicationExpr as an in/out parameter
+	// instead of using the return value, to save on mutates and free up the
+	// return value.
+
+	/// Passes extra layout arguments for sized polymorphic type parameters.
+	ast::vector<ast::Expr>::iterator passTypeVars(
+		ast::ApplicationExpr * expr,
+		ast::FunctionType const * funcType );
+	/// Wraps a function application with a new temporary for the
+	/// out-parameter return value.
+	ast::Expr const * addRetParam(
+		ast::ApplicationExpr * expr, ast::Type const * retType );
+	/// Wraps a function application returning a polymorphic type with a new
+	/// temporary for the out-parameter return value.
+	ast::Expr const * addDynRetParam(
+		ast::ApplicationExpr * expr, ast::Type const * polyType );
+	/// Modify a call so it passes the function through the correct adapter.
+	ast::Expr const * applyAdapter(
+		ast::ApplicationExpr * expr,
+		ast::FunctionType const * function );
+	/// Convert a single argument into its boxed form to pass the parameter.
+	void boxParam( ast::ptr<ast::Expr> & arg,
+		ast::Type const * formal, TypeVarMap const & exprTyVars );
+	/// Box every argument from arg forward, matching the functionType
+	/// parameter list. arg should point into expr's argument list.
+	void boxParams(
+		ast::ApplicationExpr const * expr,
+		ast::vector<ast::Expr>::iterator arg,
+		ast::FunctionType const * function,
+		const TypeVarMap & typeVars );
+	/// Adds the inferred parameters derived from the assertions of the
+	/// expression to the call.
+	void addInferredParams(
+		ast::ApplicationExpr * expr,
+		ast::vector<ast::Expr>::iterator arg,
+		ast::FunctionType const * functionType,
+		const TypeVarMap & typeVars );
+	/// Stores assignment operators from assertion list in
+	/// local map of assignment operations.
+	void passAdapters(
+		ast::ApplicationExpr * expr,
+		ast::FunctionType const * type,
+		const TypeVarMap & typeVars );
+	/// Create an adapter function based on the type of the adaptee and the
+	/// real type with the type substitutions applied.
+	ast::FunctionDecl * makeAdapter(
+		ast::FunctionType const * adaptee,
+		ast::FunctionType const * realType,
+		std::string const & mangleName,
+		TypeVarMap const & typeVars,
+		CodeLocation const & location ) const;
+	/// Replaces intrinsic operator functions with their arithmetic desugaring.
+	ast::Expr const * handleIntrinsics( ast::ApplicationExpr const * );
+	/// Inserts a new temporary variable into the current scope with an
+	/// auto-generated name.
+	ast::ObjectDecl * makeTemporary(
+		CodeLocation const & location, ast::Type const * type );
+
+	TypeVarMap scopeTypeVars;
+	ScopedMap< std::string, ast::DeclWithType const * > adapters;
+	std::map< ast::ApplicationExpr const *, ast::Expr const * > retVals;
+	ast::DeclWithType const * retval;
+	UniqueName tmpNamer;
+};
+
+/// Replaces a polymorphic type with its concrete equivalant under the
+/// current environment (returns itself if concrete).
+/// If `doClone` is set to false, will not clone interior types
+ast::Type const * replaceWithConcrete(
+		ast::Type const * type,
+		ast::TypeSubstitution const & typeSubs,
+		bool doCopy = true );
+
+/// Replaces all the type parameters of a generic type with their
+/// concrete equivalents under the current environment.
+void replaceParametersWithConcrete(
+		ast::vector<ast::Expr> & params,
+		ast::TypeSubstitution const & typeSubs ) {
+	for ( ast::ptr<ast::Expr> & paramExpr : params ) {
+		ast::TypeExpr const * param = paramExpr.as<ast::TypeExpr>();
+		assertf( param, "Aggregate parameters should be type expressions." );
+		paramExpr = ast::mutate_field( param, &ast::TypeExpr::type,
+			replaceWithConcrete( param->type.get(), typeSubs, false ) );
+	}
+}
+
+ast::Type const * replaceWithConcrete(
+		ast::Type const * type,
+		ast::TypeSubstitution const & typeSubs,
+		bool doCopy ) {
+	if ( auto instType = dynamic_cast<ast::TypeInstType const *>( type ) ) {
+		ast::Type const * concrete = typeSubs.lookup( instType );
+		return ( nullptr != concrete ) ? concrete : instType;
+	} else if ( auto structType =
+			dynamic_cast<ast::StructInstType const *>( type ) ) {
+		ast::StructInstType * newType =
+			doCopy ? ast::deepCopy( structType ) : ast::mutate( structType );
+		replaceParametersWithConcrete( newType->params, typeSubs );
+		return newType;
+	} else if ( auto unionType =
+			dynamic_cast<ast::UnionInstType const *>( type ) ) {
+		ast::UnionInstType * newType =
+			doCopy ? ast::deepCopy( unionType ) : ast::mutate( unionType );
+		replaceParametersWithConcrete( newType->params, typeSubs );
+		return newType;
+	} else {
+		return type;
+	}
+}
+
+std::string makePolyMonoSuffix(
+		ast::FunctionType const * function,
+		TypeVarMap const & typeVars ) {
+	// If the return type or a parameter type involved polymorphic types,
+	// then the adapter will need to take those polymorphic types as pointers.
+	// Therefore, there can be two different functions with the same mangled
+	// name, so we need to further mangle the names.
+	std::stringstream name;
+	for ( auto ret : function->returns ) {
+		name << ( isPolyType( ret, typeVars ) ? 'P' : 'M' );
+	}
+	name << '_';
+	for ( auto arg : function->params ) {
+		name << ( isPolyType( arg, typeVars ) ? 'P' : 'M' );
+	}
+	return name.str();
+}
+
+std::string mangleAdapterName(
+		ast::FunctionType const * function,
+		TypeVarMap const & typeVars ) {
+	return Mangle::mangle( function, {} )
+		+ makePolyMonoSuffix( function, typeVars );
+}
+
+std::string makeAdapterName( std::string const & mangleName ) {
+	return "_adapter" + mangleName;
+}
+
+void makeRetParam( ast::FunctionType * type ) {
+	ast::ptr<ast::Type> & retParam = type->returns.front();
+
+	// Make a new parameter that is a pointer to the type of the old return value.
+	retParam = new ast::PointerType( retParam.get() );
+	type->params.emplace( type->params.begin(), retParam );
+
+	// We don't need the return value any more.
+	type->returns.clear();
+}
+
+ast::FunctionType * makeAdapterType(
+		ast::FunctionType const * adaptee,
+		TypeVarMap const & typeVars ) {
+	ast::FunctionType * adapter = ast::deepCopy( adaptee );
+	if ( isDynRet( adapter, typeVars ) ) {
+		makeRetParam( adapter );
+	}
+	adapter->params.emplace( adapter->params.begin(),
+		new ast::PointerType( new ast::FunctionType( ast::VariableArgs ) )
+	);
+	return adapter;
+}
+
+CallAdapter::CallAdapter() : tmpNamer( "_temp" ) {}
+
+void CallAdapter::previsit( ast::Decl const * ) {
+	// Prevent type declaration information from leaking out.
+	GuardScope( scopeTypeVars );
+}
+
+ast::FunctionDecl const * CallAdapter::previsit( ast::FunctionDecl const * decl ) {
+	// Prevent type declaration information from leaking out.
+	GuardScope( scopeTypeVars );
+
+	if ( nullptr == decl->stmts ) {
+		return decl;
+	}
+
+	GuardValue( retval );
+
+	// Process polymorphic return value.
+	retval = nullptr;
+	ast::FunctionType const * type = decl->type;
+	if ( isDynRet( type ) && decl->linkage != ast::Linkage::C ) {
+		retval = decl->returns.front();
+
+		// Give names to unnamed return values.
+		if ( "" == retval->name ) {
+			auto mutRet = ast::mutate( retval );
+			mutRet->name = "_retparam";
+			mutRet->linkage = ast::Linkage::C;
+			retval = mutRet;
+			decl = ast::mutate_field_index( decl,
+				&ast::FunctionDecl::returns, 0, mutRet );
+		}
+	}
+
+	// The formal_usage/expr_id values may be off if we get them from the
+	// type, trying the declaration instead.
+	makeTypeVarMap( type, scopeTypeVars );
+
+	// Get all needed adapters from the call. We will forward them.
+	ast::vector<ast::FunctionType> functions;
+	for ( ast::ptr<ast::VariableExpr> const & assertion : type->assertions ) {
+		auto atype = assertion->result.get();
+		findFunction( atype, functions, scopeTypeVars, needsAdapter );
+	}
+
+	for ( ast::ptr<ast::Type> const & arg : type->params ) {
+		findFunction( arg, functions, scopeTypeVars, needsAdapter );
+	}
+
+	for ( auto funcType : functions ) {
+		std::string mangleName = mangleAdapterName( funcType, scopeTypeVars );
+		if ( adapters.contains( mangleName ) ) continue;
+		std::string adapterName = makeAdapterName( mangleName );
+		// TODO: The forwarding here is problematic because these
+		// declarations are not rooted anywhere in the translation unit.
+		adapters.insert(
+			mangleName,
+			new ast::ObjectDecl(
+				decl->location,
+				adapterName,
+				new ast::PointerType(
+					makeAdapterType( funcType, scopeTypeVars ) ),
+				nullptr, // init
+				ast::Storage::Classes(),
+				ast::Linkage::C
+			)
+		);
+	}
+
+	return decl;
+}
+
+void CallAdapter::previsit( ast::TypeDecl const * decl ) {
+	addToTypeVarMap( decl, scopeTypeVars );
+}
+
+void CallAdapter::previsit( ast::CommaExpr const * expr ) {
+	// Attempting to find application expressions that were mutated by the
+	// copy constructor passes to use an explicit return variable, so that
+	// the variable can be reused as a parameter to the call rather than
+	// creating a new temporary variable. Previously this step was an
+	// optimization, but with the introduction of tuples and UniqueExprs,
+	// it is necessary to ensure that they use the same variable.
+	// Essentially, looking for pattern:
+	// (x=f(...), x)
+	// To compound the issue, the right side can be *x, etc.
+	// because of lvalue-returning functions
+	if ( auto assign = expr->arg1.as<ast::UntypedExpr>() ) {
+		if ( CodeGen::isAssignment( ast::getFunctionName( assign ) ) ) {
+			assert( 2 == assign->args.size() );
+			if ( auto app = assign->args.back().as<ast::ApplicationExpr>() ) {
+				// First argument is assignable, so it must be an lvalue,
+				// so it should be legal to takes its address.
+				retVals.insert_or_assign( app, assign->args.front() );
+			}
+		}
+	}
+}
+
+ast::Expr const * CallAdapter::postvisit( ast::ApplicationExpr const * expr ) {
+	assert( expr->func->result );
+	ast::FunctionType const * function = getFunctionType( expr->func->result );
+	assertf( function, "ApplicationExpr has non-function type %s",
+			toCString( expr->func->result ) );
+
+	if ( auto newExpr = handleIntrinsics( expr ) ) {
+		return newExpr;
+	}
+
+	ast::ApplicationExpr * mutExpr = ast::mutate( expr );
+	ast::Expr const * ret = expr;
+
+	// TODO: This entire section should probably be refactored to do less
+	// pushing to the front/middle of a vector.
+	ptrdiff_t initArgCount = mutExpr->args.size();
+
+	TypeVarMap exprTypeVars;
+	// TODO: Should this take into account the variables already bound in
+	// scopeTypeVars ([ex] remove them from exprTypeVars)?
+	makeTypeVarMap( function, exprTypeVars );
+	auto dynRetType = isDynRet( function, exprTypeVars );
+
+	// NOTE: addDynRetParam needs to know the actual (generated) return type
+	// so it can make a temporary variable, so pass the result type form the
+	// `expr` `passTypeVars` needs to know the program-text return type ([ex]
+	// the distinction between _conc_T30 and T3(int)) concRetType may not be
+	// a good name in one or both of these places.
+	if ( dynRetType ) {
+		ast::Type const * result = mutExpr->result;
+		ast::Type const * concRetType = result->isVoid() ? nullptr : result;
+		// [Comment from before translation.]
+		// Used to use dynRetType instead of concRetType.
+		ret = addDynRetParam( mutExpr, concRetType );
+	} else if ( needsAdapter( function, scopeTypeVars )
+			&& !needsAdapter( function, exprTypeVars ) ) {
+		// Change the application so it calls the adapter rather than the
+		// passed function.
+		ret = applyAdapter( mutExpr, function );
+	}
+
+	assert( typeSubs );
+	ast::vector<ast::Expr>::iterator argIt =
+		passTypeVars( mutExpr, function );
+	addInferredParams( mutExpr, argIt, function, exprTypeVars );
+
+	argIt = mutExpr->args.begin();
+	std::advance( argIt, ( mutExpr->args.size() - initArgCount ) );
+
+	boxParams( mutExpr, argIt, function, exprTypeVars );
+	passAdapters( mutExpr, function, exprTypeVars );
+
+	return ret;
+}
+
+bool isPolyDeref( ast::UntypedExpr const * expr,
+		TypeVarMap const & typeVars,
+		ast::TypeSubstitution const * typeSubs ) {
+	if ( expr->result && isPolyType( expr->result, typeVars, typeSubs ) ) {
+		if ( auto name = expr->func.as<ast::NameExpr>() ) {
+			if ( "*?" == name->name ) {
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+ast::Expr const * CallAdapter::postvisit( ast::UntypedExpr const * expr ) {
+	if ( isPolyDeref( expr, scopeTypeVars, typeSubs ) ) {
+		return expr->args.front();
+	}
+	return expr;
+}
+
+void CallAdapter::previsit( ast::AddressExpr const * ) {
+	visit_children = false;
+}
+
+ast::Expr const * CallAdapter::postvisit( ast::AddressExpr const * expr ) {
+	assert( expr->arg->result );
+	assert( !expr->arg->result->isVoid() );
+
+	bool doesNeedAdapter = false;
+	if ( auto un = expr->arg.as<ast::UntypedExpr>() ) {
+		if ( isPolyDeref( un, scopeTypeVars, typeSubs ) ) {
+			if ( auto app = un->args.front().as<ast::ApplicationExpr>() ) {
+				assert( app->func->result );
+				auto function = getFunctionType( app->func->result );
+				assert( function );
+				doesNeedAdapter = needsAdapter( function, scopeTypeVars );
+			}
+		}
+	}
+	// isPolyType check needs to happen before mutating expr arg,
+	// so pull it forward out of the if condition.
+	expr = ast::mutate_field( expr, &ast::AddressExpr::arg,
+			expr->arg->accept( *visitor ) );
+	// But must happen after mutate, since argument might change
+	// (ex. intrinsic *?, ?[?]) re-evaluate above comment.
+	bool polyType = isPolyType( expr->arg->result, scopeTypeVars, typeSubs );
+	if ( polyType || doesNeedAdapter ) {
+		ast::Expr * ret = ast::mutate( expr->arg.get() );
+		ret->result = ast::deepCopy( expr->result );
+		return ret;
+	} else {
+		return expr;
+	}
+}
+
+ast::ReturnStmt const * CallAdapter::previsit( ast::ReturnStmt const * stmt ) {
+	// Since retval is set when the return type is dynamic, this function
+	// should have been converted to void return & out parameter.
+	if ( retval && stmt->expr ) {
+		assert( stmt->expr->result );
+		assert( !stmt->expr->result->isVoid() );
+		return ast::mutate_field( stmt, &ast::ReturnStmt::expr, nullptr );
+	}
+	return stmt;
+}
+
+void CallAdapter::beginScope() {
+	adapters.beginScope();
+}
+
+void CallAdapter::endScope() {
+	adapters.endScope();
+}
+
+/// Find instances of polymorphic type parameters.
+struct PolyFinder {
+	TypeVarMap const & typeVars;
+	bool result = false;
+	PolyFinder( TypeVarMap const & tvs ) : typeVars( tvs ) {}
+
+	void previsit( ast::TypeInstType const * type ) {
+		if ( isPolyType( type, typeVars ) ) result = true;
+	}
+};
+
+/// True if these is an instance of a polymorphic type parameter in the type.
+bool hasPolymorphism( ast::Type const * type, TypeVarMap const & typeVars ) {
+	return ast::Pass<PolyFinder>::read( type, typeVars );
+}
+
+ast::vector<ast::Expr>::iterator CallAdapter::passTypeVars(
+		ast::ApplicationExpr * expr,
+		ast::FunctionType const * function ) {
+	assert( typeSubs );
+	ast::vector<ast::Expr>::iterator arg = expr->args.begin();
+	// Pass size/align for type variables.
+	for ( ast::ptr<ast::TypeInstType> const & typeVar : function->forall ) {
+		if ( !typeVar->base->isComplete() ) continue;
+		ast::Type const * concrete = typeSubs->lookup( typeVar );
+		if ( !concrete ) {
+			// Should this be an assertion?
+			SemanticError( expr, toString( typeSubs,
+				"\nunbound type variable: ", typeVar->typeString(),
+				" in application " ) );
+		}
+		arg = expr->args.insert( arg,
+			new ast::SizeofExpr( expr->location, ast::deepCopy( concrete ) ) );
+		arg++;
+		arg = expr->args.insert( arg,
+			new ast::AlignofExpr( expr->location, ast::deepCopy( concrete ) ) );
+		arg++;
+	}
+	return arg;
+}
+
+ast::Expr const * CallAdapter::addRetParam(
+		ast::ApplicationExpr * expr, ast::Type const * retType ) {
+	// Create temporary to hold return value of polymorphic function and
+	// produce that temporary as a result using a comma expression.
+	assert( retType );
+
+	ast::Expr * paramExpr = nullptr;
+	// Try to use existing return value parameter if it exists,
+	// otherwise create a new temporary.
+	if ( retVals.count( expr ) ) {
+		paramExpr = ast::deepCopy( retVals[ expr ] );
+	} else {
+		auto newObj = makeTemporary( expr->location, ast::deepCopy( retType ) );
+		paramExpr = new ast::VariableExpr( expr->location, newObj );
+	}
+	ast::Expr * retExpr = ast::deepCopy( paramExpr );
+
+	// If the type of the temporary is not polpmorphic, box temporary by
+	// taking its address; otherwise the temporary is already boxed and can
+	// be used directly.
+	if ( !isPolyType( paramExpr->result, scopeTypeVars, typeSubs ) ) {
+		paramExpr = new ast::AddressExpr( paramExpr->location, paramExpr );
+	}
+	// Add argument to function call.
+	expr->args.insert( expr->args.begin(), paramExpr );
+	// Build a comma expression to call the function and return a value.
+	ast::CommaExpr * comma = new ast::CommaExpr(
+		expr->location, expr, retExpr );
+	comma->env = expr->env;
+	expr->env = nullptr;
+	return comma;
+}
+
+ast::Expr const * CallAdapter::addDynRetParam(
+		ast::ApplicationExpr * expr, ast::Type const * polyType ) {
+	assert( typeSubs );
+	ast::Type const * concrete = replaceWithConcrete( polyType, *typeSubs );
+	// Add out-parameter for return value.
+	return addRetParam( expr, concrete );
+}
+
+ast::Expr const * CallAdapter::applyAdapter(
+		ast::ApplicationExpr * expr,
+		ast::FunctionType const * function ) {
+	ast::Expr const * ret = expr;
+	if ( isDynRet( function, scopeTypeVars ) ) {
+		ret = addRetParam( expr, function->returns.front() );
+	}
+	std::string mangleName = mangleAdapterName( function, scopeTypeVars );
+	std::string adapterName = makeAdapterName( mangleName );
+
+	// Cast adaptee to `void (*)()`, since it may have any type inside a
+	// polymorphic function.
+	ast::Type const * adapteeType = new ast::PointerType(
+		new ast::FunctionType( ast::VariableArgs ) );
+	expr->args.insert( expr->args.begin(),
+		new ast::CastExpr( expr->location, expr->func, adapteeType ) );
+	// The result field is never set on NameExpr. / Now it is.
+	auto head = new ast::NameExpr( expr->location, adapterName );
+	head->result = ast::deepCopy( adapteeType );
+	expr->func = head;
+
+	return ret;
+}
+
+/// Cast parameters to polymorphic functions so that types are replaced with
+/// `void *` if they are type parameters in the formal type.
+/// This gets rid of warnings from gcc.
+void addCast(
+		ast::ptr<ast::Expr> & actual,
+		ast::Type const * formal,
+		TypeVarMap const & typeVars ) {
+	// Type contains polymorphism, but isn't exactly a polytype, in which
+	// case it has some real actual type (ex. unsigned int) and casting to
+	// `void *` is wrong.
+	if ( hasPolymorphism( formal, typeVars )
+			&& !isPolyType( formal, typeVars ) ) {
+		ast::Type const * newType = ast::deepCopy( formal );
+		newType = scrubTypeVars( newType, typeVars );
+		actual = new ast::CastExpr( actual->location, actual, newType );
+	}
+}
+
+void CallAdapter::boxParam( ast::ptr<ast::Expr> & arg,
+		ast::Type const * param, TypeVarMap const & exprTypeVars ) {
+	assertf( arg->result, "arg does not have result: %s", toCString( arg ) );
+	addCast( arg, param, exprTypeVars );
+	if ( !needsBoxing( param, arg->result, exprTypeVars, typeSubs ) ) {
+		return;
+	}
+	CodeLocation const & location = arg->location;
+
+	if ( arg->get_lvalue() ) {
+		// The argument expression may be CFA lvalue, but not C lvalue,
+		// so apply generalizedLvalue transformations.
+		// if ( auto var = dynamic_cast<ast::VariableExpr const *>( arg ) ) {
+		//  if ( dynamic_cast<ast::ArrayType const *>( varExpr->var->get_type() ) ){
+		//      // temporary hack - don't box arrays, because &arr is not the same as &arr[0]
+		//      return;
+		//  }
+		// }
+		arg = generalizedLvalue( new ast::AddressExpr( arg->location, arg ) );
+		if ( !ResolvExpr::typesCompatible( param, arg->result ) ) {
+			// Silence warnings by casting boxed parameters when the actually
+			// type does not match up with the formal type.
+			arg = new ast::CastExpr( location, arg, ast::deepCopy( param ) );
+		}
+	} else {
+		// Use type computed in unification to declare boxed variables.
+		ast::ptr<ast::Type> newType = ast::deepCopy( param );
+		if ( typeSubs ) typeSubs->apply( newType );
+		ast::ObjectDecl * newObj = makeTemporary( location, newType );
+		auto assign = ast::UntypedExpr::createCall( location, "?=?", {
+			new ast::VariableExpr( location, newObj ),
+			arg,
+		} );
+		stmtsToAddBefore.push_back( new ast::ExprStmt( location, assign ) );
+		arg = new ast::AddressExpr(
+			new ast::VariableExpr( location, newObj ) );
+	}
+}
+
+void CallAdapter::boxParams(
+		ast::ApplicationExpr const * expr,
+		ast::vector<ast::Expr>::iterator arg,
+		ast::FunctionType const * function,
+		const TypeVarMap & typeVars ) {
+	for ( auto param : function->params ) {
+		assertf( arg != expr->args.end(),
+			"boxParams: missing argument for param %s to %s in %s",
+			toCString( param ), toCString( function ), toCString( expr ) );
+		boxParam( *arg, param, typeVars );
+		++arg;
+	}
+}
+
+void CallAdapter::addInferredParams(
+		ast::ApplicationExpr * expr,
+		ast::vector<ast::Expr>::iterator arg,
+		ast::FunctionType const * functionType,
+		TypeVarMap const & typeVars ) {
+	ast::vector<ast::Expr>::iterator cur = arg;
+	for ( auto assertion : functionType->assertions ) {
+		auto inferParam = expr->inferred.inferParams().find(
+			assertion->var->uniqueId );
+		assertf( inferParam != expr->inferred.inferParams().end(),
+			"addInferredParams missing inferred parameter: %s in: %s",
+			toCString( assertion ), toCString( expr ) );
+		ast::ptr<ast::Expr> newExpr = ast::deepCopy( inferParam->second.expr );
+		boxParam( newExpr, assertion->result, typeVars );
+		cur = expr->args.insert( cur, newExpr.release() );
+		++cur;
+	}
+}
+
+/// Modifies the ApplicationExpr to accept adapter functions for its
+/// assertion and parameters, declares the required adapters.
+void CallAdapter::passAdapters(
+		ast::ApplicationExpr * expr,
+		ast::FunctionType const * type,
+		const TypeVarMap & exprTypeVars ) {
+	// Collect a list of function types passed as parameters or implicit
+	// parameters (assertions).
+	ast::vector<ast::Type> const & paramList = type->params;
+	ast::vector<ast::FunctionType> functions;
+
+	for ( ast::ptr<ast::VariableExpr> const & assertion : type->assertions ) {
+		findFunction( assertion->result, functions, exprTypeVars, needsAdapter );
+	}
+	for ( ast::ptr<ast::Type> const & arg : paramList ) {
+		findFunction( arg, functions, exprTypeVars, needsAdapter );
+	}
+
+	// Parameter function types for which an appropriate adapter has been
+	// generated. We cannot use the types after applying substitutions,
+	// since two different parameter types may be unified to the same type.
+	std::set<std::string> adaptersDone;
+
+	CodeLocation const & location = expr->location;
+
+	for ( ast::ptr<ast::FunctionType> const & funcType : functions ) {
+		std::string mangleName = Mangle::mangle( funcType );
+
+		// Only attempt to create an adapter or pass one as a parameter if we
+		// haven't already done so for this pre-substitution parameter
+		// function type.
+		// The second part of the result if is if the element was inserted.
+		if ( !adaptersDone.insert( mangleName ).second ) continue;
+
+		// Apply substitution to type variables to figure out what the
+		// adapter's type should look like. (Copy to make the release safe.)
+		assert( typeSubs );
+		auto result = typeSubs->apply( ast::deepCopy( funcType ) );
+		ast::FunctionType * realType = ast::mutate( result.node.release() );
+		mangleName = Mangle::mangle( realType );
+		mangleName += makePolyMonoSuffix( funcType, exprTypeVars );
+
+		// Check if the adapter has already been created, or has to be.
+		using AdapterIter = decltype(adapters)::iterator;
+		AdapterIter adapter = adapters.find( mangleName );
+		if ( adapter == adapters.end() ) {
+			ast::FunctionDecl * newAdapter = makeAdapter(
+				funcType, realType, mangleName, exprTypeVars, location );
+			std::pair<AdapterIter, bool> answer =
+				adapters.insert( mangleName, newAdapter );
+			adapter = answer.first;
+			stmtsToAddBefore.push_back(
+				new ast::DeclStmt( location, newAdapter ) );
+		}
+		assert( adapter != adapters.end() );
+
+		// Add the approprate adapter as a parameter.
+		expr->args.insert( expr->args.begin(),
+			new ast::VariableExpr( location, adapter->second ) );
+	}
+}
+
+// Parameter and argument may be used wrong around here.
+ast::Expr * makeAdapterArg(
+		ast::DeclWithType const * param,
+		ast::Type const * arg,
+		ast::Type const * realParam,
+		TypeVarMap const & typeVars,
+		CodeLocation const & location ) {
+	assert( param );
+	assert( arg );
+	assert( realParam );
+	if ( isPolyType( realParam, typeVars ) && !isPolyType( arg ) ) {
+		ast::UntypedExpr * deref = ast::UntypedExpr::createDeref(
+			location,
+			new ast::CastExpr( location,
+				new ast::VariableExpr( location, param ),
+				new ast::PointerType( ast::deepCopy( arg ) )
+			)
+		);
+		deref->result = ast::deepCopy( arg );
+		return deref;
+	}
+	return new ast::VariableExpr( location, param );
+}
+
+// This seems to be one of the problematic functions.
+void addAdapterParams(
+		ast::ApplicationExpr * adaptee,
+		ast::vector<ast::Type>::const_iterator arg,
+		ast::vector<ast::DeclWithType>::iterator param,
+		ast::vector<ast::DeclWithType>::iterator paramEnd,
+		ast::vector<ast::Type>::const_iterator realParam,
+		TypeVarMap const & typeVars,
+		CodeLocation const & location ) {
+	UniqueName paramNamer( "_p" );
+	for ( ; param != paramEnd ; ++param, ++arg, ++realParam ) {
+		if ( "" == (*param)->name ) {
+			auto mutParam = (*param).get_and_mutate();
+			mutParam->name = paramNamer.newName();
+			mutParam->linkage = ast::Linkage::C;
+		}
+		adaptee->args.push_back(
+			makeAdapterArg( *param, *arg, *realParam, typeVars, location ) );
+	}
+}
+
+ast::FunctionDecl * CallAdapter::makeAdapter(
+		ast::FunctionType const * adaptee,
+		ast::FunctionType const * realType,
+		std::string const & mangleName,
+		TypeVarMap const & typeVars,
+		CodeLocation const & location ) const {
+	ast::FunctionType * adapterType = makeAdapterType( adaptee, typeVars );
+	adapterType = ast::mutate( scrubTypeVars( adapterType, typeVars ) );
+
+	// Some of these names will be overwritten, but it gives a default.
+	UniqueName pNamer( "_param" );
+	UniqueName rNamer( "_ret" );
+
+	bool first = true;
+
+	ast::FunctionDecl * adapterDecl = new ast::FunctionDecl( location,
+		makeAdapterName( mangleName ),
+		{}, // forall
+		{}, // assertions
+		map_range<ast::vector<ast::DeclWithType>>( adapterType->params,
+				[&pNamer, &location, &first]( ast::ptr<ast::Type> const & param ) {
+			// [Trying to make the generated code match exactly more often.]
+			if ( first ) {
+				first = false;
+				return new ast::ObjectDecl( location, "_adaptee", param );
+			}
+			return new ast::ObjectDecl( location, pNamer.newName(), param );
+		} ),
+		map_range<ast::vector<ast::DeclWithType>>( adapterType->returns,
+				[&rNamer, &location]( ast::ptr<ast::Type> const & retval ) {
+			return new ast::ObjectDecl( location, rNamer.newName(), retval );
+		} ),
+		nullptr, // stmts
+		{}, // storage
+		ast::Linkage::C
+	);
+
+	ast::DeclWithType * adapteeDecl =
+		adapterDecl->params.front().get_and_mutate();
+	adapteeDecl->name = "_adaptee";
+
+	// Do not carry over attributes to real type parameters/return values.
+	auto mutRealType = ast::mutate( realType );
+	for ( ast::ptr<ast::Type> & decl : mutRealType->params ) {
+		if ( decl->attributes.empty() ) continue;
+		auto mut = ast::mutate( decl.get() );
+		mut->attributes.clear();
+		decl = mut;
+	}
+	for ( ast::ptr<ast::Type> & decl : mutRealType->returns ) {
+		if ( decl->attributes.empty() ) continue;
+		auto mut = ast::mutate( decl.get() );
+		mut->attributes.clear();
+		decl = mut;
+	}
+	realType = mutRealType;
+
+	ast::ApplicationExpr * adapteeApp = new ast::ApplicationExpr( location,
+		new ast::CastExpr( location,
+			new ast::VariableExpr( location, adapteeDecl ),
+			new ast::PointerType( realType )
+		)
+	);
+
+	for ( auto group : group_iterate( realType->assertions,
+			adapterType->assertions, adaptee->assertions ) ) {
+		auto assertArg = std::get<0>( group );
+		auto assertParam = std::get<1>( group );
+		auto assertReal = std::get<2>( group );
+		adapteeApp->args.push_back( makeAdapterArg(
+			assertParam->var, assertArg->var->get_type(),
+			assertReal->var->get_type(), typeVars, location
+		) );
+	}
+
+	ast::vector<ast::Type>::const_iterator
+		arg = realType->params.begin(),
+		param = adapterType->params.begin(),
+		realParam = adaptee->params.begin();
+	ast::vector<ast::DeclWithType>::iterator
+		paramDecl = adapterDecl->params.begin();
+	// Skip adaptee parameter in the adapter type.
+	++param;
+	++paramDecl;
+
+	ast::Stmt * bodyStmt;
+	// Returns void/nothing.
+	if ( realType->returns.empty() ) {
+		addAdapterParams( adapteeApp, arg, paramDecl, adapterDecl->params.end(),
+			realParam, typeVars, location );
+		bodyStmt = new ast::ExprStmt( location, adapteeApp );
+	// Returns a polymorphic type.
+	} else if ( isDynType( adaptee->returns.front(), typeVars ) ) {
+		ast::UntypedExpr * assign = new ast::UntypedExpr( location,
+			new ast::NameExpr( location, "?=?" ) );
+		ast::UntypedExpr * deref = ast::UntypedExpr::createDeref( location,
+			new ast::CastExpr( location,
+				new ast::VariableExpr( location, *paramDecl++ ),
+				new ast::PointerType(
+					ast::deepCopy( realType->returns.front() ) ) ) );
+		assign->args.push_back( deref );
+		addAdapterParams( adapteeApp, arg, paramDecl, adapterDecl->params.end(),
+			realParam, typeVars, location );
+		assign->args.push_back( adapteeApp );
+		bodyStmt = new ast::ExprStmt( location, assign );
+	// Adapter for a function that returns a monomorphic value.
+	} else {
+		addAdapterParams( adapteeApp, arg, paramDecl, adapterDecl->params.end(),
+				realParam, typeVars, location );
+		bodyStmt = new ast::ReturnStmt( location, adapteeApp );
+	}
+
+	adapterDecl->stmts = new ast::CompoundStmt( location, { bodyStmt } );
+	return adapterDecl;
+}
+
+ast::Expr const * makeIncrDecrExpr(
+		CodeLocation const & location,
+		ast::ApplicationExpr const * expr,
+		ast::Type const * polyType,
+		bool isIncr ) {
+	ast::NameExpr * opExpr =
+			new ast::NameExpr( location, isIncr ? "?+=?" : "?-=?" );
+	ast::UntypedExpr * addAssign = new ast::UntypedExpr( location, opExpr );
+	if ( auto address = expr->args.front().as<ast::AddressExpr>() ) {
+		addAssign->args.push_back( address->arg );
+	} else {
+		addAssign->args.push_back( expr->args.front() );
+	}
+	addAssign->args.push_back( new ast::NameExpr( location,
+		sizeofName( Mangle::mangleType( polyType ) ) ) );
+	addAssign->result = ast::deepCopy( expr->result );
+	addAssign->env = expr->env ? expr->env : addAssign->env;
+	return addAssign;
+}
+
+/// Handles intrinsic functions for postvisit ApplicationExpr.
+ast::Expr const * CallAdapter::handleIntrinsics(
+		ast::ApplicationExpr const * expr ) {
+	auto varExpr = expr->func.as<ast::VariableExpr>();
+	if ( !varExpr || varExpr->var->linkage != ast::Linkage::Intrinsic ) {
+		return nullptr;
+	}
+	std::string const & varName = varExpr->var->name;
+
+	// Index Intrinsic:
+	if ( "?[?]" == varName ) {
+		assert( expr->result );
+		assert( 2 == expr->args.size() );
+
+		ast::Type const * baseType1 =
+			isPolyPtr( expr->args.front()->result, scopeTypeVars, typeSubs );
+		ast::Type const * baseType2 =
+			isPolyPtr( expr->args.back()->result, scopeTypeVars, typeSubs );
+		// If neither argument is a polymorphic pointer, do nothing.
+		if ( !baseType1 && !baseType2 ) {
+			return expr;
+		}
+		// The arguments cannot both be polymorphic pointers.
+		assert( !baseType1 || !baseType2 );
+		// (So exactly one of the arguments is a polymorphic pointer.)
+
+		CodeLocation const & location = expr->location;
+		CodeLocation const & location1 = expr->args.front()->location;
+		CodeLocation const & location2 = expr->args.back()->location;
+
+		ast::UntypedExpr * ret = new ast::UntypedExpr( location,
+				new ast::NameExpr( location, "?+?" ) );
+		if ( baseType1 ) {
+			auto multiply = ast::UntypedExpr::createCall( location2, "?*?", {
+				expr->args.back(),
+				new ast::SizeofExpr( location1, deepCopy( baseType1 ) ),
+			} );
+			ret->args.push_back( expr->args.front() );
+			ret->args.push_back( multiply );
+		} else {
+			assert( baseType2 );
+			auto multiply = ast::UntypedExpr::createCall( location1, "?*?", {
+				expr->args.front(),
+				new ast::SizeofExpr( location2, deepCopy( baseType2 ) ),
+			} );
+			ret->args.push_back( multiply );
+			ret->args.push_back( expr->args.back() );
+		}
+		ret->result = ast::deepCopy( expr->result );
+		ret->env = expr->env ? expr->env : ret->env;
+		return ret;
+	// Dereference Intrinsic:
+	} else if ( "*?" == varName ) {
+		assert( expr->result );
+		assert( 1 == expr->args.size() );
+
+		// If this isn't for a poly type, then do nothing.
+		if ( !isPolyType( expr->result, scopeTypeVars, typeSubs ) ) {
+			return expr;
+		}
+
+		// Remove dereference from polymorphic types since they are boxed.
+		ast::Expr * ret = ast::deepCopy( expr->args.front() );
+		// Fix expression type to remove pointer.
+		ret->result = expr->result;
+		ret->env = expr->env ? expr->env : ret->env;
+		return ret;
+	// Post-Increment/Decrement Intrinsics:
+	} else if ( "?++" == varName || "?--" == varName ) {
+		assert( expr->result );
+		assert( 1 == expr->args.size() );
+
+		ast::Type const * baseType =
+			isPolyType( expr->result, scopeTypeVars, typeSubs );
+		if ( nullptr == baseType ) {
+			return expr;
+		}
+		ast::Type * tempType = ast::deepCopy( expr->result );
+		if ( typeSubs ) {
+			auto result = typeSubs->apply( tempType );
+			tempType = ast::mutate( result.node.release() );
+		}
+		CodeLocation const & location = expr->location;
+		ast::ObjectDecl * newObj = makeTemporary( location, tempType );
+		ast::VariableExpr * tempExpr =
+			new ast::VariableExpr( location, newObj );
+		ast::UntypedExpr * assignExpr = new ast::UntypedExpr( location,
+			new ast::NameExpr( location, "?=?" ) );
+		assignExpr->args.push_back( ast::deepCopy( tempExpr ) );
+		if ( auto address = expr->args.front().as<ast::AddressExpr>() ) {
+			assignExpr->args.push_back( ast::deepCopy( address->arg ) );
+		} else {
+			assignExpr->args.push_back( ast::deepCopy( expr->args.front() ) );
+		}
+		return new ast::CommaExpr( location,
+			new ast::CommaExpr( location,
+				assignExpr,
+				makeIncrDecrExpr( location, expr, baseType, "?++" == varName )
+			),
+			tempExpr
+		);
+	// Pre-Increment/Decrement Intrinsics:
+	} else if ( "++?" == varName || "--?" == varName ) {
+		assert( expr->result );
+		assert( 1 == expr->args.size() );
+
+		ast::Type const * baseType =
+			isPolyType( expr->result, scopeTypeVars, typeSubs );
+		if ( nullptr == baseType ) {
+			return expr;
+		}
+		return makeIncrDecrExpr(
+			expr->location, expr, baseType, "++?" == varName );
+	// Addition and Subtration Intrinsics:
+	} else if ( "?+?" == varName || "?-?" == varName ) {
+		assert( expr->result );
+		assert( 2 == expr->args.size() );
+
+		auto baseType1 =
+			isPolyPtr( expr->args.front()->result, scopeTypeVars, typeSubs );
+		auto baseType2 =
+			isPolyPtr( expr->args.back()->result, scopeTypeVars, typeSubs );
+
+		CodeLocation const & location = expr->location;
+		CodeLocation const & location1 = expr->args.front()->location;
+		CodeLocation const & location2 = expr->args.back()->location;
+		// LHS op RHS -> (LHS op RHS) / sizeof(LHS)
+		if ( baseType1 && baseType2 ) {
+			auto divide = ast::UntypedExpr::createCall( location, "?/?", {
+				expr,
+				new ast::SizeofExpr( location, deepCopy( baseType1 ) ),
+			} );
+			if ( expr->env ) divide->env = expr->env;
+			return divide;
+		// LHS op RHS -> LHS op (RHS * sizeof(LHS))
+		} else if ( baseType1 ) {
+			auto multiply = ast::UntypedExpr::createCall( location2, "?*?", {
+				expr->args.back(),
+				new ast::SizeofExpr( location1, deepCopy( baseType1 ) ),
+			} );
+			return ast::mutate_field_index(
+				expr, &ast::ApplicationExpr::args, 1, multiply );
+		// LHS op RHS -> (LHS * sizeof(RHS)) op RHS
+		} else if ( baseType2 ) {
+			auto multiply = ast::UntypedExpr::createCall( location1, "?*?", {
+				expr->args.front(),
+				new ast::SizeofExpr( location2, deepCopy( baseType2 ) ),
+			} );
+			return ast::mutate_field_index(
+				expr, &ast::ApplicationExpr::args, 0, multiply );
+		}
+	// Addition and Subtration Relative Assignment Intrinsics:
+	} else if ( "?+=?" == varName || "?-=?" == varName ) {
+		assert( expr->result );
+		assert( 2 == expr->args.size() );
+
+		CodeLocation const & location1 = expr->args.front()->location;
+		CodeLocation const & location2 = expr->args.back()->location;
+		auto baseType = isPolyPtr( expr->result, scopeTypeVars, typeSubs );
+		// LHS op RHS -> LHS op (RHS * sizeof(LHS))
+		if ( baseType ) {
+			auto multiply = ast::UntypedExpr::createCall( location2, "?*?", {
+				expr->args.back(),
+				new ast::SizeofExpr( location1, deepCopy( baseType ) ),
+			} );
+			return ast::mutate_field_index(
+				expr, &ast::ApplicationExpr::args, 1, multiply );
+		}
+	}
+	return expr;
+}
+
+ast::ObjectDecl * CallAdapter::makeTemporary(
+		CodeLocation const & location, ast::Type const * type ) {
+	auto newObj = new ast::ObjectDecl( location, tmpNamer.newName(), type );
+	stmtsToAddBefore.push_back( new ast::DeclStmt( location, newObj ) );
+	return newObj;
+}
+
+// --------------------------------------------------------------------------
+/// Modifies declarations to accept implicit parameters.
+/// * Move polymorphic returns in function types to pointer-type parameters.
+/// * Adds type size and assertion parameters to parameter lists.
+struct DeclAdapter final {
+	ast::FunctionDecl const * previsit( ast::FunctionDecl const * decl );
+	ast::FunctionDecl const * postvisit( ast::FunctionDecl const * decl );
+private:
+	void addAdapters( ast::FunctionDecl * decl, TypeVarMap & localTypeVars );
+};
+
+// size/align/offset parameters may not be used, so add the unused attribute.
+ast::ObjectDecl * makeObj(
+		CodeLocation const & location, std::string const & name ) {
+	return new ast::ObjectDecl( location, name,
+		makeSizeAlignType(),
+		nullptr, ast::Storage::Classes(), ast::Linkage::C, nullptr,
+		{ new ast::Attribute( "unused" ) } );
+}
+
+ast::FunctionDecl const * DeclAdapter::previsit( ast::FunctionDecl const * decl ) {
+	TypeVarMap localTypeVars;
+	makeTypeVarMap( decl, localTypeVars );
+
+	auto mutDecl = mutate( decl );
+
+	// Move polymorphic return type to parameter list.
+	if ( isDynRet( mutDecl->type ) ) {
+		auto ret = strict_dynamic_cast<ast::ObjectDecl *>(
+			mutDecl->returns.front().get_and_mutate() );
+		ret->set_type( new ast::PointerType( ret->type ) );
+		mutDecl->params.insert( mutDecl->params.begin(), ret );
+		mutDecl->returns.erase( mutDecl->returns.begin() );
+		ret->init = nullptr;
+	}
+
+	// Add size/align and assertions for type parameters to parameter list.
+	ast::vector<ast::DeclWithType> inferredParams;
+	ast::vector<ast::DeclWithType> layoutParams;
+	for ( ast::ptr<ast::TypeDecl> & typeParam : mutDecl->type_params ) {
+		auto mutParam = mutate( typeParam.get() );
+		// Add all size and alignment parameters to parameter list.
+		if ( mutParam->isComplete() ) {
+			ast::TypeInstType paramType( mutParam );
+			std::string paramName = Mangle::mangleType( &paramType );
+
+			auto sizeParam = makeObj( typeParam->location, sizeofName( paramName ) );
+			layoutParams.emplace_back( sizeParam );
+
+			auto alignParam = makeObj( typeParam->location, alignofName( paramName ) );
+			layoutParams.emplace_back( alignParam );
+		}
+		// Assertions should be stored in the main list.
+		assert( mutParam->assertions.empty() );
+		typeParam = mutParam;
+	}
+	for ( ast::ptr<ast::DeclWithType> & assert : mutDecl->assertions ) {
+		// Assertion parameters may not be used in body,
+		// pass along with unused attribute.
+		assert.get_and_mutate()->attributes.push_back(
+			new ast::Attribute( "unused" ) );
+		inferredParams.push_back( assert );
+	}
+	mutDecl->assertions.clear();
+
+	// Prepend each argument group. From last group to first. addAdapters
+	// does do the same, it just does it itself and see all other parameters.
+	spliceBegin( mutDecl->params, inferredParams );
+	spliceBegin( mutDecl->params, layoutParams );
+	addAdapters( mutDecl, localTypeVars );
+
+	// Now have to update the type to match the declaration.
+	ast::FunctionType * type = new ast::FunctionType(
+		mutDecl->type->isVarArgs, mutDecl->type->qualifiers );
+	// The forall clauses don't match until Eraser. The assertions are empty.
+	for ( auto param : mutDecl->params ) {
+		type->params.emplace_back( param->get_type() );
+	}
+	for ( auto retval : mutDecl->returns ) {
+		type->returns.emplace_back( retval->get_type() );
+	}
+	mutDecl->type = type;
+
+	return mutDecl;
+}
+
+ast::FunctionDecl const * DeclAdapter::postvisit(
+		ast::FunctionDecl const * decl ) {
+	ast::FunctionDecl * mutDecl = mutate( decl );
+	if ( !mutDecl->returns.empty() && mutDecl->stmts
+			// Intrinsic functions won't be using the _retval so no need to
+			// generate it.
+			&& mutDecl->linkage != ast::Linkage::Intrinsic
+			// Remove check for prefix once thunks properly use ctor/dtors.
+			&& !isPrefix( mutDecl->name, "_thunk" )
+			&& !isPrefix( mutDecl->name, "_adapter" ) ) {
+		assert( 1 == mutDecl->returns.size() );
+		ast::DeclWithType const * retval = mutDecl->returns.front();
+		if ( "" == retval->name ) {
+			retval = ast::mutate_field(
+				retval, &ast::DeclWithType::name, "_retval" );
+			mutDecl->returns.front() = retval;
+		}
+		auto stmts = mutDecl->stmts.get_and_mutate();
+		stmts->kids.push_front( new ast::DeclStmt( retval->location, retval ) );
+		ast::DeclWithType * newRet = ast::deepCopy( retval );
+		mutDecl->returns.front() = newRet;
+	}
+	// Errors should have been caught by this point, remove initializers from
+	// parameters to allow correct codegen of default arguments.
+	for ( ast::ptr<ast::DeclWithType> & param : mutDecl->params ) {
+		if ( auto obj = param.as<ast::ObjectDecl>() ) {
+			param = ast::mutate_field( obj, &ast::ObjectDecl::init, nullptr );
+		}
+	}
+	return mutDecl;
+}
+
+void DeclAdapter::addAdapters(
+		ast::FunctionDecl * mutDecl, TypeVarMap & localTypeVars ) {
+	ast::vector<ast::FunctionType> functions;
+	for ( ast::ptr<ast::DeclWithType> & arg : mutDecl->params ) {
+		ast::Type const * type = arg->get_type();
+		type = findAndReplaceFunction( type, functions, localTypeVars, needsAdapter );
+		arg.get_and_mutate()->set_type( type );
+	}
+	std::set<std::string> adaptersDone;
+	for ( ast::ptr<ast::FunctionType> const & func : functions ) {
+		std::string mangleName = mangleAdapterName( func, localTypeVars );
+		if ( adaptersDone.find( mangleName ) != adaptersDone.end() ) {
+			continue;
+		}
+		std::string adapterName = makeAdapterName( mangleName );
+		// The adapter may not actually be used, so make sure it has unused.
+		mutDecl->params.insert( mutDecl->params.begin(), new ast::ObjectDecl(
+			mutDecl->location, adapterName,
+			new ast::PointerType( makeAdapterType( func, localTypeVars ) ),
+			nullptr, {}, {}, nullptr,
+			{ new ast::Attribute( "unused" ) } ) );
+		adaptersDone.insert( adaptersDone.begin(), mangleName );
+	}
+}
+
+// --------------------------------------------------------------------------
+// TODO: Ideally, there would be no floating nodes at all.
+/// Corrects the floating nodes created in CallAdapter.
+struct RewireAdapters final : public ast::WithGuards {
+	ScopedMap<std::string, ast::ObjectDecl const *> adapters;
+	void beginScope() { adapters.beginScope(); }
+	void endScope() { adapters.endScope(); }
+	void previsit( ast::FunctionDecl const * decl );
+	ast::VariableExpr const * previsit( ast::VariableExpr const * expr );
+};
+
+void RewireAdapters::previsit( ast::FunctionDecl const * decl ) {
+	GuardScope( adapters );
+	for ( ast::ptr<ast::DeclWithType> const & param : decl->params ) {
+		if ( auto objectParam = param.as<ast::ObjectDecl>() ) {
+			adapters.insert( objectParam->name, objectParam );
+		}
+	}
+}
+
+ast::VariableExpr const * RewireAdapters::previsit(
+		ast::VariableExpr const * expr ) {
+	// If the node is not floating, we can skip.
+	if ( expr->var->isManaged() ) return expr;
+	auto it = adapters.find( expr->var->name );
+	assertf( it != adapters.end(), "Could not correct floating node." );
+	return ast::mutate_field( expr, &ast::VariableExpr::var, it->second );
+}
+
+// --------------------------------------------------------------------------
+/// Inserts code to access polymorphic layout inforation.
+/// * Replaces member and size/alignment/offsetof expressions on polymorphic
+///   generic types with calculated expressions.
+/// * Replaces member expressions for polymorphic types with calculated
+///   add-field-offset-and-dereference.
+/// * Calculates polymorphic offsetof expressions from offset array.
+/// * Inserts dynamic calculation of polymorphic type layouts where needed.
+struct PolyGenericCalculator final :
+		public ast::WithConstTypeSubstitution,
+		public ast::WithDeclsToAdd<>,
+		public ast::WithGuards,
+		public ast::WithStmtsToAdd<>,
+		public ast::WithVisitorRef<PolyGenericCalculator> {
+	PolyGenericCalculator();
+
+	void previsit( ast::FunctionDecl const * decl );
+	void previsit( ast::TypedefDecl const * decl );
+	void previsit( ast::TypeDecl const * decl );
+	ast::Decl const * postvisit( ast::TypeDecl const * decl );
+	ast::StructDecl const * previsit( ast::StructDecl const * decl );
+	ast::UnionDecl const * previsit( ast::UnionDecl const * decl );
+	ast::DeclStmt const * previsit( ast::DeclStmt const * stmt );
+	ast::Expr const * postvisit( ast::MemberExpr const * expr );
+	void previsit( ast::AddressExpr const * expr );
+	ast::Expr const * postvisit( ast::AddressExpr const * expr );
+	ast::Expr const * postvisit( ast::SizeofExpr const * expr );
+	ast::Expr const * postvisit( ast::AlignofExpr const * expr );
+	ast::Expr const * postvisit( ast::OffsetofExpr const * expr );
+	ast::Expr const * postvisit( ast::OffsetPackExpr const * expr );
+
+	void beginScope();
+	void endScope();
+private:
+	/// Makes a new variable in the current scope with the given name,
+	/// type and optional initializer.
+	ast::ObjectDecl * makeVar(
+			CodeLocation const & location, std::string const & name,
+			ast::Type const * type, ast::Init const * init = nullptr );
+	/// Returns true if the type has a dynamic layout;
+	/// such a layout will be stored in appropriately-named local variables
+	/// when the function returns.
+	bool findGeneric( CodeLocation const & location, ast::Type const * );
+	/// Adds type parameters to the layout call; will generate the
+	/// appropriate parameters if needed.
+	void addSTypeParamsToLayoutCall(
+		ast::UntypedExpr * layoutCall,
+		const ast::vector<ast::Type> & otypeParams );
+	/// Change the type of generic aggregate members to char[].
+	void mutateMembers( ast::AggregateDecl * aggr );
+	/// Returns the calculated sizeof expression for type, or nullptr for use
+	/// C sizeof().
+	ast::Expr const * genSizeof( CodeLocation const &, ast::Type const * );
+	/// Enters a new scope for type-variables,
+	/// adding the type variables from the provided type.
+	void beginTypeScope( ast::Type const * );
+
+	/// The type variables and polymorphic parameters currently in scope.
+	TypeVarMap scopeTypeVars;
+	/// Set of generic type layouts known in the current scope,
+	/// indexed by sizeofName.
+	ScopedSet<std::string> knownLayouts;
+	/// Set of non-generic types for which the offset array exists in the
+	/// current scope, indexed by offsetofName.
+	ScopedSet<std::string> knownOffsets;
+	/// Namer for VLA (variable length array) buffers.
+	UniqueName bufNamer;
+	/// If the argument of an AddressExpr is MemberExpr, it is stored here.
+	ast::MemberExpr const * addrMember = nullptr;
+};
+
+PolyGenericCalculator::PolyGenericCalculator() :
+	knownLayouts(), knownOffsets(), bufNamer( "_buf" )
+{}
+
+/// Converts polymorphic type into a suitable monomorphic representation.
+/// Currently: __attribute__((aligned(8) )) char[size_T];
+ast::Type * polyToMonoType( CodeLocation const & location,
+		ast::Type const * declType ) {
+	auto charType = new ast::BasicType( ast::BasicType::Char );
+	auto size = new ast::NameExpr( location,
+		sizeofName( Mangle::mangleType( declType ) ) );
+	auto aligned = new ast::Attribute( "aligned",
+		{ ast::ConstantExpr::from_int( location, 8 ) } );
+	auto ret = new ast::ArrayType( charType, size,
+		ast::VariableLen, ast::DynamicDim, ast::CV::Qualifiers() );
+	ret->attributes.push_back( aligned );
+	return ret;
+}
+
+void PolyGenericCalculator::previsit( ast::FunctionDecl const * decl ) {
+	GuardScope( *this );
+	beginTypeScope( decl->type );
+}
+
+void PolyGenericCalculator::previsit( ast::TypedefDecl const * decl ) {
+	assertf( false, "All typedef declarations should be removed." );
+	beginTypeScope( decl->base );
+}
+
+void PolyGenericCalculator::previsit( ast::TypeDecl const * decl ) {
+	addToTypeVarMap( decl, scopeTypeVars );
+}
+
+ast::Decl const * PolyGenericCalculator::postvisit(
+		ast::TypeDecl const * decl ) {
+	ast::Type const * base = decl->base;
+	if ( nullptr == base ) return decl;
+
+	// Add size/align variables for opaque type declarations.
+	ast::TypeInstType inst( decl->name, decl );
+	std::string typeName = Mangle::mangleType( &inst );
+	ast::Type * layoutType = new ast::BasicType(
+		ast::BasicType::LongUnsignedInt );
+
+	ast::ObjectDecl * sizeDecl = new ast::ObjectDecl( decl->location,
+		sizeofName( typeName ), layoutType,
+		new ast::SingleInit( decl->location,
+			new ast::SizeofExpr( decl->location, deepCopy( base ) )
+		)
+	);
+	ast::ObjectDecl * alignDecl = new ast::ObjectDecl( decl->location,
+		alignofName( typeName ), layoutType,
+		new ast::SingleInit( decl->location,
+			new ast::AlignofExpr( decl->location, deepCopy( base ) )
+		)
+	);
+
+	// Ensure that the initializing sizeof/alignof exprs are properly mutated.
+	sizeDecl->accept( *visitor );
+	alignDecl->accept( *visitor );
+
+	// A little trick to replace this with two declarations.
+	// Adding after makes sure that there is no conflict with adding stmts.
+	declsToAddAfter.push_back( alignDecl );
+	return sizeDecl;
+}
+
+ast::StructDecl const * PolyGenericCalculator::previsit(
+		ast::StructDecl const * decl ) {
+	auto mutDecl = mutate( decl );
+	mutateMembers( mutDecl );
+	return mutDecl;
+}
+
+ast::UnionDecl const * PolyGenericCalculator::previsit(
+		ast::UnionDecl const * decl ) {
+	auto mutDecl = mutate( decl );
+	mutateMembers( mutDecl );
+	return mutDecl;
+}
+
+ast::DeclStmt const * PolyGenericCalculator::previsit( ast::DeclStmt const * stmt ) {
+	ast::ObjectDecl const * decl = stmt->decl.as<ast::ObjectDecl>();
+	if ( !decl || !findGeneric( decl->location, decl->type ) ) {
+		return stmt;
+	}
+
+	// Change initialization of a polymorphic value object to allocate via a
+	// variable-length-array (alloca cannot be safely used in loops).
+	ast::ObjectDecl * newBuf = new ast::ObjectDecl( decl->location,
+		bufNamer.newName(),
+		polyToMonoType( decl->location, decl->type ),
+		nullptr, {}, ast::Linkage::C
+	);
+	stmtsToAddBefore.push_back( new ast::DeclStmt( stmt->location, newBuf ) );
+
+	// If the object has a cleanup attribute, the clean-up should be on the
+	// buffer, not the pointer. [Perhaps this should be lifted?]
+	auto matchAndMove = [newBuf]( ast::ptr<ast::Attribute> & attr ) {
+		if ( "cleanup" == attr->name ) {
+			newBuf->attributes.push_back( attr );
+			return true;
+		}
+		return false;
+	};
+
+	auto mutDecl = mutate( decl );
+
+	// Forally, side effects are not safe in this function. But it works.
+	erase_if( mutDecl->attributes, matchAndMove );
+
+	mutDecl->init = new ast::SingleInit( decl->location,
+		new ast::VariableExpr( decl->location, newBuf ) );
+
+	return ast::mutate_field( stmt, &ast::DeclStmt::decl, mutDecl );
+}
+
+/// Checks if memberDecl matches the decl from an aggregate.
+bool isMember( ast::DeclWithType const * memberDecl, ast::Decl const * decl ) {
+	// No matter the field, if the name is different it is not the same.
+	if ( memberDecl->name != decl->name ) {
+		return false;
+	}
+
+	if ( memberDecl->name.empty() ) {
+		// Plan-9 Field: Match on unique_id.
+		return ( memberDecl->uniqueId == decl->uniqueId );
+	}
+
+	ast::DeclWithType const * declWithType =
+		strict_dynamic_cast<ast::DeclWithType const *>( decl );
+
+	if ( memberDecl->mangleName.empty() || declWithType->mangleName.empty() ) {
+		// Tuple-Element Field: Expect neither had mangled name;
+		// accept match on simple name (like field_2) only.
+		assert( memberDecl->mangleName.empty() );
+		assert( declWithType->mangleName.empty() );
+		return true;
+	}
+
+	// Ordinary Field: Use full name to accommodate overloading.
+	return ( memberDecl->mangleName == declWithType->mangleName );
+}
+
+/// Finds the member in the base list that matches the given declaration;
+/// returns its index, or -1 if not present.
+long findMember( ast::DeclWithType const * memberDecl,
+		const ast::vector<ast::Decl> & baseDecls ) {
+	for ( auto pair : enumerate( baseDecls ) ) {
+		if ( isMember( memberDecl, pair.val.get() ) ) {
+			return pair.idx;
+		}
+	}
+	return -1;
+}
+
+/// Returns an index expression into the offset array for a type.
+ast::Expr * makeOffsetIndex( CodeLocation const & location,
+		ast::Type const * objectType, long i ) {
+	std::string name = offsetofName( Mangle::mangleType( objectType ) );
+	return ast::UntypedExpr::createCall( location, "?[?]", {
+		new ast::NameExpr( location, name ),
+		ast::ConstantExpr::from_ulong( location, i ),
+	} );
+}
+
+ast::Expr const * PolyGenericCalculator::postvisit(
+		ast::MemberExpr const * expr ) {
+	// Only mutate member expressions for polymorphic types.
+	ast::Type const * objectType = hasPolyBase(
+		expr->aggregate->result, scopeTypeVars
+	);
+	if ( !objectType ) return expr;
+	// Ensure layout for this type is available.
+	// The boolean result is ignored.
+	findGeneric( expr->location, objectType );
+
+	// Replace member expression with dynamically-computed layout expression.
+	ast::Expr * newMemberExpr = nullptr;
+	if ( auto structType = dynamic_cast<ast::StructInstType const *>( objectType ) ) {
+		long offsetIndex = findMember( expr->member, structType->base->members );
+		if ( -1 == offsetIndex ) return expr;
+
+		// Replace member expression with pointer to struct plus offset.
+		ast::UntypedExpr * fieldLoc = new ast::UntypedExpr( expr->location,
+				new ast::NameExpr( expr->location, "?+?" ) );
+		ast::Expr * aggr = deepCopy( expr->aggregate );
+		aggr->env = nullptr;
+		fieldLoc->args.push_back( aggr );
+		fieldLoc->args.push_back(
+			makeOffsetIndex( expr->location, objectType, offsetIndex ) );
+		fieldLoc->result = deepCopy( expr->result );
+		newMemberExpr = fieldLoc;
+	// Union members are all at offset zero, so just use the aggregate expr.
+	} else if ( dynamic_cast<ast::UnionInstType const *>( objectType ) ) {
+		ast::Expr * aggr = deepCopy( expr->aggregate );
+		aggr->env = nullptr;
+		aggr->result = deepCopy( expr->result );
+		newMemberExpr = aggr;
+	} else {
+		return expr;
+	}
+	assert( newMemberExpr );
+
+	// Must apply the generic substitution to the member type to handle cases
+	// where the member is a generic parameter subsituted by a known concrete
+	// type. [ex]
+	//	forall( T ) struct Box { T x; }
+	//	forall( T ) void f() {
+	//		Box( T * ) b; b.x;
+	//	}
+	// TODO: expr->result should be exactly expr->member->get_type() after
+	// substitution, so it doesn't seem like it should be necessary to apply
+	// the substitution manually. For some reason this is not currently the
+	// case. This requires more investigation.
+	ast::ptr<ast::Type> memberType = deepCopy( expr->member->get_type() );
+	ast::TypeSubstitution sub = genericSubstitution( objectType );
+	sub.apply( memberType );
+
+	// Not all members of a polymorphic type are themselves of a polymorphic
+	// type; in this cas the member expression should be wrapped and
+	// dereferenced to form an lvalue.
+	if ( !isPolyType( memberType, scopeTypeVars ) ) {
+		auto ptrCastExpr = new ast::CastExpr( expr->location, newMemberExpr,
+			new ast::PointerType( memberType ) );
+		auto derefExpr = ast::UntypedExpr::createDeref( expr->location,
+			ptrCastExpr );
+		newMemberExpr = derefExpr;
+	}
+
+	return newMemberExpr;
+}
+
+void PolyGenericCalculator::previsit( ast::AddressExpr const * expr ) {
+	GuardValue( addrMember ) = expr->arg.as<ast::MemberExpr>();
+}
+
+ast::Expr const * PolyGenericCalculator::postvisit(
+		ast::AddressExpr const * expr ) {
+	// arg has to have been a MemberExpr and has been mutated.
+	if ( nullptr == addrMember || expr->arg == addrMember ) {
+		return expr;
+	}
+	ast::UntypedExpr const * untyped = expr->arg.as<ast::UntypedExpr>();
+	if ( !untyped || getFunctionName( untyped ) != "?+?" ) {
+		return expr;
+	}
+	// MemberExpr was converted to pointer + offset; and it is not valid C to
+	// take the address of an addition, so strip away the address-of.
+	// It also preserves the env value.
+	return ast::mutate_field( expr->arg.get(), &ast::Expr::env, expr->env );
+}
+
+ast::Expr const * PolyGenericCalculator::postvisit(
+		ast::SizeofExpr const * expr ) {
+	ast::Type const * type = expr->type ? expr->type : expr->expr->result;
+	ast::Expr const * gen = genSizeof( expr->location, type );
+	return ( gen ) ? gen : expr;
+}
+
+ast::Expr const * PolyGenericCalculator::postvisit(
+		ast::AlignofExpr const * expr ) {
+	ast::Type const * type = expr->type ? expr->type : expr->expr->result;
+	if ( findGeneric( expr->location, type ) ) {
+		return new ast::NameExpr( expr->location,
+			alignofName( Mangle::mangleType( type ) ) );
+	} else {
+		return expr;
+	}
+}
+
+ast::Expr const * PolyGenericCalculator::postvisit(
+		ast::OffsetofExpr const * expr ) {
+	ast::Type const * type = expr->type;
+	if ( !findGeneric( expr->location, type ) ) return expr;
+
+	// Structures replace offsetof expression with an index into offset array.
+	if ( auto structType = dynamic_cast<ast::StructInstType const *>( type ) ) {
+		long offsetIndex = findMember( expr->member, structType->base->members );
+		if ( -1 == offsetIndex ) return expr;
+
+		return makeOffsetIndex( expr->location, type, offsetIndex );
+	// All union members are at offset zero.
+	} else if ( dynamic_cast<ast::UnionInstType const *>( type ) ) {
+		return ast::ConstantExpr::from_ulong( expr->location, 0 );
+	} else {
+		return expr;
+	}
+}
+
+ast::Expr const * PolyGenericCalculator::postvisit(
+		ast::OffsetPackExpr const * expr ) {
+	ast::StructInstType const * type = expr->type;
+
+	// Pull offset back from generated type information.
+	if ( findGeneric( expr->location, type ) ) {
+		return new ast::NameExpr( expr->location,
+			offsetofName( Mangle::mangleType( type ) ) );
+	}
+
+	std::string offsetName = offsetofName( Mangle::mangleType( type ) );
+	// Use the already generated offsets for this type.
+	if ( knownOffsets.contains( offsetName ) ) {
+		return new ast::NameExpr( expr->location, offsetName );
+	}
+
+	knownOffsets.insert( offsetName );
+
+	auto baseMembers = type->base->members;
+	ast::Type const * offsetType = new ast::BasicType(
+		ast::BasicType::LongUnsignedInt );
+
+	// Build initializer list for offset array.
+	ast::vector<ast::Init> inits;
+	for ( ast::ptr<ast::Decl> & member : baseMembers ) {
+		auto memberDecl = member.as<ast::DeclWithType>();
+		assertf( memberDecl, "Requesting offset of non-DWT member: %s",
+			toCString( member ) );
+		inits.push_back( new ast::SingleInit( expr->location,
+			new ast::OffsetofExpr( expr->location,
+				deepCopy( type ),
+				memberDecl
+			)
+		) );
+	}
+
+	auto offsetArray = makeVar( expr->location, offsetName,
+		new ast::ArrayType(
+			offsetType,
+			ast::ConstantExpr::from_ulong( expr->location, baseMembers.size() ),
+			ast::FixedLen,
+			ast::DynamicDim
+		),
+		new ast::ListInit( expr->location, std::move( inits ) )
+	);
+
+	return new ast::VariableExpr( expr->location, offsetArray );
+}
+
+void PolyGenericCalculator::beginScope() {
+	knownLayouts.beginScope();
+	knownOffsets.beginScope();
+}
+
+void PolyGenericCalculator::endScope() {
+	knownOffsets.endScope();
+	knownLayouts.endScope();
+}
+
+ast::ObjectDecl * PolyGenericCalculator::makeVar(
+		CodeLocation const & location, std::string const & name,
+		ast::Type const * type, ast::Init const * init ) {
+	ast::ObjectDecl * ret = new ast::ObjectDecl( location, name, type, init );
+	stmtsToAddBefore.push_back( new ast::DeclStmt( location, ret ) );
+	return ret;
+}
+
+/// Returns true if any of the otype parameters have a dynamic layout; and
+/// puts all otype parameters in the output list.
+bool findGenericParams(
+		ast::vector<ast::Type> & out,
+		ast::vector<ast::TypeDecl> const & baseParams,
+		ast::vector<ast::Expr> const & typeParams ) {
+	bool hasDynamicLayout = false;
+
+	for ( auto pair : group_iterate( baseParams, typeParams ) ) {
+		auto baseParam = std::get<0>( pair );
+		auto typeParam = std::get<1>( pair );
+		if ( !baseParam->isComplete() ) continue;
+		ast::TypeExpr const * typeExpr = typeParam.as<ast::TypeExpr>();
+		assertf( typeExpr, "All type parameters should be type expressions." );
+
+		ast::Type const * type = typeExpr->type.get();
+		out.push_back( type );
+		if ( isPolyType( type ) ) hasDynamicLayout = true;
+	}
+
+	return hasDynamicLayout;
+}
+
+bool PolyGenericCalculator::findGeneric(
+		CodeLocation const & location, ast::Type const * type ) {
+	type = replaceTypeInst( type, typeSubs );
+
+	if ( auto inst = dynamic_cast<ast::TypeInstType const *>( type ) ) {
+		// Assumes that getting put in the scopeTypeVars includes having the
+		// layout variables set.
+		if ( scopeTypeVars.contains( *inst ) ) {
+			return true;
+		}
+	} else if ( auto inst = dynamic_cast<ast::StructInstType const *>( type ) ) {
+		// Check if this type already has a layout generated for it.
+		std::string typeName = Mangle::mangleType( type );
+		if ( knownLayouts.contains( typeName ) ) return true;
+
+		// Check if any type parameters have dynamic layout;
+		// If none do, this type is (or will be) monomorphized.
+		ast::vector<ast::Type> sizedParams;
+		if ( !findGenericParams( sizedParams,
+				inst->base->params, inst->params ) ) {
+			return false;
+		}
+
+		// Insert local variables for layout and generate call to layout
+		// function.
+		// Done early so as not to interfere with the later addition of
+		// parameters to the layout call.
+		knownLayouts.insert( typeName );
+		ast::Type const * layoutType = makeSizeAlignType();
+
+		int memberCount = inst->base->members.size();
+		if ( 0 == memberCount ) {
+			// All empty structures have the same layout (size 1, align 1).
+			makeVar( location,
+				sizeofName( typeName ), layoutType,
+				new ast::SingleInit( location,
+						ast::ConstantExpr::from_ulong( location, 1 ) ) );
+			makeVar( location,
+				alignofName( typeName ), ast::deepCopy( layoutType ),
+				new ast::SingleInit( location,
+						ast::ConstantExpr::from_ulong( location, 1 ) ) );
+			// Since 0-length arrays are forbidden in C, skip the offset array.
+		} else {
+			ast::ObjectDecl const * sizeofVar = makeVar( location,
+				sizeofName( typeName ), deepCopy( layoutType ), nullptr );
+			ast::ObjectDecl const * alignofVar = makeVar( location,
+				alignofName( typeName ), deepCopy( layoutType ), nullptr );
+			ast::ObjectDecl const * offsetofVar = makeVar( location,
+				offsetofName( typeName ),
+				new ast::ArrayType(
+					layoutType,
+					ast::ConstantExpr::from_int( location, memberCount ),
+					ast::FixedLen,
+					ast::DynamicDim
+				),
+				nullptr
+			);
+
+			// Generate call to layout function.
+			ast::UntypedExpr * layoutCall = new ast::UntypedExpr( location,
+				new ast::NameExpr( location, layoutofName( inst->base ) ),
+				{
+					new ast::AddressExpr(
+						new ast::VariableExpr( location, sizeofVar ) ),
+					new ast::AddressExpr(
+						new ast::VariableExpr( location, alignofVar ) ),
+					new ast::VariableExpr( location, offsetofVar ),
+				} );
+
+			addSTypeParamsToLayoutCall( layoutCall, sizedParams );
+
+			stmtsToAddBefore.emplace_back(
+				new ast::ExprStmt( location, layoutCall ) );
+		}
+
+		return true;
+	} else if ( auto inst = dynamic_cast<ast::UnionInstType const *>( type ) ) {
+		// Check if this type already has a layout generated for it.
+		std::string typeName = Mangle::mangleType( type );
+		if ( knownLayouts.contains( typeName ) ) return true;
+
+		// Check if any type parameters have dynamic layout;
+		// If none do, this type is (or will be) monomorphized.
+		ast::vector<ast::Type> sizedParams;
+		if ( !findGenericParams( sizedParams,
+				inst->base->params, inst->params ) ) {
+			return false;
+		}
+
+		// Insert local variables for layout and generate call to layout
+		// function.
+		// Done early so as not to interfere with the later addition of
+		// parameters to the layout call.
+		knownLayouts.insert( typeName );
+		ast::Type const * layoutType = makeSizeAlignType();
+
+		ast::ObjectDecl * sizeofVar = makeVar( location,
+			sizeofName( typeName ), layoutType );
+		ast::ObjectDecl * alignofVar = makeVar( location,
+			alignofName( typeName ), ast::deepCopy( layoutType ) );
+
+		ast::UntypedExpr * layoutCall = new ast::UntypedExpr( location,
+			new ast::NameExpr( location, layoutofName( inst->base ) ),
+			{
+				new ast::AddressExpr(
+					new ast::VariableExpr( location, sizeofVar ) ),
+				new ast::AddressExpr(
+					new ast::VariableExpr( location, alignofVar ) ),
+			} );
+
+		addSTypeParamsToLayoutCall( layoutCall, sizedParams );
+
+		stmtsToAddBefore.emplace_back(
+			new ast::ExprStmt( location, layoutCall ) );
+
+		return true;
+	}
+	return false;
+}
+
+void PolyGenericCalculator::addSTypeParamsToLayoutCall(
+		ast::UntypedExpr * layoutCall,
+		const ast::vector<ast::Type> & otypeParams ) {
+	CodeLocation const & location = layoutCall->location;
+	ast::vector<ast::Expr> & args = layoutCall->args;
+	for ( ast::ptr<ast::Type> const & param : otypeParams ) {
+		if ( findGeneric( location, param ) ) {
+			// Push size/align vars for a generic parameter back.
+			std::string paramName = Mangle::mangleType( param );
+			args.emplace_back(
+				new ast::NameExpr( location, sizeofName( paramName ) ) );
+			args.emplace_back(
+				new ast::NameExpr( location, alignofName( paramName ) ) );
+		} else {
+			args.emplace_back(
+				new ast::SizeofExpr( location, ast::deepCopy( param ) ) );
+			args.emplace_back(
+				new ast::AlignofExpr( location, ast::deepCopy( param ) ) );
+		}
+	}
+}
+
+void PolyGenericCalculator::mutateMembers( ast::AggregateDecl * aggr ) {
+	std::set<std::string> genericParams;
+	for ( ast::ptr<ast::TypeDecl> const & decl : aggr->params ) {
+		genericParams.insert( decl->name );
+	}
+	for ( ast::ptr<ast::Decl> & decl : aggr->members ) {
+		auto field = decl.as<ast::ObjectDecl>();
+		if ( nullptr == field ) continue;
+
+		ast::Type const * type = replaceTypeInst( field->type, typeSubs );
+		auto typeInst = dynamic_cast<ast::TypeInstType const *>( type );
+		if ( nullptr == typeInst ) continue;
+
+		// Do not try to monoporphize generic parameters.
+		if ( scopeTypeVars.contains( ast::TypeEnvKey( *typeInst ) ) &&
+				!genericParams.count( typeInst->name ) ) {
+			// Polymorphic aggregate members should be converted into
+			// monomorphic members. Using char[size_T] here respects
+			// the expected sizing rules of an aggregate type.
+			decl = ast::mutate_field( field, &ast::ObjectDecl::type,
+				polyToMonoType( field->location, field->type ) );
+		}
+	}
+}
+
+ast::Expr const * PolyGenericCalculator::genSizeof(
+		CodeLocation const & location, ast::Type const * type ) {
+	if ( auto * array = dynamic_cast<ast::ArrayType const *>( type ) ) {
+		// Generate calculated size for possibly generic array.
+		ast::Expr const * sizeofBase = genSizeof( location, array->base );
+		if ( nullptr == sizeofBase ) return nullptr;
+		ast::Expr const * dim = array->dimension;
+		return makeOp( location, "?*?", sizeofBase, dim );
+	} else if ( findGeneric( location, type ) ) {
+		// Generate calculated size for generic type.
+		return new ast::NameExpr( location, sizeofName(
+				Mangle::mangleType( type ) ) );
+	} else {
+		return nullptr;
+	}
+}
+
+void PolyGenericCalculator::beginTypeScope( ast::Type const * type ) {
+	GuardScope( scopeTypeVars );
+	makeTypeVarMap( type, scopeTypeVars );
+}
+
+// --------------------------------------------------------------------------
+/// Removes unneeded or incorrect type information.
+/// * Replaces initialization of polymorphic values with alloca.
+/// * Replaces declaration of dtype/ftype with appropriate void expression.
+/// * Replaces sizeof expressions of polymorphic types with a variable.
+/// * Strips fields from generic structure declarations.
+struct Eraser final :
+		public ast::WithGuards {
+	void guardTypeVarMap( ast::Type const * type ) {
+		GuardScope( scopeTypeVars );
+		makeTypeVarMap( type, scopeTypeVars );
+	}
+
+	ast::ObjectDecl const * previsit( ast::ObjectDecl const * decl );
+	ast::FunctionDecl const * previsit( ast::FunctionDecl const * decl );
+	ast::FunctionDecl const * postvisit( ast::FunctionDecl const * decl );
+	ast::TypedefDecl const * previsit( ast::TypedefDecl const * decl );
+	ast::StructDecl const * previsit( ast::StructDecl const * decl );
+	ast::UnionDecl const * previsit( ast::UnionDecl const * decl );
+	void previsit( ast::TypeDecl const * decl );
+	void previsit( ast::PointerType const * type );
+	void previsit( ast::FunctionType const * type );
+public:
+	TypeVarMap scopeTypeVars;
+};
+
+ast::ObjectDecl const * Eraser::previsit( ast::ObjectDecl const * decl ) {
+	guardTypeVarMap( decl->type );
+	return scrubAllTypeVars( decl );
+}
+
+ast::FunctionDecl const * Eraser::previsit( ast::FunctionDecl const * decl ) {
+	guardTypeVarMap( decl->type );
+	return scrubAllTypeVars( decl );
+}
+
+ast::FunctionDecl const * Eraser::postvisit( ast::FunctionDecl const * decl ) {
+	if ( decl->type_params.empty() ) return decl;
+	auto mutDecl = mutate( decl );
+	mutDecl->type_params.clear();
+	return mutDecl;
+}
+
+ast::TypedefDecl const * Eraser::previsit( ast::TypedefDecl const * decl ) {
+	guardTypeVarMap( decl->base );
+	return scrubAllTypeVars( decl );
+}
+
+/// Strips the members from a generic aggregate.
+template<typename node_t>
+node_t const * stripGenericMembers( node_t const * decl ) {
+	if ( decl->params.empty() ) return decl;
+	auto mutDecl = ast::mutate( decl );
+	mutDecl->members.clear();
+	return mutDecl;
+}
+
+ast::StructDecl const * Eraser::previsit( ast::StructDecl const * decl ) {
+	return stripGenericMembers( decl );
+}
+
+ast::UnionDecl const * Eraser::previsit( ast::UnionDecl const * decl ) {
+	return stripGenericMembers( decl );
+}
+
+void Eraser::previsit( ast::TypeDecl const * decl ) {
+	addToTypeVarMap( decl, scopeTypeVars );
+}
+
+void Eraser::previsit( ast::PointerType const * type ) {
+	guardTypeVarMap( type );
+}
+
+void Eraser::previsit( ast::FunctionType const * type ) {
+	guardTypeVarMap( type );
+}
+
+} // namespace
+
+// --------------------------------------------------------------------------
+void box( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<LayoutFunctionBuilder>::run( translationUnit );
+	ast::Pass<CallAdapter>::run( translationUnit );
+	ast::Pass<DeclAdapter>::run( translationUnit );
+	ast::Pass<RewireAdapters>::run( translationUnit );
+	ast::Pass<PolyGenericCalculator>::run( translationUnit );
+	ast::Pass<Eraser>::run( translationUnit );
+}
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/GenPoly/BoxNew.cpp
===================================================================
--- src/GenPoly/BoxNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,2270 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// BoxNew.cpp -- Implement polymorphic function calls and types.
-//
-// Author           : Andrew Beach
-// Created On       : Thr Oct  6 13:39:00 2022
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Oct  2 17:00:00 2023
-// Update Count     : 0
-//
-
-#include "Box.h"
-
-#include "AST/Decl.hpp"                // for Decl, FunctionDecl, ...
-#include "AST/Expr.hpp"                // for AlignofExpr, ConstantExpr, ...
-#include "AST/Init.hpp"                // for Init, SingleInit
-#include "AST/Inspect.hpp"             // for getFunctionName
-#include "AST/Pass.hpp"                // for Pass, WithDeclsToAdd, ...
-#include "AST/Stmt.hpp"                // for CompoundStmt, ExprStmt, ...
-#include "AST/Vector.hpp"              // for vector
-#include "AST/GenericSubstitution.hpp" // for genericSubstitution
-#include "CodeGen/OperatorTable.h"     // for isAssignment
-#include "Common/ScopedMap.h"          // for ScopedMap
-#include "Common/UniqueName.h"         // for UniqueName
-#include "Common/utility.h"            // for toCString, group_iterate
-#include "GenPoly/FindFunction.h"      // for findFunction
-#include "GenPoly/GenPoly.h"           // for getFunctionType, ...
-#include "GenPoly/Lvalue.h"            // for generalizedLvalue
-#include "GenPoly/ScopedSet.h"         // for ScopedSet
-#include "GenPoly/ScrubTyVars.h"       // for scrubTypeVars, scrubAllTypeVars
-#include "ResolvExpr/Unify.h"          // for typesCompatible
-#include "SymTab/Mangler.h"            // for mangle, mangleType
-
-namespace GenPoly {
-
-namespace {
-
-// --------------------------------------------------------------------------
-/// Adds layout-generation functions to polymorphic types.
-struct LayoutFunctionBuilder final :
-		public ast::WithDeclsToAdd<>,
-		public ast::WithShortCircuiting,
-		public ast::WithVisitorRef<LayoutFunctionBuilder> {
-	void previsit( ast::StructDecl const * decl );
-	void previsit( ast::UnionDecl const * decl );
-};
-
-/// Get all sized type declarations; those that affect a layout function.
-ast::vector<ast::TypeDecl> takeSizedParams(
-		ast::vector<ast::TypeDecl> const & decls ) {
-	ast::vector<ast::TypeDecl> sizedParams;
-	for ( ast::ptr<ast::TypeDecl> const & decl : decls ) {
-		if ( decl->isComplete() ) {
-			sizedParams.emplace_back( decl );
-		}
-	}
-	return sizedParams;
-}
-
-ast::BasicType * makeSizeAlignType() {
-	return new ast::BasicType( ast::BasicType::LongUnsignedInt );
-}
-
-/// Adds parameters for otype size and alignment to a function type.
-void addSTypeParams(
-		ast::vector<ast::DeclWithType> & params,
-		ast::vector<ast::TypeDecl> const & sizedParams ) {
-	for ( ast::ptr<ast::TypeDecl> const & sizedParam : sizedParams ) {
-		ast::TypeInstType inst( sizedParam );
-		std::string paramName = Mangle::mangleType( &inst );
-		params.emplace_back( new ast::ObjectDecl(
-			sizedParam->location,
-			sizeofName( paramName ),
-			makeSizeAlignType()
-		) );
-		params.emplace_back( new ast::ObjectDecl(
-			sizedParam->location,
-			alignofName( paramName ),
-			makeSizeAlignType()
-		) );
-	}
-}
-
-ast::Type * makeSizeAlignOutType() {
-	return new ast::PointerType( makeSizeAlignType() );
-}
-
-struct LayoutData {
-	ast::FunctionDecl * function;
-	ast::ObjectDecl * sizeofParam;
-	ast::ObjectDecl * alignofParam;
-	ast::ObjectDecl * offsetofParam;
-};
-
-LayoutData buildLayoutFunction(
-		CodeLocation const & location, ast::AggregateDecl const * aggr,
-		ast::vector<ast::TypeDecl> const & sizedParams,
-		bool isInFunction, bool isStruct ) {
-	ast::ObjectDecl * sizeParam = new ast::ObjectDecl(
-		location,
-		sizeofName( aggr->name ),
-		makeSizeAlignOutType()
-	);
-	ast::ObjectDecl * alignParam = new ast::ObjectDecl(
-		location,
-		alignofName( aggr->name ),
-		makeSizeAlignOutType()
-	);
-	ast::ObjectDecl * offsetParam = nullptr;
-	ast::vector<ast::DeclWithType> params = { sizeParam, alignParam };
-	if ( isStruct ) {
-		offsetParam = new ast::ObjectDecl(
-			location,
-			offsetofName( aggr->name ),
-			makeSizeAlignOutType()
-		);
-		params.push_back( offsetParam );
-	}
-	addSTypeParams( params, sizedParams );
-
-	// Routines at global scope marked "static" to prevent multiple
-	// definitions is separate translation units because each unit generates
-	// copies of the default routines for each aggregate.
-	ast::FunctionDecl * layoutDecl = new ast::FunctionDecl(
-		location,
-		layoutofName( aggr ),
-		{}, // forall
-		{}, // assertions
-		std::move( params ),
-		{}, // returns
-		new ast::CompoundStmt( location ),
-		isInFunction ? ast::Storage::Classes() : ast::Storage::Static,
-		ast::Linkage::AutoGen,
-		{}, // attrs
-		ast::Function::Inline,
-		ast::FixedArgs
-	);
-	layoutDecl->fixUniqueId();
-	return LayoutData{ layoutDecl, sizeParam, alignParam, offsetParam };
-}
-
-/// Makes a binary operation.
-ast::Expr * makeOp( CodeLocation const & location, std::string const & name,
-		ast::Expr const * lhs, ast::Expr const * rhs ) {
-	return new ast::UntypedExpr( location,
-		new ast::NameExpr( location, name ), { lhs, rhs } );
-}
-
-/// Make a binary operation and wrap it in a statement.
-ast::Stmt * makeOpStmt( CodeLocation const & location, std::string const & name,
-		ast::Expr const * lhs, ast::Expr const * rhs ) {
-	return new ast::ExprStmt( location, makeOp( location, name, lhs, rhs ) );
-}
-
-/// Returns the dereference of a local pointer variable.
-ast::Expr * derefVar(
-		CodeLocation const & location, ast::ObjectDecl const * var ) {
-	return ast::UntypedExpr::createDeref( location,
-		new ast::VariableExpr( location, var ) );
-}
-
-/// Makes an if-statement with a single-expression then and no else.
-ast::Stmt * makeCond( CodeLocation const & location,
-		ast::Expr const * cond, ast::Expr const * thenPart ) {
-	return new ast::IfStmt( location,
-		cond, new ast::ExprStmt( location, thenPart ), nullptr );
-}
-
-/// Makes a statement that aligns lhs to rhs (rhs should be an integer
-/// power of two).
-ast::Stmt * makeAlignTo( CodeLocation const & location,
-		ast::Expr const * lhs, ast::Expr const * rhs ) {
-	// Check that the lhs is zeroed out to the level of rhs.
-	ast::Expr * ifCond = makeOp( location, "?&?", lhs,
-		makeOp( location, "?-?", rhs,
-				ast::ConstantExpr::from_ulong( location, 1 ) ) );
-	// If not aligned, increment to alignment.
-	ast::Expr * ifExpr = makeOp( location, "?+=?", ast::deepCopy( lhs ),
-		makeOp( location, "?-?", ast::deepCopy( rhs ),
-				ast::deepCopy( ifCond ) ) );
-	return makeCond( location, ifCond, ifExpr );
-}
-
-/// Makes a statement that assigns rhs to lhs if lhs < rhs.
-ast::Stmt * makeAssignMax( CodeLocation const & location,
-		ast::Expr const * lhs, ast::Expr const * rhs ) {
-	return makeCond( location,
-		makeOp( location, "?<?", ast::deepCopy( lhs ), ast::deepCopy( rhs ) ),
-		makeOp( location, "?=?", lhs, rhs ) );
-}
-
-void LayoutFunctionBuilder::previsit( ast::StructDecl const * decl ) {
-	// Do not generate layout function for empty tag structures.
-	visit_children = false;
-	if ( decl->members.empty() ) return;
-
-	// Get parameters that can change layout, exiting early if none.
-	ast::vector<ast::TypeDecl> sizedParams =
-		takeSizedParams( decl->params );
-	if ( sizedParams.empty() ) return;
-
-	CodeLocation const & location = decl->location;
-
-	// Build layout function signature.
-	LayoutData layout = buildLayoutFunction(
-		location, decl, sizedParams, isInFunction(), true );
-	ast::FunctionDecl * layoutDecl = layout.function;
-	// Also return these or extract them from the parameter list?
-	ast::ObjectDecl const * sizeofParam = layout.sizeofParam;
-	ast::ObjectDecl const * alignofParam = layout.alignofParam;
-	ast::ObjectDecl const * offsetofParam = layout.offsetofParam;
-	assert( nullptr != layout.offsetofParam );
-
-	// Calculate structure layout in function body.
-	// Initialize size and alignment to 0 and 1
-	// (Will have at least one member to update size).
-	auto & kids = layoutDecl->stmts.get_and_mutate()->kids;
-	kids.emplace_back( makeOpStmt( location, "?=?",
-		derefVar( location, sizeofParam ),
-		ast::ConstantExpr::from_ulong( location, 0 )
-	) );
-	kids.emplace_back( makeOpStmt( location, "?=?",
-		derefVar( location, alignofParam ),
-		ast::ConstantExpr::from_ulong( location, 1 )
-	) );
-	// TODO: Polymorphic types will be out of the struct declaration scope.
-	// Should be removed by PolyGenericCalculator.
-	for ( auto const & member : enumerate( decl->members ) ) {
-		auto dwt = member.val.strict_as<ast::DeclWithType>();
-		ast::Type const * memberType = dwt->get_type();
-
-		if ( 0 < member.idx ) {
-			// Make sure all later members have padding to align them.
-			kids.emplace_back( makeAlignTo( location,
-				derefVar( location, sizeofParam ),
-				new ast::AlignofExpr( location, ast::deepCopy( memberType ) )
-			) );
-		}
-
-		// Place current size in the current offset index.
-		kids.emplace_back( makeOpStmt( location, "?=?",
-			makeOp( location, "?[?]",
-				new ast::VariableExpr( location, offsetofParam ),
-				ast::ConstantExpr::from_ulong( location, member.idx ) ),
-			derefVar( location, sizeofParam ) ) );
-
-		// Add member size to current size.
-		kids.emplace_back( makeOpStmt( location, "?+=?",
-			derefVar( location, sizeofParam ),
-			new ast::SizeofExpr( location, ast::deepCopy( memberType ) ) ) );
-
-		// Take max of member alignment and global alignment.
-		// (As align is always 2^n, this will always be a multiple of both.)
-		kids.emplace_back( makeAssignMax( location,
-			derefVar( location, alignofParam ),
-			new ast::AlignofExpr( location, ast::deepCopy( memberType ) ) ) );
-	}
-	// Make sure the type is end-padded to a multiple of its alignment.
-	kids.emplace_back( makeAlignTo( location,
-		derefVar( location, sizeofParam ),
-		derefVar( location, alignofParam ) ) );
-
-	declsToAddAfter.emplace_back( layoutDecl );
-}
-
-void LayoutFunctionBuilder::previsit( ast::UnionDecl const * decl ) {
-	visit_children = false;
-	// Do not generate layout function for empty tag unions.
-	if ( decl->members.empty() ) return;
-
-	// Get parameters that can change layout, exiting early if none.
-	ast::vector<ast::TypeDecl> sizedParams =
-		takeSizedParams( decl->params );
-	if ( sizedParams.empty() ) return;
-
-	CodeLocation const & location = decl->location;
-
-	// Build layout function signature.
-	LayoutData layout = buildLayoutFunction(
-		location, decl, sizedParams, isInFunction(), false );
-	ast::FunctionDecl * layoutDecl = layout.function;
-	// Also return these or extract them from the parameter list?
-	ast::ObjectDecl const * sizeofParam = layout.sizeofParam;
-	ast::ObjectDecl const * alignofParam = layout.alignofParam;
-	assert( nullptr == layout.offsetofParam );
-
-	// Calculate union layout in function body.
-	// Both are simply the maximum for union (actually align is always the
-	// LCM, but with powers of two that is also the maximum).
-	auto & kids = layoutDecl->stmts.get_and_mutate()->kids;
-	kids.emplace_back( makeOpStmt( location,
-		"?=?", derefVar( location, sizeofParam ),
-		ast::ConstantExpr::from_ulong( location, 1 )
-	) );
-	kids.emplace_back( makeOpStmt( location,
-		"?=?", derefVar( location, alignofParam ),
-		ast::ConstantExpr::from_ulong( location, 1 )
-	) );
-	// TODO: Polymorphic types will be out of the union declaration scope.
-	for ( auto const & member : decl->members ) {
-		auto dwt = member.strict_as<ast::DeclWithType>();
-		ast::Type const * memberType = dwt->get_type();
-
-		// Take max member size and global size.
-		kids.emplace_back( makeAssignMax( location,
-			derefVar( location, sizeofParam ),
-			new ast::SizeofExpr( location, ast::deepCopy( memberType ) )
-		) );
-
-		// Take max of member alignment and global alignment.
-		kids.emplace_back( makeAssignMax( location,
-			derefVar( location, alignofParam ),
-			new ast::AlignofExpr( location, ast::deepCopy( memberType ) )
-		) );
-	}
-	kids.emplace_back( makeAlignTo( location,
-		derefVar( location, sizeofParam ),
-		derefVar( location, alignofParam ) ) );
-
-	declsToAddAfter.emplace_back( layoutDecl );
-}
-
-// --------------------------------------------------------------------------
-/// Application expression transformer.
-/// * Replaces polymorphic return types with out-parameters.
-/// * Replaces call to polymorphic functions with adapter calls which handles
-///   dynamic arguments and return values.
-/// * Adds appropriate type variables to the function calls.
-struct CallAdapter final :
-		public ast::WithConstTypeSubstitution,
-		public ast::WithGuards,
-		public ast::WithShortCircuiting,
-		public ast::WithStmtsToAdd<>,
-		public ast::WithVisitorRef<CallAdapter> {
-	CallAdapter();
-
-	void previsit( ast::Decl const * decl );
-	ast::FunctionDecl const * previsit( ast::FunctionDecl const * decl );
-	void previsit( ast::TypeDecl const * decl );
-	void previsit( ast::CommaExpr const * expr );
-	ast::Expr const * postvisit( ast::ApplicationExpr const * expr );
-	ast::Expr const * postvisit( ast::UntypedExpr const * expr );
-	void previsit( ast::AddressExpr const * expr );
-	ast::Expr const * postvisit( ast::AddressExpr const * expr );
-	ast::ReturnStmt const * previsit( ast::ReturnStmt const * stmt );
-
-	void beginScope();
-	void endScope();
-private:
-	// Many helpers here use a mutable ApplicationExpr as an in/out parameter
-	// instead of using the return value, to save on mutates and free up the
-	// return value.
-
-	/// Passes extra layout arguments for sized polymorphic type parameters.
-	ast::vector<ast::Expr>::iterator passTypeVars(
-		ast::ApplicationExpr * expr,
-		ast::FunctionType const * funcType );
-	/// Wraps a function application with a new temporary for the
-	/// out-parameter return value.
-	ast::Expr const * addRetParam(
-		ast::ApplicationExpr * expr, ast::Type const * retType );
-	/// Wraps a function application returning a polymorphic type with a new
-	/// temporary for the out-parameter return value.
-	ast::Expr const * addDynRetParam(
-		ast::ApplicationExpr * expr, ast::Type const * polyType );
-	/// Modify a call so it passes the function through the correct adapter.
-	ast::Expr const * applyAdapter(
-		ast::ApplicationExpr * expr,
-		ast::FunctionType const * function );
-	/// Convert a single argument into its boxed form to pass the parameter.
-	void boxParam( ast::ptr<ast::Expr> & arg,
-		ast::Type const * formal, TypeVarMap const & exprTyVars );
-	/// Box every argument from arg forward, matching the functionType
-	/// parameter list. arg should point into expr's argument list.
-	void boxParams(
-		ast::ApplicationExpr const * expr,
-		ast::vector<ast::Expr>::iterator arg,
-		ast::FunctionType const * function,
-		const TypeVarMap & typeVars );
-	/// Adds the inferred parameters derived from the assertions of the
-	/// expression to the call.
-	void addInferredParams(
-		ast::ApplicationExpr * expr,
-		ast::vector<ast::Expr>::iterator arg,
-		ast::FunctionType const * functionType,
-		const TypeVarMap & typeVars );
-	/// Stores assignment operators from assertion list in
-	/// local map of assignment operations.
-	void passAdapters(
-		ast::ApplicationExpr * expr,
-		ast::FunctionType const * type,
-		const TypeVarMap & typeVars );
-	/// Create an adapter function based on the type of the adaptee and the
-	/// real type with the type substitutions applied.
-	ast::FunctionDecl * makeAdapter(
-		ast::FunctionType const * adaptee,
-		ast::FunctionType const * realType,
-		std::string const & mangleName,
-		TypeVarMap const & typeVars,
-		CodeLocation const & location ) const;
-	/// Replaces intrinsic operator functions with their arithmetic desugaring.
-	ast::Expr const * handleIntrinsics( ast::ApplicationExpr const * );
-	/// Inserts a new temporary variable into the current scope with an
-	/// auto-generated name.
-	ast::ObjectDecl * makeTemporary(
-		CodeLocation const & location, ast::Type const * type );
-
-	TypeVarMap scopeTypeVars;
-	ScopedMap< std::string, ast::DeclWithType const * > adapters;
-	std::map< ast::ApplicationExpr const *, ast::Expr const * > retVals;
-	ast::DeclWithType const * retval;
-	UniqueName tmpNamer;
-};
-
-/// Replaces a polymorphic type with its concrete equivalant under the
-/// current environment (returns itself if concrete).
-/// If `doClone` is set to false, will not clone interior types
-ast::Type const * replaceWithConcrete(
-		ast::Type const * type,
-		ast::TypeSubstitution const & typeSubs,
-		bool doCopy = true );
-
-/// Replaces all the type parameters of a generic type with their
-/// concrete equivalents under the current environment.
-void replaceParametersWithConcrete(
-		ast::vector<ast::Expr> & params,
-		ast::TypeSubstitution const & typeSubs ) {
-	for ( ast::ptr<ast::Expr> & paramExpr : params ) {
-		ast::TypeExpr const * param = paramExpr.as<ast::TypeExpr>();
-		assertf( param, "Aggregate parameters should be type expressions." );
-		paramExpr = ast::mutate_field( param, &ast::TypeExpr::type,
-			replaceWithConcrete( param->type.get(), typeSubs, false ) );
-	}
-}
-
-ast::Type const * replaceWithConcrete(
-		ast::Type const * type,
-		ast::TypeSubstitution const & typeSubs,
-		bool doCopy ) {
-	if ( auto instType = dynamic_cast<ast::TypeInstType const *>( type ) ) {
-		ast::Type const * concrete = typeSubs.lookup( instType );
-		return ( nullptr != concrete ) ? concrete : instType;
-	} else if ( auto structType =
-			dynamic_cast<ast::StructInstType const *>( type ) ) {
-		ast::StructInstType * newType =
-			doCopy ? ast::deepCopy( structType ) : ast::mutate( structType );
-		replaceParametersWithConcrete( newType->params, typeSubs );
-		return newType;
-	} else if ( auto unionType =
-			dynamic_cast<ast::UnionInstType const *>( type ) ) {
-		ast::UnionInstType * newType =
-			doCopy ? ast::deepCopy( unionType ) : ast::mutate( unionType );
-		replaceParametersWithConcrete( newType->params, typeSubs );
-		return newType;
-	} else {
-		return type;
-	}
-}
-
-std::string makePolyMonoSuffix(
-		ast::FunctionType const * function,
-		TypeVarMap const & typeVars ) {
-	// If the return type or a parameter type involved polymorphic types,
-	// then the adapter will need to take those polymorphic types as pointers.
-	// Therefore, there can be two different functions with the same mangled
-	// name, so we need to further mangle the names.
-	std::stringstream name;
-	for ( auto ret : function->returns ) {
-		name << ( isPolyType( ret, typeVars ) ? 'P' : 'M' );
-	}
-	name << '_';
-	for ( auto arg : function->params ) {
-		name << ( isPolyType( arg, typeVars ) ? 'P' : 'M' );
-	}
-	return name.str();
-}
-
-std::string mangleAdapterName(
-		ast::FunctionType const * function,
-		TypeVarMap const & typeVars ) {
-	return Mangle::mangle( function, {} )
-		+ makePolyMonoSuffix( function, typeVars );
-}
-
-std::string makeAdapterName( std::string const & mangleName ) {
-	return "_adapter" + mangleName;
-}
-
-void makeRetParam( ast::FunctionType * type ) {
-	ast::ptr<ast::Type> & retParam = type->returns.front();
-
-	// Make a new parameter that is a pointer to the type of the old return value.
-	retParam = new ast::PointerType( retParam.get() );
-	type->params.emplace( type->params.begin(), retParam );
-
-	// We don't need the return value any more.
-	type->returns.clear();
-}
-
-ast::FunctionType * makeAdapterType(
-		ast::FunctionType const * adaptee,
-		TypeVarMap const & typeVars ) {
-	ast::FunctionType * adapter = ast::deepCopy( adaptee );
-	if ( isDynRet( adapter, typeVars ) ) {
-		makeRetParam( adapter );
-	}
-	adapter->params.emplace( adapter->params.begin(),
-		new ast::PointerType( new ast::FunctionType( ast::VariableArgs ) )
-	);
-	return adapter;
-}
-
-CallAdapter::CallAdapter() : tmpNamer( "_temp" ) {}
-
-void CallAdapter::previsit( ast::Decl const * ) {
-	// Prevent type declaration information from leaking out.
-	GuardScope( scopeTypeVars );
-}
-
-ast::FunctionDecl const * CallAdapter::previsit( ast::FunctionDecl const * decl ) {
-	// Prevent type declaration information from leaking out.
-	GuardScope( scopeTypeVars );
-
-	if ( nullptr == decl->stmts ) {
-		return decl;
-	}
-
-	GuardValue( retval );
-
-	// Process polymorphic return value.
-	retval = nullptr;
-	ast::FunctionType const * type = decl->type;
-	if ( isDynRet( type ) && decl->linkage != ast::Linkage::C ) {
-		retval = decl->returns.front();
-
-		// Give names to unnamed return values.
-		if ( "" == retval->name ) {
-			auto mutRet = ast::mutate( retval );
-			mutRet->name = "_retparam";
-			mutRet->linkage = ast::Linkage::C;
-			retval = mutRet;
-			decl = ast::mutate_field_index( decl,
-				&ast::FunctionDecl::returns, 0, mutRet );
-		}
-	}
-
-	// The formal_usage/expr_id values may be off if we get them from the
-	// type, trying the declaration instead.
-	makeTypeVarMap( type, scopeTypeVars );
-
-	// Get all needed adapters from the call. We will forward them.
-	ast::vector<ast::FunctionType> functions;
-	for ( ast::ptr<ast::VariableExpr> const & assertion : type->assertions ) {
-		auto atype = assertion->result.get();
-		findFunction( atype, functions, scopeTypeVars, needsAdapter );
-	}
-
-	for ( ast::ptr<ast::Type> const & arg : type->params ) {
-		findFunction( arg, functions, scopeTypeVars, needsAdapter );
-	}
-
-	for ( auto funcType : functions ) {
-		std::string mangleName = mangleAdapterName( funcType, scopeTypeVars );
-		if ( adapters.contains( mangleName ) ) continue;
-		std::string adapterName = makeAdapterName( mangleName );
-		// TODO: The forwarding here is problematic because these
-		// declarations are not rooted anywhere in the translation unit.
-		adapters.insert(
-			mangleName,
-			new ast::ObjectDecl(
-				decl->location,
-				adapterName,
-				new ast::PointerType(
-					makeAdapterType( funcType, scopeTypeVars ) ),
-				nullptr, // init
-				ast::Storage::Classes(),
-				ast::Linkage::C
-			)
-		);
-	}
-
-	return decl;
-}
-
-void CallAdapter::previsit( ast::TypeDecl const * decl ) {
-	addToTypeVarMap( decl, scopeTypeVars );
-}
-
-void CallAdapter::previsit( ast::CommaExpr const * expr ) {
-	// Attempting to find application expressions that were mutated by the
-	// copy constructor passes to use an explicit return variable, so that
-	// the variable can be reused as a parameter to the call rather than
-	// creating a new temporary variable. Previously this step was an
-	// optimization, but with the introduction of tuples and UniqueExprs,
-	// it is necessary to ensure that they use the same variable.
-	// Essentially, looking for pattern:
-	// (x=f(...), x)
-	// To compound the issue, the right side can be *x, etc.
-	// because of lvalue-returning functions
-	if ( auto assign = expr->arg1.as<ast::UntypedExpr>() ) {
-		if ( CodeGen::isAssignment( ast::getFunctionName( assign ) ) ) {
-			assert( 2 == assign->args.size() );
-			if ( auto app = assign->args.back().as<ast::ApplicationExpr>() ) {
-				// First argument is assignable, so it must be an lvalue,
-				// so it should be legal to takes its address.
-				retVals.insert_or_assign( app, assign->args.front() );
-			}
-		}
-	}
-}
-
-ast::Expr const * CallAdapter::postvisit( ast::ApplicationExpr const * expr ) {
-	assert( expr->func->result );
-	ast::FunctionType const * function = getFunctionType( expr->func->result );
-	assertf( function, "ApplicationExpr has non-function type %s",
-			toCString( expr->func->result ) );
-
-	if ( auto newExpr = handleIntrinsics( expr ) ) {
-		return newExpr;
-	}
-
-	ast::ApplicationExpr * mutExpr = ast::mutate( expr );
-	ast::Expr const * ret = expr;
-
-	// TODO: This entire section should probably be refactored to do less
-	// pushing to the front/middle of a vector.
-	ptrdiff_t initArgCount = mutExpr->args.size();
-
-	TypeVarMap exprTypeVars;
-	// TODO: Should this take into account the variables already bound in
-	// scopeTypeVars ([ex] remove them from exprTypeVars)?
-	makeTypeVarMap( function, exprTypeVars );
-	auto dynRetType = isDynRet( function, exprTypeVars );
-
-	// NOTE: addDynRetParam needs to know the actual (generated) return type
-	// so it can make a temporary variable, so pass the result type form the
-	// `expr` `passTypeVars` needs to know the program-text return type ([ex]
-	// the distinction between _conc_T30 and T3(int)) concRetType may not be
-	// a good name in one or both of these places.
-	if ( dynRetType ) {
-		ast::Type const * result = mutExpr->result;
-		ast::Type const * concRetType = result->isVoid() ? nullptr : result;
-		// [Comment from before translation.]
-		// Used to use dynRetType instead of concRetType.
-		ret = addDynRetParam( mutExpr, concRetType );
-	} else if ( needsAdapter( function, scopeTypeVars )
-			&& !needsAdapter( function, exprTypeVars ) ) {
-		// Change the application so it calls the adapter rather than the
-		// passed function.
-		ret = applyAdapter( mutExpr, function );
-	}
-
-	assert( typeSubs );
-	ast::vector<ast::Expr>::iterator argIt =
-		passTypeVars( mutExpr, function );
-	addInferredParams( mutExpr, argIt, function, exprTypeVars );
-
-	argIt = mutExpr->args.begin();
-	std::advance( argIt, ( mutExpr->args.size() - initArgCount ) );
-
-	boxParams( mutExpr, argIt, function, exprTypeVars );
-	passAdapters( mutExpr, function, exprTypeVars );
-
-	return ret;
-}
-
-bool isPolyDeref( ast::UntypedExpr const * expr,
-		TypeVarMap const & typeVars,
-		ast::TypeSubstitution const * typeSubs ) {
-	if ( expr->result && isPolyType( expr->result, typeVars, typeSubs ) ) {
-		if ( auto name = expr->func.as<ast::NameExpr>() ) {
-			if ( "*?" == name->name ) {
-				return true;
-			}
-		}
-	}
-	return false;
-}
-
-ast::Expr const * CallAdapter::postvisit( ast::UntypedExpr const * expr ) {
-	if ( isPolyDeref( expr, scopeTypeVars, typeSubs ) ) {
-		return expr->args.front();
-	}
-	return expr;
-}
-
-void CallAdapter::previsit( ast::AddressExpr const * ) {
-	visit_children = false;
-}
-
-ast::Expr const * CallAdapter::postvisit( ast::AddressExpr const * expr ) {
-	assert( expr->arg->result );
-	assert( !expr->arg->result->isVoid() );
-
-	bool doesNeedAdapter = false;
-	if ( auto un = expr->arg.as<ast::UntypedExpr>() ) {
-		if ( isPolyDeref( un, scopeTypeVars, typeSubs ) ) {
-			if ( auto app = un->args.front().as<ast::ApplicationExpr>() ) {
-				assert( app->func->result );
-				auto function = getFunctionType( app->func->result );
-				assert( function );
-				doesNeedAdapter = needsAdapter( function, scopeTypeVars );
-			}
-		}
-	}
-	// isPolyType check needs to happen before mutating expr arg,
-	// so pull it forward out of the if condition.
-	expr = ast::mutate_field( expr, &ast::AddressExpr::arg,
-			expr->arg->accept( *visitor ) );
-	// But must happen after mutate, since argument might change
-	// (ex. intrinsic *?, ?[?]) re-evaluate above comment.
-	bool polyType = isPolyType( expr->arg->result, scopeTypeVars, typeSubs );
-	if ( polyType || doesNeedAdapter ) {
-		ast::Expr * ret = ast::mutate( expr->arg.get() );
-		ret->result = ast::deepCopy( expr->result );
-		return ret;
-	} else {
-		return expr;
-	}
-}
-
-ast::ReturnStmt const * CallAdapter::previsit( ast::ReturnStmt const * stmt ) {
-	// Since retval is set when the return type is dynamic, this function
-	// should have been converted to void return & out parameter.
-	if ( retval && stmt->expr ) {
-		assert( stmt->expr->result );
-		assert( !stmt->expr->result->isVoid() );
-		return ast::mutate_field( stmt, &ast::ReturnStmt::expr, nullptr );
-	}
-	return stmt;
-}
-
-void CallAdapter::beginScope() {
-	adapters.beginScope();
-}
-
-void CallAdapter::endScope() {
-	adapters.endScope();
-}
-
-/// Find instances of polymorphic type parameters.
-struct PolyFinder {
-	TypeVarMap const & typeVars;
-	bool result = false;
-	PolyFinder( TypeVarMap const & tvs ) : typeVars( tvs ) {}
-
-	void previsit( ast::TypeInstType const * type ) {
-		if ( isPolyType( type, typeVars ) ) result = true;
-	}
-};
-
-/// True if these is an instance of a polymorphic type parameter in the type.
-bool hasPolymorphism( ast::Type const * type, TypeVarMap const & typeVars ) {
-	return ast::Pass<PolyFinder>::read( type, typeVars );
-}
-
-ast::vector<ast::Expr>::iterator CallAdapter::passTypeVars(
-		ast::ApplicationExpr * expr,
-		ast::FunctionType const * function ) {
-	assert( typeSubs );
-	ast::vector<ast::Expr>::iterator arg = expr->args.begin();
-	// Pass size/align for type variables.
-	for ( ast::ptr<ast::TypeInstType> const & typeVar : function->forall ) {
-		if ( !typeVar->base->isComplete() ) continue;
-		ast::Type const * concrete = typeSubs->lookup( typeVar );
-		if ( !concrete ) {
-			// Should this be an assertion?
-			SemanticError( expr, toString( typeSubs,
-				"\nunbound type variable: ", typeVar->typeString(),
-				" in application " ) );
-		}
-		arg = expr->args.insert( arg,
-			new ast::SizeofExpr( expr->location, ast::deepCopy( concrete ) ) );
-		arg++;
-		arg = expr->args.insert( arg,
-			new ast::AlignofExpr( expr->location, ast::deepCopy( concrete ) ) );
-		arg++;
-	}
-	return arg;
-}
-
-ast::Expr const * CallAdapter::addRetParam(
-		ast::ApplicationExpr * expr, ast::Type const * retType ) {
-	// Create temporary to hold return value of polymorphic function and
-	// produce that temporary as a result using a comma expression.
-	assert( retType );
-
-	ast::Expr * paramExpr = nullptr;
-	// Try to use existing return value parameter if it exists,
-	// otherwise create a new temporary.
-	if ( retVals.count( expr ) ) {
-		paramExpr = ast::deepCopy( retVals[ expr ] );
-	} else {
-		auto newObj = makeTemporary( expr->location, ast::deepCopy( retType ) );
-		paramExpr = new ast::VariableExpr( expr->location, newObj );
-	}
-	ast::Expr * retExpr = ast::deepCopy( paramExpr );
-
-	// If the type of the temporary is not polpmorphic, box temporary by
-	// taking its address; otherwise the temporary is already boxed and can
-	// be used directly.
-	if ( !isPolyType( paramExpr->result, scopeTypeVars, typeSubs ) ) {
-		paramExpr = new ast::AddressExpr( paramExpr->location, paramExpr );
-	}
-	// Add argument to function call.
-	expr->args.insert( expr->args.begin(), paramExpr );
-	// Build a comma expression to call the function and return a value.
-	ast::CommaExpr * comma = new ast::CommaExpr(
-		expr->location, expr, retExpr );
-	comma->env = expr->env;
-	expr->env = nullptr;
-	return comma;
-}
-
-ast::Expr const * CallAdapter::addDynRetParam(
-		ast::ApplicationExpr * expr, ast::Type const * polyType ) {
-	assert( typeSubs );
-	ast::Type const * concrete = replaceWithConcrete( polyType, *typeSubs );
-	// Add out-parameter for return value.
-	return addRetParam( expr, concrete );
-}
-
-ast::Expr const * CallAdapter::applyAdapter(
-		ast::ApplicationExpr * expr,
-		ast::FunctionType const * function ) {
-	ast::Expr const * ret = expr;
-	if ( isDynRet( function, scopeTypeVars ) ) {
-		ret = addRetParam( expr, function->returns.front() );
-	}
-	std::string mangleName = mangleAdapterName( function, scopeTypeVars );
-	std::string adapterName = makeAdapterName( mangleName );
-
-	// Cast adaptee to `void (*)()`, since it may have any type inside a
-	// polymorphic function.
-	ast::Type const * adapteeType = new ast::PointerType(
-		new ast::FunctionType( ast::VariableArgs ) );
-	expr->args.insert( expr->args.begin(),
-		new ast::CastExpr( expr->location, expr->func, adapteeType ) );
-	// The result field is never set on NameExpr. / Now it is.
-	auto head = new ast::NameExpr( expr->location, adapterName );
-	head->result = ast::deepCopy( adapteeType );
-	expr->func = head;
-
-	return ret;
-}
-
-/// Cast parameters to polymorphic functions so that types are replaced with
-/// `void *` if they are type parameters in the formal type.
-/// This gets rid of warnings from gcc.
-void addCast(
-		ast::ptr<ast::Expr> & actual,
-		ast::Type const * formal,
-		TypeVarMap const & typeVars ) {
-	// Type contains polymorphism, but isn't exactly a polytype, in which
-	// case it has some real actual type (ex. unsigned int) and casting to
-	// `void *` is wrong.
-	if ( hasPolymorphism( formal, typeVars )
-			&& !isPolyType( formal, typeVars ) ) {
-		ast::Type const * newType = ast::deepCopy( formal );
-		newType = scrubTypeVars( newType, typeVars );
-		actual = new ast::CastExpr( actual->location, actual, newType );
-	}
-}
-
-void CallAdapter::boxParam( ast::ptr<ast::Expr> & arg,
-		ast::Type const * param, TypeVarMap const & exprTypeVars ) {
-	assertf( arg->result, "arg does not have result: %s", toCString( arg ) );
-	addCast( arg, param, exprTypeVars );
-	if ( !needsBoxing( param, arg->result, exprTypeVars, typeSubs ) ) {
-		return;
-	}
-	CodeLocation const & location = arg->location;
-
-	if ( arg->get_lvalue() ) {
-		// The argument expression may be CFA lvalue, but not C lvalue,
-		// so apply generalizedLvalue transformations.
-		// if ( auto var = dynamic_cast<ast::VariableExpr const *>( arg ) ) {
-		//  if ( dynamic_cast<ast::ArrayType const *>( varExpr->var->get_type() ) ){
-		//      // temporary hack - don't box arrays, because &arr is not the same as &arr[0]
-		//      return;
-		//  }
-		// }
-		arg = generalizedLvalue( new ast::AddressExpr( arg->location, arg ) );
-		if ( !ResolvExpr::typesCompatible( param, arg->result ) ) {
-			// Silence warnings by casting boxed parameters when the actually
-			// type does not match up with the formal type.
-			arg = new ast::CastExpr( location, arg, ast::deepCopy( param ) );
-		}
-	} else {
-		// Use type computed in unification to declare boxed variables.
-		ast::ptr<ast::Type> newType = ast::deepCopy( param );
-		if ( typeSubs ) typeSubs->apply( newType );
-		ast::ObjectDecl * newObj = makeTemporary( location, newType );
-		auto assign = ast::UntypedExpr::createCall( location, "?=?", {
-			new ast::VariableExpr( location, newObj ),
-			arg,
-		} );
-		stmtsToAddBefore.push_back( new ast::ExprStmt( location, assign ) );
-		arg = new ast::AddressExpr(
-			new ast::VariableExpr( location, newObj ) );
-	}
-}
-
-void CallAdapter::boxParams(
-		ast::ApplicationExpr const * expr,
-		ast::vector<ast::Expr>::iterator arg,
-		ast::FunctionType const * function,
-		const TypeVarMap & typeVars ) {
-	for ( auto param : function->params ) {
-		assertf( arg != expr->args.end(),
-			"boxParams: missing argument for param %s to %s in %s",
-			toCString( param ), toCString( function ), toCString( expr ) );
-		boxParam( *arg, param, typeVars );
-		++arg;
-	}
-}
-
-void CallAdapter::addInferredParams(
-		ast::ApplicationExpr * expr,
-		ast::vector<ast::Expr>::iterator arg,
-		ast::FunctionType const * functionType,
-		TypeVarMap const & typeVars ) {
-	ast::vector<ast::Expr>::iterator cur = arg;
-	for ( auto assertion : functionType->assertions ) {
-		auto inferParam = expr->inferred.inferParams().find(
-			assertion->var->uniqueId );
-		assertf( inferParam != expr->inferred.inferParams().end(),
-			"addInferredParams missing inferred parameter: %s in: %s",
-			toCString( assertion ), toCString( expr ) );
-		ast::ptr<ast::Expr> newExpr = ast::deepCopy( inferParam->second.expr );
-		boxParam( newExpr, assertion->result, typeVars );
-		cur = expr->args.insert( cur, newExpr.release() );
-		++cur;
-	}
-}
-
-/// Modifies the ApplicationExpr to accept adapter functions for its
-/// assertion and parameters, declares the required adapters.
-void CallAdapter::passAdapters(
-		ast::ApplicationExpr * expr,
-		ast::FunctionType const * type,
-		const TypeVarMap & exprTypeVars ) {
-	// Collect a list of function types passed as parameters or implicit
-	// parameters (assertions).
-	ast::vector<ast::Type> const & paramList = type->params;
-	ast::vector<ast::FunctionType> functions;
-
-	for ( ast::ptr<ast::VariableExpr> const & assertion : type->assertions ) {
-		findFunction( assertion->result, functions, exprTypeVars, needsAdapter );
-	}
-	for ( ast::ptr<ast::Type> const & arg : paramList ) {
-		findFunction( arg, functions, exprTypeVars, needsAdapter );
-	}
-
-	// Parameter function types for which an appropriate adapter has been
-	// generated. We cannot use the types after applying substitutions,
-	// since two different parameter types may be unified to the same type.
-	std::set<std::string> adaptersDone;
-
-	CodeLocation const & location = expr->location;
-
-	for ( ast::ptr<ast::FunctionType> const & funcType : functions ) {
-		std::string mangleName = Mangle::mangle( funcType );
-
-		// Only attempt to create an adapter or pass one as a parameter if we
-		// haven't already done so for this pre-substitution parameter
-		// function type.
-		// The second part of the result if is if the element was inserted.
-		if ( !adaptersDone.insert( mangleName ).second ) continue;
-
-		// Apply substitution to type variables to figure out what the
-		// adapter's type should look like. (Copy to make the release safe.)
-		assert( typeSubs );
-		auto result = typeSubs->apply( ast::deepCopy( funcType ) );
-		ast::FunctionType * realType = ast::mutate( result.node.release() );
-		mangleName = Mangle::mangle( realType );
-		mangleName += makePolyMonoSuffix( funcType, exprTypeVars );
-
-		// Check if the adapter has already been created, or has to be.
-		using AdapterIter = decltype(adapters)::iterator;
-		AdapterIter adapter = adapters.find( mangleName );
-		if ( adapter == adapters.end() ) {
-			ast::FunctionDecl * newAdapter = makeAdapter(
-				funcType, realType, mangleName, exprTypeVars, location );
-			std::pair<AdapterIter, bool> answer =
-				adapters.insert( mangleName, newAdapter );
-			adapter = answer.first;
-			stmtsToAddBefore.push_back(
-				new ast::DeclStmt( location, newAdapter ) );
-		}
-		assert( adapter != adapters.end() );
-
-		// Add the approprate adapter as a parameter.
-		expr->args.insert( expr->args.begin(),
-			new ast::VariableExpr( location, adapter->second ) );
-	}
-}
-
-// Parameter and argument may be used wrong around here.
-ast::Expr * makeAdapterArg(
-		ast::DeclWithType const * param,
-		ast::Type const * arg,
-		ast::Type const * realParam,
-		TypeVarMap const & typeVars,
-		CodeLocation const & location ) {
-	assert( param );
-	assert( arg );
-	assert( realParam );
-	if ( isPolyType( realParam, typeVars ) && !isPolyType( arg ) ) {
-		ast::UntypedExpr * deref = ast::UntypedExpr::createDeref(
-			location,
-			new ast::CastExpr( location,
-				new ast::VariableExpr( location, param ),
-				new ast::PointerType( ast::deepCopy( arg ) )
-			)
-		);
-		deref->result = ast::deepCopy( arg );
-		return deref;
-	}
-	return new ast::VariableExpr( location, param );
-}
-
-// This seems to be one of the problematic functions.
-void addAdapterParams(
-		ast::ApplicationExpr * adaptee,
-		ast::vector<ast::Type>::const_iterator arg,
-		ast::vector<ast::DeclWithType>::iterator param,
-		ast::vector<ast::DeclWithType>::iterator paramEnd,
-		ast::vector<ast::Type>::const_iterator realParam,
-		TypeVarMap const & typeVars,
-		CodeLocation const & location ) {
-	UniqueName paramNamer( "_p" );
-	for ( ; param != paramEnd ; ++param, ++arg, ++realParam ) {
-		if ( "" == (*param)->name ) {
-			auto mutParam = (*param).get_and_mutate();
-			mutParam->name = paramNamer.newName();
-			mutParam->linkage = ast::Linkage::C;
-		}
-		adaptee->args.push_back(
-			makeAdapterArg( *param, *arg, *realParam, typeVars, location ) );
-	}
-}
-
-ast::FunctionDecl * CallAdapter::makeAdapter(
-		ast::FunctionType const * adaptee,
-		ast::FunctionType const * realType,
-		std::string const & mangleName,
-		TypeVarMap const & typeVars,
-		CodeLocation const & location ) const {
-	ast::FunctionType * adapterType = makeAdapterType( adaptee, typeVars );
-	adapterType = ast::mutate( scrubTypeVars( adapterType, typeVars ) );
-
-	// Some of these names will be overwritten, but it gives a default.
-	UniqueName pNamer( "_param" );
-	UniqueName rNamer( "_ret" );
-
-	bool first = true;
-
-	ast::FunctionDecl * adapterDecl = new ast::FunctionDecl( location,
-		makeAdapterName( mangleName ),
-		{}, // forall
-		{}, // assertions
-		map_range<ast::vector<ast::DeclWithType>>( adapterType->params,
-				[&pNamer, &location, &first]( ast::ptr<ast::Type> const & param ) {
-			// [Trying to make the generated code match exactly more often.]
-			if ( first ) {
-				first = false;
-				return new ast::ObjectDecl( location, "_adaptee", param );
-			}
-			return new ast::ObjectDecl( location, pNamer.newName(), param );
-		} ),
-		map_range<ast::vector<ast::DeclWithType>>( adapterType->returns,
-				[&rNamer, &location]( ast::ptr<ast::Type> const & retval ) {
-			return new ast::ObjectDecl( location, rNamer.newName(), retval );
-		} ),
-		nullptr, // stmts
-		{}, // storage
-		ast::Linkage::C
-	);
-
-	ast::DeclWithType * adapteeDecl =
-		adapterDecl->params.front().get_and_mutate();
-	adapteeDecl->name = "_adaptee";
-
-	// Do not carry over attributes to real type parameters/return values.
-	auto mutRealType = ast::mutate( realType );
-	for ( ast::ptr<ast::Type> & decl : mutRealType->params ) {
-		if ( decl->attributes.empty() ) continue;
-		auto mut = ast::mutate( decl.get() );
-		mut->attributes.clear();
-		decl = mut;
-	}
-	for ( ast::ptr<ast::Type> & decl : mutRealType->returns ) {
-		if ( decl->attributes.empty() ) continue;
-		auto mut = ast::mutate( decl.get() );
-		mut->attributes.clear();
-		decl = mut;
-	}
-	realType = mutRealType;
-
-	ast::ApplicationExpr * adapteeApp = new ast::ApplicationExpr( location,
-		new ast::CastExpr( location,
-			new ast::VariableExpr( location, adapteeDecl ),
-			new ast::PointerType( realType )
-		)
-	);
-
-	for ( auto group : group_iterate( realType->assertions,
-			adapterType->assertions, adaptee->assertions ) ) {
-		auto assertArg = std::get<0>( group );
-		auto assertParam = std::get<1>( group );
-		auto assertReal = std::get<2>( group );
-		adapteeApp->args.push_back( makeAdapterArg(
-			assertParam->var, assertArg->var->get_type(),
-			assertReal->var->get_type(), typeVars, location
-		) );
-	}
-
-	ast::vector<ast::Type>::const_iterator
-		arg = realType->params.begin(),
-		param = adapterType->params.begin(),
-		realParam = adaptee->params.begin();
-	ast::vector<ast::DeclWithType>::iterator
-		paramDecl = adapterDecl->params.begin();
-	// Skip adaptee parameter in the adapter type.
-	++param;
-	++paramDecl;
-
-	ast::Stmt * bodyStmt;
-	// Returns void/nothing.
-	if ( realType->returns.empty() ) {
-		addAdapterParams( adapteeApp, arg, paramDecl, adapterDecl->params.end(),
-			realParam, typeVars, location );
-		bodyStmt = new ast::ExprStmt( location, adapteeApp );
-	// Returns a polymorphic type.
-	} else if ( isDynType( adaptee->returns.front(), typeVars ) ) {
-		ast::UntypedExpr * assign = new ast::UntypedExpr( location,
-			new ast::NameExpr( location, "?=?" ) );
-		ast::UntypedExpr * deref = ast::UntypedExpr::createDeref( location,
-			new ast::CastExpr( location,
-				new ast::VariableExpr( location, *paramDecl++ ),
-				new ast::PointerType(
-					ast::deepCopy( realType->returns.front() ) ) ) );
-		assign->args.push_back( deref );
-		addAdapterParams( adapteeApp, arg, paramDecl, adapterDecl->params.end(),
-			realParam, typeVars, location );
-		assign->args.push_back( adapteeApp );
-		bodyStmt = new ast::ExprStmt( location, assign );
-	// Adapter for a function that returns a monomorphic value.
-	} else {
-		addAdapterParams( adapteeApp, arg, paramDecl, adapterDecl->params.end(),
-				realParam, typeVars, location );
-		bodyStmt = new ast::ReturnStmt( location, adapteeApp );
-	}
-
-	adapterDecl->stmts = new ast::CompoundStmt( location, { bodyStmt } );
-	return adapterDecl;
-}
-
-ast::Expr const * makeIncrDecrExpr(
-		CodeLocation const & location,
-		ast::ApplicationExpr const * expr,
-		ast::Type const * polyType,
-		bool isIncr ) {
-	ast::NameExpr * opExpr =
-			new ast::NameExpr( location, isIncr ? "?+=?" : "?-=?" );
-	ast::UntypedExpr * addAssign = new ast::UntypedExpr( location, opExpr );
-	if ( auto address = expr->args.front().as<ast::AddressExpr>() ) {
-		addAssign->args.push_back( address->arg );
-	} else {
-		addAssign->args.push_back( expr->args.front() );
-	}
-	addAssign->args.push_back( new ast::NameExpr( location,
-		sizeofName( Mangle::mangleType( polyType ) ) ) );
-	addAssign->result = ast::deepCopy( expr->result );
-	addAssign->env = expr->env ? expr->env : addAssign->env;
-	return addAssign;
-}
-
-/// Handles intrinsic functions for postvisit ApplicationExpr.
-ast::Expr const * CallAdapter::handleIntrinsics(
-		ast::ApplicationExpr const * expr ) {
-	auto varExpr = expr->func.as<ast::VariableExpr>();
-	if ( !varExpr || varExpr->var->linkage != ast::Linkage::Intrinsic ) {
-		return nullptr;
-	}
-	std::string const & varName = varExpr->var->name;
-
-	// Index Intrinsic:
-	if ( "?[?]" == varName ) {
-		assert( expr->result );
-		assert( 2 == expr->args.size() );
-
-		ast::Type const * baseType1 =
-			isPolyPtr( expr->args.front()->result, scopeTypeVars, typeSubs );
-		ast::Type const * baseType2 =
-			isPolyPtr( expr->args.back()->result, scopeTypeVars, typeSubs );
-		// If neither argument is a polymorphic pointer, do nothing.
-		if ( !baseType1 && !baseType2 ) {
-			return expr;
-		}
-		// The arguments cannot both be polymorphic pointers.
-		assert( !baseType1 || !baseType2 );
-		// (So exactly one of the arguments is a polymorphic pointer.)
-
-		CodeLocation const & location = expr->location;
-		CodeLocation const & location1 = expr->args.front()->location;
-		CodeLocation const & location2 = expr->args.back()->location;
-
-		ast::UntypedExpr * ret = new ast::UntypedExpr( location,
-				new ast::NameExpr( location, "?+?" ) );
-		if ( baseType1 ) {
-			auto multiply = ast::UntypedExpr::createCall( location2, "?*?", {
-				expr->args.back(),
-				new ast::SizeofExpr( location1, deepCopy( baseType1 ) ),
-			} );
-			ret->args.push_back( expr->args.front() );
-			ret->args.push_back( multiply );
-		} else {
-			assert( baseType2 );
-			auto multiply = ast::UntypedExpr::createCall( location1, "?*?", {
-				expr->args.front(),
-				new ast::SizeofExpr( location2, deepCopy( baseType2 ) ),
-			} );
-			ret->args.push_back( multiply );
-			ret->args.push_back( expr->args.back() );
-		}
-		ret->result = ast::deepCopy( expr->result );
-		ret->env = expr->env ? expr->env : ret->env;
-		return ret;
-	// Dereference Intrinsic:
-	} else if ( "*?" == varName ) {
-		assert( expr->result );
-		assert( 1 == expr->args.size() );
-
-		// If this isn't for a poly type, then do nothing.
-		if ( !isPolyType( expr->result, scopeTypeVars, typeSubs ) ) {
-			return expr;
-		}
-
-		// Remove dereference from polymorphic types since they are boxed.
-		ast::Expr * ret = ast::deepCopy( expr->args.front() );
-		// Fix expression type to remove pointer.
-		ret->result = expr->result;
-		ret->env = expr->env ? expr->env : ret->env;
-		return ret;
-	// Post-Increment/Decrement Intrinsics:
-	} else if ( "?++" == varName || "?--" == varName ) {
-		assert( expr->result );
-		assert( 1 == expr->args.size() );
-
-		ast::Type const * baseType =
-			isPolyType( expr->result, scopeTypeVars, typeSubs );
-		if ( nullptr == baseType ) {
-			return expr;
-		}
-		ast::Type * tempType = ast::deepCopy( expr->result );
-		if ( typeSubs ) {
-			auto result = typeSubs->apply( tempType );
-			tempType = ast::mutate( result.node.release() );
-		}
-		CodeLocation const & location = expr->location;
-		ast::ObjectDecl * newObj = makeTemporary( location, tempType );
-		ast::VariableExpr * tempExpr =
-			new ast::VariableExpr( location, newObj );
-		ast::UntypedExpr * assignExpr = new ast::UntypedExpr( location,
-			new ast::NameExpr( location, "?=?" ) );
-		assignExpr->args.push_back( ast::deepCopy( tempExpr ) );
-		if ( auto address = expr->args.front().as<ast::AddressExpr>() ) {
-			assignExpr->args.push_back( ast::deepCopy( address->arg ) );
-		} else {
-			assignExpr->args.push_back( ast::deepCopy( expr->args.front() ) );
-		}
-		return new ast::CommaExpr( location,
-			new ast::CommaExpr( location,
-				assignExpr,
-				makeIncrDecrExpr( location, expr, baseType, "?++" == varName )
-			),
-			tempExpr
-		);
-	// Pre-Increment/Decrement Intrinsics:
-	} else if ( "++?" == varName || "--?" == varName ) {
-		assert( expr->result );
-		assert( 1 == expr->args.size() );
-
-		ast::Type const * baseType =
-			isPolyType( expr->result, scopeTypeVars, typeSubs );
-		if ( nullptr == baseType ) {
-			return expr;
-		}
-		return makeIncrDecrExpr(
-			expr->location, expr, baseType, "++?" == varName );
-	// Addition and Subtration Intrinsics:
-	} else if ( "?+?" == varName || "?-?" == varName ) {
-		assert( expr->result );
-		assert( 2 == expr->args.size() );
-
-		auto baseType1 =
-			isPolyPtr( expr->args.front()->result, scopeTypeVars, typeSubs );
-		auto baseType2 =
-			isPolyPtr( expr->args.back()->result, scopeTypeVars, typeSubs );
-
-		CodeLocation const & location = expr->location;
-		CodeLocation const & location1 = expr->args.front()->location;
-		CodeLocation const & location2 = expr->args.back()->location;
-		// LHS op RHS -> (LHS op RHS) / sizeof(LHS)
-		if ( baseType1 && baseType2 ) {
-			auto divide = ast::UntypedExpr::createCall( location, "?/?", {
-				expr,
-				new ast::SizeofExpr( location, deepCopy( baseType1 ) ),
-			} );
-			if ( expr->env ) divide->env = expr->env;
-			return divide;
-		// LHS op RHS -> LHS op (RHS * sizeof(LHS))
-		} else if ( baseType1 ) {
-			auto multiply = ast::UntypedExpr::createCall( location2, "?*?", {
-				expr->args.back(),
-				new ast::SizeofExpr( location1, deepCopy( baseType1 ) ),
-			} );
-			return ast::mutate_field_index(
-				expr, &ast::ApplicationExpr::args, 1, multiply );
-		// LHS op RHS -> (LHS * sizeof(RHS)) op RHS
-		} else if ( baseType2 ) {
-			auto multiply = ast::UntypedExpr::createCall( location1, "?*?", {
-				expr->args.front(),
-				new ast::SizeofExpr( location2, deepCopy( baseType2 ) ),
-			} );
-			return ast::mutate_field_index(
-				expr, &ast::ApplicationExpr::args, 0, multiply );
-		}
-	// Addition and Subtration Relative Assignment Intrinsics:
-	} else if ( "?+=?" == varName || "?-=?" == varName ) {
-		assert( expr->result );
-		assert( 2 == expr->args.size() );
-
-		CodeLocation const & location1 = expr->args.front()->location;
-		CodeLocation const & location2 = expr->args.back()->location;
-		auto baseType = isPolyPtr( expr->result, scopeTypeVars, typeSubs );
-		// LHS op RHS -> LHS op (RHS * sizeof(LHS))
-		if ( baseType ) {
-			auto multiply = ast::UntypedExpr::createCall( location2, "?*?", {
-				expr->args.back(),
-				new ast::SizeofExpr( location1, deepCopy( baseType ) ),
-			} );
-			return ast::mutate_field_index(
-				expr, &ast::ApplicationExpr::args, 1, multiply );
-		}
-	}
-	return expr;
-}
-
-ast::ObjectDecl * CallAdapter::makeTemporary(
-		CodeLocation const & location, ast::Type const * type ) {
-	auto newObj = new ast::ObjectDecl( location, tmpNamer.newName(), type );
-	stmtsToAddBefore.push_back( new ast::DeclStmt( location, newObj ) );
-	return newObj;
-}
-
-// --------------------------------------------------------------------------
-/// Modifies declarations to accept implicit parameters.
-/// * Move polymorphic returns in function types to pointer-type parameters.
-/// * Adds type size and assertion parameters to parameter lists.
-struct DeclAdapter final {
-	ast::FunctionDecl const * previsit( ast::FunctionDecl const * decl );
-	ast::FunctionDecl const * postvisit( ast::FunctionDecl const * decl );
-private:
-	void addAdapters( ast::FunctionDecl * decl, TypeVarMap & localTypeVars );
-};
-
-// size/align/offset parameters may not be used, so add the unused attribute.
-ast::ObjectDecl * makeObj(
-		CodeLocation const & location, std::string const & name ) {
-	return new ast::ObjectDecl( location, name,
-		makeSizeAlignType(),
-		nullptr, ast::Storage::Classes(), ast::Linkage::C, nullptr,
-		{ new ast::Attribute( "unused" ) } );
-}
-
-ast::FunctionDecl const * DeclAdapter::previsit( ast::FunctionDecl const * decl ) {
-	TypeVarMap localTypeVars;
-	makeTypeVarMap( decl, localTypeVars );
-
-	auto mutDecl = mutate( decl );
-
-	// Move polymorphic return type to parameter list.
-	if ( isDynRet( mutDecl->type ) ) {
-		auto ret = strict_dynamic_cast<ast::ObjectDecl *>(
-			mutDecl->returns.front().get_and_mutate() );
-		ret->set_type( new ast::PointerType( ret->type ) );
-		mutDecl->params.insert( mutDecl->params.begin(), ret );
-		mutDecl->returns.erase( mutDecl->returns.begin() );
-		ret->init = nullptr;
-	}
-
-	// Add size/align and assertions for type parameters to parameter list.
-	ast::vector<ast::DeclWithType> inferredParams;
-	ast::vector<ast::DeclWithType> layoutParams;
-	for ( ast::ptr<ast::TypeDecl> & typeParam : mutDecl->type_params ) {
-		auto mutParam = mutate( typeParam.get() );
-		// Add all size and alignment parameters to parameter list.
-		if ( mutParam->isComplete() ) {
-			ast::TypeInstType paramType( mutParam );
-			std::string paramName = Mangle::mangleType( &paramType );
-
-			auto sizeParam = makeObj( typeParam->location, sizeofName( paramName ) );
-			layoutParams.emplace_back( sizeParam );
-
-			auto alignParam = makeObj( typeParam->location, alignofName( paramName ) );
-			layoutParams.emplace_back( alignParam );
-		}
-		// Assertions should be stored in the main list.
-		assert( mutParam->assertions.empty() );
-		typeParam = mutParam;
-	}
-	for ( ast::ptr<ast::DeclWithType> & assert : mutDecl->assertions ) {
-		// Assertion parameters may not be used in body,
-		// pass along with unused attribute.
-		assert.get_and_mutate()->attributes.push_back(
-			new ast::Attribute( "unused" ) );
-		inferredParams.push_back( assert );
-	}
-	mutDecl->assertions.clear();
-
-	// Prepend each argument group. From last group to first. addAdapters
-	// does do the same, it just does it itself and see all other parameters.
-	spliceBegin( mutDecl->params, inferredParams );
-	spliceBegin( mutDecl->params, layoutParams );
-	addAdapters( mutDecl, localTypeVars );
-
-	// Now have to update the type to match the declaration.
-	ast::FunctionType * type = new ast::FunctionType(
-		mutDecl->type->isVarArgs, mutDecl->type->qualifiers );
-	// The forall clauses don't match until Eraser. The assertions are empty.
-	for ( auto param : mutDecl->params ) {
-		type->params.emplace_back( param->get_type() );
-	}
-	for ( auto retval : mutDecl->returns ) {
-		type->returns.emplace_back( retval->get_type() );
-	}
-	mutDecl->type = type;
-
-	return mutDecl;
-}
-
-ast::FunctionDecl const * DeclAdapter::postvisit(
-		ast::FunctionDecl const * decl ) {
-	ast::FunctionDecl * mutDecl = mutate( decl );
-	if ( !mutDecl->returns.empty() && mutDecl->stmts
-			// Intrinsic functions won't be using the _retval so no need to
-			// generate it.
-			&& mutDecl->linkage != ast::Linkage::Intrinsic
-			// Remove check for prefix once thunks properly use ctor/dtors.
-			&& !isPrefix( mutDecl->name, "_thunk" )
-			&& !isPrefix( mutDecl->name, "_adapter" ) ) {
-		assert( 1 == mutDecl->returns.size() );
-		ast::DeclWithType const * retval = mutDecl->returns.front();
-		if ( "" == retval->name ) {
-			retval = ast::mutate_field(
-				retval, &ast::DeclWithType::name, "_retval" );
-			mutDecl->returns.front() = retval;
-		}
-		auto stmts = mutDecl->stmts.get_and_mutate();
-		stmts->kids.push_front( new ast::DeclStmt( retval->location, retval ) );
-		ast::DeclWithType * newRet = ast::deepCopy( retval );
-		mutDecl->returns.front() = newRet;
-	}
-	// Errors should have been caught by this point, remove initializers from
-	// parameters to allow correct codegen of default arguments.
-	for ( ast::ptr<ast::DeclWithType> & param : mutDecl->params ) {
-		if ( auto obj = param.as<ast::ObjectDecl>() ) {
-			param = ast::mutate_field( obj, &ast::ObjectDecl::init, nullptr );
-		}
-	}
-	return mutDecl;
-}
-
-void DeclAdapter::addAdapters(
-		ast::FunctionDecl * mutDecl, TypeVarMap & localTypeVars ) {
-	ast::vector<ast::FunctionType> functions;
-	for ( ast::ptr<ast::DeclWithType> & arg : mutDecl->params ) {
-		ast::Type const * type = arg->get_type();
-		type = findAndReplaceFunction( type, functions, localTypeVars, needsAdapter );
-		arg.get_and_mutate()->set_type( type );
-	}
-	std::set<std::string> adaptersDone;
-	for ( ast::ptr<ast::FunctionType> const & func : functions ) {
-		std::string mangleName = mangleAdapterName( func, localTypeVars );
-		if ( adaptersDone.find( mangleName ) != adaptersDone.end() ) {
-			continue;
-		}
-		std::string adapterName = makeAdapterName( mangleName );
-		// The adapter may not actually be used, so make sure it has unused.
-		mutDecl->params.insert( mutDecl->params.begin(), new ast::ObjectDecl(
-			mutDecl->location, adapterName,
-			new ast::PointerType( makeAdapterType( func, localTypeVars ) ),
-			nullptr, {}, {}, nullptr,
-			{ new ast::Attribute( "unused" ) } ) );
-		adaptersDone.insert( adaptersDone.begin(), mangleName );
-	}
-}
-
-// --------------------------------------------------------------------------
-// TODO: Ideally, there would be no floating nodes at all.
-/// Corrects the floating nodes created in CallAdapter.
-struct RewireAdapters final : public ast::WithGuards {
-	ScopedMap<std::string, ast::ObjectDecl const *> adapters;
-	void beginScope() { adapters.beginScope(); }
-	void endScope() { adapters.endScope(); }
-	void previsit( ast::FunctionDecl const * decl );
-	ast::VariableExpr const * previsit( ast::VariableExpr const * expr );
-};
-
-void RewireAdapters::previsit( ast::FunctionDecl const * decl ) {
-	GuardScope( adapters );
-	for ( ast::ptr<ast::DeclWithType> const & param : decl->params ) {
-		if ( auto objectParam = param.as<ast::ObjectDecl>() ) {
-			adapters.insert( objectParam->name, objectParam );
-		}
-	}
-}
-
-ast::VariableExpr const * RewireAdapters::previsit(
-		ast::VariableExpr const * expr ) {
-	// If the node is not floating, we can skip.
-	if ( expr->var->isManaged() ) return expr;
-	auto it = adapters.find( expr->var->name );
-	assertf( it != adapters.end(), "Could not correct floating node." );
-	return ast::mutate_field( expr, &ast::VariableExpr::var, it->second );
-}
-
-// --------------------------------------------------------------------------
-/// Inserts code to access polymorphic layout inforation.
-/// * Replaces member and size/alignment/offsetof expressions on polymorphic
-///   generic types with calculated expressions.
-/// * Replaces member expressions for polymorphic types with calculated
-///   add-field-offset-and-dereference.
-/// * Calculates polymorphic offsetof expressions from offset array.
-/// * Inserts dynamic calculation of polymorphic type layouts where needed.
-struct PolyGenericCalculator final :
-		public ast::WithConstTypeSubstitution,
-		public ast::WithDeclsToAdd<>,
-		public ast::WithGuards,
-		public ast::WithStmtsToAdd<>,
-		public ast::WithVisitorRef<PolyGenericCalculator> {
-	PolyGenericCalculator();
-
-	void previsit( ast::FunctionDecl const * decl );
-	void previsit( ast::TypedefDecl const * decl );
-	void previsit( ast::TypeDecl const * decl );
-	ast::Decl const * postvisit( ast::TypeDecl const * decl );
-	ast::StructDecl const * previsit( ast::StructDecl const * decl );
-	ast::UnionDecl const * previsit( ast::UnionDecl const * decl );
-	ast::DeclStmt const * previsit( ast::DeclStmt const * stmt );
-	ast::Expr const * postvisit( ast::MemberExpr const * expr );
-	void previsit( ast::AddressExpr const * expr );
-	ast::Expr const * postvisit( ast::AddressExpr const * expr );
-	ast::Expr const * postvisit( ast::SizeofExpr const * expr );
-	ast::Expr const * postvisit( ast::AlignofExpr const * expr );
-	ast::Expr const * postvisit( ast::OffsetofExpr const * expr );
-	ast::Expr const * postvisit( ast::OffsetPackExpr const * expr );
-
-	void beginScope();
-	void endScope();
-private:
-	/// Makes a new variable in the current scope with the given name,
-	/// type and optional initializer.
-	ast::ObjectDecl * makeVar(
-			CodeLocation const & location, std::string const & name,
-			ast::Type const * type, ast::Init const * init = nullptr );
-	/// Returns true if the type has a dynamic layout;
-	/// such a layout will be stored in appropriately-named local variables
-	/// when the function returns.
-	bool findGeneric( CodeLocation const & location, ast::Type const * );
-	/// Adds type parameters to the layout call; will generate the
-	/// appropriate parameters if needed.
-	void addSTypeParamsToLayoutCall(
-		ast::UntypedExpr * layoutCall,
-		const ast::vector<ast::Type> & otypeParams );
-	/// Change the type of generic aggregate members to char[].
-	void mutateMembers( ast::AggregateDecl * aggr );
-	/// Returns the calculated sizeof expression for type, or nullptr for use
-	/// C sizeof().
-	ast::Expr const * genSizeof( CodeLocation const &, ast::Type const * );
-	/// Enters a new scope for type-variables,
-	/// adding the type variables from the provided type.
-	void beginTypeScope( ast::Type const * );
-
-	/// The type variables and polymorphic parameters currently in scope.
-	TypeVarMap scopeTypeVars;
-	/// Set of generic type layouts known in the current scope,
-	/// indexed by sizeofName.
-	ScopedSet<std::string> knownLayouts;
-	/// Set of non-generic types for which the offset array exists in the
-	/// current scope, indexed by offsetofName.
-	ScopedSet<std::string> knownOffsets;
-	/// Namer for VLA (variable length array) buffers.
-	UniqueName bufNamer;
-	/// If the argument of an AddressExpr is MemberExpr, it is stored here.
-	ast::MemberExpr const * addrMember = nullptr;
-};
-
-PolyGenericCalculator::PolyGenericCalculator() :
-	knownLayouts(), knownOffsets(), bufNamer( "_buf" )
-{}
-
-/// Converts polymorphic type into a suitable monomorphic representation.
-/// Currently: __attribute__((aligned(8) )) char[size_T];
-ast::Type * polyToMonoType( CodeLocation const & location,
-		ast::Type const * declType ) {
-	auto charType = new ast::BasicType( ast::BasicType::Char );
-	auto size = new ast::NameExpr( location,
-		sizeofName( Mangle::mangleType( declType ) ) );
-	auto aligned = new ast::Attribute( "aligned",
-		{ ast::ConstantExpr::from_int( location, 8 ) } );
-	auto ret = new ast::ArrayType( charType, size,
-		ast::VariableLen, ast::DynamicDim, ast::CV::Qualifiers() );
-	ret->attributes.push_back( aligned );
-	return ret;
-}
-
-void PolyGenericCalculator::previsit( ast::FunctionDecl const * decl ) {
-	GuardScope( *this );
-	beginTypeScope( decl->type );
-}
-
-void PolyGenericCalculator::previsit( ast::TypedefDecl const * decl ) {
-	assertf( false, "All typedef declarations should be removed." );
-	beginTypeScope( decl->base );
-}
-
-void PolyGenericCalculator::previsit( ast::TypeDecl const * decl ) {
-	addToTypeVarMap( decl, scopeTypeVars );
-}
-
-ast::Decl const * PolyGenericCalculator::postvisit(
-		ast::TypeDecl const * decl ) {
-	ast::Type const * base = decl->base;
-	if ( nullptr == base ) return decl;
-
-	// Add size/align variables for opaque type declarations.
-	ast::TypeInstType inst( decl->name, decl );
-	std::string typeName = Mangle::mangleType( &inst );
-	ast::Type * layoutType = new ast::BasicType(
-		ast::BasicType::LongUnsignedInt );
-
-	ast::ObjectDecl * sizeDecl = new ast::ObjectDecl( decl->location,
-		sizeofName( typeName ), layoutType,
-		new ast::SingleInit( decl->location,
-			new ast::SizeofExpr( decl->location, deepCopy( base ) )
-		)
-	);
-	ast::ObjectDecl * alignDecl = new ast::ObjectDecl( decl->location,
-		alignofName( typeName ), layoutType,
-		new ast::SingleInit( decl->location,
-			new ast::AlignofExpr( decl->location, deepCopy( base ) )
-		)
-	);
-
-	// Ensure that the initializing sizeof/alignof exprs are properly mutated.
-	sizeDecl->accept( *visitor );
-	alignDecl->accept( *visitor );
-
-	// A little trick to replace this with two declarations.
-	// Adding after makes sure that there is no conflict with adding stmts.
-	declsToAddAfter.push_back( alignDecl );
-	return sizeDecl;
-}
-
-ast::StructDecl const * PolyGenericCalculator::previsit(
-		ast::StructDecl const * decl ) {
-	auto mutDecl = mutate( decl );
-	mutateMembers( mutDecl );
-	return mutDecl;
-}
-
-ast::UnionDecl const * PolyGenericCalculator::previsit(
-		ast::UnionDecl const * decl ) {
-	auto mutDecl = mutate( decl );
-	mutateMembers( mutDecl );
-	return mutDecl;
-}
-
-ast::DeclStmt const * PolyGenericCalculator::previsit( ast::DeclStmt const * stmt ) {
-	ast::ObjectDecl const * decl = stmt->decl.as<ast::ObjectDecl>();
-	if ( !decl || !findGeneric( decl->location, decl->type ) ) {
-		return stmt;
-	}
-
-	// Change initialization of a polymorphic value object to allocate via a
-	// variable-length-array (alloca cannot be safely used in loops).
-	ast::ObjectDecl * newBuf = new ast::ObjectDecl( decl->location,
-		bufNamer.newName(),
-		polyToMonoType( decl->location, decl->type ),
-		nullptr, {}, ast::Linkage::C
-	);
-	stmtsToAddBefore.push_back( new ast::DeclStmt( stmt->location, newBuf ) );
-
-	// If the object has a cleanup attribute, the clean-up should be on the
-	// buffer, not the pointer. [Perhaps this should be lifted?]
-	auto matchAndMove = [newBuf]( ast::ptr<ast::Attribute> & attr ) {
-		if ( "cleanup" == attr->name ) {
-			newBuf->attributes.push_back( attr );
-			return true;
-		}
-		return false;
-	};
-
-	auto mutDecl = mutate( decl );
-
-	// Forally, side effects are not safe in this function. But it works.
-	erase_if( mutDecl->attributes, matchAndMove );
-
-	mutDecl->init = new ast::SingleInit( decl->location,
-		new ast::VariableExpr( decl->location, newBuf ) );
-
-	return ast::mutate_field( stmt, &ast::DeclStmt::decl, mutDecl );
-}
-
-/// Checks if memberDecl matches the decl from an aggregate.
-bool isMember( ast::DeclWithType const * memberDecl, ast::Decl const * decl ) {
-	// No matter the field, if the name is different it is not the same.
-	if ( memberDecl->name != decl->name ) {
-		return false;
-	}
-
-	if ( memberDecl->name.empty() ) {
-		// Plan-9 Field: Match on unique_id.
-		return ( memberDecl->uniqueId == decl->uniqueId );
-	}
-
-	ast::DeclWithType const * declWithType =
-		strict_dynamic_cast<ast::DeclWithType const *>( decl );
-
-	if ( memberDecl->mangleName.empty() || declWithType->mangleName.empty() ) {
-		// Tuple-Element Field: Expect neither had mangled name;
-		// accept match on simple name (like field_2) only.
-		assert( memberDecl->mangleName.empty() );
-		assert( declWithType->mangleName.empty() );
-		return true;
-	}
-
-	// Ordinary Field: Use full name to accommodate overloading.
-	return ( memberDecl->mangleName == declWithType->mangleName );
-}
-
-/// Finds the member in the base list that matches the given declaration;
-/// returns its index, or -1 if not present.
-long findMember( ast::DeclWithType const * memberDecl,
-		const ast::vector<ast::Decl> & baseDecls ) {
-	for ( auto pair : enumerate( baseDecls ) ) {
-		if ( isMember( memberDecl, pair.val.get() ) ) {
-			return pair.idx;
-		}
-	}
-	return -1;
-}
-
-/// Returns an index expression into the offset array for a type.
-ast::Expr * makeOffsetIndex( CodeLocation const & location,
-		ast::Type const * objectType, long i ) {
-	std::string name = offsetofName( Mangle::mangleType( objectType ) );
-	return ast::UntypedExpr::createCall( location, "?[?]", {
-		new ast::NameExpr( location, name ),
-		ast::ConstantExpr::from_ulong( location, i ),
-	} );
-}
-
-ast::Expr const * PolyGenericCalculator::postvisit(
-		ast::MemberExpr const * expr ) {
-	// Only mutate member expressions for polymorphic types.
-	ast::Type const * objectType = hasPolyBase(
-		expr->aggregate->result, scopeTypeVars
-	);
-	if ( !objectType ) return expr;
-	// Ensure layout for this type is available.
-	// The boolean result is ignored.
-	findGeneric( expr->location, objectType );
-
-	// Replace member expression with dynamically-computed layout expression.
-	ast::Expr * newMemberExpr = nullptr;
-	if ( auto structType = dynamic_cast<ast::StructInstType const *>( objectType ) ) {
-		long offsetIndex = findMember( expr->member, structType->base->members );
-		if ( -1 == offsetIndex ) return expr;
-
-		// Replace member expression with pointer to struct plus offset.
-		ast::UntypedExpr * fieldLoc = new ast::UntypedExpr( expr->location,
-				new ast::NameExpr( expr->location, "?+?" ) );
-		ast::Expr * aggr = deepCopy( expr->aggregate );
-		aggr->env = nullptr;
-		fieldLoc->args.push_back( aggr );
-		fieldLoc->args.push_back(
-			makeOffsetIndex( expr->location, objectType, offsetIndex ) );
-		fieldLoc->result = deepCopy( expr->result );
-		newMemberExpr = fieldLoc;
-	// Union members are all at offset zero, so just use the aggregate expr.
-	} else if ( dynamic_cast<ast::UnionInstType const *>( objectType ) ) {
-		ast::Expr * aggr = deepCopy( expr->aggregate );
-		aggr->env = nullptr;
-		aggr->result = deepCopy( expr->result );
-		newMemberExpr = aggr;
-	} else {
-		return expr;
-	}
-	assert( newMemberExpr );
-
-	// Must apply the generic substitution to the member type to handle cases
-	// where the member is a generic parameter subsituted by a known concrete
-	// type. [ex]
-	//	forall( T ) struct Box { T x; }
-	//	forall( T ) void f() {
-	//		Box( T * ) b; b.x;
-	//	}
-	// TODO: expr->result should be exactly expr->member->get_type() after
-	// substitution, so it doesn't seem like it should be necessary to apply
-	// the substitution manually. For some reason this is not currently the
-	// case. This requires more investigation.
-	ast::ptr<ast::Type> memberType = deepCopy( expr->member->get_type() );
-	ast::TypeSubstitution sub = genericSubstitution( objectType );
-	sub.apply( memberType );
-
-	// Not all members of a polymorphic type are themselves of a polymorphic
-	// type; in this cas the member expression should be wrapped and
-	// dereferenced to form an lvalue.
-	if ( !isPolyType( memberType, scopeTypeVars ) ) {
-		auto ptrCastExpr = new ast::CastExpr( expr->location, newMemberExpr,
-			new ast::PointerType( memberType ) );
-		auto derefExpr = ast::UntypedExpr::createDeref( expr->location,
-			ptrCastExpr );
-		newMemberExpr = derefExpr;
-	}
-
-	return newMemberExpr;
-}
-
-void PolyGenericCalculator::previsit( ast::AddressExpr const * expr ) {
-	GuardValue( addrMember ) = expr->arg.as<ast::MemberExpr>();
-}
-
-ast::Expr const * PolyGenericCalculator::postvisit(
-		ast::AddressExpr const * expr ) {
-	// arg has to have been a MemberExpr and has been mutated.
-	if ( nullptr == addrMember || expr->arg == addrMember ) {
-		return expr;
-	}
-	ast::UntypedExpr const * untyped = expr->arg.as<ast::UntypedExpr>();
-	if ( !untyped || getFunctionName( untyped ) != "?+?" ) {
-		return expr;
-	}
-	// MemberExpr was converted to pointer + offset; and it is not valid C to
-	// take the address of an addition, so strip away the address-of.
-	// It also preserves the env value.
-	return ast::mutate_field( expr->arg.get(), &ast::Expr::env, expr->env );
-}
-
-ast::Expr const * PolyGenericCalculator::postvisit(
-		ast::SizeofExpr const * expr ) {
-	ast::Type const * type = expr->type ? expr->type : expr->expr->result;
-	ast::Expr const * gen = genSizeof( expr->location, type );
-	return ( gen ) ? gen : expr;
-}
-
-ast::Expr const * PolyGenericCalculator::postvisit(
-		ast::AlignofExpr const * expr ) {
-	ast::Type const * type = expr->type ? expr->type : expr->expr->result;
-	if ( findGeneric( expr->location, type ) ) {
-		return new ast::NameExpr( expr->location,
-			alignofName( Mangle::mangleType( type ) ) );
-	} else {
-		return expr;
-	}
-}
-
-ast::Expr const * PolyGenericCalculator::postvisit(
-		ast::OffsetofExpr const * expr ) {
-	ast::Type const * type = expr->type;
-	if ( !findGeneric( expr->location, type ) ) return expr;
-
-	// Structures replace offsetof expression with an index into offset array.
-	if ( auto structType = dynamic_cast<ast::StructInstType const *>( type ) ) {
-		long offsetIndex = findMember( expr->member, structType->base->members );
-		if ( -1 == offsetIndex ) return expr;
-
-		return makeOffsetIndex( expr->location, type, offsetIndex );
-	// All union members are at offset zero.
-	} else if ( dynamic_cast<ast::UnionInstType const *>( type ) ) {
-		return ast::ConstantExpr::from_ulong( expr->location, 0 );
-	} else {
-		return expr;
-	}
-}
-
-ast::Expr const * PolyGenericCalculator::postvisit(
-		ast::OffsetPackExpr const * expr ) {
-	ast::StructInstType const * type = expr->type;
-
-	// Pull offset back from generated type information.
-	if ( findGeneric( expr->location, type ) ) {
-		return new ast::NameExpr( expr->location,
-			offsetofName( Mangle::mangleType( type ) ) );
-	}
-
-	std::string offsetName = offsetofName( Mangle::mangleType( type ) );
-	// Use the already generated offsets for this type.
-	if ( knownOffsets.contains( offsetName ) ) {
-		return new ast::NameExpr( expr->location, offsetName );
-	}
-
-	knownOffsets.insert( offsetName );
-
-	auto baseMembers = type->base->members;
-	ast::Type const * offsetType = new ast::BasicType(
-		ast::BasicType::LongUnsignedInt );
-
-	// Build initializer list for offset array.
-	ast::vector<ast::Init> inits;
-	for ( ast::ptr<ast::Decl> & member : baseMembers ) {
-		auto memberDecl = member.as<ast::DeclWithType>();
-		assertf( memberDecl, "Requesting offset of non-DWT member: %s",
-			toCString( member ) );
-		inits.push_back( new ast::SingleInit( expr->location,
-			new ast::OffsetofExpr( expr->location,
-				deepCopy( type ),
-				memberDecl
-			)
-		) );
-	}
-
-	auto offsetArray = makeVar( expr->location, offsetName,
-		new ast::ArrayType(
-			offsetType,
-			ast::ConstantExpr::from_ulong( expr->location, baseMembers.size() ),
-			ast::FixedLen,
-			ast::DynamicDim
-		),
-		new ast::ListInit( expr->location, std::move( inits ) )
-	);
-
-	return new ast::VariableExpr( expr->location, offsetArray );
-}
-
-void PolyGenericCalculator::beginScope() {
-	knownLayouts.beginScope();
-	knownOffsets.beginScope();
-}
-
-void PolyGenericCalculator::endScope() {
-	knownOffsets.endScope();
-	knownLayouts.endScope();
-}
-
-ast::ObjectDecl * PolyGenericCalculator::makeVar(
-		CodeLocation const & location, std::string const & name,
-		ast::Type const * type, ast::Init const * init ) {
-	ast::ObjectDecl * ret = new ast::ObjectDecl( location, name, type, init );
-	stmtsToAddBefore.push_back( new ast::DeclStmt( location, ret ) );
-	return ret;
-}
-
-/// Returns true if any of the otype parameters have a dynamic layout; and
-/// puts all otype parameters in the output list.
-bool findGenericParams(
-		ast::vector<ast::Type> & out,
-		ast::vector<ast::TypeDecl> const & baseParams,
-		ast::vector<ast::Expr> const & typeParams ) {
-	bool hasDynamicLayout = false;
-
-	for ( auto pair : group_iterate( baseParams, typeParams ) ) {
-		auto baseParam = std::get<0>( pair );
-		auto typeParam = std::get<1>( pair );
-		if ( !baseParam->isComplete() ) continue;
-		ast::TypeExpr const * typeExpr = typeParam.as<ast::TypeExpr>();
-		assertf( typeExpr, "All type parameters should be type expressions." );
-
-		ast::Type const * type = typeExpr->type.get();
-		out.push_back( type );
-		if ( isPolyType( type ) ) hasDynamicLayout = true;
-	}
-
-	return hasDynamicLayout;
-}
-
-bool PolyGenericCalculator::findGeneric(
-		CodeLocation const & location, ast::Type const * type ) {
-	type = replaceTypeInst( type, typeSubs );
-
-	if ( auto inst = dynamic_cast<ast::TypeInstType const *>( type ) ) {
-		// Assumes that getting put in the scopeTypeVars includes having the
-		// layout variables set.
-		if ( scopeTypeVars.contains( *inst ) ) {
-			return true;
-		}
-	} else if ( auto inst = dynamic_cast<ast::StructInstType const *>( type ) ) {
-		// Check if this type already has a layout generated for it.
-		std::string typeName = Mangle::mangleType( type );
-		if ( knownLayouts.contains( typeName ) ) return true;
-
-		// Check if any type parameters have dynamic layout;
-		// If none do, this type is (or will be) monomorphized.
-		ast::vector<ast::Type> sizedParams;
-		if ( !findGenericParams( sizedParams,
-				inst->base->params, inst->params ) ) {
-			return false;
-		}
-
-		// Insert local variables for layout and generate call to layout
-		// function.
-		// Done early so as not to interfere with the later addition of
-		// parameters to the layout call.
-		knownLayouts.insert( typeName );
-		ast::Type const * layoutType = makeSizeAlignType();
-
-		int memberCount = inst->base->members.size();
-		if ( 0 == memberCount ) {
-			// All empty structures have the same layout (size 1, align 1).
-			makeVar( location,
-				sizeofName( typeName ), layoutType,
-				new ast::SingleInit( location,
-						ast::ConstantExpr::from_ulong( location, 1 ) ) );
-			makeVar( location,
-				alignofName( typeName ), ast::deepCopy( layoutType ),
-				new ast::SingleInit( location,
-						ast::ConstantExpr::from_ulong( location, 1 ) ) );
-			// Since 0-length arrays are forbidden in C, skip the offset array.
-		} else {
-			ast::ObjectDecl const * sizeofVar = makeVar( location,
-				sizeofName( typeName ), deepCopy( layoutType ), nullptr );
-			ast::ObjectDecl const * alignofVar = makeVar( location,
-				alignofName( typeName ), deepCopy( layoutType ), nullptr );
-			ast::ObjectDecl const * offsetofVar = makeVar( location,
-				offsetofName( typeName ),
-				new ast::ArrayType(
-					layoutType,
-					ast::ConstantExpr::from_int( location, memberCount ),
-					ast::FixedLen,
-					ast::DynamicDim
-				),
-				nullptr
-			);
-
-			// Generate call to layout function.
-			ast::UntypedExpr * layoutCall = new ast::UntypedExpr( location,
-				new ast::NameExpr( location, layoutofName( inst->base ) ),
-				{
-					new ast::AddressExpr(
-						new ast::VariableExpr( location, sizeofVar ) ),
-					new ast::AddressExpr(
-						new ast::VariableExpr( location, alignofVar ) ),
-					new ast::VariableExpr( location, offsetofVar ),
-				} );
-
-			addSTypeParamsToLayoutCall( layoutCall, sizedParams );
-
-			stmtsToAddBefore.emplace_back(
-				new ast::ExprStmt( location, layoutCall ) );
-		}
-
-		return true;
-	} else if ( auto inst = dynamic_cast<ast::UnionInstType const *>( type ) ) {
-		// Check if this type already has a layout generated for it.
-		std::string typeName = Mangle::mangleType( type );
-		if ( knownLayouts.contains( typeName ) ) return true;
-
-		// Check if any type parameters have dynamic layout;
-		// If none do, this type is (or will be) monomorphized.
-		ast::vector<ast::Type> sizedParams;
-		if ( !findGenericParams( sizedParams,
-				inst->base->params, inst->params ) ) {
-			return false;
-		}
-
-		// Insert local variables for layout and generate call to layout
-		// function.
-		// Done early so as not to interfere with the later addition of
-		// parameters to the layout call.
-		knownLayouts.insert( typeName );
-		ast::Type const * layoutType = makeSizeAlignType();
-
-		ast::ObjectDecl * sizeofVar = makeVar( location,
-			sizeofName( typeName ), layoutType );
-		ast::ObjectDecl * alignofVar = makeVar( location,
-			alignofName( typeName ), ast::deepCopy( layoutType ) );
-
-		ast::UntypedExpr * layoutCall = new ast::UntypedExpr( location,
-			new ast::NameExpr( location, layoutofName( inst->base ) ),
-			{
-				new ast::AddressExpr(
-					new ast::VariableExpr( location, sizeofVar ) ),
-				new ast::AddressExpr(
-					new ast::VariableExpr( location, alignofVar ) ),
-			} );
-
-		addSTypeParamsToLayoutCall( layoutCall, sizedParams );
-
-		stmtsToAddBefore.emplace_back(
-			new ast::ExprStmt( location, layoutCall ) );
-
-		return true;
-	}
-	return false;
-}
-
-void PolyGenericCalculator::addSTypeParamsToLayoutCall(
-		ast::UntypedExpr * layoutCall,
-		const ast::vector<ast::Type> & otypeParams ) {
-	CodeLocation const & location = layoutCall->location;
-	ast::vector<ast::Expr> & args = layoutCall->args;
-	for ( ast::ptr<ast::Type> const & param : otypeParams ) {
-		if ( findGeneric( location, param ) ) {
-			// Push size/align vars for a generic parameter back.
-			std::string paramName = Mangle::mangleType( param );
-			args.emplace_back(
-				new ast::NameExpr( location, sizeofName( paramName ) ) );
-			args.emplace_back(
-				new ast::NameExpr( location, alignofName( paramName ) ) );
-		} else {
-			args.emplace_back(
-				new ast::SizeofExpr( location, ast::deepCopy( param ) ) );
-			args.emplace_back(
-				new ast::AlignofExpr( location, ast::deepCopy( param ) ) );
-		}
-	}
-}
-
-void PolyGenericCalculator::mutateMembers( ast::AggregateDecl * aggr ) {
-	std::set<std::string> genericParams;
-	for ( ast::ptr<ast::TypeDecl> const & decl : aggr->params ) {
-		genericParams.insert( decl->name );
-	}
-	for ( ast::ptr<ast::Decl> & decl : aggr->members ) {
-		auto field = decl.as<ast::ObjectDecl>();
-		if ( nullptr == field ) continue;
-
-		ast::Type const * type = replaceTypeInst( field->type, typeSubs );
-		auto typeInst = dynamic_cast<ast::TypeInstType const *>( type );
-		if ( nullptr == typeInst ) continue;
-
-		// Do not try to monoporphize generic parameters.
-		if ( scopeTypeVars.contains( ast::TypeEnvKey( *typeInst ) ) &&
-				!genericParams.count( typeInst->name ) ) {
-			// Polymorphic aggregate members should be converted into
-			// monomorphic members. Using char[size_T] here respects
-			// the expected sizing rules of an aggregate type.
-			decl = ast::mutate_field( field, &ast::ObjectDecl::type,
-				polyToMonoType( field->location, field->type ) );
-		}
-	}
-}
-
-ast::Expr const * PolyGenericCalculator::genSizeof(
-		CodeLocation const & location, ast::Type const * type ) {
-	if ( auto * array = dynamic_cast<ast::ArrayType const *>( type ) ) {
-		// Generate calculated size for possibly generic array.
-		ast::Expr const * sizeofBase = genSizeof( location, array->base );
-		if ( nullptr == sizeofBase ) return nullptr;
-		ast::Expr const * dim = array->dimension;
-		return makeOp( location, "?*?", sizeofBase, dim );
-	} else if ( findGeneric( location, type ) ) {
-		// Generate calculated size for generic type.
-		return new ast::NameExpr( location, sizeofName(
-				Mangle::mangleType( type ) ) );
-	} else {
-		return nullptr;
-	}
-}
-
-void PolyGenericCalculator::beginTypeScope( ast::Type const * type ) {
-	GuardScope( scopeTypeVars );
-	makeTypeVarMap( type, scopeTypeVars );
-}
-
-// --------------------------------------------------------------------------
-/// Removes unneeded or incorrect type information.
-/// * Replaces initialization of polymorphic values with alloca.
-/// * Replaces declaration of dtype/ftype with appropriate void expression.
-/// * Replaces sizeof expressions of polymorphic types with a variable.
-/// * Strips fields from generic structure declarations.
-struct Eraser final :
-		public ast::WithGuards {
-	void guardTypeVarMap( ast::Type const * type ) {
-		GuardScope( scopeTypeVars );
-		makeTypeVarMap( type, scopeTypeVars );
-	}
-
-	ast::ObjectDecl const * previsit( ast::ObjectDecl const * decl );
-	ast::FunctionDecl const * previsit( ast::FunctionDecl const * decl );
-	ast::FunctionDecl const * postvisit( ast::FunctionDecl const * decl );
-	ast::TypedefDecl const * previsit( ast::TypedefDecl const * decl );
-	ast::StructDecl const * previsit( ast::StructDecl const * decl );
-	ast::UnionDecl const * previsit( ast::UnionDecl const * decl );
-	void previsit( ast::TypeDecl const * decl );
-	void previsit( ast::PointerType const * type );
-	void previsit( ast::FunctionType const * type );
-public:
-	TypeVarMap scopeTypeVars;
-};
-
-ast::ObjectDecl const * Eraser::previsit( ast::ObjectDecl const * decl ) {
-	guardTypeVarMap( decl->type );
-	return scrubAllTypeVars( decl );
-}
-
-ast::FunctionDecl const * Eraser::previsit( ast::FunctionDecl const * decl ) {
-	guardTypeVarMap( decl->type );
-	return scrubAllTypeVars( decl );
-}
-
-ast::FunctionDecl const * Eraser::postvisit( ast::FunctionDecl const * decl ) {
-	if ( decl->type_params.empty() ) return decl;
-	auto mutDecl = mutate( decl );
-	mutDecl->type_params.clear();
-	return mutDecl;
-}
-
-ast::TypedefDecl const * Eraser::previsit( ast::TypedefDecl const * decl ) {
-	guardTypeVarMap( decl->base );
-	return scrubAllTypeVars( decl );
-}
-
-/// Strips the members from a generic aggregate.
-template<typename node_t>
-node_t const * stripGenericMembers( node_t const * decl ) {
-	if ( decl->params.empty() ) return decl;
-	auto mutDecl = ast::mutate( decl );
-	mutDecl->members.clear();
-	return mutDecl;
-}
-
-ast::StructDecl const * Eraser::previsit( ast::StructDecl const * decl ) {
-	return stripGenericMembers( decl );
-}
-
-ast::UnionDecl const * Eraser::previsit( ast::UnionDecl const * decl ) {
-	return stripGenericMembers( decl );
-}
-
-void Eraser::previsit( ast::TypeDecl const * decl ) {
-	addToTypeVarMap( decl, scopeTypeVars );
-}
-
-void Eraser::previsit( ast::PointerType const * type ) {
-	guardTypeVarMap( type );
-}
-
-void Eraser::previsit( ast::FunctionType const * type ) {
-	guardTypeVarMap( type );
-}
-
-} // namespace
-
-// --------------------------------------------------------------------------
-void box( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<LayoutFunctionBuilder>::run( translationUnit );
-	ast::Pass<CallAdapter>::run( translationUnit );
-	ast::Pass<DeclAdapter>::run( translationUnit );
-	ast::Pass<RewireAdapters>::run( translationUnit );
-	ast::Pass<PolyGenericCalculator>::run( translationUnit );
-	ast::Pass<Eraser>::run( translationUnit );
-}
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/InstantiateGeneric.cpp
===================================================================
--- src/GenPoly/InstantiateGeneric.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/GenPoly/InstantiateGeneric.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,735 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// InstantiateGeneric.cpp -- Create concrete instances of generic types.
+//
+// Author           : Andrew Beach
+// Created On       : Tue Aug 16 10:51:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Oct 31 16:48:00 2022
+// Update Count     : 1
+//
+
+#include "InstantiateGeneric.h"
+
+#include <cassert>                     // for assertf, assert
+#include <set>                         // for set
+#include <utility>                     // for move, pair
+#include <vector>                      // for vector
+
+#include "AST/Copy.hpp"                // for deepCopy
+#include "AST/Create.hpp"              // for asForward
+#include "AST/Inspect.hpp"             // for getFunction
+#include "AST/Pass.hpp"                // for Pass, WithGuard, WithShortCi...
+#include "AST/TranslationUnit.hpp"     // for TranslationUnit
+#include "AST/Vector.hpp"              // for vector
+#include "CodeGen/OperatorTable.h"     // for isAssignment
+#include "Common/ScopedMap.h"          // for ScopedMap
+#include "Common/UniqueName.h"         // for UniqueName
+#include "GenPoly/GenPoly.h"           // for isPolyType, typesPolyCompatible
+#include "GenPoly/ScrubTyVars.h"       // for scrubAll
+#include "ResolvExpr/AdjustExprType.hpp"  // for adjustExprType
+#include "ResolvExpr/Unify.h"          // for typesCompatible
+
+namespace GenPoly {
+
+namespace {
+
+// Utilities:
+
+using type_vector = ast::vector< ast::TypeExpr >;
+
+/// Abstracts type equality for a list of parameter types.
+struct TypeList {
+	TypeList() : params() {}
+	TypeList( ast::vector< ast::Type > const & params ) :
+		params( params ) {}
+	TypeList( ast::vector< ast::Type > && params ) :
+		params( std::move( params ) ) {}
+	TypeList( TypeList const & that ) : params( that.params ) {}
+	TypeList( TypeList && that ) : params( std::move( that.params ) ) {}
+
+	TypeList( ast::vector< ast::TypeExpr > const & exprs ) :
+			params() {
+		for ( auto expr : exprs ) {
+			params.emplace_back( ast::deepCopy( expr->type ) );
+		}
+	}
+
+	TypeList & operator=( TypeList const & that ) {
+		params = that.params;
+		return *this;
+	}
+
+	TypeList & operator=( TypeList && that ) {
+		params = std::move( that.params );
+		return *this;
+	}
+
+	bool operator==( TypeList const & that ) const {
+		if ( params.size() != that.params.size() ) {
+			return false;
+		}
+
+		for ( auto it = params.begin(), jt = that.params.begin() ;
+				it != params.end() ; ++it, ++jt ) {
+			if ( !typesPolyCompatible( it->get(), jt->get() ) ) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	ast::vector<ast::Type> params;
+};
+
+/// Maps a key and a TypeList to a valuue. Also supports scoping.
+class InstantiationMap final {
+	/// Wraps value for a specific (AggregateDecl, TypeList) combination.
+	using Instantiation = std::pair<TypeList, ast::ptr<ast::AggregateDecl>>;
+	/// List of TypeLists paired with the appropriate values.
+	using ValueList = std::vector<Instantiation>;
+	/// Underlying map type; maps keys to a linear list of corresponding
+	/// TypeLists and values.
+	using InnerMap = ScopedMap<ast::ptr<ast::AggregateDecl>, ValueList>;
+
+	InnerMap data;
+public:
+	void beginScope() { data.beginScope(); }
+	void endScope() { data.endScope(); }
+
+	/// Gets the value for the (declaration, type list) pair,
+	/// returns null if no such value exists.
+	ast::AggregateDecl const * lookup(
+			ast::AggregateDecl const * key,
+			type_vector const & params ) const {
+		// This type repackaging is used for the helpers.
+		ast::ptr<ast::AggregateDecl> ptr = key;
+		TypeList typeList( params );
+
+		// Scan scopes for matches to the key.
+		for ( auto insts = data.find( key ) ;
+				insts != data.end() ; insts = data.findNext( insts, ptr )) {
+			for ( auto inst = insts->second.rbegin() ;
+					inst != insts->second.rend() ; ++inst ) {
+				if ( inst->first == typeList ) {
+					return inst->second;
+				}
+			}
+		}
+		return nullptr;
+	}
+
+	/// Adds a value for a (key, type list) pair to the current scope.
+	void insert( ast::AggregateDecl const * key, type_vector const & params,
+			ast::AggregateDecl const * value ) {
+		auto it = data.findAt( data.currentScope(), key );
+		if ( it == data.end() ) {
+			data.insert( key, ValueList( 1,
+				Instantiation( TypeList( params ), value ) ) );
+		} else {
+			it->second.emplace_back(
+				Instantiation( TypeList( params ), value ) );
+		}
+	}
+};
+
+/// Possible options for a given specialization of a generic type.
+enum class GenericType {
+	/// Concrete instatiation based solely on {d,f}type-to-void conversions.
+	dtypeStatic,
+	/// Concrete instatiation requiring at least one parameters type.
+	concrete,
+	/// No concrete instantiation.
+	dynamic
+};
+
+GenericType & operator|=( GenericType & gt, const GenericType & ht ) {
+	if ( gt < ht ) gt = ht;
+	return gt;
+}
+
+bool isDtypeStatic( ast::vector<ast::TypeDecl> const & baseParams ) {
+	return std::all_of( baseParams.begin(), baseParams.end(),
+		[]( ast::TypeDecl const * td ){ return !td->isComplete(); }
+	);
+}
+
+/// Makes substitutions of params into baseParams; returns dtypeStatic if
+/// there is a concrete instantiation based only on {d,f}type-to-void
+/// conversions, concrete if there is a concrete instantiation requiring at
+/// least one parameter type, and dynamic if there is no concrete instantiation.
+GenericType makeSubstitutions(
+		ast::vector<ast::TypeExpr> & out,
+		ast::vector<ast::TypeDecl> const & baseParams,
+		ast::vector<ast::Expr> const & params ) {
+	GenericType gt = GenericType::dtypeStatic;
+
+	// Substitute concrete types for given parameters,
+	// using incomplete types for placeholders.
+	auto baseParam = baseParams.begin();
+	auto param = params.begin();
+	for ( ; baseParam != baseParams.end() && param != params.end() ;
+			++baseParam, ++param ) {
+		ast::TypeExpr const * paramExpr = param->as<ast::TypeExpr>();
+		assertf( paramExpr, "Aggregate parameters should be type expressions." );
+
+		if ( (*baseParam)->isComplete() ) {
+			// Substitute parameter for complete (otype or sized dtype) type.
+			if ( isPolyType( paramExpr->type ) ) {
+				// Substitute polymorphic parameter type in to generic type.
+				out.push_back( ast::deepCopy( paramExpr ) );
+				gt = GenericType::dynamic;
+			} else {
+				// Normalize possibly dtype-static parameter type.
+				out.emplace_back( new ast::TypeExpr( paramExpr->location,
+					scrubAllTypeVars( ast::deepCopy( paramExpr->type ) ) ) );
+				gt |= GenericType::concrete;
+			}
+		} else switch ( (*baseParam)->kind ) {
+		case ast::TypeDecl::Dtype:
+			// Here, pretend that any incomplete dtype is `void`.
+			out.emplace_back( new ast::TypeExpr( paramExpr->location,
+				new ast::VoidType() ) );
+			break;
+		case ast::TypeDecl::Ftype:
+			// Here, pretend that any ftype is `void (*)(void)`.
+			out.emplace_back( new ast::TypeExpr( paramExpr->location,
+				new ast::FunctionType() ) );
+			break;
+		case ast::TypeDecl::Ttype:
+			assertf( false, "Ttype parameters are not currently allowed as parameters to generic types." );
+			break;
+		default:
+			assertf( false, "Unhandled type parameter kind" );
+			break;
+		}
+	}
+
+	assertf( baseParam == baseParams.end(), "Base Parameters not exausted." );
+	assertf( param == params.end(), "Type parameters not exausted." );
+	return gt;
+}
+
+/// Substitutes types of members according to baseParams => typeSubs,
+/// returning the result in a new vector.
+ast::vector<ast::Decl> substituteMembers(
+		ast::vector<ast::Decl> const & members,
+		ast::vector<ast::TypeDecl> const & baseParams,
+		ast::vector<ast::TypeExpr> const & typeSubs ) {
+	ast::vector<ast::Decl> out;
+	ast::TypeSubstitution subs( baseParams, typeSubs );
+	for ( ast::ptr<ast::Decl> const & member : members ) {
+		// Create a manual copy to avoid in-place mutation.
+		// If being a PureVisitor is decided to be part of apply's interface,
+		// then we can actually skip this step as it will never mutate in-
+		// place. (Then we don't need the extra guard to free temp value.)
+		ast::ptr<ast::Decl> copy = ast::deepCopy( member.get() );
+		auto result = subs.apply( copy.get() );
+		out.emplace_back( result.node );
+	}
+	return out;
+}
+
+/// Substitutes types of members according to baseParams => typeSubs,
+/// modifying them in-place.
+void substituteMembersHere(
+		ast::vector<ast::Decl> & members,
+		ast::vector<ast::TypeDecl> const & baseParams,
+		ast::vector<ast::TypeExpr> const & typeSubs ) {
+	ast::TypeSubstitution subs( baseParams, typeSubs );
+	for ( ast::ptr<ast::Decl> & member : members ) {
+		// The member must be mutated in place to avoid breaking
+		assert( member->unique() );
+
+		auto field = member.strict_as<ast::ObjectDecl>();
+		auto result = subs.apply( field->type.get() );
+		auto copy = ast::mutate_field(
+			field, &ast::ObjectDecl::type, result.node );
+
+		// I'm not kidding, it is very important.
+		assert( copy == member.get() );
+	}
+}
+
+/// Strips the instances' type parameters.
+void stripInstParams( ast::BaseInstType * inst ) {
+	inst->params.clear();
+}
+
+bool isGenericType( ast::Type const * type ) {
+	if ( auto inst = dynamic_cast<ast::StructInstType const *>( type ) ) {
+		return !inst->params.empty();
+	} else if ( auto inst = dynamic_cast<ast::UnionInstType const *>( type ) ) {
+		return !inst->params.empty();
+	} else {
+		return false;
+	}
+}
+
+// The Passes:
+
+struct FixDtypeStatic final :
+		public ast::WithGuards,
+		public ast::WithVisitorRef<FixDtypeStatic>,
+		public ast::WithShortCircuiting,
+		public ast::WithStmtsToAdd<> {
+	ast::ApplicationExpr const * previsit( ast::ApplicationExpr const * expr );
+	void previsit( ast::AddressExpr const * expr );
+
+	ast::Expr const * postvisit( ast::MemberExpr const * expr );
+private:
+	template<typename Aggr>
+	ast::Expr const * fixMemberExpr(
+		Aggr const * inst, ast::MemberExpr const * memberExpr );
+
+	ast::Expr const * fixMemberExpr(
+		ast::vector<ast::TypeDecl> const & baseParams,
+		ast::MemberExpr const * memberExpr );
+
+	bool isLValueArg = false;
+};
+
+ast::ApplicationExpr const * FixDtypeStatic::previsit(
+		ast::ApplicationExpr const * expr ) {
+	GuardValue( isLValueArg ) = false;
+	ast::Decl const * function = ast::getFunction( expr );
+	if ( ast::Linkage::Intrinsic != function->linkage
+			|| !CodeGen::isAssignment( function->name ) ) {
+		return expr;
+	}
+
+	// Explicity visit children because only the first element must be a
+	// C lvalue (normally, it can only send info to both or neither).
+	visit_children = false;
+	expr = mutate_field( expr, &ast::ApplicationExpr::env,
+		maybe_accept( expr->env.get(), *visitor ) );
+	expr = mutate_field( expr, &ast::ApplicationExpr::result,
+		maybe_accept( expr->result.get(), *visitor ) );
+	expr = mutate_field( expr, &ast::ApplicationExpr::func,
+		maybe_accept( expr->func.get(), *visitor ) );
+	isLValueArg = true;
+	for ( unsigned i = 0; i < expr->args.size(); ++i ) {
+		ast::Expr const * newExpr = expr->args[i]->accept( *visitor );
+		// This is declared here for lifetime reasons.
+		ast::ptr<ast::CastExpr> cast;
+		if ( newExpr != expr->args[i].get() &&
+				(cast = dynamic_cast<ast::CastExpr const *>( newExpr )) ) {
+			newExpr = cast->arg.get();
+		}
+		expr = mutate_field_index( expr, &ast::ApplicationExpr::args,
+			i, newExpr );
+		isLValueArg = false;
+	}
+	return expr;
+}
+
+void FixDtypeStatic::previsit( ast::AddressExpr const * ) {
+	// The argument of an address expression (`&`) must be a C lvalue.
+	GuardValue( isLValueArg ) = true;
+}
+
+ast::Expr const * FixDtypeStatic::postvisit( ast::MemberExpr const * expr ) {
+	ast::ptr<ast::Type> const & type = expr->aggregate->result;
+	if ( auto inst = type.as<ast::StructInstType>() ) {
+		if ( !inst->params.empty() ) return fixMemberExpr( inst, expr );
+	} else if ( auto inst = type.as<ast::UnionInstType>() ) {
+		if ( !inst->params.empty() ) return fixMemberExpr( inst, expr );
+	}
+	return expr;
+}
+
+template<typename Aggr>
+ast::Expr const * FixDtypeStatic::fixMemberExpr(
+		Aggr const * inst, ast::MemberExpr const * memberExpr ) {
+	return fixMemberExpr( inst->base->params, memberExpr );
+}
+
+ast::Expr const * FixDtypeStatic::fixMemberExpr(
+		ast::vector<ast::TypeDecl> const & baseParams,
+		ast::MemberExpr const * memberExpr ) {
+	// Need to cast dtype-static member expressions to their actual type
+	// before the actual type type is erased.
+	// NOTE: The casts here have the third argument (isGenerated) set to
+	// ExplicitCast so that they casts persist until Box, where they are needed.
+
+	if ( !isDtypeStatic( baseParams ) ||
+			ResolvExpr::typesCompatible(
+				memberExpr->result,
+				memberExpr->member->get_type() ) ) {
+		return memberExpr;
+	}
+
+	// Type of member and type of expression differ.
+	ast::Type const * concType = ast::deepCopy( memberExpr->result );
+	CodeLocation const & location = memberExpr->location;
+	if ( isLValueArg ) {
+		// The result must be a C lvalue expression. So make a new reference
+		// variable with the correct actual type to replace the
+		// member expression.
+		//   forall(T &)
+		//   struct Ptr {
+		//     T * x;
+		//   };
+		//   Ptr(int) p;
+		//   int i;
+		// The original expression:
+		//   p.x = &i;
+		// Becomes the expression/declaration:
+		//   int *& _dtype_static_member_0;
+		//   (_dtype_static_member_0 = (int**)&p.x,
+		//    _dtype_static_member_0) = &i;
+
+		// The declaration is simple:
+		static UniqueName tmpNamer( "_dtype_static_member_" );
+		ast::ObjectDecl * tmp = new ast::ObjectDecl( location,
+			tmpNamer.newName(),
+			new ast::ReferenceType( concType ),
+			nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::C
+		);
+		stmtsToAddBefore.push_back( new ast::DeclStmt( location, tmp ) );
+
+		// The expression is more complex, uses references and reference /
+		// pointer parity. But breaking it up risks reordering.
+		return new ast::CommaExpr( location,
+			ast::UntypedExpr::createAssign( location,
+				new ast::VariableExpr( location, tmp ),
+				new ast::CastExpr( location,
+					new ast::AddressExpr( location, memberExpr ),
+					new ast::PointerType( ast::deepCopy( concType ) ),
+					ast::ExplicitCast
+				)
+			),
+			new ast::VariableExpr( location, tmp )
+		);
+	} else {
+		// Here, it can simply add a cast to actual types.
+		return new ast::CastExpr( location,
+			memberExpr,
+			concType,
+			ast::ExplicitCast
+		);
+	}
+}
+
+struct GenericInstantiator final :
+		public ast::WithCodeLocation,
+		public ast::WithConstTypeSubstitution,
+		public ast::WithDeclsToAdd<>,
+		public ast::WithGuards,
+		public ast::WithVisitorRef<GenericInstantiator>
+{
+	/// Map of (generic type, parameter list) pairs
+	/// to concrete type instantiations.
+	InstantiationMap instantiations;
+	/// Set of types which are dtype-only generic
+	/// (and therefore have static layout).
+	std::set<ast::AggregateDecl const *> dtypeStatics;
+	/// Namer for concrete types.
+	UniqueName typeNamer;
+	/// Should not make use of type environment to replace types of function
+	/// parameter and return values.
+	bool inFunctionType = false;
+	/// Index of current member, used to recreate MemberExprs with the
+	/// member from an instantiation.
+	int memberIndex = -1;
+
+	GenericInstantiator() :
+		instantiations(), dtypeStatics(), typeNamer("_conc_") {}
+
+	ast::Type const * postvisit( ast::StructInstType const * inst );
+	ast::Type const * postvisit( ast::UnionInstType const * inst );
+
+	void previsit( ast::MemberExpr const * expr );
+	ast::Expr const * postvisit( ast::MemberExpr const * expr );
+	ast::Expr const * postvisit( ast::Expr const * expr );
+	ast::Designation const * postvisit( ast::Designation const * );
+
+	void previsit( ast::ParseNode const * node ) {
+		GuardValue( location ) = &node->location;
+	}
+	void previsit( ast::FunctionType const * ) {
+		GuardValue( inFunctionType ) = true;
+	}
+	void beginScope() {
+		instantiations.beginScope();
+	}
+	void endScope() {
+		instantiations.endScope();
+	}
+private:
+	/// Wrap instantiation lookup for structures.
+	ast::StructDecl const * lookup(
+		ast::StructInstType const * inst, type_vector const & typeSubs ) const;
+	/// Wrap instantiation lookup for unions.
+	ast::UnionDecl const * lookup(
+		ast::UnionInstType const * inst, type_vector const & typeSubs ) const;
+	/// Wrap instantiation insertion for structures.
+	void insert( ast::StructInstType const * inst,
+		type_vector const & typeSubs, ast::StructDecl const * decl );
+	/// Wrap instantiation insertion for unions.
+	void insert( ast::UnionInstType const * inst,
+		type_vector const & typeSubs, ast::UnionDecl const * decl );
+
+	void replaceParametersWithConcrete( ast::vector<ast::Expr> & params );
+	ast::Type const * replaceWithConcrete( ast::Type const * type, bool doClone );
+
+	template<typename AggrDecl>
+	ast::Type const * fixInstType( ast::SueInstType<AggrDecl> const * inst );
+
+	/// Strips a dtype-static aggregate decl of its type parameters,
+	/// marks it as stripped.
+	void stripDtypeParams( ast::AggregateDecl * base,
+		ast::vector<ast::TypeDecl> & baseParams,
+		ast::vector<ast::TypeExpr> const & typeSubs );
+};
+
+ast::Type const * GenericInstantiator::postvisit(
+		ast::StructInstType const * inst ) {
+	return fixInstType( inst );
+}
+
+ast::Type const * GenericInstantiator::postvisit(
+		ast::UnionInstType const * inst ) {
+	return fixInstType( inst );
+}
+
+template<typename AggrDecl>
+ast::Type const * GenericInstantiator::fixInstType(
+		ast::SueInstType<AggrDecl> const * inst ) {
+	assert( location );
+
+	// There is nothing to mutate if the params are empty.
+	if ( inst->params.empty() ) return inst;
+
+	// Need to replace type variables to ensure that generic types are
+	// instantiated for the return values of polymorphic functions (in
+	// particular, for thunks, because they are not [currently] copy
+	// constructed).
+	// (This used to be run only on structures, but I believe both need it.)
+	inst = strict_dynamic_cast<ast::SueInstType<AggrDecl> const *>(
+		replaceWithConcrete( inst, false ) );
+
+	// Check for an already-instantiatiated dtype-static type.
+	if ( dtypeStatics.find( inst->base ) != dtypeStatics.end() ) {
+		auto mutInst = ast::mutate( inst );
+		stripInstParams( mutInst );
+		return mutInst;
+	}
+
+	// Check if the type can be concretely instantiated;
+	// and put substitutions in typeSubs.
+	assertf( inst->base, "Base data-type has parameters." );
+	ast::vector<ast::TypeExpr> typeSubs;
+	GenericType gt = makeSubstitutions( typeSubs, inst->base->params, inst->params );
+	switch ( gt ) {
+	case GenericType::dtypeStatic:
+	{
+		auto mutInst = ast::mutate( inst );
+		assert( mutInst->base->unique() );
+		auto mutBase = mutInst->base.get_and_mutate();
+		stripDtypeParams( mutBase, mutBase->params, typeSubs );
+		stripInstParams( mutInst );
+		return mutInst;
+	}
+	case GenericType::concrete:
+	{
+		// Make concrete instantiation of generic type.
+		AggrDecl const * concDecl = lookup( inst, typeSubs );
+		if ( !concDecl ) {
+			// Set concDecl to new type, insert type declaration
+			// into statements to add.
+			AggrDecl * newDecl = new AggrDecl( *location,
+				typeNamer.newName( inst->name )
+			);
+			newDecl->body = inst->base->body;
+			newDecl->members = substituteMembers(
+				inst->base->members,
+				inst->base->params,
+				typeSubs
+			);
+
+			// Forward declare before recursion. (TODO: Only when needed, #199.)
+			insert( inst, typeSubs, newDecl );
+			if ( AggrDecl const * forwardDecl = ast::asForward( newDecl ) ) {
+				declsToAddBefore.push_back( forwardDecl );
+			}
+			// Recursively instantiate members:
+			concDecl = strict_dynamic_cast<AggrDecl const *>(
+				newDecl->accept( *visitor ) );
+			// Must occur before declaration is added so
+			// that member instantiation appear first.
+			declsToAddBefore.push_back( concDecl );
+		}
+		return new ast::SueInstType<AggrDecl>( concDecl, inst->qualifiers );
+	}
+	case GenericType::dynamic:
+		// Do nothing.
+	default:
+		// Should never happen.
+		return inst;
+	}
+}
+
+void GenericInstantiator::previsit( ast::MemberExpr const * expr ) {
+	GuardValue( location ) = &expr->location;
+	GuardValue( memberIndex ) = -1;
+	// Only run on expressions where the field being accessed is generic.
+	if ( isGenericType( expr->aggregate->result ) ) {
+		// Find the location of the member:
+		ast::AggregateDecl const * aggr =
+			expr->aggregate->result.strict_as<ast::BaseInstType>()->aggr();
+		ast::vector<ast::Decl> const & members = aggr->members;
+		auto it = std::find( members.begin(), members.end(), expr->member );
+		memberIndex = std::distance( members.begin(), it );
+		assertf( memberIndex < (int)members.size(), "Could not find member %s in generic type %s.", toString( expr->member ).c_str(), toString( expr->aggregate ).c_str() );
+	}
+}
+
+ast::Expr const * GenericInstantiator::postvisit(
+		ast::MemberExpr const * expr ) {
+	if ( memberIndex == -1 ) {
+		return expr;
+	}
+
+	// Using the location from the generic type, find the member
+	// in the instantiation and rebuild the member expression.
+	ast::AggregateDecl const * aggr =
+		expr->aggregate->result.strict_as<ast::BaseInstType>()->aggr();
+	assertf( memberIndex < (int)aggr->members.size(), "Instantiation somehow has fewer members than the generic type." );
+	ast::Decl const * member = *std::next( aggr->members.begin(), memberIndex );
+	assertf( member->name == expr->member->name, "Instantiation has different member order than the generic type. %s / %s", toString( member ).c_str(), toString( expr->member ).c_str() );
+	auto field = strict_dynamic_cast< ast::DeclWithType const * >( member );
+	ast::MemberExpr * ret = new ast::MemberExpr( expr->location,
+		field,
+		ast::deepCopy( expr->aggregate )
+	);
+	// For pointer decay:
+	ret->result = ResolvExpr::adjustExprType(
+		ret->result,
+		ast::TypeEnvironment(),
+		ast::SymbolTable()
+	);
+	ret->env = expr->env;
+	return ret;
+}
+
+ast::Expr const * GenericInstantiator::postvisit( ast::Expr const * expr ) {
+	// We are not modifying env on MemberExpr, but that seems to work.
+	if ( expr->env ) {
+		auto newEnv = expr->env->accept( *visitor );
+		expr = ast::mutate_field( expr, &ast::Expr::env, newEnv );
+	}
+	return expr;
+}
+
+// This attempts to figure out what the final name of the field will be.
+// Pretty printing can cause this to become incorrect.
+std::string getPrintName( ast::DeclWithType const * decl ) {
+	return ( decl->linkage.is_mangled )
+		? decl->scopedMangleName() : decl->name;
+}
+
+ast::Designation const * GenericInstantiator::postvisit(
+		ast::Designation const * designation ) {
+	// Disconnect designator names from their fields.
+	// It is now incorrect to point at the generic definition where the used
+	// type now is replaced with a concrete instance. Ideally, new variable
+	// expressions would point at fields in the concrete instances, but that
+	// is work and that information should not be needed this late in
+	// compilation.
+
+	// Modify all designations, even if not needed.
+	auto mutNode = mutate( designation );
+	for ( ast::ptr<ast::Expr> & designator : mutNode->designators ) {
+		if ( auto var = designator.as<ast::VariableExpr>() ) {
+			designator = new ast::NameExpr(
+				var->location, getPrintName( var->var ) );
+		}
+	}
+	return mutNode;
+}
+
+ast::StructDecl const * GenericInstantiator::lookup(
+		ast::StructInstType const * inst,
+		type_vector const & typeSubs ) const {
+	auto ret = instantiations.lookup( inst->base, typeSubs );
+	return strict_dynamic_cast<ast::StructDecl const *, nullptr>( ret );
+}
+
+ast::UnionDecl const * GenericInstantiator::lookup(
+		ast::UnionInstType const * inst,
+		type_vector const & typeSubs ) const {
+	auto ret = instantiations.lookup( inst->base, typeSubs );
+	return strict_dynamic_cast<ast::UnionDecl const *, nullptr>( ret );
+}
+
+void GenericInstantiator::insert( ast::StructInstType const * inst,
+		type_vector const & typeSubs, ast::StructDecl const * decl ) {
+	instantiations.insert( inst->base, typeSubs, decl );
+}
+
+void GenericInstantiator::insert( ast::UnionInstType const * inst,
+		type_vector const & typeSubs, ast::UnionDecl const * decl ) {
+	instantiations.insert( inst->base, typeSubs, decl );
+}
+
+void GenericInstantiator::replaceParametersWithConcrete(
+		ast::vector<ast::Expr> & params ) {
+	for ( ast::ptr<ast::Expr> & param : params ) {
+		auto paramType = param.as<ast::TypeExpr>();
+		assertf( paramType, "Aggregate parameters should be type expressions." );
+		auto type = replaceWithConcrete( paramType->type, false );
+		param = ast::mutate_field( paramType, &ast::TypeExpr::type, type );
+	}
+}
+
+ast::Type const * GenericInstantiator::replaceWithConcrete(
+		ast::Type const * type, bool doClone ) {
+	if ( auto inst = dynamic_cast<ast::TypeInstType const *>( type ) ) {
+		if ( typeSubs && !inFunctionType ) {
+			ast::Type const * concType = typeSubs->lookup( inst );
+			return ast::deepCopy( ( concType ) ? concType : inst );
+		}
+	} else if ( auto inst = dynamic_cast<ast::StructInstType const *>( type ) ) {
+		auto mut = ( doClone ) ? ast::deepCopy( inst ) : ast::mutate( inst );
+		replaceParametersWithConcrete( mut->params );
+		return mut;
+	} else if ( auto inst = dynamic_cast<ast::UnionInstType const *>( type ) ) {
+		auto mut = ( doClone ) ? ast::deepCopy( inst ) : ast::mutate( inst );
+		replaceParametersWithConcrete( mut->params );
+		return mut;
+	}
+	return type;
+}
+
+void GenericInstantiator::stripDtypeParams(
+		ast::AggregateDecl * base,
+		ast::vector<ast::TypeDecl> & baseParams,
+		ast::vector<ast::TypeExpr> const & typeSubs ) {
+	substituteMembersHere( base->members, baseParams, typeSubs );
+
+	baseParams.clear();
+
+	dtypeStatics.insert( base );
+}
+
+} // namespace
+
+void instantiateGeneric( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<FixDtypeStatic>::run( translationUnit );
+	ast::Pass<GenericInstantiator>::run( translationUnit );
+}
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/GenPoly/InstantiateGenericNew.cpp
===================================================================
--- src/GenPoly/InstantiateGenericNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,735 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// InstantiateGenericNew.cpp -- Create concrete instances of generic types.
-//
-// Author           : Andrew Beach
-// Created On       : Tue Aug 16 10:51:00 2022
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Oct 31 16:48:00 2022
-// Update Count     : 1
-//
-
-#include "InstantiateGeneric.h"
-
-#include <cassert>                     // for assertf, assert
-#include <set>                         // for set
-#include <utility>                     // for move, pair
-#include <vector>                      // for vector
-
-#include "AST/Copy.hpp"                // for deepCopy
-#include "AST/Create.hpp"              // for asForward
-#include "AST/Inspect.hpp"             // for getFunction
-#include "AST/Pass.hpp"                // for Pass, WithGuard, WithShortCi...
-#include "AST/TranslationUnit.hpp"     // for TranslationUnit
-#include "AST/Vector.hpp"              // for vector
-#include "CodeGen/OperatorTable.h"     // for isAssignment
-#include "Common/ScopedMap.h"          // for ScopedMap
-#include "Common/UniqueName.h"         // for UniqueName
-#include "GenPoly/GenPoly.h"           // for isPolyType, typesPolyCompatible
-#include "GenPoly/ScrubTyVars.h"       // for scrubAll
-#include "ResolvExpr/AdjustExprType.hpp"  // for adjustExprType
-#include "ResolvExpr/Unify.h"          // for typesCompatible
-
-namespace GenPoly {
-
-namespace {
-
-// Utilities:
-
-using type_vector = ast::vector< ast::TypeExpr >;
-
-/// Abstracts type equality for a list of parameter types.
-struct TypeList {
-	TypeList() : params() {}
-	TypeList( ast::vector< ast::Type > const & params ) :
-		params( params ) {}
-	TypeList( ast::vector< ast::Type > && params ) :
-		params( std::move( params ) ) {}
-	TypeList( TypeList const & that ) : params( that.params ) {}
-	TypeList( TypeList && that ) : params( std::move( that.params ) ) {}
-
-	TypeList( ast::vector< ast::TypeExpr > const & exprs ) :
-			params() {
-		for ( auto expr : exprs ) {
-			params.emplace_back( ast::deepCopy( expr->type ) );
-		}
-	}
-
-	TypeList & operator=( TypeList const & that ) {
-		params = that.params;
-		return *this;
-	}
-
-	TypeList & operator=( TypeList && that ) {
-		params = std::move( that.params );
-		return *this;
-	}
-
-	bool operator==( TypeList const & that ) const {
-		if ( params.size() != that.params.size() ) {
-			return false;
-		}
-
-		for ( auto it = params.begin(), jt = that.params.begin() ;
-				it != params.end() ; ++it, ++jt ) {
-			if ( !typesPolyCompatible( it->get(), jt->get() ) ) {
-				return false;
-			}
-		}
-		return true;
-	}
-
-	ast::vector<ast::Type> params;
-};
-
-/// Maps a key and a TypeList to a valuue. Also supports scoping.
-class InstantiationMap final {
-	/// Wraps value for a specific (AggregateDecl, TypeList) combination.
-	using Instantiation = std::pair<TypeList, ast::ptr<ast::AggregateDecl>>;
-	/// List of TypeLists paired with the appropriate values.
-	using ValueList = std::vector<Instantiation>;
-	/// Underlying map type; maps keys to a linear list of corresponding
-	/// TypeLists and values.
-	using InnerMap = ScopedMap<ast::ptr<ast::AggregateDecl>, ValueList>;
-
-	InnerMap data;
-public:
-	void beginScope() { data.beginScope(); }
-	void endScope() { data.endScope(); }
-
-	/// Gets the value for the (declaration, type list) pair,
-	/// returns null if no such value exists.
-	ast::AggregateDecl const * lookup(
-			ast::AggregateDecl const * key,
-			type_vector const & params ) const {
-		// This type repackaging is used for the helpers.
-		ast::ptr<ast::AggregateDecl> ptr = key;
-		TypeList typeList( params );
-
-		// Scan scopes for matches to the key.
-		for ( auto insts = data.find( key ) ;
-				insts != data.end() ; insts = data.findNext( insts, ptr )) {
-			for ( auto inst = insts->second.rbegin() ;
-					inst != insts->second.rend() ; ++inst ) {
-				if ( inst->first == typeList ) {
-					return inst->second;
-				}
-			}
-		}
-		return nullptr;
-	}
-
-	/// Adds a value for a (key, type list) pair to the current scope.
-	void insert( ast::AggregateDecl const * key, type_vector const & params,
-			ast::AggregateDecl const * value ) {
-		auto it = data.findAt( data.currentScope(), key );
-		if ( it == data.end() ) {
-			data.insert( key, ValueList( 1,
-				Instantiation( TypeList( params ), value ) ) );
-		} else {
-			it->second.emplace_back(
-				Instantiation( TypeList( params ), value ) );
-		}
-	}
-};
-
-/// Possible options for a given specialization of a generic type.
-enum class GenericType {
-	/// Concrete instatiation based solely on {d,f}type-to-void conversions.
-	dtypeStatic,
-	/// Concrete instatiation requiring at least one parameters type.
-	concrete,
-	/// No concrete instantiation.
-	dynamic
-};
-
-GenericType & operator|=( GenericType & gt, const GenericType & ht ) {
-	if ( gt < ht ) gt = ht;
-	return gt;
-}
-
-bool isDtypeStatic( ast::vector<ast::TypeDecl> const & baseParams ) {
-	return std::all_of( baseParams.begin(), baseParams.end(),
-		[]( ast::TypeDecl const * td ){ return !td->isComplete(); }
-	);
-}
-
-/// Makes substitutions of params into baseParams; returns dtypeStatic if
-/// there is a concrete instantiation based only on {d,f}type-to-void
-/// conversions, concrete if there is a concrete instantiation requiring at
-/// least one parameter type, and dynamic if there is no concrete instantiation.
-GenericType makeSubstitutions(
-		ast::vector<ast::TypeExpr> & out,
-		ast::vector<ast::TypeDecl> const & baseParams,
-		ast::vector<ast::Expr> const & params ) {
-	GenericType gt = GenericType::dtypeStatic;
-
-	// Substitute concrete types for given parameters,
-	// using incomplete types for placeholders.
-	auto baseParam = baseParams.begin();
-	auto param = params.begin();
-	for ( ; baseParam != baseParams.end() && param != params.end() ;
-			++baseParam, ++param ) {
-		ast::TypeExpr const * paramExpr = param->as<ast::TypeExpr>();
-		assertf( paramExpr, "Aggregate parameters should be type expressions." );
-
-		if ( (*baseParam)->isComplete() ) {
-			// Substitute parameter for complete (otype or sized dtype) type.
-			if ( isPolyType( paramExpr->type ) ) {
-				// Substitute polymorphic parameter type in to generic type.
-				out.push_back( ast::deepCopy( paramExpr ) );
-				gt = GenericType::dynamic;
-			} else {
-				// Normalize possibly dtype-static parameter type.
-				out.emplace_back( new ast::TypeExpr( paramExpr->location,
-					scrubAllTypeVars( ast::deepCopy( paramExpr->type ) ) ) );
-				gt |= GenericType::concrete;
-			}
-		} else switch ( (*baseParam)->kind ) {
-		case ast::TypeDecl::Dtype:
-			// Here, pretend that any incomplete dtype is `void`.
-			out.emplace_back( new ast::TypeExpr( paramExpr->location,
-				new ast::VoidType() ) );
-			break;
-		case ast::TypeDecl::Ftype:
-			// Here, pretend that any ftype is `void (*)(void)`.
-			out.emplace_back( new ast::TypeExpr( paramExpr->location,
-				new ast::FunctionType() ) );
-			break;
-		case ast::TypeDecl::Ttype:
-			assertf( false, "Ttype parameters are not currently allowed as parameters to generic types." );
-			break;
-		default:
-			assertf( false, "Unhandled type parameter kind" );
-			break;
-		}
-	}
-
-	assertf( baseParam == baseParams.end(), "Base Parameters not exausted." );
-	assertf( param == params.end(), "Type parameters not exausted." );
-	return gt;
-}
-
-/// Substitutes types of members according to baseParams => typeSubs,
-/// returning the result in a new vector.
-ast::vector<ast::Decl> substituteMembers(
-		ast::vector<ast::Decl> const & members,
-		ast::vector<ast::TypeDecl> const & baseParams,
-		ast::vector<ast::TypeExpr> const & typeSubs ) {
-	ast::vector<ast::Decl> out;
-	ast::TypeSubstitution subs( baseParams, typeSubs );
-	for ( ast::ptr<ast::Decl> const & member : members ) {
-		// Create a manual copy to avoid in-place mutation.
-		// If being a PureVisitor is decided to be part of apply's interface,
-		// then we can actually skip this step as it will never mutate in-
-		// place. (Then we don't need the extra guard to free temp value.)
-		ast::ptr<ast::Decl> copy = ast::deepCopy( member.get() );
-		auto result = subs.apply( copy.get() );
-		out.emplace_back( result.node );
-	}
-	return out;
-}
-
-/// Substitutes types of members according to baseParams => typeSubs,
-/// modifying them in-place.
-void substituteMembersHere(
-		ast::vector<ast::Decl> & members,
-		ast::vector<ast::TypeDecl> const & baseParams,
-		ast::vector<ast::TypeExpr> const & typeSubs ) {
-	ast::TypeSubstitution subs( baseParams, typeSubs );
-	for ( ast::ptr<ast::Decl> & member : members ) {
-		// The member must be mutated in place to avoid breaking
-		assert( member->unique() );
-
-		auto field = member.strict_as<ast::ObjectDecl>();
-		auto result = subs.apply( field->type.get() );
-		auto copy = ast::mutate_field(
-			field, &ast::ObjectDecl::type, result.node );
-
-		// I'm not kidding, it is very important.
-		assert( copy == member.get() );
-	}
-}
-
-/// Strips the instances' type parameters.
-void stripInstParams( ast::BaseInstType * inst ) {
-	inst->params.clear();
-}
-
-bool isGenericType( ast::Type const * type ) {
-	if ( auto inst = dynamic_cast<ast::StructInstType const *>( type ) ) {
-		return !inst->params.empty();
-	} else if ( auto inst = dynamic_cast<ast::UnionInstType const *>( type ) ) {
-		return !inst->params.empty();
-	} else {
-		return false;
-	}
-}
-
-// The Passes:
-
-struct FixDtypeStatic final :
-		public ast::WithGuards,
-		public ast::WithVisitorRef<FixDtypeStatic>,
-		public ast::WithShortCircuiting,
-		public ast::WithStmtsToAdd<> {
-	ast::ApplicationExpr const * previsit( ast::ApplicationExpr const * expr );
-	void previsit( ast::AddressExpr const * expr );
-
-	ast::Expr const * postvisit( ast::MemberExpr const * expr );
-private:
-	template<typename Aggr>
-	ast::Expr const * fixMemberExpr(
-		Aggr const * inst, ast::MemberExpr const * memberExpr );
-
-	ast::Expr const * fixMemberExpr(
-		ast::vector<ast::TypeDecl> const & baseParams,
-		ast::MemberExpr const * memberExpr );
-
-	bool isLValueArg = false;
-};
-
-ast::ApplicationExpr const * FixDtypeStatic::previsit(
-		ast::ApplicationExpr const * expr ) {
-	GuardValue( isLValueArg ) = false;
-	ast::Decl const * function = ast::getFunction( expr );
-	if ( ast::Linkage::Intrinsic != function->linkage
-			|| !CodeGen::isAssignment( function->name ) ) {
-		return expr;
-	}
-
-	// Explicity visit children because only the first element must be a
-	// C lvalue (normally, it can only send info to both or neither).
-	visit_children = false;
-	expr = mutate_field( expr, &ast::ApplicationExpr::env,
-		maybe_accept( expr->env.get(), *visitor ) );
-	expr = mutate_field( expr, &ast::ApplicationExpr::result,
-		maybe_accept( expr->result.get(), *visitor ) );
-	expr = mutate_field( expr, &ast::ApplicationExpr::func,
-		maybe_accept( expr->func.get(), *visitor ) );
-	isLValueArg = true;
-	for ( unsigned i = 0; i < expr->args.size(); ++i ) {
-		ast::Expr const * newExpr = expr->args[i]->accept( *visitor );
-		// This is declared here for lifetime reasons.
-		ast::ptr<ast::CastExpr> cast;
-		if ( newExpr != expr->args[i].get() &&
-				(cast = dynamic_cast<ast::CastExpr const *>( newExpr )) ) {
-			newExpr = cast->arg.get();
-		}
-		expr = mutate_field_index( expr, &ast::ApplicationExpr::args,
-			i, newExpr );
-		isLValueArg = false;
-	}
-	return expr;
-}
-
-void FixDtypeStatic::previsit( ast::AddressExpr const * ) {
-	// The argument of an address expression (`&`) must be a C lvalue.
-	GuardValue( isLValueArg ) = true;
-}
-
-ast::Expr const * FixDtypeStatic::postvisit( ast::MemberExpr const * expr ) {
-	ast::ptr<ast::Type> const & type = expr->aggregate->result;
-	if ( auto inst = type.as<ast::StructInstType>() ) {
-		if ( !inst->params.empty() ) return fixMemberExpr( inst, expr );
-	} else if ( auto inst = type.as<ast::UnionInstType>() ) {
-		if ( !inst->params.empty() ) return fixMemberExpr( inst, expr );
-	}
-	return expr;
-}
-
-template<typename Aggr>
-ast::Expr const * FixDtypeStatic::fixMemberExpr(
-		Aggr const * inst, ast::MemberExpr const * memberExpr ) {
-	return fixMemberExpr( inst->base->params, memberExpr );
-}
-
-ast::Expr const * FixDtypeStatic::fixMemberExpr(
-		ast::vector<ast::TypeDecl> const & baseParams,
-		ast::MemberExpr const * memberExpr ) {
-	// Need to cast dtype-static member expressions to their actual type
-	// before the actual type type is erased.
-	// NOTE: The casts here have the third argument (isGenerated) set to
-	// ExplicitCast so that they casts persist until Box, where they are needed.
-
-	if ( !isDtypeStatic( baseParams ) ||
-			ResolvExpr::typesCompatible(
-				memberExpr->result,
-				memberExpr->member->get_type() ) ) {
-		return memberExpr;
-	}
-
-	// Type of member and type of expression differ.
-	ast::Type const * concType = ast::deepCopy( memberExpr->result );
-	CodeLocation const & location = memberExpr->location;
-	if ( isLValueArg ) {
-		// The result must be a C lvalue expression. So make a new reference
-		// variable with the correct actual type to replace the
-		// member expression.
-		//   forall(T &)
-		//   struct Ptr {
-		//     T * x;
-		//   };
-		//   Ptr(int) p;
-		//   int i;
-		// The original expression:
-		//   p.x = &i;
-		// Becomes the expression/declaration:
-		//   int *& _dtype_static_member_0;
-		//   (_dtype_static_member_0 = (int**)&p.x,
-		//    _dtype_static_member_0) = &i;
-
-		// The declaration is simple:
-		static UniqueName tmpNamer( "_dtype_static_member_" );
-		ast::ObjectDecl * tmp = new ast::ObjectDecl( location,
-			tmpNamer.newName(),
-			new ast::ReferenceType( concType ),
-			nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::C
-		);
-		stmtsToAddBefore.push_back( new ast::DeclStmt( location, tmp ) );
-
-		// The expression is more complex, uses references and reference /
-		// pointer parity. But breaking it up risks reordering.
-		return new ast::CommaExpr( location,
-			ast::UntypedExpr::createAssign( location,
-				new ast::VariableExpr( location, tmp ),
-				new ast::CastExpr( location,
-					new ast::AddressExpr( location, memberExpr ),
-					new ast::PointerType( ast::deepCopy( concType ) ),
-					ast::ExplicitCast
-				)
-			),
-			new ast::VariableExpr( location, tmp )
-		);
-	} else {
-		// Here, it can simply add a cast to actual types.
-		return new ast::CastExpr( location,
-			memberExpr,
-			concType,
-			ast::ExplicitCast
-		);
-	}
-}
-
-struct GenericInstantiator final :
-		public ast::WithCodeLocation,
-		public ast::WithConstTypeSubstitution,
-		public ast::WithDeclsToAdd<>,
-		public ast::WithGuards,
-		public ast::WithVisitorRef<GenericInstantiator>
-{
-	/// Map of (generic type, parameter list) pairs
-	/// to concrete type instantiations.
-	InstantiationMap instantiations;
-	/// Set of types which are dtype-only generic
-	/// (and therefore have static layout).
-	std::set<ast::AggregateDecl const *> dtypeStatics;
-	/// Namer for concrete types.
-	UniqueName typeNamer;
-	/// Should not make use of type environment to replace types of function
-	/// parameter and return values.
-	bool inFunctionType = false;
-	/// Index of current member, used to recreate MemberExprs with the
-	/// member from an instantiation.
-	int memberIndex = -1;
-
-	GenericInstantiator() :
-		instantiations(), dtypeStatics(), typeNamer("_conc_") {}
-
-	ast::Type const * postvisit( ast::StructInstType const * inst );
-	ast::Type const * postvisit( ast::UnionInstType const * inst );
-
-	void previsit( ast::MemberExpr const * expr );
-	ast::Expr const * postvisit( ast::MemberExpr const * expr );
-	ast::Expr const * postvisit( ast::Expr const * expr );
-	ast::Designation const * postvisit( ast::Designation const * );
-
-	void previsit( ast::ParseNode const * node ) {
-		GuardValue( location ) = &node->location;
-	}
-	void previsit( ast::FunctionType const * ) {
-		GuardValue( inFunctionType ) = true;
-	}
-	void beginScope() {
-		instantiations.beginScope();
-	}
-	void endScope() {
-		instantiations.endScope();
-	}
-private:
-	/// Wrap instantiation lookup for structures.
-	ast::StructDecl const * lookup(
-		ast::StructInstType const * inst, type_vector const & typeSubs ) const;
-	/// Wrap instantiation lookup for unions.
-	ast::UnionDecl const * lookup(
-		ast::UnionInstType const * inst, type_vector const & typeSubs ) const;
-	/// Wrap instantiation insertion for structures.
-	void insert( ast::StructInstType const * inst,
-		type_vector const & typeSubs, ast::StructDecl const * decl );
-	/// Wrap instantiation insertion for unions.
-	void insert( ast::UnionInstType const * inst,
-		type_vector const & typeSubs, ast::UnionDecl const * decl );
-
-	void replaceParametersWithConcrete( ast::vector<ast::Expr> & params );
-	ast::Type const * replaceWithConcrete( ast::Type const * type, bool doClone );
-
-	template<typename AggrDecl>
-	ast::Type const * fixInstType( ast::SueInstType<AggrDecl> const * inst );
-
-	/// Strips a dtype-static aggregate decl of its type parameters,
-	/// marks it as stripped.
-	void stripDtypeParams( ast::AggregateDecl * base,
-		ast::vector<ast::TypeDecl> & baseParams,
-		ast::vector<ast::TypeExpr> const & typeSubs );
-};
-
-ast::Type const * GenericInstantiator::postvisit(
-		ast::StructInstType const * inst ) {
-	return fixInstType( inst );
-}
-
-ast::Type const * GenericInstantiator::postvisit(
-		ast::UnionInstType const * inst ) {
-	return fixInstType( inst );
-}
-
-template<typename AggrDecl>
-ast::Type const * GenericInstantiator::fixInstType(
-		ast::SueInstType<AggrDecl> const * inst ) {
-	assert( location );
-
-	// There is nothing to mutate if the params are empty.
-	if ( inst->params.empty() ) return inst;
-
-	// Need to replace type variables to ensure that generic types are
-	// instantiated for the return values of polymorphic functions (in
-	// particular, for thunks, because they are not [currently] copy
-	// constructed).
-	// (This used to be run only on structures, but I believe both need it.)
-	inst = strict_dynamic_cast<ast::SueInstType<AggrDecl> const *>(
-		replaceWithConcrete( inst, false ) );
-
-	// Check for an already-instantiatiated dtype-static type.
-	if ( dtypeStatics.find( inst->base ) != dtypeStatics.end() ) {
-		auto mutInst = ast::mutate( inst );
-		stripInstParams( mutInst );
-		return mutInst;
-	}
-
-	// Check if the type can be concretely instantiated;
-	// and put substitutions in typeSubs.
-	assertf( inst->base, "Base data-type has parameters." );
-	ast::vector<ast::TypeExpr> typeSubs;
-	GenericType gt = makeSubstitutions( typeSubs, inst->base->params, inst->params );
-	switch ( gt ) {
-	case GenericType::dtypeStatic:
-	{
-		auto mutInst = ast::mutate( inst );
-		assert( mutInst->base->unique() );
-		auto mutBase = mutInst->base.get_and_mutate();
-		stripDtypeParams( mutBase, mutBase->params, typeSubs );
-		stripInstParams( mutInst );
-		return mutInst;
-	}
-	case GenericType::concrete:
-	{
-		// Make concrete instantiation of generic type.
-		AggrDecl const * concDecl = lookup( inst, typeSubs );
-		if ( !concDecl ) {
-			// Set concDecl to new type, insert type declaration
-			// into statements to add.
-			AggrDecl * newDecl = new AggrDecl( *location,
-				typeNamer.newName( inst->name )
-			);
-			newDecl->body = inst->base->body;
-			newDecl->members = substituteMembers(
-				inst->base->members,
-				inst->base->params,
-				typeSubs
-			);
-
-			// Forward declare before recursion. (TODO: Only when needed, #199.)
-			insert( inst, typeSubs, newDecl );
-			if ( AggrDecl const * forwardDecl = ast::asForward( newDecl ) ) {
-				declsToAddBefore.push_back( forwardDecl );
-			}
-			// Recursively instantiate members:
-			concDecl = strict_dynamic_cast<AggrDecl const *>(
-				newDecl->accept( *visitor ) );
-			// Must occur before declaration is added so
-			// that member instantiation appear first.
-			declsToAddBefore.push_back( concDecl );
-		}
-		return new ast::SueInstType<AggrDecl>( concDecl, inst->qualifiers );
-	}
-	case GenericType::dynamic:
-		// Do nothing.
-	default:
-		// Should never happen.
-		return inst;
-	}
-}
-
-void GenericInstantiator::previsit( ast::MemberExpr const * expr ) {
-	GuardValue( location ) = &expr->location;
-	GuardValue( memberIndex ) = -1;
-	// Only run on expressions where the field being accessed is generic.
-	if ( isGenericType( expr->aggregate->result ) ) {
-		// Find the location of the member:
-		ast::AggregateDecl const * aggr =
-			expr->aggregate->result.strict_as<ast::BaseInstType>()->aggr();
-		ast::vector<ast::Decl> const & members = aggr->members;
-		auto it = std::find( members.begin(), members.end(), expr->member );
-		memberIndex = std::distance( members.begin(), it );
-		assertf( memberIndex < (int)members.size(), "Could not find member %s in generic type %s.", toString( expr->member ).c_str(), toString( expr->aggregate ).c_str() );
-	}
-}
-
-ast::Expr const * GenericInstantiator::postvisit(
-		ast::MemberExpr const * expr ) {
-	if ( memberIndex == -1 ) {
-		return expr;
-	}
-
-	// Using the location from the generic type, find the member
-	// in the instantiation and rebuild the member expression.
-	ast::AggregateDecl const * aggr =
-		expr->aggregate->result.strict_as<ast::BaseInstType>()->aggr();
-	assertf( memberIndex < (int)aggr->members.size(), "Instantiation somehow has fewer members than the generic type." );
-	ast::Decl const * member = *std::next( aggr->members.begin(), memberIndex );
-	assertf( member->name == expr->member->name, "Instantiation has different member order than the generic type. %s / %s", toString( member ).c_str(), toString( expr->member ).c_str() );
-	auto field = strict_dynamic_cast< ast::DeclWithType const * >( member );
-	ast::MemberExpr * ret = new ast::MemberExpr( expr->location,
-		field,
-		ast::deepCopy( expr->aggregate )
-	);
-	// For pointer decay:
-	ret->result = ResolvExpr::adjustExprType(
-		ret->result,
-		ast::TypeEnvironment(),
-		ast::SymbolTable()
-	);
-	ret->env = expr->env;
-	return ret;
-}
-
-ast::Expr const * GenericInstantiator::postvisit( ast::Expr const * expr ) {
-	// We are not modifying env on MemberExpr, but that seems to work.
-	if ( expr->env ) {
-		auto newEnv = expr->env->accept( *visitor );
-		expr = ast::mutate_field( expr, &ast::Expr::env, newEnv );
-	}
-	return expr;
-}
-
-// This attempts to figure out what the final name of the field will be.
-// Pretty printing can cause this to become incorrect.
-std::string getPrintName( ast::DeclWithType const * decl ) {
-	return ( decl->linkage.is_mangled )
-		? decl->scopedMangleName() : decl->name;
-}
-
-ast::Designation const * GenericInstantiator::postvisit(
-		ast::Designation const * designation ) {
-	// Disconnect designator names from their fields.
-	// It is now incorrect to point at the generic definition where the used
-	// type now is replaced with a concrete instance. Ideally, new variable
-	// expressions would point at fields in the concrete instances, but that
-	// is work and that information should not be needed this late in
-	// compilation.
-
-	// Modify all designations, even if not needed.
-	auto mutNode = mutate( designation );
-	for ( ast::ptr<ast::Expr> & designator : mutNode->designators ) {
-		if ( auto var = designator.as<ast::VariableExpr>() ) {
-			designator = new ast::NameExpr(
-				var->location, getPrintName( var->var ) );
-		}
-	}
-	return mutNode;
-}
-
-ast::StructDecl const * GenericInstantiator::lookup(
-		ast::StructInstType const * inst,
-		type_vector const & typeSubs ) const {
-	auto ret = instantiations.lookup( inst->base, typeSubs );
-	return strict_dynamic_cast<ast::StructDecl const *, nullptr>( ret );
-}
-
-ast::UnionDecl const * GenericInstantiator::lookup(
-		ast::UnionInstType const * inst,
-		type_vector const & typeSubs ) const {
-	auto ret = instantiations.lookup( inst->base, typeSubs );
-	return strict_dynamic_cast<ast::UnionDecl const *, nullptr>( ret );
-}
-
-void GenericInstantiator::insert( ast::StructInstType const * inst,
-		type_vector const & typeSubs, ast::StructDecl const * decl ) {
-	instantiations.insert( inst->base, typeSubs, decl );
-}
-
-void GenericInstantiator::insert( ast::UnionInstType const * inst,
-		type_vector const & typeSubs, ast::UnionDecl const * decl ) {
-	instantiations.insert( inst->base, typeSubs, decl );
-}
-
-void GenericInstantiator::replaceParametersWithConcrete(
-		ast::vector<ast::Expr> & params ) {
-	for ( ast::ptr<ast::Expr> & param : params ) {
-		auto paramType = param.as<ast::TypeExpr>();
-		assertf( paramType, "Aggregate parameters should be type expressions." );
-		auto type = replaceWithConcrete( paramType->type, false );
-		param = ast::mutate_field( paramType, &ast::TypeExpr::type, type );
-	}
-}
-
-ast::Type const * GenericInstantiator::replaceWithConcrete(
-		ast::Type const * type, bool doClone ) {
-	if ( auto inst = dynamic_cast<ast::TypeInstType const *>( type ) ) {
-		if ( typeSubs && !inFunctionType ) {
-			ast::Type const * concType = typeSubs->lookup( inst );
-			return ast::deepCopy( ( concType ) ? concType : inst );
-		}
-	} else if ( auto inst = dynamic_cast<ast::StructInstType const *>( type ) ) {
-		auto mut = ( doClone ) ? ast::deepCopy( inst ) : ast::mutate( inst );
-		replaceParametersWithConcrete( mut->params );
-		return mut;
-	} else if ( auto inst = dynamic_cast<ast::UnionInstType const *>( type ) ) {
-		auto mut = ( doClone ) ? ast::deepCopy( inst ) : ast::mutate( inst );
-		replaceParametersWithConcrete( mut->params );
-		return mut;
-	}
-	return type;
-}
-
-void GenericInstantiator::stripDtypeParams(
-		ast::AggregateDecl * base,
-		ast::vector<ast::TypeDecl> & baseParams,
-		ast::vector<ast::TypeExpr> const & typeSubs ) {
-	substituteMembersHere( base->members, baseParams, typeSubs );
-
-	baseParams.clear();
-
-	dtypeStatics.insert( base );
-}
-
-} // namespace
-
-void instantiateGeneric( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<FixDtypeStatic>::run( translationUnit );
-	ast::Pass<GenericInstantiator>::run( translationUnit );
-}
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/Lvalue.cpp
===================================================================
--- src/GenPoly/Lvalue.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/GenPoly/Lvalue.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,631 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Lvalue.cpp -- Clean up lvalues and remove references.
+//
+// Author           : Andrew Beach
+// Created On       : Thu Sep 15 14:08:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Oct  6  9:59:00 2022
+// Update Count     : 0
+//
+
+#include "Lvalue.h"
+
+#include <set>
+#include <iostream>
+
+#include "AST/Copy.hpp"                // for deepCopy
+#include "AST/Expr.hpp"
+#include "AST/Inspect.hpp"
+#include "AST/LinkageSpec.hpp"         // for Linkage
+#include "AST/Pass.hpp"
+#include "Common/SemanticError.h"      // for SemanticWarning
+#include "Common/ToString.hpp"         // for toCString
+#include "Common/UniqueName.h"         // for UniqueName
+#include "GenPoly/GenPoly.h"           // for genFunctionType
+#include "ResolvExpr/typeops.h"        // for typesCompatible
+#include "ResolvExpr/Unify.h"          // for unify
+
+#if 0
+#define PRINT(x) x
+#else
+#define PRINT(x)
+#endif
+
+namespace GenPoly {
+
+namespace {
+
+/// Intrinsic functions that return references now instead return lvalues.
+struct FixIntrinsicResults final : public ast::WithGuards {
+	enum {
+		NoSkip,
+		Skip,
+		SkipInProgress,
+	} skip = NoSkip;
+
+	void previsit( ast::AsmExpr const * ) {
+		GuardValue( skip ) = Skip;
+	}
+	void previsit( ast::ApplicationExpr const * ) {
+		GuardValue( skip ) = (skip == Skip) ? SkipInProgress : NoSkip;
+	}
+
+	ast::Expr const * postvisit( ast::ApplicationExpr const * expr );
+	void previsit( ast::FunctionDecl const * decl );
+	bool inIntrinsic = false;
+};
+
+/// Add de-references around address-of operations on reference types.
+struct AddressRef final :
+		public ast::WithConstTranslationUnit,
+		public ast::WithGuards,
+		public ast::WithShortCircuiting,
+		public ast::WithVisitorRef<AddressRef> {
+	void previsit( ast::AddressExpr const * expr );
+	ast::Expr const * postvisit( ast::AddressExpr const * expr );
+	void previsit( ast::Expr const * expr );
+	ast::ApplicationExpr const * previsit( ast::ApplicationExpr const * expr );
+	void previsit( ast::SingleInit const * init );
+
+	void handleNonAddr( ast::Expr const * expr );
+
+	bool first = true;
+	bool current = false;
+	bool addCast = false;
+	int refDepth = 0;
+};
+
+/// Handles casts between references and pointers,
+/// creating temporaries for the conversion.
+struct ReferenceConversions final :
+		public ast::WithConstTranslationUnit,
+		public ast::WithGuards, public ast::WithStmtsToAdd<> {
+	ast::Expr const * postvisit( ast::CastExpr const * expr );
+	ast::Expr const * postvisit( ast::AddressExpr const * expr );
+};
+
+/// Intrinsic functions that take reference parameters don't actually do.
+/// Their reference arguments must be implicity dereferenced.
+/// TODO Also appears to contain redundent code with AddressRef
+struct FixIntrinsicArgs final :
+		public ast::WithConstTranslationUnit {
+	ast::Expr const * postvisit( ast::ApplicationExpr const * expr );
+};
+
+/// Removes redundant &* / *& patterns that may be generated.
+struct CollapseAddressDeref final {
+	ast::Expr const * postvisit( ast::AddressExpr const * expr );
+	ast::Expr const * postvisit( ast::ApplicationExpr const * expr );
+};
+
+/// GCC-like Generalized Lvalues (which have since been removed from GCC).
+/// https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Lvalues.html#Lvalues
+/// Replaces &(a,b) with (a, &b), &(a ? b : c) with (a ? &b : &c)
+struct GeneralizedLvalue final :
+		public ast::WithVisitorRef<GeneralizedLvalue> {
+	ast::Expr const * postvisit( ast::AddressExpr const * expr );
+	ast::Expr const * postvisit( ast::MemberExpr const * expr );
+
+	template<typename Node, typename Func>
+	ast::Expr const * applyTransformation(
+	      Node const * expr, ast::ptr<ast::Expr> Node::*field, Func mkExpr );
+};
+
+/// Replace all reference types with pointer types.
+struct ReferenceTypeElimination final {
+	ast::Type const * postvisit( ast::ReferenceType const * type );
+};
+
+/// True for intrinsic function calls that return an lvalue in C.
+bool isIntrinsicReference( ast::Expr const * expr ) {
+	// The known intrinsic-reference prelude functions.
+	static std::set<std::string> const lvalueFunctions = { "*?", "?[?]" };
+	if ( auto untyped = dynamic_cast<ast::UntypedExpr const *>( expr ) ) {
+		std::string fname = ast::getFunctionName( untyped );
+		return lvalueFunctions.count( fname );
+	} else if ( auto app = dynamic_cast<ast::ApplicationExpr const *>( expr ) ) {
+		if ( auto func = ast::getFunction( app ) ) {
+			return func->linkage == ast::Linkage::Intrinsic
+				&& lvalueFunctions.count( func->name );
+		}
+	}
+	return false;
+}
+
+// A maybe typed variant of the createDeref function (only UntypedExpr).
+ast::Expr * mkDeref(
+		ast::TranslationGlobal const & global, ast::Expr const * arg ) {
+	if ( global.dereference ) {
+		// Note: Reference depth can be arbitrarily deep here,
+		// so peel off the outermost pointer/reference, not just
+		// pointer because they are effecitvely equivalent in this pass
+		ast::VariableExpr * deref = new ast::VariableExpr(
+			arg->location, global.dereference );
+		deref->result = new ast::PointerType( deref->result );
+		ast::Type const * base = ast::getPointerBase( arg->result );
+		assertf( base, "expected pointer type in dereference (type was %s)", toString( arg->result ).c_str() );
+		ast::ApplicationExpr * ret =
+			new ast::ApplicationExpr( arg->location, deref, { arg } );
+		ret->result = ast::deepCopy( base );
+		return ret;
+	} else {
+		return ast::UntypedExpr::createDeref( arg->location, arg );
+	}
+}
+
+ast::Expr const * FixIntrinsicResults::postvisit(
+		ast::ApplicationExpr const * expr ) {
+	if ( skip == SkipInProgress || !isIntrinsicReference( expr ) ) {
+		return expr;
+	}
+	// Eliminate reference types from intrinsic applications
+	// now they return lvalues.
+	ast::ptr<ast::ReferenceType> result =
+			expr->result.strict_as<ast::ReferenceType>();
+	expr = ast::mutate_field( expr, &ast::ApplicationExpr::result,
+			ast::deepCopy( result->base ) );
+	if ( inIntrinsic ) {
+		return expr;
+	}
+	// When not in an intrinsic function, add a cast to don't add cast when
+	// in an intrinsic function, since they already have the cast.
+	auto * ret = new ast::CastExpr( expr->location, expr, result.get() );
+	ret->env = expr->env;
+	return ret;
+}
+
+void FixIntrinsicResults::previsit( ast::FunctionDecl const * decl ) {
+	GuardValue( inIntrinsic ) = decl->linkage == ast::Linkage::Intrinsic;
+}
+
+void AddressRef::previsit( ast::AddressExpr const * ) {
+	// Is this the first address-of in the chain?
+	GuardValue( current ) = first;
+	// Later references will not be for next address-of to be first in chain.
+	GuardValue( first ) = false;
+	// If is the outermost address-of in a chain:
+	if ( current ) {
+		// Set depth to 0 so that postvisit can
+		// find the innermost address-of easily.
+		GuardValue( refDepth ) = 0;
+	}
+}
+
+ast::Expr const * AddressRef::postvisit( ast::AddressExpr const * expr ) {
+	PRINT( std::cerr << "addr ref at " << expr << std::endl; )
+	if ( 0 == refDepth ) {
+		PRINT( std::cerr << "depth 0, get new depth..." << std::endl; )
+		// Is this the innermost address-of in a chain? record depth D.
+		if ( isIntrinsicReference( expr->arg ) ) {
+			assertf( false, "AddrRef : address-of should not have intrinsic reference argument: %s", toCString( expr->arg )  );
+		} else {
+			// try to avoid ?[?]
+			// TODO is this condition still necessary? intrinsicReferences
+			// should have a cast around them at this point, so I don't think
+			// this condition ever fires.
+			refDepth = expr->arg->result->referenceDepth();
+			PRINT( std::cerr << "arg not intrinsic reference, new depth is: " << refDepth << std::endl; )
+		}
+	}
+	if ( current ) {
+		PRINT( std::cerr << "current, depth is: " << refDepth << std::endl; )
+		ast::Expr const * ret = expr;
+		while ( refDepth ) {
+			// Add one dereference for each address-of in the chain.
+			ret = mkDeref( transUnit().global, ret );
+			--refDepth;
+		}
+
+		// if addrExpr depth is 0, then the result is a pointer because the
+		// arg was depth 1 and not lvalue. This means the dereference result
+		// is not a reference, is lvalue, and one less pointer depth than the
+		// addrExpr. Thus the cast is meaningless.
+		// TODO: One thing to double check is whether it is possible for the
+		// types to differ outside of the single pointer level (i.e. can the
+		// base type of addrExpr differ from the type of addrExpr-arg?). If
+		// so then the cast might need to be added, conditional on a more
+		// sophisticated check.
+		if ( addCast && 0 != expr->result->referenceDepth() ) {
+			PRINT( std::cerr << "adding cast to " << expr->result << std::endl; )
+			return new ast::CastExpr( expr->location,
+				ret, ast::deepCopy( expr->result ) );
+		}
+		return ret;
+	}
+	PRINT( std::cerr << "not current..." << std::endl; )
+	return expr;
+}
+
+void AddressRef::previsit( ast::Expr const * expr ) {
+	handleNonAddr( expr );
+	GuardValue( addCast ) = false;
+}
+
+// So we want to skip traversing to the head?
+ast::ApplicationExpr const * AddressRef::previsit(
+		ast::ApplicationExpr const * expr ) {
+	visit_children = false;
+	GuardValue( addCast );
+	handleNonAddr( expr );
+	auto mutExpr = ast::mutate( expr );
+	for ( ast::ptr<ast::Expr> & arg : mutExpr->args ) {
+		addCast = true;
+		arg = arg->accept( *visitor );
+	}
+	return mutExpr;
+}
+
+void AddressRef::previsit( ast::SingleInit const * ) {
+	// Each initialization context with address-of requires a cast.
+	GuardValue( addCast ) = true;
+}
+
+// idea: &&&E: get outer &, inner &
+// at inner &, record depth D of reference type of argument of &.
+// at auter &, add D derefs.
+void AddressRef::handleNonAddr( ast::Expr const * ) {
+	// non-address-of: reset status variables:
+	// * current expr is NOT the first address-of expr in an address-of chain.
+	// * next seen address-of expr IS the first in the chain.
+	GuardValue( current ) = false;
+	GuardValue( first ) = true;
+}
+
+ast::Expr const * ReferenceConversions::postvisit(
+		ast::CastExpr const * expr ) {
+	// TODO: Is it possible to convert directly between reference types with
+	// a different base. e.g.
+	//   int x;
+	//   (double&)x;
+	// At the moment, I (who?) am working off of the assumption that this is
+	// illegal, thus the cast becomes redundant after this pass, so trash the
+	// cast altogether. If that changes, care must be taken to insert the
+	// correct pointer casts in the right places.
+
+	// Note: reference depth difference is the determining factor in what
+	// code is run, rather than whether something is reference type or not,
+	// since conversion still needs to occur when both types are references
+	// that differ in depth.
+	ast::Type const * dstType = expr->result.get();
+	ast::Type const * srcType = expr->arg->result.get();
+	assertf( dstType, "Cast to no type in: %s", toCString( expr ) );
+	assertf( srcType, "Cast from no type in: %s", toCString( expr ) );
+	int dstDepth = dstType->referenceDepth();
+	int srcDepth = srcType->referenceDepth();
+	int diff = dstDepth - srcDepth;
+
+	if ( 0 < diff && !expr->arg->get_lvalue() ) {
+		// rvalue to reference conversion -- introduce temporary
+		// know that reference depth of cast argument is 0
+		//   (int &&&)3;
+		// becomes
+		//   int __ref_tmp_0 = 3;
+		//   int & __ref_tmp_1 = &__ref_tmp_0;
+		//   int && __ref_tmp_2 = &__ref_tmp_1;
+		//   &__ref_tmp_2;
+		// The last & comes from the remaining reference conversion code.
+		SemanticWarning( expr->arg->location,
+			Warning::RvalueToReferenceConversion, toCString( expr->arg ) );
+
+		static UniqueName tmpNamer( "__ref_tmp_" );
+		ast::ObjectDecl * tmp = new ast::ObjectDecl( expr->arg->location,
+			tmpNamer.newName(),
+			ast::deepCopy( expr->arg->result ),
+			new ast::SingleInit( expr->arg->location, expr->arg ) );
+		PRINT( std::cerr << "make tmp: " << tmp << std::endl; )
+		stmtsToAddBefore.push_back( new ast::DeclStmt( tmp->location, tmp ) );
+		for ( int i = 0 ; i < dstDepth - 1 ; ++i ) {
+			ast::ObjectDecl * newTmp = new ast::ObjectDecl( tmp->location,
+				tmpNamer.newName(),
+				new ast::ReferenceType( ast::deepCopy( tmp->type ) ),
+				new ast::SingleInit( tmp->location,
+					new ast::AddressExpr( tmp->location,
+						new ast::VariableExpr( tmp->location, tmp ) ) ) );
+			PRINT( std::cerr << "make tmp: " << i << ": " << newTmp << std::endl; )
+			stmtsToAddBefore.push_back(
+				new ast::DeclStmt( newTmp->location, newTmp ) );
+			tmp = newTmp;
+		}
+		// Update diff so that remaining code works out correctly.
+		expr = ast::mutate_field( expr, &ast::CastExpr::arg,
+			new ast::VariableExpr( tmp->location, tmp ) );
+		PRINT( std::cerr << "update cast to: " << expr << std::endl; )
+		srcType = expr->arg->result;
+		srcDepth = srcType->referenceDepth();
+		diff = dstDepth - srcDepth;
+		assert( 1 == diff );
+	}
+
+	// Handle conversion between different depths.
+	PRINT(
+		if ( dstDepth || srcDepth ) {
+			std::cerr << "dstType: " << dstType << " / srcType: " << srcType << '\n';
+			std::cerr << "depth: " << dstDepth << " / " << srcDepth << std::endl;
+		}
+	)
+	// Conversion to type with more depth/more references.
+	// Add address-of for each level of difference.
+	if ( 0 < diff ) {
+		ast::Expr * ret = ast::mutate( expr->arg.get() );
+		for ( int i = 0 ; i < diff ; ++i ) {
+			ret = new ast::AddressExpr( ret->location, ret );
+		}
+		if ( expr->arg->get_lvalue() &&
+				!ResolvExpr::typesCompatible(
+					srcType,
+					strict_dynamic_cast<ast::ReferenceType const *>( dstType )->base ) ) {
+			// Must keep cast if cast-to type is different from the actual type.
+			return ast::mutate_field( expr, &ast::CastExpr::arg, ret );
+		}
+		ret->env = expr->env;
+		ret->result = expr->result;
+		return ret;
+	// Conversion to type with less depth/fewer references.
+	// Add dereferences for each level of difference.
+	} else if ( diff < 0 ) {
+		ast::Expr * ret = ast::mutate( expr->arg.get() );
+		for ( int i = 0 ; i < -diff ; ++i ) {
+			ret = mkDeref( transUnit().global, ret );
+		}
+		// Must keep cast if types are different.
+		if ( !ResolvExpr::typesCompatibleIgnoreQualifiers(
+				dstType->stripReferences(),
+				srcType->stripReferences() ) ) {
+			return ast::mutate_field( expr, &ast::CastExpr::arg, ret );
+		}
+		ret->env = expr->env;
+		ret->result = expr->result;
+		// The result must be an lvalue.
+		assert( ret->get_lvalue() );
+		return ret;
+	// Conversion with the same depth.
+	} else {
+		assert( 0 == diff );
+		// Remove useless generated casts.
+		if ( expr->isGenerated &&
+				ResolvExpr::typesCompatible(
+					expr->result,
+					expr->arg->result ) ) {
+			PRINT(
+				std::cerr << "types are compatible, removing cast: " << expr << '\n';
+				std::cerr << "-- " << expr->result << '\n';
+				std::cerr << "-- " << expr->arg->result << std::endl;
+			)
+			return ast::mutate_field( expr->arg.get(),
+					&ast::Expr::env, expr->env.get() );
+		}
+		return expr;
+	}
+}
+
+ast::Expr const * ReferenceConversions::postvisit(
+		ast::AddressExpr const * expr ) {
+	// Inner expression may have been lvalue to reference conversion, which
+	// becomes an address expression. In this case, remove the outer address
+	// expression and return the argument.
+	// TODO: It's possible that this might catch too much and require a more
+	// sophisticated check. TODO What check are we talking about here?
+	return expr;
+}
+
+ast::Expr const * FixIntrinsicArgs::postvisit(
+		ast::ApplicationExpr const * expr ) {
+	// Intrinsic functions don't really take reference-typed parameters,
+	// so they require an implicit dereference on their arguments.
+	auto function = ast::getFunction( expr );
+	if ( function == nullptr ) {
+		return expr;
+	}
+
+	ast::FunctionType const * ftype = GenPoly::getFunctionType( function->get_type() );
+	assertf( ftype, "Function declaration does not have function type." );
+	// Can be of different lengths only when function is variadic.
+	assertf( ftype->params.size() == expr->args.size() || ftype->isVarArgs,
+		"ApplicationExpr args do not match formal parameter type." );
+	assertf( ftype->params.size() <= expr->args.size(),
+		"Cannot have more parameters than arguments." );
+
+	unsigned int i = 0;
+	unsigned int const end = ftype->params.size();
+
+	// This is used to make sure we get a zip on shortests.
+	if ( end == i ) return expr;
+
+	// This mutate could be redundent, but it is simpler this way.
+	auto mutExpr = ast::mutate( expr );
+
+	for ( auto pair : unsafe_group_iterate( mutExpr->args, ftype->params ) ) {
+		ast::ptr<ast::Expr> & arg = std::get<0>( pair );
+		ast::ptr<ast::Type> const & formal = std::get<1>( pair );
+		PRINT(
+			std::cerr << "pair<0>: " << arg.get() << std::endl;
+			std::cerr << " -- " << arg->result << std::endl;
+			std::cerr << "pair<1>: " << formal << std::endl;
+		)
+		//if ( dynamic_cast<ast::ReferenceType const *>( formal.get() ) ) {
+		if ( formal.as<ast::ReferenceType>() ) {
+			PRINT( std::cerr << "===formal is reference" << std::endl; )
+			// TODO: It's likely that the second condition should be
+			// `... && ! isIntrinsicReference( arg )`, but this requires
+			// investigation.
+
+			if ( ast::Linkage::Intrinsic != function->linkage
+					&& isIntrinsicReference( arg ) ) {
+				// Needed for definition of prelude functions, etc.
+				// If argument is dereference or array subscript, the result
+				// isn't REALLY a reference, but non-intrinsic functions
+				// expect a reference: take address
+
+				// TODO: OK, so this should be cut?!
+				// NODE: Previously, this condition fixed
+				//   void f(int *&);
+				//   int & x = ...;
+				//   f(&x);
+				// But now this is taken care of by a reference cast added by
+				// AddressRef. Need to find a new example or remove this
+				// branch.
+				PRINT(
+					std::cerr << "===is intrinsic arg in non-intrinsic call - adding address" << std::endl;
+				)
+				arg = new ast::AddressExpr( arg->location, arg );
+			} else if ( ast::Linkage::Intrinsic == function->linkage
+					&& arg->result->referenceDepth() != 0 ) {
+				// Argument is a 'real' reference, but function expects a C
+				// lvalue: Add a dereference to the reference-typed argument.
+				PRINT(
+					std::cerr << "===is non-intrinsic arg in intrinsic call - adding deref to arg" << std::endl;
+				)
+				ast::Type const * base = ast::getPointerBase( arg->result );
+				assertf( base, "parameter is reference, arg must be pointer or reference: %s", toString( arg->result ).c_str() );
+				ast::PointerType * ptr = new ast::PointerType( ast::deepCopy( base ) );
+				arg = ast::mutate_field( arg.get(),
+						&ast::ApplicationExpr::result, ptr );
+				arg = mkDeref( transUnit().global, arg );
+			}
+		}
+		++i;
+		if ( end == i ) break;
+	}
+	return mutExpr;
+}
+
+ast::Expr const * CollapseAddressDeref::postvisit(
+		ast::AddressExpr const * expr ) {
+	ast::Expr const * arg = expr->arg;
+	if ( isIntrinsicReference( arg ) ) {
+		std::string fname = ast::getFunctionName( arg );
+		if ( fname == "*?" ) {
+			ast::Expr const * arg0 = ast::getCallArg( arg, 0 );
+			ast::Expr * ret = ast::mutate( arg0 );
+			ret->env = expr->env;
+			return ret;
+		}
+	} else if ( auto cast = dynamic_cast<ast::CastExpr const *>( arg ) ) {
+		// Need to move cast to pointer type out a level since address of
+		// pointer is not valid C code (can be introduced in prior passes,
+		// e.g., InstantiateGeneric)
+		if ( ast::getPointerBase( cast->result ) ) {
+			auto mutExpr = ast::mutate( expr );
+			auto mutCast = strict_dynamic_cast<ast::CastExpr *>(
+					ast::mutate( mutExpr->arg.release() ) );
+			mutExpr->arg = mutCast->arg;
+			mutCast->arg = mutExpr;
+			mutCast->result = new ast::PointerType( mutCast->result );
+			return mutCast;
+		}
+	}
+	return expr;
+}
+
+ast::Expr const * CollapseAddressDeref::postvisit(
+		ast::ApplicationExpr const * expr ) {
+	if ( isIntrinsicReference( expr ) ) {
+		std::string fname = ast::getFunctionName( expr );
+		if ( fname == "*?" ) {
+			assert( 1 == expr->args.size() );
+			ast::Expr const * arg = ast::getCallArg( expr, 0 );
+			// xxx - this isn't right, because it can remove casts that
+			// should be there...
+			//	while ( auto cast = dynamic_cast< ast::CastExpr const * >( arg ) ) {
+			//		arg = cast->arg;
+			//	}
+			if ( auto addr = dynamic_cast<ast::AddressExpr const *>( arg ) ) {
+				return ast::mutate_field( addr->arg.get(),
+						&ast::Expr::env, expr->env.get() );
+			}
+		}
+	}
+	return expr;
+}
+
+ast::Expr const * GeneralizedLvalue::postvisit(
+		ast::AddressExpr const * expr ) {
+	return applyTransformation( expr, &ast::AddressExpr::arg,
+		[]( ast::Expr const * arg ) {
+			return new ast::AddressExpr( arg->location, arg );
+		}
+	);
+}
+
+ast::Expr const * GeneralizedLvalue::postvisit(
+		ast::MemberExpr const * expr ) {
+	return applyTransformation( expr, &ast::MemberExpr::aggregate,
+		[expr]( ast::Expr const * aggr ) {
+			return new ast::MemberExpr( aggr->location, expr->member, aggr );
+		}
+	);
+}
+
+template<typename Node, typename Func>
+ast::Expr const * GeneralizedLvalue::applyTransformation(
+		Node const * expr, ast::ptr<ast::Expr> Node::*field, Func mkExpr ) {
+	ast::ptr<ast::Expr> const & arg = expr->*field;
+	if ( auto commaArg = arg.as<ast::CommaExpr>() ) {
+		ast::Expr const * arg1 = ast::deepCopy( commaArg->arg1 );
+		ast::Expr const * arg2 = ast::deepCopy( commaArg->arg2 );
+		ast::Expr const * ret = new ast::CommaExpr(
+			commaArg->location, arg1, mkExpr( arg2 )->accept( *visitor ) );
+		return ret;
+	} else if ( auto condArg = arg.as<ast::ConditionalExpr>() ) {
+		ast::Expr const * arg1 = ast::deepCopy( condArg->arg1 );
+		ast::Expr const * arg2 = ast::deepCopy( condArg->arg2 );
+		ast::Expr const * arg3 = ast::deepCopy( condArg->arg3 );
+		ast::ConditionalExpr * ret = new ast::ConditionalExpr(
+			condArg->location, arg1, mkExpr( arg2 )->accept( *visitor ),
+			mkExpr( arg3 )->accept( *visitor ) );
+
+		// Conditional expr type may not be either of the arguments,
+		// so unify to get the result.
+		// TODO: Maybe I could create a wrapper for this.
+		ast::ptr<ast::Type> common = nullptr;
+		ast::TypeEnvironment newEnv;
+		ast::AssertionSet needAssertions, haveAssertions;
+		ast::OpenVarSet openVars;
+		ResolvExpr::unify( ret->arg2->result, ret->arg3->result, newEnv,
+			needAssertions, haveAssertions, openVars, common );
+		ret->result = common ? common : ast::deepCopy( ret->arg2->result );
+		return ret;
+	}
+	return expr;
+}
+
+ast::Type const * ReferenceTypeElimination::postvisit(
+		ast::ReferenceType const * type ) {
+	return new ast::PointerType( type->base, type->qualifiers );
+}
+
+} // namespace
+
+// Stored elsewhere (Lvalue2, initially false):
+extern bool referencesEliminated;
+
+void convertLvalue( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<FixIntrinsicResults>::run( translationUnit );
+	ast::Pass<AddressRef>::run( translationUnit );
+	ast::Pass<ReferenceConversions>::run( translationUnit );
+	ast::Pass<FixIntrinsicArgs>::run( translationUnit );
+	ast::Pass<CollapseAddressDeref>::run( translationUnit );
+	ast::Pass<GeneralizedLvalue>::run( translationUnit );
+	// Last because other passes need reference types to work.
+	ast::Pass<ReferenceTypeElimination>::run( translationUnit );
+	// From this point forward, nothing should create reference types.
+	referencesEliminated = true;
+}
+
+ast::Expr const * generalizedLvalue( ast::Expr const * expr ) {
+	ast::Pass<GeneralizedLvalue> visitor;
+	return expr->accept( visitor );
+}
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/GenPoly/LvalueNew.cpp
===================================================================
--- src/GenPoly/LvalueNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,631 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// LvalueNew.cpp -- Clean up lvalues and remove references.
-//
-// Author           : Andrew Beach
-// Created On       : Thu Sep 15 14:08:00 2022
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Oct  6  9:59:00 2022
-// Update Count     : 0
-//
-
-#include "Lvalue.h"
-
-#include <set>
-#include <iostream>
-
-#include "AST/Copy.hpp"                // for deepCopy
-#include "AST/Expr.hpp"
-#include "AST/Inspect.hpp"
-#include "AST/LinkageSpec.hpp"         // for Linkage
-#include "AST/Pass.hpp"
-#include "Common/SemanticError.h"      // for SemanticWarning
-#include "Common/ToString.hpp"         // for toCString
-#include "Common/UniqueName.h"         // for UniqueName
-#include "GenPoly/GenPoly.h"           // for genFunctionType
-#include "ResolvExpr/typeops.h"        // for typesCompatible
-#include "ResolvExpr/Unify.h"          // for unify
-
-#if 0
-#define PRINT(x) x
-#else
-#define PRINT(x)
-#endif
-
-namespace GenPoly {
-
-namespace {
-
-/// Intrinsic functions that return references now instead return lvalues.
-struct FixIntrinsicResults final : public ast::WithGuards {
-	enum {
-		NoSkip,
-		Skip,
-		SkipInProgress,
-	} skip = NoSkip;
-
-	void previsit( ast::AsmExpr const * ) {
-		GuardValue( skip ) = Skip;
-	}
-	void previsit( ast::ApplicationExpr const * ) {
-		GuardValue( skip ) = (skip == Skip) ? SkipInProgress : NoSkip;
-	}
-
-	ast::Expr const * postvisit( ast::ApplicationExpr const * expr );
-	void previsit( ast::FunctionDecl const * decl );
-	bool inIntrinsic = false;
-};
-
-/// Add de-references around address-of operations on reference types.
-struct AddressRef final :
-		public ast::WithConstTranslationUnit,
-		public ast::WithGuards,
-		public ast::WithShortCircuiting,
-		public ast::WithVisitorRef<AddressRef> {
-	void previsit( ast::AddressExpr const * expr );
-	ast::Expr const * postvisit( ast::AddressExpr const * expr );
-	void previsit( ast::Expr const * expr );
-	ast::ApplicationExpr const * previsit( ast::ApplicationExpr const * expr );
-	void previsit( ast::SingleInit const * init );
-
-	void handleNonAddr( ast::Expr const * expr );
-
-	bool first = true;
-	bool current = false;
-	bool addCast = false;
-	int refDepth = 0;
-};
-
-/// Handles casts between references and pointers,
-/// creating temporaries for the conversion.
-struct ReferenceConversions final :
-		public ast::WithConstTranslationUnit,
-		public ast::WithGuards, public ast::WithStmtsToAdd<> {
-	ast::Expr const * postvisit( ast::CastExpr const * expr );
-	ast::Expr const * postvisit( ast::AddressExpr const * expr );
-};
-
-/// Intrinsic functions that take reference parameters don't actually do.
-/// Their reference arguments must be implicity dereferenced.
-/// TODO Also appears to contain redundent code with AddressRef
-struct FixIntrinsicArgs final :
-		public ast::WithConstTranslationUnit {
-	ast::Expr const * postvisit( ast::ApplicationExpr const * expr );
-};
-
-/// Removes redundant &* / *& patterns that may be generated.
-struct CollapseAddressDeref final {
-	ast::Expr const * postvisit( ast::AddressExpr const * expr );
-	ast::Expr const * postvisit( ast::ApplicationExpr const * expr );
-};
-
-/// GCC-like Generalized Lvalues (which have since been removed from GCC).
-/// https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Lvalues.html#Lvalues
-/// Replaces &(a,b) with (a, &b), &(a ? b : c) with (a ? &b : &c)
-struct GeneralizedLvalue final :
-		public ast::WithVisitorRef<GeneralizedLvalue> {
-	ast::Expr const * postvisit( ast::AddressExpr const * expr );
-	ast::Expr const * postvisit( ast::MemberExpr const * expr );
-
-	template<typename Node, typename Func>
-	ast::Expr const * applyTransformation(
-	      Node const * expr, ast::ptr<ast::Expr> Node::*field, Func mkExpr );
-};
-
-/// Replace all reference types with pointer types.
-struct ReferenceTypeElimination final {
-	ast::Type const * postvisit( ast::ReferenceType const * type );
-};
-
-/// True for intrinsic function calls that return an lvalue in C.
-bool isIntrinsicReference( ast::Expr const * expr ) {
-	// The known intrinsic-reference prelude functions.
-	static std::set<std::string> const lvalueFunctions = { "*?", "?[?]" };
-	if ( auto untyped = dynamic_cast<ast::UntypedExpr const *>( expr ) ) {
-		std::string fname = ast::getFunctionName( untyped );
-		return lvalueFunctions.count( fname );
-	} else if ( auto app = dynamic_cast<ast::ApplicationExpr const *>( expr ) ) {
-		if ( auto func = ast::getFunction( app ) ) {
-			return func->linkage == ast::Linkage::Intrinsic
-				&& lvalueFunctions.count( func->name );
-		}
-	}
-	return false;
-}
-
-// A maybe typed variant of the createDeref function (only UntypedExpr).
-ast::Expr * mkDeref(
-		ast::TranslationGlobal const & global, ast::Expr const * arg ) {
-	if ( global.dereference ) {
-		// Note: Reference depth can be arbitrarily deep here,
-		// so peel off the outermost pointer/reference, not just
-		// pointer because they are effecitvely equivalent in this pass
-		ast::VariableExpr * deref = new ast::VariableExpr(
-			arg->location, global.dereference );
-		deref->result = new ast::PointerType( deref->result );
-		ast::Type const * base = ast::getPointerBase( arg->result );
-		assertf( base, "expected pointer type in dereference (type was %s)", toString( arg->result ).c_str() );
-		ast::ApplicationExpr * ret =
-			new ast::ApplicationExpr( arg->location, deref, { arg } );
-		ret->result = ast::deepCopy( base );
-		return ret;
-	} else {
-		return ast::UntypedExpr::createDeref( arg->location, arg );
-	}
-}
-
-ast::Expr const * FixIntrinsicResults::postvisit(
-		ast::ApplicationExpr const * expr ) {
-	if ( skip == SkipInProgress || !isIntrinsicReference( expr ) ) {
-		return expr;
-	}
-	// Eliminate reference types from intrinsic applications
-	// now they return lvalues.
-	ast::ptr<ast::ReferenceType> result =
-			expr->result.strict_as<ast::ReferenceType>();
-	expr = ast::mutate_field( expr, &ast::ApplicationExpr::result,
-			ast::deepCopy( result->base ) );
-	if ( inIntrinsic ) {
-		return expr;
-	}
-	// When not in an intrinsic function, add a cast to don't add cast when
-	// in an intrinsic function, since they already have the cast.
-	auto * ret = new ast::CastExpr( expr->location, expr, result.get() );
-	ret->env = expr->env;
-	return ret;
-}
-
-void FixIntrinsicResults::previsit( ast::FunctionDecl const * decl ) {
-	GuardValue( inIntrinsic ) = decl->linkage == ast::Linkage::Intrinsic;
-}
-
-void AddressRef::previsit( ast::AddressExpr const * ) {
-	// Is this the first address-of in the chain?
-	GuardValue( current ) = first;
-	// Later references will not be for next address-of to be first in chain.
-	GuardValue( first ) = false;
-	// If is the outermost address-of in a chain:
-	if ( current ) {
-		// Set depth to 0 so that postvisit can
-		// find the innermost address-of easily.
-		GuardValue( refDepth ) = 0;
-	}
-}
-
-ast::Expr const * AddressRef::postvisit( ast::AddressExpr const * expr ) {
-	PRINT( std::cerr << "addr ref at " << expr << std::endl; )
-	if ( 0 == refDepth ) {
-		PRINT( std::cerr << "depth 0, get new depth..." << std::endl; )
-		// Is this the innermost address-of in a chain? record depth D.
-		if ( isIntrinsicReference( expr->arg ) ) {
-			assertf( false, "AddrRef : address-of should not have intrinsic reference argument: %s", toCString( expr->arg )  );
-		} else {
-			// try to avoid ?[?]
-			// TODO is this condition still necessary? intrinsicReferences
-			// should have a cast around them at this point, so I don't think
-			// this condition ever fires.
-			refDepth = expr->arg->result->referenceDepth();
-			PRINT( std::cerr << "arg not intrinsic reference, new depth is: " << refDepth << std::endl; )
-		}
-	}
-	if ( current ) {
-		PRINT( std::cerr << "current, depth is: " << refDepth << std::endl; )
-		ast::Expr const * ret = expr;
-		while ( refDepth ) {
-			// Add one dereference for each address-of in the chain.
-			ret = mkDeref( transUnit().global, ret );
-			--refDepth;
-		}
-
-		// if addrExpr depth is 0, then the result is a pointer because the
-		// arg was depth 1 and not lvalue. This means the dereference result
-		// is not a reference, is lvalue, and one less pointer depth than the
-		// addrExpr. Thus the cast is meaningless.
-		// TODO: One thing to double check is whether it is possible for the
-		// types to differ outside of the single pointer level (i.e. can the
-		// base type of addrExpr differ from the type of addrExpr-arg?). If
-		// so then the cast might need to be added, conditional on a more
-		// sophisticated check.
-		if ( addCast && 0 != expr->result->referenceDepth() ) {
-			PRINT( std::cerr << "adding cast to " << expr->result << std::endl; )
-			return new ast::CastExpr( expr->location,
-				ret, ast::deepCopy( expr->result ) );
-		}
-		return ret;
-	}
-	PRINT( std::cerr << "not current..." << std::endl; )
-	return expr;
-}
-
-void AddressRef::previsit( ast::Expr const * expr ) {
-	handleNonAddr( expr );
-	GuardValue( addCast ) = false;
-}
-
-// So we want to skip traversing to the head?
-ast::ApplicationExpr const * AddressRef::previsit(
-		ast::ApplicationExpr const * expr ) {
-	visit_children = false;
-	GuardValue( addCast );
-	handleNonAddr( expr );
-	auto mutExpr = ast::mutate( expr );
-	for ( ast::ptr<ast::Expr> & arg : mutExpr->args ) {
-		addCast = true;
-		arg = arg->accept( *visitor );
-	}
-	return mutExpr;
-}
-
-void AddressRef::previsit( ast::SingleInit const * ) {
-	// Each initialization context with address-of requires a cast.
-	GuardValue( addCast ) = true;
-}
-
-// idea: &&&E: get outer &, inner &
-// at inner &, record depth D of reference type of argument of &.
-// at auter &, add D derefs.
-void AddressRef::handleNonAddr( ast::Expr const * ) {
-	// non-address-of: reset status variables:
-	// * current expr is NOT the first address-of expr in an address-of chain.
-	// * next seen address-of expr IS the first in the chain.
-	GuardValue( current ) = false;
-	GuardValue( first ) = true;
-}
-
-ast::Expr const * ReferenceConversions::postvisit(
-		ast::CastExpr const * expr ) {
-	// TODO: Is it possible to convert directly between reference types with
-	// a different base. e.g.
-	//   int x;
-	//   (double&)x;
-	// At the moment, I (who?) am working off of the assumption that this is
-	// illegal, thus the cast becomes redundant after this pass, so trash the
-	// cast altogether. If that changes, care must be taken to insert the
-	// correct pointer casts in the right places.
-
-	// Note: reference depth difference is the determining factor in what
-	// code is run, rather than whether something is reference type or not,
-	// since conversion still needs to occur when both types are references
-	// that differ in depth.
-	ast::Type const * dstType = expr->result.get();
-	ast::Type const * srcType = expr->arg->result.get();
-	assertf( dstType, "Cast to no type in: %s", toCString( expr ) );
-	assertf( srcType, "Cast from no type in: %s", toCString( expr ) );
-	int dstDepth = dstType->referenceDepth();
-	int srcDepth = srcType->referenceDepth();
-	int diff = dstDepth - srcDepth;
-
-	if ( 0 < diff && !expr->arg->get_lvalue() ) {
-		// rvalue to reference conversion -- introduce temporary
-		// know that reference depth of cast argument is 0
-		//   (int &&&)3;
-		// becomes
-		//   int __ref_tmp_0 = 3;
-		//   int & __ref_tmp_1 = &__ref_tmp_0;
-		//   int && __ref_tmp_2 = &__ref_tmp_1;
-		//   &__ref_tmp_2;
-		// The last & comes from the remaining reference conversion code.
-		SemanticWarning( expr->arg->location,
-			Warning::RvalueToReferenceConversion, toCString( expr->arg ) );
-
-		static UniqueName tmpNamer( "__ref_tmp_" );
-		ast::ObjectDecl * tmp = new ast::ObjectDecl( expr->arg->location,
-			tmpNamer.newName(),
-			ast::deepCopy( expr->arg->result ),
-			new ast::SingleInit( expr->arg->location, expr->arg ) );
-		PRINT( std::cerr << "make tmp: " << tmp << std::endl; )
-		stmtsToAddBefore.push_back( new ast::DeclStmt( tmp->location, tmp ) );
-		for ( int i = 0 ; i < dstDepth - 1 ; ++i ) {
-			ast::ObjectDecl * newTmp = new ast::ObjectDecl( tmp->location,
-				tmpNamer.newName(),
-				new ast::ReferenceType( ast::deepCopy( tmp->type ) ),
-				new ast::SingleInit( tmp->location,
-					new ast::AddressExpr( tmp->location,
-						new ast::VariableExpr( tmp->location, tmp ) ) ) );
-			PRINT( std::cerr << "make tmp: " << i << ": " << newTmp << std::endl; )
-			stmtsToAddBefore.push_back(
-				new ast::DeclStmt( newTmp->location, newTmp ) );
-			tmp = newTmp;
-		}
-		// Update diff so that remaining code works out correctly.
-		expr = ast::mutate_field( expr, &ast::CastExpr::arg,
-			new ast::VariableExpr( tmp->location, tmp ) );
-		PRINT( std::cerr << "update cast to: " << expr << std::endl; )
-		srcType = expr->arg->result;
-		srcDepth = srcType->referenceDepth();
-		diff = dstDepth - srcDepth;
-		assert( 1 == diff );
-	}
-
-	// Handle conversion between different depths.
-	PRINT(
-		if ( dstDepth || srcDepth ) {
-			std::cerr << "dstType: " << dstType << " / srcType: " << srcType << '\n';
-			std::cerr << "depth: " << dstDepth << " / " << srcDepth << std::endl;
-		}
-	)
-	// Conversion to type with more depth/more references.
-	// Add address-of for each level of difference.
-	if ( 0 < diff ) {
-		ast::Expr * ret = ast::mutate( expr->arg.get() );
-		for ( int i = 0 ; i < diff ; ++i ) {
-			ret = new ast::AddressExpr( ret->location, ret );
-		}
-		if ( expr->arg->get_lvalue() &&
-				!ResolvExpr::typesCompatible(
-					srcType,
-					strict_dynamic_cast<ast::ReferenceType const *>( dstType )->base ) ) {
-			// Must keep cast if cast-to type is different from the actual type.
-			return ast::mutate_field( expr, &ast::CastExpr::arg, ret );
-		}
-		ret->env = expr->env;
-		ret->result = expr->result;
-		return ret;
-	// Conversion to type with less depth/fewer references.
-	// Add dereferences for each level of difference.
-	} else if ( diff < 0 ) {
-		ast::Expr * ret = ast::mutate( expr->arg.get() );
-		for ( int i = 0 ; i < -diff ; ++i ) {
-			ret = mkDeref( transUnit().global, ret );
-		}
-		// Must keep cast if types are different.
-		if ( !ResolvExpr::typesCompatibleIgnoreQualifiers(
-				dstType->stripReferences(),
-				srcType->stripReferences() ) ) {
-			return ast::mutate_field( expr, &ast::CastExpr::arg, ret );
-		}
-		ret->env = expr->env;
-		ret->result = expr->result;
-		// The result must be an lvalue.
-		assert( ret->get_lvalue() );
-		return ret;
-	// Conversion with the same depth.
-	} else {
-		assert( 0 == diff );
-		// Remove useless generated casts.
-		if ( expr->isGenerated &&
-				ResolvExpr::typesCompatible(
-					expr->result,
-					expr->arg->result ) ) {
-			PRINT(
-				std::cerr << "types are compatible, removing cast: " << expr << '\n';
-				std::cerr << "-- " << expr->result << '\n';
-				std::cerr << "-- " << expr->arg->result << std::endl;
-			)
-			return ast::mutate_field( expr->arg.get(),
-					&ast::Expr::env, expr->env.get() );
-		}
-		return expr;
-	}
-}
-
-ast::Expr const * ReferenceConversions::postvisit(
-		ast::AddressExpr const * expr ) {
-	// Inner expression may have been lvalue to reference conversion, which
-	// becomes an address expression. In this case, remove the outer address
-	// expression and return the argument.
-	// TODO: It's possible that this might catch too much and require a more
-	// sophisticated check. TODO What check are we talking about here?
-	return expr;
-}
-
-ast::Expr const * FixIntrinsicArgs::postvisit(
-		ast::ApplicationExpr const * expr ) {
-	// Intrinsic functions don't really take reference-typed parameters,
-	// so they require an implicit dereference on their arguments.
-	auto function = ast::getFunction( expr );
-	if ( function == nullptr ) {
-		return expr;
-	}
-
-	ast::FunctionType const * ftype = GenPoly::getFunctionType( function->get_type() );
-	assertf( ftype, "Function declaration does not have function type." );
-	// Can be of different lengths only when function is variadic.
-	assertf( ftype->params.size() == expr->args.size() || ftype->isVarArgs,
-		"ApplicationExpr args do not match formal parameter type." );
-	assertf( ftype->params.size() <= expr->args.size(),
-		"Cannot have more parameters than arguments." );
-
-	unsigned int i = 0;
-	unsigned int const end = ftype->params.size();
-
-	// This is used to make sure we get a zip on shortests.
-	if ( end == i ) return expr;
-
-	// This mutate could be redundent, but it is simpler this way.
-	auto mutExpr = ast::mutate( expr );
-
-	for ( auto pair : unsafe_group_iterate( mutExpr->args, ftype->params ) ) {
-		ast::ptr<ast::Expr> & arg = std::get<0>( pair );
-		ast::ptr<ast::Type> const & formal = std::get<1>( pair );
-		PRINT(
-			std::cerr << "pair<0>: " << arg.get() << std::endl;
-			std::cerr << " -- " << arg->result << std::endl;
-			std::cerr << "pair<1>: " << formal << std::endl;
-		)
-		//if ( dynamic_cast<ast::ReferenceType const *>( formal.get() ) ) {
-		if ( formal.as<ast::ReferenceType>() ) {
-			PRINT( std::cerr << "===formal is reference" << std::endl; )
-			// TODO: It's likely that the second condition should be
-			// `... && ! isIntrinsicReference( arg )`, but this requires
-			// investigation.
-
-			if ( ast::Linkage::Intrinsic != function->linkage
-					&& isIntrinsicReference( arg ) ) {
-				// Needed for definition of prelude functions, etc.
-				// If argument is dereference or array subscript, the result
-				// isn't REALLY a reference, but non-intrinsic functions
-				// expect a reference: take address
-
-				// TODO: OK, so this should be cut?!
-				// NODE: Previously, this condition fixed
-				//   void f(int *&);
-				//   int & x = ...;
-				//   f(&x);
-				// But now this is taken care of by a reference cast added by
-				// AddressRef. Need to find a new example or remove this
-				// branch.
-				PRINT(
-					std::cerr << "===is intrinsic arg in non-intrinsic call - adding address" << std::endl;
-				)
-				arg = new ast::AddressExpr( arg->location, arg );
-			} else if ( ast::Linkage::Intrinsic == function->linkage
-					&& arg->result->referenceDepth() != 0 ) {
-				// Argument is a 'real' reference, but function expects a C
-				// lvalue: Add a dereference to the reference-typed argument.
-				PRINT(
-					std::cerr << "===is non-intrinsic arg in intrinsic call - adding deref to arg" << std::endl;
-				)
-				ast::Type const * base = ast::getPointerBase( arg->result );
-				assertf( base, "parameter is reference, arg must be pointer or reference: %s", toString( arg->result ).c_str() );
-				ast::PointerType * ptr = new ast::PointerType( ast::deepCopy( base ) );
-				arg = ast::mutate_field( arg.get(),
-						&ast::ApplicationExpr::result, ptr );
-				arg = mkDeref( transUnit().global, arg );
-			}
-		}
-		++i;
-		if ( end == i ) break;
-	}
-	return mutExpr;
-}
-
-ast::Expr const * CollapseAddressDeref::postvisit(
-		ast::AddressExpr const * expr ) {
-	ast::Expr const * arg = expr->arg;
-	if ( isIntrinsicReference( arg ) ) {
-		std::string fname = ast::getFunctionName( arg );
-		if ( fname == "*?" ) {
-			ast::Expr const * arg0 = ast::getCallArg( arg, 0 );
-			ast::Expr * ret = ast::mutate( arg0 );
-			ret->env = expr->env;
-			return ret;
-		}
-	} else if ( auto cast = dynamic_cast<ast::CastExpr const *>( arg ) ) {
-		// Need to move cast to pointer type out a level since address of
-		// pointer is not valid C code (can be introduced in prior passes,
-		// e.g., InstantiateGeneric)
-		if ( ast::getPointerBase( cast->result ) ) {
-			auto mutExpr = ast::mutate( expr );
-			auto mutCast = strict_dynamic_cast<ast::CastExpr *>(
-					ast::mutate( mutExpr->arg.release() ) );
-			mutExpr->arg = mutCast->arg;
-			mutCast->arg = mutExpr;
-			mutCast->result = new ast::PointerType( mutCast->result );
-			return mutCast;
-		}
-	}
-	return expr;
-}
-
-ast::Expr const * CollapseAddressDeref::postvisit(
-		ast::ApplicationExpr const * expr ) {
-	if ( isIntrinsicReference( expr ) ) {
-		std::string fname = ast::getFunctionName( expr );
-		if ( fname == "*?" ) {
-			assert( 1 == expr->args.size() );
-			ast::Expr const * arg = ast::getCallArg( expr, 0 );
-			// xxx - this isn't right, because it can remove casts that
-			// should be there...
-			//	while ( auto cast = dynamic_cast< ast::CastExpr const * >( arg ) ) {
-			//		arg = cast->arg;
-			//	}
-			if ( auto addr = dynamic_cast<ast::AddressExpr const *>( arg ) ) {
-				return ast::mutate_field( addr->arg.get(),
-						&ast::Expr::env, expr->env.get() );
-			}
-		}
-	}
-	return expr;
-}
-
-ast::Expr const * GeneralizedLvalue::postvisit(
-		ast::AddressExpr const * expr ) {
-	return applyTransformation( expr, &ast::AddressExpr::arg,
-		[]( ast::Expr const * arg ) {
-			return new ast::AddressExpr( arg->location, arg );
-		}
-	);
-}
-
-ast::Expr const * GeneralizedLvalue::postvisit(
-		ast::MemberExpr const * expr ) {
-	return applyTransformation( expr, &ast::MemberExpr::aggregate,
-		[expr]( ast::Expr const * aggr ) {
-			return new ast::MemberExpr( aggr->location, expr->member, aggr );
-		}
-	);
-}
-
-template<typename Node, typename Func>
-ast::Expr const * GeneralizedLvalue::applyTransformation(
-		Node const * expr, ast::ptr<ast::Expr> Node::*field, Func mkExpr ) {
-	ast::ptr<ast::Expr> const & arg = expr->*field;
-	if ( auto commaArg = arg.as<ast::CommaExpr>() ) {
-		ast::Expr const * arg1 = ast::deepCopy( commaArg->arg1 );
-		ast::Expr const * arg2 = ast::deepCopy( commaArg->arg2 );
-		ast::Expr const * ret = new ast::CommaExpr(
-			commaArg->location, arg1, mkExpr( arg2 )->accept( *visitor ) );
-		return ret;
-	} else if ( auto condArg = arg.as<ast::ConditionalExpr>() ) {
-		ast::Expr const * arg1 = ast::deepCopy( condArg->arg1 );
-		ast::Expr const * arg2 = ast::deepCopy( condArg->arg2 );
-		ast::Expr const * arg3 = ast::deepCopy( condArg->arg3 );
-		ast::ConditionalExpr * ret = new ast::ConditionalExpr(
-			condArg->location, arg1, mkExpr( arg2 )->accept( *visitor ),
-			mkExpr( arg3 )->accept( *visitor ) );
-
-		// Conditional expr type may not be either of the arguments,
-		// so unify to get the result.
-		// TODO: Maybe I could create a wrapper for this.
-		ast::ptr<ast::Type> common = nullptr;
-		ast::TypeEnvironment newEnv;
-		ast::AssertionSet needAssertions, haveAssertions;
-		ast::OpenVarSet openVars;
-		ResolvExpr::unify( ret->arg2->result, ret->arg3->result, newEnv,
-			needAssertions, haveAssertions, openVars, common );
-		ret->result = common ? common : ast::deepCopy( ret->arg2->result );
-		return ret;
-	}
-	return expr;
-}
-
-ast::Type const * ReferenceTypeElimination::postvisit(
-		ast::ReferenceType const * type ) {
-	return new ast::PointerType( type->base, type->qualifiers );
-}
-
-} // namespace
-
-// Stored elsewhere (Lvalue2, initially false):
-extern bool referencesEliminated;
-
-void convertLvalue( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<FixIntrinsicResults>::run( translationUnit );
-	ast::Pass<AddressRef>::run( translationUnit );
-	ast::Pass<ReferenceConversions>::run( translationUnit );
-	ast::Pass<FixIntrinsicArgs>::run( translationUnit );
-	ast::Pass<CollapseAddressDeref>::run( translationUnit );
-	ast::Pass<GeneralizedLvalue>::run( translationUnit );
-	// Last because other passes need reference types to work.
-	ast::Pass<ReferenceTypeElimination>::run( translationUnit );
-	// From this point forward, nothing should create reference types.
-	referencesEliminated = true;
-}
-
-ast::Expr const * generalizedLvalue( ast::Expr const * expr ) {
-	ast::Pass<GeneralizedLvalue> visitor;
-	return expr->accept( visitor );
-}
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/Specialize.cpp
===================================================================
--- src/GenPoly/Specialize.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/GenPoly/Specialize.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,478 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Specialize.cpp -- Generate thunks to specialize polymorphic functions.
+//
+// Author           : Andrew Beach
+// Created On       : Tue Jun  7 13:37:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Jun  7 13:37:00 2022
+// Update Count     : 0
+//
+
+#include "Specialize.h"
+
+#include "AST/Copy.hpp"                  // for deepCopy
+#include "AST/Inspect.hpp"               // for isIntrinsicCallExpr
+#include "AST/Pass.hpp"                  // for Pass
+#include "AST/TypeEnvironment.hpp"       // for OpenVarSet, AssertionSet
+#include "Common/UniqueName.h"           // for UniqueName
+#include "GenPoly/GenPoly.h"             // for getFunctionType
+#include "ResolvExpr/FindOpenVars.h"     // for findOpenVars
+
+namespace GenPoly {
+
+namespace {
+
+struct SpecializeCore final :
+		public ast::WithConstTypeSubstitution,
+		public ast::WithDeclsToAdd<>,
+		public ast::WithVisitorRef<SpecializeCore> {
+	std::string paramPrefix = "_p";
+
+	ast::ApplicationExpr * handleExplicitParams(
+		const ast::ApplicationExpr * expr );
+	const ast::Expr * createThunkFunction(
+		const CodeLocation & location,
+		const ast::FunctionType * funType,
+		const ast::Expr * actual,
+		const ast::InferredParams * inferParams );
+	const ast::Expr * doSpecialization(
+		const CodeLocation & location,
+		const ast::Type * formalType,
+		const ast::Expr * actual,
+		const ast::InferredParams * inferParams );
+
+	const ast::Expr * postvisit( const ast::ApplicationExpr * expr );
+	const ast::Expr * postvisit( const ast::CastExpr * expr );
+};
+
+const ast::InferredParams * getInferredParams( const ast::Expr * expr ) {
+	const ast::Expr::InferUnion & inferred = expr->inferred;
+	if ( inferred.hasParams() ) {
+		return &inferred.inferParams();
+	} else {
+		return nullptr;
+	}
+}
+
+// Check if both types have the same structure. The leaf (non-tuple) types
+// don't have to match but the tuples must match.
+bool isTupleStructureMatching( const ast::Type * t0, const ast::Type * t1 ) {
+	const ast::TupleType * tt0 = dynamic_cast<const ast::TupleType *>( t0 );
+	const ast::TupleType * tt1 = dynamic_cast<const ast::TupleType *>( t1 );
+	if ( tt0 && tt1 ) {
+		if ( tt0->size() != tt1->size() ) {
+			return false;
+		}
+		for ( auto types : group_iterate( tt0->types, tt1->types ) ) {
+			if ( !isTupleStructureMatching(
+					std::get<0>( types ), std::get<1>( types ) ) ) {
+				return false;
+			}
+		}
+		return true;
+	}
+	return (!tt0 && !tt1);
+}
+
+// The number of elements in a list, if all tuples had been flattened.
+size_t flatTypeListSize( const std::vector<ast::ptr<ast::Type>> & types ) {
+	size_t sum = 0;
+	for ( const ast::ptr<ast::Type> & type : types ) {
+		if ( const ast::TupleType * tuple = type.as<ast::TupleType>() ) {
+			sum += flatTypeListSize( tuple->types );
+		} else {
+			sum += 1;
+		}
+	}
+	return sum;
+}
+
+// Find the total number of components in a parameter list.
+size_t functionParameterSize( const ast::FunctionType * type ) {
+	return flatTypeListSize( type->params );
+}
+
+bool needsPolySpecialization(
+		const ast::Type * /*formalType*/,
+		const ast::Type * actualType,
+		const ast::TypeSubstitution * subs ) {
+	if ( !subs ) {
+		return false;
+	}
+
+	using namespace ResolvExpr;
+	ast::OpenVarSet openVars, closedVars;
+	ast::AssertionSet need, have; // unused
+	ast::TypeEnvironment env; // unused
+	// findOpenVars( formalType, openVars, closedVars, need, have, FirstClosed );
+	findOpenVars( actualType, openVars, closedVars, need, have, env, FirstOpen );
+	for ( const ast::OpenVarSet::value_type & openVar : openVars ) {
+		const ast::Type * boundType = subs->lookup( openVar.first );
+		// If the variable is not bound, move onto the next variable.
+		if ( !boundType ) continue;
+
+		// Is the variable cound to another type variable?
+		if ( auto inst = dynamic_cast<const ast::TypeInstType *>( boundType ) ) {
+			if ( closedVars.find( *inst ) == closedVars.end() ) {
+				return true;
+			} else {
+				assertf(false, "closed: %s", inst->name.c_str());
+			}
+		// Otherwise, the variable is bound to a concrete type.
+		} else {
+			return true;
+		}
+	}
+	// None of the type variables are bound.
+	return false;
+}
+
+bool needsTupleSpecialization(
+		const ast::Type * formalType, const ast::Type * actualType ) {
+	// Needs tuple specialization if the structure of the formal type and
+	// actual type do not match.
+
+	// This is the case if the formal type has ttype polymorphism, or if the structure  of tuple types
+	// between the function do not match exactly.
+	if ( const ast::FunctionType * ftype = getFunctionType( formalType ) ) {
+		// A pack in the parameter or return type requires specialization.
+		if ( ftype->isTtype() ) {
+			return true;
+		}
+		// Conversion of 0 to a function type does not require specialization.
+		if ( dynamic_cast<const ast::ZeroType *>( actualType ) ) {
+			return false;
+		}
+		const ast::FunctionType * atype =
+			getFunctionType( actualType->stripReferences() );
+		assertf( atype,
+			"formal type is a function type, but actual type is not: %s",
+			toString( actualType ).c_str() );
+		// Can't tuple specialize if parameter sizes deeply-differ.
+		if ( functionParameterSize( ftype ) != functionParameterSize( atype ) ) {
+			return false;
+		}
+		// If tuple parameter size matches but actual parameter sizes differ
+		// then there needs to be specialization.
+		if ( ftype->params.size() != atype->params.size() ) {
+			return true;
+		}
+		// Total parameter size can be the same, while individual parameters
+		// can have different structure.
+		for ( auto pairs : group_iterate( ftype->params, atype->params ) ) {
+			if ( !isTupleStructureMatching(
+					std::get<0>( pairs ), std::get<1>( pairs ) ) ) {
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+bool needsSpecialization(
+		const ast::Type * formalType, const ast::Type * actualType,
+		const ast::TypeSubstitution * subs ) {
+	return needsPolySpecialization( formalType, actualType, subs )
+		|| needsTupleSpecialization( formalType, actualType );
+}
+
+ast::ApplicationExpr * SpecializeCore::handleExplicitParams(
+		const ast::ApplicationExpr * expr ) {
+	assert( expr->func->result );
+	const ast::FunctionType * func = getFunctionType( expr->func->result );
+	assert( func );
+
+	ast::ApplicationExpr * mut = ast::mutate( expr );
+
+	std::vector<ast::ptr<ast::Type>>::const_iterator formal;
+	std::vector<ast::ptr<ast::Expr>>::iterator actual;
+	for ( formal = func->params.begin(), actual = mut->args.begin() ;
+			formal != func->params.end() && actual != mut->args.end() ;
+			++formal, ++actual ) {
+		*actual = doSpecialization( (*actual)->location,
+			*formal, *actual, getInferredParams( expr ) );
+	}
+	return mut;
+}
+
+// Explode assuming simple cases: either type is pure tuple (but not tuple
+// expr) or type is non-tuple.
+template<typename OutputIterator>
+void explodeSimple( const CodeLocation & location,
+		const ast::Expr * expr, OutputIterator out ) {
+	// Recurse on tuple types using index expressions on each component.
+	if ( auto tuple = expr->result.as<ast::TupleType>() ) {
+		ast::ptr<ast::Expr> cleanup = expr;
+		for ( unsigned int i = 0 ; i < tuple->size() ; ++i ) {
+			explodeSimple( location,
+				new ast::TupleIndexExpr( location, expr, i ), out );
+		}
+	// For a non-tuple type, output a clone of the expression.
+	} else {
+		*out++ = expr;
+	}
+}
+
+// Restructures arguments to match the structure of the formal parameters
+// of the actual function. Returns the next structured argument.
+template<typename Iterator>
+const ast::Expr * structureArg(
+		const CodeLocation& location, const ast::ptr<ast::Type> & type,
+		Iterator & begin, const Iterator & end ) {
+	if ( auto tuple = type.as<ast::TupleType>() ) {
+		std::vector<ast::ptr<ast::Expr>> exprs;
+		for ( const ast::ptr<ast::Type> & t : *tuple ) {
+			exprs.push_back( structureArg( location, t, begin, end ) );
+		}
+		return new ast::TupleExpr( location, std::move( exprs ) );
+	} else {
+		assertf( begin != end, "reached the end of the arguments while structuring" );
+		return *begin++;
+	}
+}
+
+struct TypeInstFixer final : public ast::WithShortCircuiting {
+	std::map<const ast::TypeDecl *, std::pair<int, int>> typeMap;
+
+	void previsit(const ast::TypeDecl *) { visit_children = false; }
+	const ast::TypeInstType * postvisit(const ast::TypeInstType * typeInst) {
+		if (typeMap.count(typeInst->base)) {
+			ast::TypeInstType * newInst = mutate(typeInst);
+			auto const & pair = typeMap[typeInst->base];
+			newInst->expr_id = pair.first;
+			newInst->formal_usage = pair.second;
+			return newInst;
+		}
+		return typeInst;
+	}
+};
+
+const ast::Expr * SpecializeCore::createThunkFunction(
+		const CodeLocation & location,
+		const ast::FunctionType * funType,
+		const ast::Expr * actual,
+		const ast::InferredParams * inferParams ) {
+	// One set of unique names per program.
+	static UniqueName thunkNamer("_thunk");
+
+	const ast::FunctionType * newType = ast::deepCopy( funType );
+	if ( typeSubs ) {
+		// Must replace only occurrences of type variables
+		// that occure free in the thunk's type.
+		auto result = typeSubs->applyFree( newType );
+		newType = result.node.release();
+	}
+
+	using DWTVector = std::vector<ast::ptr<ast::DeclWithType>>;
+	using DeclVector = std::vector<ast::ptr<ast::TypeDecl>>;
+
+	UniqueName paramNamer( paramPrefix );
+
+	// Create new thunk with same signature as formal type.
+	ast::Pass<TypeInstFixer> fixer;
+	for (const auto & kv : newType->forall) {
+		if (fixer.core.typeMap.count(kv->base)) {
+			std::cerr << location << ' ' << kv->base->name
+				<< ' ' << kv->expr_id << '_' << kv->formal_usage
+				<< ',' << fixer.core.typeMap[kv->base].first
+				<< '_' << fixer.core.typeMap[kv->base].second << std::endl;
+			assertf(false, "multiple formals in specialize");
+		}
+		else {
+			fixer.core.typeMap[kv->base] = std::make_pair(kv->expr_id, kv->formal_usage);
+		}
+	}
+
+	ast::CompoundStmt * thunkBody = new ast::CompoundStmt( location );
+	ast::FunctionDecl * thunkFunc = new ast::FunctionDecl(
+		location,
+		thunkNamer.newName(),
+		map_range<DeclVector>( newType->forall, []( const ast::TypeInstType * inst ) {
+			return ast::deepCopy( inst->base );
+		} ),
+		map_range<DWTVector>( newType->assertions, []( const ast::VariableExpr * expr ) {
+			return ast::deepCopy( expr->var );
+		} ),
+		map_range<DWTVector>( newType->params, [&location, &paramNamer]( const ast::Type * type ) {
+			return new ast::ObjectDecl( location, paramNamer.newName(), ast::deepCopy( type ) );
+		} ),
+		map_range<DWTVector>( newType->returns, [&location, &paramNamer]( const ast::Type * type ) {
+			return new ast::ObjectDecl( location, paramNamer.newName(), ast::deepCopy( type ) );
+		} ),
+		thunkBody,
+		ast::Storage::Classes(),
+		ast::Linkage::C
+		);
+
+	thunkFunc->fixUniqueId();
+
+	// Thunks may be generated and not used, avoid them.
+	thunkFunc->attributes.push_back( new ast::Attribute( "unused" ) );
+
+	// Global thunks must be static to avoid collitions.
+	// Nested thunks must not be unique and hence, not static.
+	thunkFunc->storage.is_static = !isInFunction();
+
+	// Weave thunk parameters into call to actual function,
+	// naming thunk parameters as we go.
+	ast::ApplicationExpr * app = new ast::ApplicationExpr( location, actual );
+
+	const ast::FunctionType * actualType = ast::deepCopy( getFunctionType( actual->result ) );
+	if ( typeSubs ) {
+		// Need to apply the environment to the actual function's type,
+		// since it may itself be polymorphic.
+		auto result = typeSubs->apply( actualType );
+		actualType = result.node.release();
+	}
+
+	ast::ptr<ast::FunctionType> actualTypeManager = actualType;
+
+	std::vector<ast::ptr<ast::Expr>> args;
+	for ( ast::ptr<ast::DeclWithType> & param : thunkFunc->params ) {
+		// Name each thunk parameter and explode it.
+		// These are then threaded back into the actual function call.
+		ast::DeclWithType * mutParam = ast::mutate( param.get() );
+		explodeSimple( location, new ast::VariableExpr( location, mutParam ),
+			std::back_inserter( args ) );
+	}
+
+	// Walk parameters to the actual function alongside the exploded thunk
+	// parameters and restructure the arguments to match the actual parameters.
+	std::vector<ast::ptr<ast::Expr>>::iterator
+		argBegin = args.begin(), argEnd = args.end();
+	for ( const auto & actualArg : actualType->params ) {
+		app->args.push_back(
+			structureArg( location, actualArg.get(), argBegin, argEnd ) );
+	}
+	assertf( argBegin == argEnd, "Did not structure all arguments." );
+
+	app->accept(fixer); // this should modify in place
+
+	app->env = ast::TypeSubstitution::newFromExpr( app, typeSubs );
+	if ( inferParams ) {
+		app->inferred.inferParams() = *inferParams;
+	}
+
+	// Handle any specializations that may still be present.
+	{
+		std::string oldParamPrefix = paramPrefix;
+		paramPrefix += "p";
+		std::list<ast::ptr<ast::Decl>> oldDecls;
+		oldDecls.splice( oldDecls.end(), declsToAddBefore );
+
+		app->accept( *visitor );
+		// Write recursive specializations into the thunk body.
+		for ( const ast::ptr<ast::Decl> & decl : declsToAddBefore ) {
+			thunkBody->push_back( new ast::DeclStmt( decl->location, decl ) );
+		}
+
+		declsToAddBefore = std::move( oldDecls );
+		paramPrefix = std::move( oldParamPrefix );
+	}
+
+	// Add return (or valueless expression) to the thunk.
+	ast::Stmt * appStmt;
+	if ( funType->returns.empty() ) {
+		appStmt = new ast::ExprStmt( app->location, app );
+	} else {
+		appStmt = new ast::ReturnStmt( app->location, app );
+	}
+	thunkBody->push_back( appStmt );
+
+	// Add the thunk definition:
+	declsToAddBefore.push_back( thunkFunc );
+
+	// Return address of thunk function as replacement expression.
+	return new ast::AddressExpr( location,
+		new ast::VariableExpr( location, thunkFunc ) );
+}
+
+const ast::Expr * SpecializeCore::doSpecialization(
+		const CodeLocation & location,
+		const ast::Type * formalType,
+		const ast::Expr * actual,
+		const ast::InferredParams * inferParams ) {
+	assertf( actual->result, "attempting to specialize an untyped expression" );
+	if ( needsSpecialization( formalType, actual->result, typeSubs ) ) {
+		if ( const ast::FunctionType * type = getFunctionType( formalType ) ) {
+			if ( const ast::ApplicationExpr * expr =
+					dynamic_cast<const ast::ApplicationExpr *>( actual ) ) {
+				return createThunkFunction( location, type, expr->func, inferParams );
+			} else if ( auto expr =
+					dynamic_cast<const ast::VariableExpr *>( actual ) ) {
+				return createThunkFunction( location, type, expr, inferParams );
+			} else {
+				// (I don't even know what that comment means.)
+				// This likely won't work, as anything that could build an ApplicationExpr probably hit one of the previous two branches
+				return createThunkFunction( location, type, actual, inferParams );
+			}
+		} else {
+			return actual;
+		}
+	} else {
+		return actual;
+	}
+}
+
+const ast::Expr * SpecializeCore::postvisit(
+		const ast::ApplicationExpr * expr ) {
+	if ( ast::isIntrinsicCallExpr( expr ) ) {
+		return expr;
+	}
+
+	// Create thunks for the inferred parameters.
+	// This is not needed for intrinsic calls, because they aren't
+	// actually passed to the function. It needs to handle explicit params
+	// before inferred params so that explicit params do not recieve a
+	// changed set of inferParams (and change them again).
+	// Alternatively, if order starts to matter then copy expr's inferParams
+	// and pass them to handleExplicitParams.
+	ast::ApplicationExpr * mut = handleExplicitParams( expr );
+	if ( !mut->inferred.hasParams() ) {
+		return mut;
+	}
+	ast::InferredParams & inferParams = mut->inferred.inferParams();
+	for ( ast::InferredParams::value_type & inferParam : inferParams ) {
+		inferParam.second.expr = doSpecialization(
+			inferParam.second.expr->location,
+			inferParam.second.formalType,
+			inferParam.second.expr,
+			getInferredParams( inferParam.second.expr )
+		);
+	}
+	return mut;
+}
+
+const ast::Expr * SpecializeCore::postvisit( const ast::CastExpr * expr ) {
+	if ( expr->result->isVoid() ) {
+		// No specialization if there is no return value.
+		return expr;
+	}
+	const ast::Expr * specialized = doSpecialization(
+		expr->location, expr->result, expr->arg, getInferredParams( expr ) );
+	if ( specialized != expr->arg ) {
+		// Assume that the specialization incorporates the cast.
+		return specialized;
+	} else {
+		return expr;
+	}
+}
+
+} // namespace
+
+void convertSpecializations( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<SpecializeCore>::run( translationUnit );
+}
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/GenPoly/SpecializeNew.cpp
===================================================================
--- src/GenPoly/SpecializeNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,478 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// SpecializeNew.cpp -- Generate thunks to specialize polymorphic functions.
-//
-// Author           : Andrew Beach
-// Created On       : Tue Jun  7 13:37:00 2022
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Jun  7 13:37:00 2022
-// Update Count     : 0
-//
-
-#include "Specialize.h"
-
-#include "AST/Copy.hpp"                  // for deepCopy
-#include "AST/Inspect.hpp"               // for isIntrinsicCallExpr
-#include "AST/Pass.hpp"                  // for Pass
-#include "AST/TypeEnvironment.hpp"       // for OpenVarSet, AssertionSet
-#include "Common/UniqueName.h"           // for UniqueName
-#include "GenPoly/GenPoly.h"             // for getFunctionType
-#include "ResolvExpr/FindOpenVars.h"     // for findOpenVars
-
-namespace GenPoly {
-
-namespace {
-
-struct SpecializeCore final :
-		public ast::WithConstTypeSubstitution,
-		public ast::WithDeclsToAdd<>,
-		public ast::WithVisitorRef<SpecializeCore> {
-	std::string paramPrefix = "_p";
-
-	ast::ApplicationExpr * handleExplicitParams(
-		const ast::ApplicationExpr * expr );
-	const ast::Expr * createThunkFunction(
-		const CodeLocation & location,
-		const ast::FunctionType * funType,
-		const ast::Expr * actual,
-		const ast::InferredParams * inferParams );
-	const ast::Expr * doSpecialization(
-		const CodeLocation & location,
-		const ast::Type * formalType,
-		const ast::Expr * actual,
-		const ast::InferredParams * inferParams );
-
-	const ast::Expr * postvisit( const ast::ApplicationExpr * expr );
-	const ast::Expr * postvisit( const ast::CastExpr * expr );
-};
-
-const ast::InferredParams * getInferredParams( const ast::Expr * expr ) {
-	const ast::Expr::InferUnion & inferred = expr->inferred;
-	if ( inferred.hasParams() ) {
-		return &inferred.inferParams();
-	} else {
-		return nullptr;
-	}
-}
-
-// Check if both types have the same structure. The leaf (non-tuple) types
-// don't have to match but the tuples must match.
-bool isTupleStructureMatching( const ast::Type * t0, const ast::Type * t1 ) {
-	const ast::TupleType * tt0 = dynamic_cast<const ast::TupleType *>( t0 );
-	const ast::TupleType * tt1 = dynamic_cast<const ast::TupleType *>( t1 );
-	if ( tt0 && tt1 ) {
-		if ( tt0->size() != tt1->size() ) {
-			return false;
-		}
-		for ( auto types : group_iterate( tt0->types, tt1->types ) ) {
-			if ( !isTupleStructureMatching(
-					std::get<0>( types ), std::get<1>( types ) ) ) {
-				return false;
-			}
-		}
-		return true;
-	}
-	return (!tt0 && !tt1);
-}
-
-// The number of elements in a list, if all tuples had been flattened.
-size_t flatTypeListSize( const std::vector<ast::ptr<ast::Type>> & types ) {
-	size_t sum = 0;
-	for ( const ast::ptr<ast::Type> & type : types ) {
-		if ( const ast::TupleType * tuple = type.as<ast::TupleType>() ) {
-			sum += flatTypeListSize( tuple->types );
-		} else {
-			sum += 1;
-		}
-	}
-	return sum;
-}
-
-// Find the total number of components in a parameter list.
-size_t functionParameterSize( const ast::FunctionType * type ) {
-	return flatTypeListSize( type->params );
-}
-
-bool needsPolySpecialization(
-		const ast::Type * /*formalType*/,
-		const ast::Type * actualType,
-		const ast::TypeSubstitution * subs ) {
-	if ( !subs ) {
-		return false;
-	}
-
-	using namespace ResolvExpr;
-	ast::OpenVarSet openVars, closedVars;
-	ast::AssertionSet need, have; // unused
-	ast::TypeEnvironment env; // unused
-	// findOpenVars( formalType, openVars, closedVars, need, have, FirstClosed );
-	findOpenVars( actualType, openVars, closedVars, need, have, env, FirstOpen );
-	for ( const ast::OpenVarSet::value_type & openVar : openVars ) {
-		const ast::Type * boundType = subs->lookup( openVar.first );
-		// If the variable is not bound, move onto the next variable.
-		if ( !boundType ) continue;
-
-		// Is the variable cound to another type variable?
-		if ( auto inst = dynamic_cast<const ast::TypeInstType *>( boundType ) ) {
-			if ( closedVars.find( *inst ) == closedVars.end() ) {
-				return true;
-			} else {
-				assertf(false, "closed: %s", inst->name.c_str());
-			}
-		// Otherwise, the variable is bound to a concrete type.
-		} else {
-			return true;
-		}
-	}
-	// None of the type variables are bound.
-	return false;
-}
-
-bool needsTupleSpecialization(
-		const ast::Type * formalType, const ast::Type * actualType ) {
-	// Needs tuple specialization if the structure of the formal type and
-	// actual type do not match.
-
-	// This is the case if the formal type has ttype polymorphism, or if the structure  of tuple types
-	// between the function do not match exactly.
-	if ( const ast::FunctionType * ftype = getFunctionType( formalType ) ) {
-		// A pack in the parameter or return type requires specialization.
-		if ( ftype->isTtype() ) {
-			return true;
-		}
-		// Conversion of 0 to a function type does not require specialization.
-		if ( dynamic_cast<const ast::ZeroType *>( actualType ) ) {
-			return false;
-		}
-		const ast::FunctionType * atype =
-			getFunctionType( actualType->stripReferences() );
-		assertf( atype,
-			"formal type is a function type, but actual type is not: %s",
-			toString( actualType ).c_str() );
-		// Can't tuple specialize if parameter sizes deeply-differ.
-		if ( functionParameterSize( ftype ) != functionParameterSize( atype ) ) {
-			return false;
-		}
-		// If tuple parameter size matches but actual parameter sizes differ
-		// then there needs to be specialization.
-		if ( ftype->params.size() != atype->params.size() ) {
-			return true;
-		}
-		// Total parameter size can be the same, while individual parameters
-		// can have different structure.
-		for ( auto pairs : group_iterate( ftype->params, atype->params ) ) {
-			if ( !isTupleStructureMatching(
-					std::get<0>( pairs ), std::get<1>( pairs ) ) ) {
-				return true;
-			}
-		}
-	}
-	return false;
-}
-
-bool needsSpecialization(
-		const ast::Type * formalType, const ast::Type * actualType,
-		const ast::TypeSubstitution * subs ) {
-	return needsPolySpecialization( formalType, actualType, subs )
-		|| needsTupleSpecialization( formalType, actualType );
-}
-
-ast::ApplicationExpr * SpecializeCore::handleExplicitParams(
-		const ast::ApplicationExpr * expr ) {
-	assert( expr->func->result );
-	const ast::FunctionType * func = getFunctionType( expr->func->result );
-	assert( func );
-
-	ast::ApplicationExpr * mut = ast::mutate( expr );
-
-	std::vector<ast::ptr<ast::Type>>::const_iterator formal;
-	std::vector<ast::ptr<ast::Expr>>::iterator actual;
-	for ( formal = func->params.begin(), actual = mut->args.begin() ;
-			formal != func->params.end() && actual != mut->args.end() ;
-			++formal, ++actual ) {
-		*actual = doSpecialization( (*actual)->location,
-			*formal, *actual, getInferredParams( expr ) );
-	}
-	return mut;
-}
-
-// Explode assuming simple cases: either type is pure tuple (but not tuple
-// expr) or type is non-tuple.
-template<typename OutputIterator>
-void explodeSimple( const CodeLocation & location,
-		const ast::Expr * expr, OutputIterator out ) {
-	// Recurse on tuple types using index expressions on each component.
-	if ( auto tuple = expr->result.as<ast::TupleType>() ) {
-		ast::ptr<ast::Expr> cleanup = expr;
-		for ( unsigned int i = 0 ; i < tuple->size() ; ++i ) {
-			explodeSimple( location,
-				new ast::TupleIndexExpr( location, expr, i ), out );
-		}
-	// For a non-tuple type, output a clone of the expression.
-	} else {
-		*out++ = expr;
-	}
-}
-
-// Restructures arguments to match the structure of the formal parameters
-// of the actual function. Returns the next structured argument.
-template<typename Iterator>
-const ast::Expr * structureArg(
-		const CodeLocation& location, const ast::ptr<ast::Type> & type,
-		Iterator & begin, const Iterator & end ) {
-	if ( auto tuple = type.as<ast::TupleType>() ) {
-		std::vector<ast::ptr<ast::Expr>> exprs;
-		for ( const ast::ptr<ast::Type> & t : *tuple ) {
-			exprs.push_back( structureArg( location, t, begin, end ) );
-		}
-		return new ast::TupleExpr( location, std::move( exprs ) );
-	} else {
-		assertf( begin != end, "reached the end of the arguments while structuring" );
-		return *begin++;
-	}
-}
-
-struct TypeInstFixer final : public ast::WithShortCircuiting {
-	std::map<const ast::TypeDecl *, std::pair<int, int>> typeMap;
-
-	void previsit(const ast::TypeDecl *) { visit_children = false; }
-	const ast::TypeInstType * postvisit(const ast::TypeInstType * typeInst) {
-		if (typeMap.count(typeInst->base)) {
-			ast::TypeInstType * newInst = mutate(typeInst);
-			auto const & pair = typeMap[typeInst->base];
-			newInst->expr_id = pair.first;
-			newInst->formal_usage = pair.second;
-			return newInst;
-		}
-		return typeInst;
-	}
-};
-
-const ast::Expr * SpecializeCore::createThunkFunction(
-		const CodeLocation & location,
-		const ast::FunctionType * funType,
-		const ast::Expr * actual,
-		const ast::InferredParams * inferParams ) {
-	// One set of unique names per program.
-	static UniqueName thunkNamer("_thunk");
-
-	const ast::FunctionType * newType = ast::deepCopy( funType );
-	if ( typeSubs ) {
-		// Must replace only occurrences of type variables
-		// that occure free in the thunk's type.
-		auto result = typeSubs->applyFree( newType );
-		newType = result.node.release();
-	}
-
-	using DWTVector = std::vector<ast::ptr<ast::DeclWithType>>;
-	using DeclVector = std::vector<ast::ptr<ast::TypeDecl>>;
-
-	UniqueName paramNamer( paramPrefix );
-
-	// Create new thunk with same signature as formal type.
-	ast::Pass<TypeInstFixer> fixer;
-	for (const auto & kv : newType->forall) {
-		if (fixer.core.typeMap.count(kv->base)) {
-			std::cerr << location << ' ' << kv->base->name
-				<< ' ' << kv->expr_id << '_' << kv->formal_usage
-				<< ',' << fixer.core.typeMap[kv->base].first
-				<< '_' << fixer.core.typeMap[kv->base].second << std::endl;
-			assertf(false, "multiple formals in specialize");
-		}
-		else {
-			fixer.core.typeMap[kv->base] = std::make_pair(kv->expr_id, kv->formal_usage);
-		}
-	}
-
-	ast::CompoundStmt * thunkBody = new ast::CompoundStmt( location );
-	ast::FunctionDecl * thunkFunc = new ast::FunctionDecl(
-		location,
-		thunkNamer.newName(),
-		map_range<DeclVector>( newType->forall, []( const ast::TypeInstType * inst ) {
-			return ast::deepCopy( inst->base );
-		} ),
-		map_range<DWTVector>( newType->assertions, []( const ast::VariableExpr * expr ) {
-			return ast::deepCopy( expr->var );
-		} ),
-		map_range<DWTVector>( newType->params, [&location, &paramNamer]( const ast::Type * type ) {
-			return new ast::ObjectDecl( location, paramNamer.newName(), ast::deepCopy( type ) );
-		} ),
-		map_range<DWTVector>( newType->returns, [&location, &paramNamer]( const ast::Type * type ) {
-			return new ast::ObjectDecl( location, paramNamer.newName(), ast::deepCopy( type ) );
-		} ),
-		thunkBody,
-		ast::Storage::Classes(),
-		ast::Linkage::C
-		);
-
-	thunkFunc->fixUniqueId();
-
-	// Thunks may be generated and not used, avoid them.
-	thunkFunc->attributes.push_back( new ast::Attribute( "unused" ) );
-
-	// Global thunks must be static to avoid collitions.
-	// Nested thunks must not be unique and hence, not static.
-	thunkFunc->storage.is_static = !isInFunction();
-
-	// Weave thunk parameters into call to actual function,
-	// naming thunk parameters as we go.
-	ast::ApplicationExpr * app = new ast::ApplicationExpr( location, actual );
-
-	const ast::FunctionType * actualType = ast::deepCopy( getFunctionType( actual->result ) );
-	if ( typeSubs ) {
-		// Need to apply the environment to the actual function's type,
-		// since it may itself be polymorphic.
-		auto result = typeSubs->apply( actualType );
-		actualType = result.node.release();
-	}
-
-	ast::ptr<ast::FunctionType> actualTypeManager = actualType;
-
-	std::vector<ast::ptr<ast::Expr>> args;
-	for ( ast::ptr<ast::DeclWithType> & param : thunkFunc->params ) {
-		// Name each thunk parameter and explode it.
-		// These are then threaded back into the actual function call.
-		ast::DeclWithType * mutParam = ast::mutate( param.get() );
-		explodeSimple( location, new ast::VariableExpr( location, mutParam ),
-			std::back_inserter( args ) );
-	}
-
-	// Walk parameters to the actual function alongside the exploded thunk
-	// parameters and restructure the arguments to match the actual parameters.
-	std::vector<ast::ptr<ast::Expr>>::iterator
-		argBegin = args.begin(), argEnd = args.end();
-	for ( const auto & actualArg : actualType->params ) {
-		app->args.push_back(
-			structureArg( location, actualArg.get(), argBegin, argEnd ) );
-	}
-	assertf( argBegin == argEnd, "Did not structure all arguments." );
-
-	app->accept(fixer); // this should modify in place
-
-	app->env = ast::TypeSubstitution::newFromExpr( app, typeSubs );
-	if ( inferParams ) {
-		app->inferred.inferParams() = *inferParams;
-	}
-
-	// Handle any specializations that may still be present.
-	{
-		std::string oldParamPrefix = paramPrefix;
-		paramPrefix += "p";
-		std::list<ast::ptr<ast::Decl>> oldDecls;
-		oldDecls.splice( oldDecls.end(), declsToAddBefore );
-
-		app->accept( *visitor );
-		// Write recursive specializations into the thunk body.
-		for ( const ast::ptr<ast::Decl> & decl : declsToAddBefore ) {
-			thunkBody->push_back( new ast::DeclStmt( decl->location, decl ) );
-		}
-
-		declsToAddBefore = std::move( oldDecls );
-		paramPrefix = std::move( oldParamPrefix );
-	}
-
-	// Add return (or valueless expression) to the thunk.
-	ast::Stmt * appStmt;
-	if ( funType->returns.empty() ) {
-		appStmt = new ast::ExprStmt( app->location, app );
-	} else {
-		appStmt = new ast::ReturnStmt( app->location, app );
-	}
-	thunkBody->push_back( appStmt );
-
-	// Add the thunk definition:
-	declsToAddBefore.push_back( thunkFunc );
-
-	// Return address of thunk function as replacement expression.
-	return new ast::AddressExpr( location,
-		new ast::VariableExpr( location, thunkFunc ) );
-}
-
-const ast::Expr * SpecializeCore::doSpecialization(
-		const CodeLocation & location,
-		const ast::Type * formalType,
-		const ast::Expr * actual,
-		const ast::InferredParams * inferParams ) {
-	assertf( actual->result, "attempting to specialize an untyped expression" );
-	if ( needsSpecialization( formalType, actual->result, typeSubs ) ) {
-		if ( const ast::FunctionType * type = getFunctionType( formalType ) ) {
-			if ( const ast::ApplicationExpr * expr =
-					dynamic_cast<const ast::ApplicationExpr *>( actual ) ) {
-				return createThunkFunction( location, type, expr->func, inferParams );
-			} else if ( auto expr =
-					dynamic_cast<const ast::VariableExpr *>( actual ) ) {
-				return createThunkFunction( location, type, expr, inferParams );
-			} else {
-				// (I don't even know what that comment means.)
-				// This likely won't work, as anything that could build an ApplicationExpr probably hit one of the previous two branches
-				return createThunkFunction( location, type, actual, inferParams );
-			}
-		} else {
-			return actual;
-		}
-	} else {
-		return actual;
-	}
-}
-
-const ast::Expr * SpecializeCore::postvisit(
-		const ast::ApplicationExpr * expr ) {
-	if ( ast::isIntrinsicCallExpr( expr ) ) {
-		return expr;
-	}
-
-	// Create thunks for the inferred parameters.
-	// This is not needed for intrinsic calls, because they aren't
-	// actually passed to the function. It needs to handle explicit params
-	// before inferred params so that explicit params do not recieve a
-	// changed set of inferParams (and change them again).
-	// Alternatively, if order starts to matter then copy expr's inferParams
-	// and pass them to handleExplicitParams.
-	ast::ApplicationExpr * mut = handleExplicitParams( expr );
-	if ( !mut->inferred.hasParams() ) {
-		return mut;
-	}
-	ast::InferredParams & inferParams = mut->inferred.inferParams();
-	for ( ast::InferredParams::value_type & inferParam : inferParams ) {
-		inferParam.second.expr = doSpecialization(
-			inferParam.second.expr->location,
-			inferParam.second.formalType,
-			inferParam.second.expr,
-			getInferredParams( inferParam.second.expr )
-		);
-	}
-	return mut;
-}
-
-const ast::Expr * SpecializeCore::postvisit( const ast::CastExpr * expr ) {
-	if ( expr->result->isVoid() ) {
-		// No specialization if there is no return value.
-		return expr;
-	}
-	const ast::Expr * specialized = doSpecialization(
-		expr->location, expr->result, expr->arg, getInferredParams( expr ) );
-	if ( specialized != expr->arg ) {
-		// Assume that the specialization incorporates the cast.
-		return specialized;
-	} else {
-		return expr;
-	}
-}
-
-} // namespace
-
-void convertSpecializations( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<SpecializeCore>::run( translationUnit );
-}
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/module.mk
===================================================================
--- src/GenPoly/module.mk	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/GenPoly/module.mk	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -22,16 +22,16 @@
 
 SRC += $(SRC_GENPOLY) \
-	GenPoly/BoxNew.cpp \
+	GenPoly/Box.cpp \
 	GenPoly/Box.h \
 	GenPoly/ErasableScopedMap.h \
 	GenPoly/FindFunction.cc \
 	GenPoly/FindFunction.h \
-	GenPoly/InstantiateGenericNew.cpp \
+	GenPoly/InstantiateGeneric.cpp \
 	GenPoly/InstantiateGeneric.h \
-	GenPoly/LvalueNew.cpp \
+	GenPoly/Lvalue.cpp \
 	GenPoly/ScopedSet.h \
 	GenPoly/ScrubTyVars.cc \
 	GenPoly/ScrubTyVars.h \
-	GenPoly/SpecializeNew.cpp \
+	GenPoly/Specialize.cpp \
 	GenPoly/Specialize.h
 
Index: src/InitTweak/FixInit.cpp
===================================================================
--- src/InitTweak/FixInit.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/InitTweak/FixInit.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,1385 @@
+#include "FixInit.h"
+
+#include <stddef.h>                    // for NULL
+#include <algorithm>                   // for set_difference, copy_if
+#include <cassert>                     // for assert, strict_dynamic_cast
+#include <iostream>                    // for operator<<, ostream, basic_ost...
+#include <iterator>                    // for insert_iterator, back_inserter
+#include <list>                        // for _List_iterator, list, list<>::...
+#include <map>                         // for _Rb_tree_iterator, _Rb_tree_co...
+#include <memory>                      // for allocator_traits<>::value_type
+#include <set>                         // for set, set<>::value_type
+#include <unordered_map>               // for unordered_map, unordered_map<>...
+#include <unordered_set>               // for unordered_set
+#include <utility>                     // for pair
+
+#include "AST/DeclReplacer.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Inspect.hpp"             // for getFunction, getPointerBase, g...
+#include "AST/Node.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Print.hpp"
+#include "AST/SymbolTable.hpp"
+#include "AST/Type.hpp"
+#include "CodeGen/OperatorTable.h"     // for isConstructor, isCtorDtor, isD...
+#include "Common/SemanticError.h"      // for SemanticError
+#include "Common/ToString.hpp"         // for toCString
+#include "Common/UniqueName.h"         // for UniqueName
+#include "FixGlobalInit.h"             // for fixGlobalInit
+#include "GenInit.h"                   // for genCtorDtor
+#include "GenPoly/GenPoly.h"           // for getFunctionType
+#include "ResolvExpr/Resolver.h"       // for findVoidExpression
+#include "ResolvExpr/Unify.h"          // for typesCompatible
+#include "SymTab/GenImplicitCall.hpp"  // for genImplicitCall
+
+bool ctordtorp = false; // print all debug
+bool ctorp = false; // print ctor debug
+bool cpctorp = false; // print copy ctor debug
+bool dtorp = false; // print dtor debug
+#define PRINT( text ) if ( ctordtorp ) { text }
+#define CP_CTOR_PRINT( text ) if ( ctordtorp || cpctorp ) { text }
+#define DTOR_PRINT( text ) if ( ctordtorp || dtorp ) { text }
+
+namespace InitTweak {
+
+namespace {
+
+// Shallow copy the pointer list for return.
+std::vector<ast::ptr<ast::TypeDecl>> getGenericParams( const ast::Type * t ) {
+	if ( auto inst = dynamic_cast<const ast::StructInstType *>( t ) ) {
+		return inst->base->params;
+	}
+	if ( auto inst = dynamic_cast<const ast::UnionInstType *>( t ) ) {
+		return inst->base->params;
+	}
+	return {};
+}
+
+/// Given type T, generate type of default ctor/dtor, i.e. function type void (*) (T &).
+ast::FunctionDecl * genDefaultFunc(
+		const CodeLocation loc,
+		const std::string fname,
+		const ast::Type * paramType,
+		bool maybePolymorphic = true) {
+	std::vector<ast::ptr<ast::TypeDecl>> typeParams;
+	if ( maybePolymorphic ) typeParams = getGenericParams( paramType );
+	auto dstParam = new ast::ObjectDecl( loc,
+		"_dst",
+		new ast::ReferenceType( paramType ),
+		nullptr,
+		{},
+		ast::Linkage::Cforall
+	);
+	return new ast::FunctionDecl( loc,
+		fname,
+		std::move(typeParams),
+		{},
+		{dstParam},
+		{},
+		new ast::CompoundStmt(loc),
+		{},
+		ast::Linkage::Cforall
+	);
+}
+
+struct SelfAssignChecker {
+	void previsit( const ast::ApplicationExpr * appExpr );
+};
+
+struct StmtExprResult {
+	const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
+};
+
+/// wrap function application expressions as ImplicitCopyCtorExpr nodes so that it is easy to identify which
+/// function calls need their parameters to be copy constructed
+struct InsertImplicitCalls : public ast::WithShortCircuiting {
+	const ast::Expr * postvisit( const ast::ApplicationExpr * appExpr );
+
+	// only handles each UniqueExpr once
+	// if order of visit does not change, this should be safe
+	void previsit (const ast::UniqueExpr *);
+
+	std::unordered_set<decltype(ast::UniqueExpr::id)> visitedIds;
+};
+
+/// generate temporary ObjectDecls for each argument and return value of each ImplicitCopyCtorExpr,
+/// generate/resolve copy construction expressions for each, and generate/resolve destructors for both
+/// arguments and return value temporaries
+struct ResolveCopyCtors final : public ast::WithGuards, public ast::WithStmtsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithVisitorRef<ResolveCopyCtors>, public ast::WithConstTranslationUnit {
+	const ast::Expr * postvisit( const ast::ImplicitCopyCtorExpr * impCpCtorExpr );
+	const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
+	const ast::UniqueExpr * previsit( const ast::UniqueExpr * unqExpr );
+
+	/// handles distant mutations of environment manually.
+	/// WithConstTypeSubstitution cannot remember where the environment is from
+
+	/// MUST be called at start of overload previsit
+	void previsit( const ast::Expr * expr);
+	/// MUST be called at return of overload postvisit
+	const ast::Expr * postvisit(const ast::Expr * expr);
+
+	/// create and resolve ctor/dtor expression: fname(var, [cpArg])
+	const ast::Expr * makeCtorDtor( const std::string & fname, const ast::ObjectDecl * var, const ast::Expr * cpArg = nullptr );
+	/// true if type does not need to be copy constructed to ensure correctness
+	bool skipCopyConstruct( const ast::Type * type );
+	ast::ptr< ast::Expr > copyConstructArg( const ast::Expr * arg, const ast::ImplicitCopyCtorExpr * impCpCtorExpr, const ast::Type * formal );
+	ast::Expr * destructRet( const ast::ObjectDecl * ret, const ast::Expr * arg );
+private:
+	/// hack to implement WithTypeSubstitution while conforming to mutation safety.
+	ast::TypeSubstitution * env         = nullptr;
+	bool                    envModified = false;
+};
+
+/// collects constructed object decls - used as a base class
+struct ObjDeclCollector : public ast::WithGuards, public ast::WithShortCircuiting {
+	// use ordered data structure to maintain ordering for set_difference and for consistent error messages
+	typedef std::list< const ast::ObjectDecl * > ObjectSet;
+	void previsit( const ast::CompoundStmt *compoundStmt );
+	void previsit( const ast::DeclStmt *stmt );
+
+	// don't go into other functions
+	void previsit( const ast::FunctionDecl * ) { visit_children = false; }
+
+protected:
+	ObjectSet curVars;
+};
+
+// debug
+template<typename ObjectSet>
+struct PrintSet {
+	PrintSet( const ObjectSet & objs ) : objs( objs ) {}
+	const ObjectSet & objs;
+};
+template<typename ObjectSet>
+PrintSet<ObjectSet> printSet( const ObjectSet & objs ) { return PrintSet<ObjectSet>( objs ); }
+template<typename ObjectSet>
+std::ostream & operator<<( std::ostream & out, const PrintSet<ObjectSet> & set) {
+	out << "{ ";
+	for ( auto & obj : set.objs ) {
+		out << obj->name << ", " ;
+	} // for
+	out << " }";
+	return out;
+}
+
+struct LabelFinder final : public ObjDeclCollector {
+	typedef std::map< std::string, ObjectSet > LabelMap;
+	// map of Label -> live variables at that label
+	LabelMap vars;
+
+	typedef ObjDeclCollector Parent;
+	using Parent::previsit;
+	void previsit( const ast::Stmt * stmt );
+
+	void previsit( const ast::CompoundStmt *compoundStmt );
+	void previsit( const ast::DeclStmt *stmt );
+};
+
+/// insert destructor calls at the appropriate places.  must happen before CtorInit nodes are removed
+/// (currently by FixInit)
+struct InsertDtors final : public ObjDeclCollector, public ast::WithStmtsToAdd<> {
+	InsertDtors( ast::Pass<LabelFinder> & finder ) : finder( finder ), labelVars( finder.core.vars ) {}
+
+	typedef ObjDeclCollector Parent;
+	using Parent::previsit;
+
+	void previsit( const ast::FunctionDecl * funcDecl );
+
+	void previsit( const ast::BranchStmt * stmt );
+private:
+	void handleGoto( const ast::BranchStmt * stmt );
+
+	ast::Pass<LabelFinder> & finder;
+	LabelFinder::LabelMap & labelVars;
+};
+
+/// expand each object declaration to use its constructor after it is declared.
+struct FixInit : public ast::WithStmtsToAdd<> {
+	static void fixInitializers( ast::TranslationUnit &translationUnit );
+
+	const ast::DeclWithType * postvisit( const ast::ObjectDecl *objDecl );
+
+	std::list< ast::ptr< ast::Decl > > staticDtorDecls;
+};
+
+/// generate default/copy ctor and dtor calls for user-defined struct ctor/dtors
+/// for any member that is missing a corresponding ctor/dtor call.
+/// error if a member is used before constructed
+struct GenStructMemberCalls final : public ast::WithGuards, public ast::WithShortCircuiting, public ast::WithSymbolTable, public ast::WithVisitorRef<GenStructMemberCalls>, public ast::WithConstTranslationUnit {
+	void previsit( const ast::FunctionDecl * funcDecl );
+	const ast::DeclWithType * postvisit( const ast::FunctionDecl * funcDecl );
+
+	void previsit( const ast::MemberExpr * memberExpr );
+	void previsit( const ast::ApplicationExpr * appExpr );
+
+	/// Note: this post mutate used to be in a separate visitor. If this pass breaks, one place to examine is whether it is
+	/// okay for this part of the recursion to occur alongside the rest.
+	const ast::Expr * postvisit( const ast::UntypedExpr * expr );
+
+	SemanticErrorException errors;
+private:
+	template< typename... Params >
+	void emit( CodeLocation, const Params &... params );
+
+	ast::FunctionDecl * function = nullptr;
+	std::set< const ast::DeclWithType * > unhandled;
+	std::map< const ast::DeclWithType *, CodeLocation > usedUninit;
+	const ast::ObjectDecl * thisParam = nullptr;
+	bool isCtor = false; // true if current function is a constructor
+	const ast::StructDecl * structDecl = nullptr;
+};
+
+/// expands ConstructorExpr nodes into comma expressions, using a temporary for the first argument
+struct FixCtorExprs final : public ast::WithDeclsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithConstTranslationUnit {
+	const ast::Expr * postvisit( const ast::ConstructorExpr * ctorExpr );
+};
+
+/// add CompoundStmts around top-level expressions so that temporaries are destroyed in the correct places.
+struct SplitExpressions : public ast::WithShortCircuiting {
+	ast::Stmt * postvisit( const ast::ExprStmt * stmt );
+	void previsit( const ast::TupleAssignExpr * expr );
+};
+
+/// find and return the destructor used in `input`. If `input` is not a simple destructor call, generate a thunk
+/// that wraps the destructor, insert it into `stmtsToAdd` and return the new function declaration
+const ast::DeclWithType * getDtorFunc( const ast::ObjectDecl * objDecl, const ast::Stmt * input, std::list< ast::ptr<ast::Stmt> > & stmtsToAdd ) {
+	const CodeLocation loc = input->location;
+	// unwrap implicit statement wrapper
+	// Statement * dtor = input;
+	assert( input );
+	// std::list< const ast::Expr * > matches;
+	auto matches = collectCtorDtorCalls( input );
+
+	if ( dynamic_cast< const ast::ExprStmt * >( input ) ) {
+		// only one destructor call in the expression
+		if ( matches.size() == 1 ) {
+			auto func = getFunction( matches.front() );
+			assertf( func, "getFunction failed to find function in %s", toString( matches.front() ).c_str() );
+
+			// cleanup argument must be a function, not an object (including function pointer)
+			if ( auto dtorFunc = dynamic_cast< const ast::FunctionDecl * > ( func ) ) {
+				if ( dtorFunc->type->forall.empty() ) {
+					// simple case where the destructor is a monomorphic function call - can simply
+					// use that function as the cleanup function.
+					return func;
+				}
+			}
+		}
+	}
+
+	// otherwise the cleanup is more complicated - need to build a single argument cleanup function that
+	// wraps the more complicated code.
+	static UniqueName dtorNamer( "__cleanup_dtor" );
+	std::string name = dtorNamer.newName();
+	ast::FunctionDecl * dtorFunc = genDefaultFunc( loc, name, objDecl->type->stripReferences(), false );
+	stmtsToAdd.push_back( new ast::DeclStmt(loc, dtorFunc ) );
+
+	// the original code contains uses of objDecl - replace them with the newly generated 'this' parameter.
+	const ast::ObjectDecl * thisParam = getParamThis( dtorFunc );
+	const ast::Expr * replacement = new ast::VariableExpr( loc, thisParam );
+
+	auto base = replacement->result->stripReferences();
+	if ( dynamic_cast< const ast::ArrayType * >( base ) || dynamic_cast< const ast::TupleType * > ( base ) ) {
+		// need to cast away reference for array types, since the destructor is generated without the reference type,
+		// and for tuple types since tuple indexing does not work directly on a reference
+		replacement = new ast::CastExpr( replacement, base );
+	}
+	auto dtor = ast::DeclReplacer::replace( input, ast::DeclReplacer::ExprMap{ std::make_pair( objDecl, replacement ) } );
+	auto mutStmts = dtorFunc->stmts.get_and_mutate();
+	mutStmts->push_back(strict_dynamic_cast<const ast::Stmt *>( dtor ));
+	dtorFunc->stmts = mutStmts;
+
+	return dtorFunc;
+}
+
+void FixInit::fixInitializers( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<FixInit> fixer;
+
+	// can't use mutateAll, because need to insert declarations at top-level
+	// can't use DeclMutator, because sometimes need to insert IfStmt, etc.
+	SemanticErrorException errors;
+	for ( auto i = translationUnit.decls.begin(); i != translationUnit.decls.end(); ++i ) {
+		try {
+			// maybeAccept( *i, fixer ); translationUnit should never contain null
+			*i = (*i)->accept(fixer);
+			translationUnit.decls.splice( i, fixer.core.staticDtorDecls );
+		} catch( SemanticErrorException &e ) {
+			errors.append( e );
+		} // try
+	} // for
+	if ( ! errors.isEmpty() ) {
+		throw errors;
+	} // if
+}
+
+const ast::StmtExpr * StmtExprResult::previsit( const ast::StmtExpr * stmtExpr ) {
+	// we might loose the result expression here so add a pointer to trace back
+	assert( stmtExpr->result );
+	const ast::Type * result = stmtExpr->result;
+	if ( ! result->isVoid() ) {
+		auto mutExpr = mutate(stmtExpr);
+		const ast::CompoundStmt * body = mutExpr->stmts;
+		assert( ! body->kids.empty() );
+		mutExpr->resultExpr = body->kids.back().strict_as<ast::ExprStmt>();
+		return mutExpr;
+	}
+	return stmtExpr;
+}
+
+ast::Stmt * SplitExpressions::postvisit( const ast::ExprStmt * stmt ) {
+	// wrap each top-level ExprStmt in a block so that destructors for argument and return temporaries are destroyed
+	// in the correct places
+	ast::CompoundStmt * ret = new ast::CompoundStmt( stmt->location, { stmt } );
+	return ret;
+}
+
+void SplitExpressions::previsit( const ast::TupleAssignExpr * ) {
+	// don't do this within TupleAssignExpr, since it is already broken up into multiple expressions
+	visit_children = false;
+}
+
+// Relatively simple structural comparison for expressions, needed to determine
+// if two expressions are "the same" (used to determine if self assignment occurs)
+struct StructuralChecker {
+	// Strip all casts and then dynamic_cast.
+	template<typename T>
+	static const T * cast( const ast::Expr * expr ) {
+		// this might be too permissive. It's possible that only particular casts are relevant.
+		while ( auto cast = dynamic_cast< const ast::CastExpr * >( expr ) ) {
+			expr = cast->arg;
+		}
+		return dynamic_cast< const T * >( expr );
+	}
+
+	void previsit( const ast::Expr * ) {
+		// anything else does not qualify
+		result = false;
+	}
+
+	// ignore casts
+	void previsit( const ast::CastExpr * ) {}
+
+	void previsit( const ast::MemberExpr * memExpr ) {
+		if ( auto otherMember = cast< ast::MemberExpr >( other ) ) {
+			if ( otherMember->member == memExpr->member ) {
+				other = otherMember->aggregate;
+				return;
+			}
+		}
+		result = false;
+	}
+
+	void previsit( const ast::VariableExpr * varExpr ) {
+		if ( auto otherVar = cast< ast::VariableExpr >( other ) ) {
+			if ( otherVar->var == varExpr->var ) {
+				return;
+			}
+		}
+		result = false;
+	}
+
+	void previsit( const ast::AddressExpr * ) {
+		if ( auto addrExpr = cast< ast::AddressExpr >( other ) ) {
+			other = addrExpr->arg;
+			return;
+		}
+		result = false;
+	}
+
+	const ast::Expr * other;
+	bool result = true;
+	StructuralChecker( const ast::Expr * other ) : other(other) {}
+};
+
+bool structurallySimilar( const ast::Expr * e1, const ast::Expr * e2 ) {
+	return ast::Pass<StructuralChecker>::read( e1, e2 );
+}
+
+void SelfAssignChecker::previsit( const ast::ApplicationExpr * appExpr ) {
+	auto function = getFunction( appExpr );
+	// Doesn't use isAssignment, because ?+=?, etc. should not count as self-assignment.
+	if ( function->name == "?=?" && appExpr->args.size() == 2
+			// Check for structural similarity (same variable use, ignore casts, etc.
+			// (but does not look too deeply, anything looking like a function is off limits).
+			&& structurallySimilar( appExpr->args.front(), appExpr->args.back() ) ) {
+		SemanticWarning( appExpr->location, Warning::SelfAssignment, toCString( appExpr->args.front() ) );
+	}
+}
+
+const ast::Expr * InsertImplicitCalls::postvisit( const ast::ApplicationExpr * appExpr ) {
+	if ( auto function = appExpr->func.as<ast::VariableExpr>() ) {
+		if ( function->var->linkage.is_builtin ) {
+			// optimization: don't need to copy construct in order to call intrinsic functions
+			return appExpr;
+		} else if ( auto funcDecl = function->var.as<ast::DeclWithType>() ) {
+			auto ftype = dynamic_cast< const ast::FunctionType * >( GenPoly::getFunctionType( funcDecl->get_type() ) );
+			assertf( ftype, "Function call without function type: %s", toString( funcDecl ).c_str() );
+			if ( CodeGen::isConstructor( funcDecl->name ) && ftype->params.size() == 2 ) {
+				auto t1 = getPointerBase( ftype->params.front() );
+				auto t2 = ftype->params.back();
+				assert( t1 );
+
+				if ( ResolvExpr::typesCompatible( t1, t2 ) ) {
+					// optimization: don't need to copy construct in order to call a copy constructor
+					return appExpr;
+				} // if
+			} else if ( CodeGen::isDestructor( funcDecl->name ) ) {
+				// correctness: never copy construct arguments to a destructor
+				return appExpr;
+			} // if
+		} // if
+	} // if
+	CP_CTOR_PRINT( std::cerr << "InsertImplicitCalls: adding a wrapper " << appExpr << std::endl; )
+
+	// wrap each function call so that it is easy to identify nodes that have to be copy constructed
+	ast::ptr<ast::TypeSubstitution> tmp = appExpr->env;
+	auto mutExpr = mutate(appExpr);
+	mutExpr->env = nullptr;
+
+	auto expr = new ast::ImplicitCopyCtorExpr( appExpr->location, mutExpr );
+	// Move the type substitution to the new top-level. The substitution
+	// is needed to obtain the type of temporary variables so that copy
+	// constructor calls can be resolved.
+	expr->env = tmp;
+	return expr;
+}
+
+void ResolveCopyCtors::previsit(const ast::Expr * expr) {
+	if ( nullptr == expr->env ) {
+		return;
+	}
+	GuardValue( env ) = expr->env->clone();
+	GuardValue( envModified ) = false;
+}
+
+const ast::Expr * ResolveCopyCtors::postvisit(const ast::Expr * expr) {
+	// No local environment, skip.
+	if ( nullptr == expr->env ) {
+		return expr;
+	// Environment was modified, mutate and replace.
+	} else if ( envModified ) {
+		auto mutExpr = mutate(expr);
+		mutExpr->env = env;
+		return mutExpr;
+	// Environment was not mutated, delete the shallow copy before guard.
+	} else {
+		delete env;
+		return expr;
+	}
+}
+
+bool ResolveCopyCtors::skipCopyConstruct( const ast::Type * type ) { return ! isConstructable( type ); }
+
+const ast::Expr * ResolveCopyCtors::makeCtorDtor( const std::string & fname, const ast::ObjectDecl * var, const ast::Expr * cpArg ) {
+	assert( var );
+	assert( var->isManaged() );
+	assert( !cpArg || cpArg->isManaged() );
+	// arrays are not copy constructed, so this should always be an ExprStmt
+	ast::ptr< ast::Stmt > stmt = genCtorDtor(var->location, fname, var, cpArg );
+	assertf( stmt, "ResolveCopyCtors: genCtorDtor returned nullptr: %s / %s / %s", fname.c_str(), toString( var ).c_str(), toString( cpArg ).c_str() );
+	auto exprStmt = stmt.strict_as<ast::ImplicitCtorDtorStmt>()->callStmt.strict_as<ast::ExprStmt>();
+	ast::ptr<ast::Expr> untyped = exprStmt->expr; // take ownership of expr
+
+	// resolve copy constructor
+	// should only be one alternative for copy ctor and dtor expressions, since all arguments are fixed
+	// (VariableExpr and already resolved expression)
+	CP_CTOR_PRINT( std::cerr << "ResolvingCtorDtor " << untyped << std::endl; )
+	ast::ptr<ast::Expr> resolved = ResolvExpr::findVoidExpression(untyped, { symtab, transUnit().global } );
+	assert( resolved );
+	if ( resolved->env ) {
+		// Extract useful information and discard new environments. Keeping them causes problems in PolyMutator passes.
+		env->add( *resolved->env );
+		envModified = true;
+		auto mut = mutate(resolved.get());
+		assertf(mut == resolved.get(), "newly resolved expression must be unique");
+		mut->env = nullptr;
+	} // if
+	if ( auto assign = resolved.as<ast::TupleAssignExpr>() ) {
+		// fix newly generated StmtExpr
+		previsit( assign->stmtExpr );
+	}
+	return resolved.release();
+}
+
+ast::ptr<ast::Expr> ResolveCopyCtors::copyConstructArg(
+	const ast::Expr * arg, const ast::ImplicitCopyCtorExpr * impCpCtorExpr, const ast::Type * formal )
+{
+	static UniqueName tempNamer("_tmp_cp");
+	const CodeLocation loc = impCpCtorExpr->location;
+	// CP_CTOR_PRINT( std::cerr << "Type Substitution: " << *env << std::endl; )
+	assert( arg->result );
+	ast::ptr<ast::Type> result = arg->result;
+	if ( skipCopyConstruct( result ) ) return arg; // skip certain non-copyable types
+
+	// type may involve type variables, so apply type substitution to get temporary variable's actual type,
+	// since result type may not be substituted (e.g., if the type does not appear in the parameter list)
+	// Use applyFree so that types bound in function pointers are not substituted, e.g. in forall(dtype T) void (*)(T).
+
+	// xxx - this originally mutates arg->result in place. is it correct?
+	assert( env );
+	result = env->applyFree( result.get() ).node;
+	auto mutResult = result.get_and_mutate();
+	mutResult->set_const(false);
+
+	auto mutArg = mutate(arg);
+	mutArg->result = mutResult;
+
+	ast::ptr<ast::Expr> guard = mutArg;
+
+	ast::ptr<ast::ObjectDecl> tmp = new ast::ObjectDecl(loc, "__tmp", mutResult, nullptr );
+
+	// create and resolve copy constructor
+	CP_CTOR_PRINT( std::cerr << "makeCtorDtor for an argument" << std::endl; )
+	auto cpCtor = makeCtorDtor( "?{}", tmp, mutArg );
+
+	if ( auto appExpr = dynamic_cast< const ast::ApplicationExpr * >( cpCtor ) ) {
+		// if the chosen constructor is intrinsic, the copy is unnecessary, so
+		// don't create the temporary and don't call the copy constructor
+		auto function = appExpr->func.strict_as<ast::VariableExpr>();
+		if ( function->var->linkage == ast::Linkage::Intrinsic ) {
+			// arguments that need to be boxed need a temporary regardless of whether the copy constructor is intrinsic,
+			// so that the object isn't changed inside of the polymorphic function
+			if ( ! GenPoly::needsBoxing( formal, result, impCpCtorExpr->callExpr, env ) ) {
+				// xxx - should arg->result be mutated? see comment above.
+				return guard;
+			}
+		}
+	}
+
+	// set a unique name for the temporary once it's certain the call is necessary
+	auto mut = tmp.get_and_mutate();
+	assertf (mut == tmp, "newly created ObjectDecl must be unique");
+	mut->name = tempNamer.newName();
+
+	// replace argument to function call with temporary
+	stmtsToAddBefore.push_back( new ast::DeclStmt(loc, tmp ) );
+	arg = cpCtor;
+	return destructRet( tmp, arg );
+
+	// impCpCtorExpr->dtors.push_front( makeCtorDtor( "^?{}", tmp ) );
+}
+
+ast::Expr * ResolveCopyCtors::destructRet( const ast::ObjectDecl * ret, const ast::Expr * arg ) {
+	auto global = transUnit().global;
+	// TODO: refactor code for generating cleanup attribute, since it's common and reused in ~3-4 places
+	// check for existing cleanup attribute before adding another(?)
+	// need to add __Destructor for _tmp_cp variables as well
+
+	assertf( global.dtorStruct, "Destructor generation requires __Destructor definition." );
+	assertf( global.dtorStruct->members.size() == 2, "__Destructor definition does not have expected fields." );
+	assertf( global.dtorDestroy, "Destructor generation requires __destroy_Destructor." );
+
+	const CodeLocation & loc = ret->location;
+
+	// generate a __Destructor for ret that calls the destructor
+	auto res = makeCtorDtor( "^?{}", ret );
+	auto dtor = mutate(res);
+
+	// if the chosen destructor is intrinsic, elide the generated dtor handler
+	if ( arg && isIntrinsicCallExpr( dtor ) ) {
+		return new ast::CommaExpr(loc, arg, new ast::VariableExpr(loc, ret ) );
+	}
+
+	if ( ! dtor->env ) dtor->env = maybeClone( env );
+	auto dtorFunc = getDtorFunc( ret, new ast::ExprStmt(loc, dtor ), stmtsToAddBefore );
+
+	auto dtorStructType = new ast::StructInstType( global.dtorStruct );
+
+	// what does this do???
+	dtorStructType->params.push_back( new ast::TypeExpr(loc, new ast::VoidType() ) );
+
+	// cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
+	auto dtorFtype = new ast::FunctionType();
+	dtorFtype->params.push_back( new ast::PointerType(new ast::VoidType( ) ) );
+	auto dtorType = new ast::PointerType( dtorFtype );
+
+	static UniqueName namer( "_ret_dtor" );
+	auto retDtor = new ast::ObjectDecl(loc, namer.newName(), dtorStructType, new ast::ListInit(loc, { new ast::SingleInit(loc, ast::ConstantExpr::null(loc) ), new ast::SingleInit(loc, new ast::CastExpr( new ast::VariableExpr(loc, dtorFunc ), dtorType ) ) } ) );
+	retDtor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, global.dtorDestroy ) } ) );
+	stmtsToAddBefore.push_back( new ast::DeclStmt(loc, retDtor ) );
+
+	if ( arg ) {
+		auto member = new ast::MemberExpr(loc, global.dtorStruct->members.front().strict_as<ast::DeclWithType>(), new ast::VariableExpr(loc, retDtor ) );
+		auto object = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, ret ) ), new ast::PointerType(new ast::VoidType() ) );
+		ast::Expr * assign = createBitwiseAssignment( member, object );
+		return new ast::CommaExpr(loc, new ast::CommaExpr(loc, arg, assign ), new ast::VariableExpr(loc, ret ) );
+	}
+	return nullptr;
+	// impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", ret ) );
+}
+
+const ast::Expr * ResolveCopyCtors::postvisit( const ast::ImplicitCopyCtorExpr *impCpCtorExpr ) {
+	CP_CTOR_PRINT( std::cerr << "ResolveCopyCtors: " << impCpCtorExpr << std::endl; )
+
+	ast::ApplicationExpr * appExpr = mutate(impCpCtorExpr->callExpr.get());
+	const ast::ObjectDecl * returnDecl = nullptr;
+	const CodeLocation loc = appExpr->location;
+
+	// take each argument and attempt to copy construct it.
+	auto ftype = GenPoly::getFunctionType( appExpr->func->result );
+	assert( ftype );
+	auto & params = ftype->params;
+	auto iter = params.begin();
+	for ( auto & arg : appExpr->args ) {
+		const ast::Type * formal = nullptr;
+		if ( iter != params.end() ) { // does not copy construct C-style variadic arguments
+			// DeclarationWithType * param = *iter++;
+			formal = *iter++;
+		}
+
+		arg = copyConstructArg( arg, impCpCtorExpr, formal );
+	} // for
+
+	// each return value from the call needs to be connected with an ObjectDecl at the call site, which is
+	// initialized with the return value and is destructed later
+	// xxx - handle named return values?
+	const ast::Type * result = appExpr->result;
+	if ( ! result->isVoid() ) {
+		static UniqueName retNamer("_tmp_cp_ret");
+		auto subResult = env->apply( result ).node;
+		auto ret = new ast::ObjectDecl(loc, retNamer.newName(), subResult, nullptr );
+		auto mutType = mutate(ret->type.get());
+		mutType->set_const( false );
+		ret->type = mutType;
+		returnDecl = ret;
+		stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
+		CP_CTOR_PRINT( std::cerr << "makeCtorDtor for a return" << std::endl; )
+	} // for
+	CP_CTOR_PRINT( std::cerr << "after Resolving: " << impCpCtorExpr << std::endl; )
+	// ------------------------------------------------------
+
+	CP_CTOR_PRINT( std::cerr << "Coming out the back..." << impCpCtorExpr << std::endl; )
+
+	// detach fields from wrapper node so that it can be deleted without deleting too much
+
+	// xxx - actual env might be somewhere else, need to keep invariant
+
+	// deletion of wrapper should be handled by pass template now
+
+	// impCpCtorExpr->callExpr = nullptr;
+	assert (appExpr->env == nullptr);
+	appExpr->env = impCpCtorExpr->env;
+	// std::swap( impCpCtorExpr->env, appExpr->env );
+	// assert( impCpCtorExpr->env == nullptr );
+	// delete impCpCtorExpr;
+
+	if ( returnDecl ) {
+		ast::Expr * assign = createBitwiseAssignment( new ast::VariableExpr(loc, returnDecl ), appExpr );
+		if ( ! dynamic_cast< const ast::ReferenceType * >( result ) ) {
+			// destructing reference returns is bad because it can cause multiple destructor calls to the same object - the returned object is not a temporary
+			assign = destructRet( returnDecl, assign );
+			assert(assign);
+		} else {
+			assign = new ast::CommaExpr(loc, assign, new ast::VariableExpr(loc, returnDecl ) );
+		}
+		// move env from appExpr to retExpr
+		// std::swap( assign->env, appExpr->env );
+		assign->env = appExpr->env;
+		// actual env is handled by common routine that replaces WithTypeSubstitution
+		return postvisit((const ast::Expr *)assign);
+	} else {
+		return postvisit((const ast::Expr *)appExpr);
+	} // if
+}
+
+const ast::StmtExpr * ResolveCopyCtors::previsit( const ast::StmtExpr * _stmtExpr ) {
+	// function call temporaries should be placed at statement-level, rather than nested inside of a new statement expression,
+	// since temporaries can be shared across sub-expressions, e.g.
+	//   [A, A] f();       // decl
+	//   g([A] x, [A] y);  // decl
+	//   g(f());           // call
+	// f is executed once, so the return temporary is shared across the tuple constructors for x and y.
+	// Explicitly mutating children instead of mutating the inner compound statement forces the temporaries to be added
+	// to the outer context, rather than inside of the statement expression.
+
+	// call the common routine that replaces WithTypeSubstitution
+	previsit((const ast::Expr *) _stmtExpr);
+
+	visit_children = false;
+	const CodeLocation loc = _stmtExpr->location;
+
+	assert( env );
+
+	symtab.enterScope();
+	// visit all statements
+	auto stmtExpr = mutate(_stmtExpr);
+	auto mutStmts = mutate(stmtExpr->stmts.get());
+
+	auto & stmts = mutStmts->kids;
+	for ( auto & stmt : stmts ) {
+		stmt = stmt->accept( *visitor );
+	} // for
+	stmtExpr->stmts = mutStmts;
+	symtab.leaveScope();
+
+	assert( stmtExpr->result );
+	// const ast::Type * result = stmtExpr->result;
+	if ( ! stmtExpr->result->isVoid() ) {
+		static UniqueName retNamer("_tmp_stmtexpr_ret");
+
+		// result = result->clone();
+		auto result = env->apply( stmtExpr->result.get() ).node;
+		if ( ! InitTweak::isConstructable( result ) ) {
+			// delete result;
+			return stmtExpr;
+		}
+		auto mutResult = result.get_and_mutate();
+		mutResult->set_const(false);
+
+		// create variable that will hold the result of the stmt expr
+		auto ret = new ast::ObjectDecl(loc, retNamer.newName(), mutResult, nullptr );
+		stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
+
+		assertf(
+			stmtExpr->resultExpr,
+			"Statement-Expression should have a resulting expression at %s:%d",
+			stmtExpr->location.filename.c_str(),
+			stmtExpr->location.first_line
+		);
+
+		const ast::ExprStmt * last = stmtExpr->resultExpr;
+		// xxx - if this is non-unique, need to copy while making resultExpr ref
+		assertf(last->unique(), "attempt to modify weakly shared statement");
+		auto mutLast = mutate(last);
+		// above assertion means in-place mutation is OK
+		try {
+			mutLast->expr = makeCtorDtor( "?{}", ret, mutLast->expr );
+		} catch(...) {
+			std::cerr << "*CFA internal error: ";
+			std::cerr << "can't resolve implicit constructor";
+			std::cerr << " at " << stmtExpr->location.filename;
+			std::cerr << ":" << stmtExpr->location.first_line << std::endl;
+
+			abort();
+		}
+
+		// add destructors after current statement
+		stmtsToAddAfter.push_back( new ast::ExprStmt(loc, makeCtorDtor( "^?{}", ret ) ) );
+
+		// must have a non-empty body, otherwise it wouldn't have a result
+		assert( ! stmts.empty() );
+
+		// if there is a return decl, add a use as the last statement; will not have return decl on non-constructable returns
+		stmts.push_back( new ast::ExprStmt(loc, new ast::VariableExpr(loc, ret ) ) );
+	} // if
+
+	assert( stmtExpr->returnDecls.empty() );
+	assert( stmtExpr->dtors.empty() );
+
+	return stmtExpr;
+}
+
+// to prevent warnings ('_unq0' may be used uninitialized in this function),
+// insert an appropriate zero initializer for UniqueExpr temporaries.
+ast::Init * makeInit( const ast::Type * t, CodeLocation const & loc ) {
+	if ( auto inst = dynamic_cast< const ast::StructInstType * >( t ) ) {
+		// initizer for empty struct must be empty
+		if ( inst->base->members.empty() ) {
+			return new ast::ListInit( loc, {} );
+		}
+	} else if ( auto inst = dynamic_cast< const ast::UnionInstType * >( t ) ) {
+		// initizer for empty union must be empty
+		if ( inst->base->members.empty() ) {
+			return new ast::ListInit( loc, {} );
+		}
+	}
+
+	return new ast::ListInit( loc, {
+		new ast::SingleInit( loc, ast::ConstantExpr::from_int( loc, 0 ) )
+	} );
+}
+
+const ast::UniqueExpr * ResolveCopyCtors::previsit( const ast::UniqueExpr * unqExpr ) {
+	visit_children = false;
+	// xxx - hack to prevent double-handling of unique exprs, otherwise too many temporary variables and destructors are generated
+	static std::unordered_map< int, const ast::UniqueExpr * > unqMap;
+	auto mutExpr = mutate(unqExpr);
+	if ( ! unqMap.count( unqExpr->id ) ) {
+		// resolve expr and find its
+
+		auto impCpCtorExpr = mutExpr->expr.as<ast::ImplicitCopyCtorExpr>();
+		// PassVisitor<ResolveCopyCtors> fixer;
+
+		mutExpr->expr = mutExpr->expr->accept( *visitor );
+		// it should never be necessary to wrap a void-returning expression in a UniqueExpr - if this assumption changes, this needs to be rethought
+		assert( unqExpr->result );
+		if ( impCpCtorExpr ) {
+			auto comma = unqExpr->expr.strict_as<ast::CommaExpr>();
+			auto var = comma->arg2.strict_as<ast::VariableExpr>();
+			// note the variable used as the result from the call
+			mutExpr->var = var;
+		} else {
+			// expr isn't a call expr, so create a new temporary variable to use to hold the value of the unique expression
+			mutExpr->object = new ast::ObjectDecl( mutExpr->location, toString("_unq", mutExpr->id), mutExpr->result, makeInit( mutExpr->result, mutExpr->location ) );
+			mutExpr->var = new ast::VariableExpr( mutExpr->location, mutExpr->object );
+		}
+
+		unqMap[mutExpr->id] = mutExpr;
+	} else {
+		// take data from other UniqueExpr to ensure consistency
+		// delete unqExpr->get_expr();
+		mutExpr->expr = unqMap[mutExpr->id]->expr;
+		// delete unqExpr->result;
+		mutExpr->result = mutExpr->expr->result;
+	}
+	return mutExpr;
+}
+
+const ast::DeclWithType * FixInit::postvisit( const ast::ObjectDecl *_objDecl ) {
+	const CodeLocation loc = _objDecl->location;
+
+	// since this removes the init field from objDecl, it must occur after children are mutated (i.e. postvisit)
+	if ( ast::ptr<ast::ConstructorInit> ctorInit = _objDecl->init.as<ast::ConstructorInit>() ) {
+		auto objDecl = mutate(_objDecl);
+
+		// could this be non-unique?
+		if (objDecl != _objDecl) {
+			std::cerr << "FixInit: non-unique object decl " << objDecl->location << objDecl->name << std::endl;
+		}
+		// a decision should have been made by the resolver, so ctor and init are not both non-NULL
+		assert( ! ctorInit->ctor || ! ctorInit->init );
+		if ( const ast::Stmt * ctor = ctorInit->ctor ) {
+			if ( objDecl->storage.is_static ) {
+				addDataSectionAttribute(objDecl);
+				// originally wanted to take advantage of gcc nested functions, but
+				// we get memory errors with this approach. To remedy this, the static
+				// variable is hoisted when the destructor needs to be called.
+				//
+				// generate:
+				// static T __objName_static_varN;
+				// void __objName_dtor_atexitN() {
+				//   __dtor__...;
+				// }
+				// int f(...) {
+				//   ...
+				//   static bool __objName_uninitialized = true;
+				//   if (__objName_uninitialized) {
+				//     __ctor(__objName);
+				//     __objName_uninitialized = false;
+				//     atexit(__objName_dtor_atexitN);
+				//   }
+				//   ...
+				// }
+
+				static UniqueName dtorCallerNamer( "_dtor_atexit" );
+
+				// static bool __objName_uninitialized = true
+				auto boolType = new ast::BasicType( ast::BasicType::Kind::Bool );
+				auto boolInitExpr = new ast::SingleInit(loc, ast::ConstantExpr::from_int(loc, 1 ) );
+				auto isUninitializedVar = new ast::ObjectDecl(loc, objDecl->mangleName + "_uninitialized", boolType, boolInitExpr, ast::Storage::Static, ast::Linkage::Cforall);
+				isUninitializedVar->fixUniqueId();
+
+				// __objName_uninitialized = false;
+				auto setTrue = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ) );
+				setTrue->args.push_back( new ast::VariableExpr(loc, isUninitializedVar ) );
+				setTrue->args.push_back( ast::ConstantExpr::from_int(loc, 0 ) );
+
+				// generate body of if
+				auto initStmts = new ast::CompoundStmt(loc);
+				auto & body = initStmts->kids;
+				body.push_back( ctor );
+				body.push_back( new ast::ExprStmt(loc, setTrue ) );
+
+				// put it all together
+				auto ifStmt = new ast::IfStmt(loc, new ast::VariableExpr(loc, isUninitializedVar ), initStmts, 0 );
+				stmtsToAddAfter.push_back( new ast::DeclStmt(loc, isUninitializedVar ) );
+				stmtsToAddAfter.push_back( ifStmt );
+
+				const ast::Stmt * dtor = ctorInit->dtor;
+
+				// these should be automatically managed once reassigned
+				// objDecl->set_init( nullptr );
+				// ctorInit->set_ctor( nullptr );
+				// ctorInit->set_dtor( nullptr );
+				if ( dtor ) {
+					// if the object has a non-trivial destructor, have to
+					// hoist it and the object into the global space and
+					// call the destructor function with atexit.
+
+					// Statement * dtorStmt = dtor->clone();
+
+					// void __objName_dtor_atexitN(...) {...}
+					ast::FunctionDecl * dtorCaller = new ast::FunctionDecl(loc, objDecl->mangleName + dtorCallerNamer.newName(), {}, {}, {}, {}, new ast::CompoundStmt(loc, {dtor}), ast::Storage::Static, ast::Linkage::C );
+					dtorCaller->fixUniqueId();
+					// dtorCaller->stmts->push_back( dtor );
+
+					// atexit(dtor_atexit);
+					auto callAtexit = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "atexit" ) );
+					callAtexit->args.push_back( new ast::VariableExpr(loc, dtorCaller ) );
+
+					body.push_back( new ast::ExprStmt(loc, callAtexit ) );
+
+					// hoist variable and dtor caller decls to list of decls that will be added into global scope
+					staticDtorDecls.push_back( objDecl );
+					staticDtorDecls.push_back( dtorCaller );
+
+					// need to rename object uniquely since it now appears
+					// at global scope and there could be multiple function-scoped
+					// static variables with the same name in different functions.
+					// Note: it isn't sufficient to modify only the mangleName, because
+					// then subsequent SymbolTable passes can choke on seeing the object's name
+					// if another object has the same name and type. An unfortunate side-effect
+					// of renaming the object is that subsequent NameExprs may fail to resolve,
+					// but there shouldn't be any remaining past this point.
+					static UniqueName staticNamer( "_static_var" );
+					objDecl->name = objDecl->name + staticNamer.newName();
+					objDecl->mangleName = Mangle::mangle( objDecl );
+					objDecl->init = nullptr;
+
+					// xxx - temporary hack: need to return a declaration, but want to hoist the current object out of this scope
+					// create a new object which is never used
+					static UniqueName dummyNamer( "_dummy" );
+					auto dummy = new ast::ObjectDecl(loc, dummyNamer.newName(), new ast::PointerType(new ast::VoidType()), nullptr, ast::Storage::Static, ast::Linkage::Cforall, 0, { new ast::Attribute("unused") } );
+					// delete ctorInit;
+					return dummy;
+				} else {
+					objDecl->init = nullptr;
+					return objDecl;
+				}
+			} else {
+				auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * > ( ctor );
+				auto ctorStmt = implicit->callStmt.as<ast::ExprStmt>();
+				const ast::ApplicationExpr * ctorCall = nullptr;
+				if ( ctorStmt && (ctorCall = isIntrinsicCallExpr( ctorStmt->expr )) && ctorCall->args.size() == 2 ) {
+					// clean up intrinsic copy constructor calls by making them into SingleInits
+					const ast::Expr * ctorArg = ctorCall->args.back();
+					// ctorCall should be gone afterwards
+					auto mutArg = mutate(ctorArg);
+					mutArg->env = ctorCall->env;
+					// std::swap( ctorArg->env, ctorCall->env );
+					objDecl->init = new ast::SingleInit(loc, mutArg );
+
+					// ctorCall->args.pop_back();
+				} else {
+					stmtsToAddAfter.push_back( ctor );
+					objDecl->init = nullptr;
+					// ctorInit->ctor = nullptr;
+				}
+
+				const ast::Stmt * dtor = ctorInit->dtor;
+				if ( dtor ) {
+					auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * >( dtor );
+					const ast::Stmt * dtorStmt = implicit->callStmt;
+
+					// don't need to call intrinsic dtor, because it does nothing, but
+					// non-intrinsic dtors must be called
+					if ( ! isIntrinsicSingleArgCallStmt( dtorStmt ) ) {
+						// set dtor location to the object's location for error messages
+						auto dtorFunc = getDtorFunc( objDecl, dtorStmt, stmtsToAddBefore );
+						objDecl->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, dtorFunc ) } ) );
+						// ctorInit->dtor = nullptr;
+					} // if
+				}
+			} // if
+		} else if ( const ast::Init * init = ctorInit->init ) {
+			objDecl->init = init;
+			// ctorInit->init = nullptr;
+		} else {
+			// no constructor and no initializer, which is okay
+			objDecl->init = nullptr;
+		} // if
+		// delete ctorInit;
+		return objDecl;
+	} // if
+	return _objDecl;
+}
+
+void ObjDeclCollector::previsit( const ast::CompoundStmt * ) {
+	GuardValue( curVars );
+}
+
+void ObjDeclCollector::previsit( const ast::DeclStmt * stmt ) {
+	// keep track of all variables currently in scope
+	if ( auto objDecl = stmt->decl.as<ast::ObjectDecl>() ) {
+		curVars.push_back( objDecl );
+	} // if
+}
+
+void LabelFinder::previsit( const ast::Stmt * stmt ) {
+	// for each label, remember the variables in scope at that label.
+	for ( auto l : stmt->labels ) {
+		vars[l] = curVars;
+	} // for
+}
+
+void LabelFinder::previsit( const ast::CompoundStmt * stmt ) {
+	previsit( (const ast::Stmt *) stmt );
+	Parent::previsit( stmt );
+}
+
+void LabelFinder::previsit( const ast::DeclStmt * stmt ) {
+	previsit( (const ast::Stmt *)stmt );
+	Parent::previsit( stmt );
+}
+
+void InsertDtors::previsit( const ast::FunctionDecl * funcDecl ) {
+	// each function needs to have its own set of labels
+	GuardValue( labelVars );
+	labelVars.clear();
+	// LabelFinder does not recurse into FunctionDecl, so need to visit
+	// its children manually.
+	if (funcDecl->type) funcDecl->type->accept(finder);
+	// maybeAccept( funcDecl->type, finder );
+	if (funcDecl->stmts) funcDecl->stmts->accept(finder) ;
+
+	// all labels for this function have been collected, insert destructors as appropriate via implicit recursion.
+}
+
+// Handle break/continue/goto in the same manner as C++.  Basic idea: any objects that are in scope at the
+// BranchStmt but not at the labelled (target) statement must be destructed.  If there are any objects in scope
+// at the target location but not at the BranchStmt then those objects would be uninitialized so notify the user
+// of the error.  See C++ Reference 6.6 Jump Statements for details.
+void InsertDtors::handleGoto( const ast::BranchStmt * stmt ) {
+	// can't do anything for computed goto
+	if ( stmt->computedTarget ) return;
+
+	assertf( stmt->target.name != "", "BranchStmt missing a label: %s", toString( stmt ).c_str() );
+	// S_L = lvars = set of objects in scope at label definition
+	// S_G = curVars = set of objects in scope at goto statement
+	ObjectSet & lvars = labelVars[ stmt->target ];
+
+	DTOR_PRINT(
+		std::cerr << "at goto label: " << stmt->target.name << std::endl;
+		std::cerr << "S_G = " << printSet( curVars ) << std::endl;
+		std::cerr << "S_L = " << printSet( lvars ) << std::endl;
+	)
+
+
+	// std::set_difference requires that the inputs be sorted.
+	lvars.sort();
+	curVars.sort();
+
+	ObjectSet diff;
+	// S_L-S_G results in set of objects whose construction is skipped - it's an error if this set is non-empty
+	std::set_difference( lvars.begin(), lvars.end(), curVars.begin(), curVars.end(), std::inserter( diff, diff.begin() ) );
+	DTOR_PRINT(
+		std::cerr << "S_L-S_G = " << printSet( diff ) << std::endl;
+	)
+	if ( ! diff.empty() ) {
+		SemanticError( stmt, std::string("jump to label '") + stmt->target.name + "' crosses initialization of " + (*diff.begin())->name + " " );
+	} // if
+}
+
+void InsertDtors::previsit( const ast::BranchStmt * stmt ) {
+	switch( stmt->kind ) {
+	case ast::BranchStmt::Continue:
+	case ast::BranchStmt::Break:
+		// could optimize the break/continue case, because the S_L-S_G check is unnecessary (this set should
+		// always be empty), but it serves as a small sanity check.
+	case ast::BranchStmt::Goto:
+		handleGoto( stmt );
+		break;
+	default:
+		assert( false );
+	} // switch
+}
+
+bool checkWarnings( const ast::FunctionDecl * funcDecl ) {
+	// only check for warnings if the current function is a user-defined
+	// constructor or destructor
+	if ( ! funcDecl ) return false;
+	if ( ! funcDecl->stmts ) return false;
+	return CodeGen::isCtorDtor( funcDecl->name ) && ! funcDecl->linkage.is_overrideable;
+}
+
+void GenStructMemberCalls::previsit( const ast::FunctionDecl * funcDecl ) {
+	GuardValue( function );
+	GuardValue( unhandled );
+	GuardValue( usedUninit );
+	GuardValue( thisParam );
+	GuardValue( isCtor );
+	GuardValue( structDecl );
+	errors = SemanticErrorException();  // clear previous errors
+
+	// need to start with fresh sets
+	unhandled.clear();
+	usedUninit.clear();
+
+	function = mutate(funcDecl);
+	// could this be non-unique?
+	if (function != funcDecl) {
+		std::cerr << "GenStructMemberCalls: non-unique FunctionDecl " << funcDecl->location << funcDecl->name << std::endl;
+	}
+
+	isCtor = CodeGen::isConstructor( function->name );
+	if ( checkWarnings( function ) ) {
+		// const ast::FunctionType * type = function->type;
+		// assert( ! type->params.empty() );
+		thisParam = function->params.front().strict_as<ast::ObjectDecl>();
+		auto thisType = getPointerBase( thisParam->get_type() );
+		auto structType = dynamic_cast< const ast::StructInstType * >( thisType );
+		if ( structType ) {
+			structDecl = structType->base;
+			for ( auto & member : structDecl->members ) {
+				if ( auto field = member.as<ast::ObjectDecl>() ) {
+					// record all of the struct type's members that need to be constructed or
+					// destructed by the end of the function
+					unhandled.insert( field );
+				}
+			}
+		}
+	}
+}
+
+const ast::DeclWithType * GenStructMemberCalls::postvisit( const ast::FunctionDecl * funcDecl ) {
+	// remove the unhandled objects from usedUninit, because a call is inserted
+	// to handle them - only objects that are later constructed are used uninitialized.
+	std::map< const ast::DeclWithType *, CodeLocation > diff;
+	// need the comparator since usedUninit and unhandled have different types
+	struct comp_t {
+		typedef decltype(usedUninit)::value_type usedUninit_t;
+		typedef decltype(unhandled)::value_type unhandled_t;
+		bool operator()(usedUninit_t x, unhandled_t y) { return x.first < y; }
+		bool operator()(unhandled_t x, usedUninit_t y) { return x < y.first; }
+	} comp;
+	std::set_difference( usedUninit.begin(), usedUninit.end(), unhandled.begin(), unhandled.end(), std::inserter( diff, diff.begin() ), comp );
+	for ( auto p : diff ) {
+		auto member = p.first;
+		auto loc = p.second;
+		// xxx - make error message better by also tracking the location that the object is constructed at?
+		emit( loc, "in ", function->name, ", field ", member->name, " used before being constructed" );
+	}
+
+	const CodeLocation loc = funcDecl->location;
+
+	if ( ! unhandled.empty() ) {
+		auto mutStmts = function->stmts.get_and_mutate();
+		// need to explicitly re-add function parameters to the indexer in order to resolve copy constructors
+		auto guard = makeFuncGuard( [this]() { symtab.enterScope(); }, [this]() { symtab.leaveScope(); } );
+		symtab.addFunction( function );
+		auto global = transUnit().global;
+
+		// need to iterate through members in reverse in order for
+		// ctor/dtor statements to come out in the right order
+		for ( auto & member : reverseIterate( structDecl->members ) ) {
+			auto field = member.as<ast::ObjectDecl>();
+			// skip non-DWT members
+			if ( ! field ) continue;
+			// skip non-constructable members
+			if ( ! tryConstruct( field ) ) continue;
+			// skip handled members
+			if ( ! unhandled.count( field ) ) continue;
+
+			// insert and resolve default/copy constructor call for each field that's unhandled
+			// std::list< const ast::Stmt * > stmt;
+			ast::Expr * arg2 = nullptr;
+			if ( function->name == "?{}" && isCopyFunction( function ) ) {
+				// if copy ctor, need to pass second-param-of-this-function.field
+				// std::list< DeclarationWithType * > & params = function->get_functionType()->get_parameters();
+				assert( function->params.size() == 2 );
+				arg2 = new ast::MemberExpr(funcDecl->location, field, new ast::VariableExpr(funcDecl->location, function->params.back() ) );
+			}
+			InitExpander srcParam( arg2 );
+			// cast away reference type and construct field.
+			ast::Expr * thisExpr = new ast::CastExpr(funcDecl->location, new ast::VariableExpr(funcDecl->location, thisParam ), thisParam->get_type()->stripReferences());
+			ast::Expr * memberDest = new ast::MemberExpr(funcDecl->location, field, thisExpr );
+			ast::ptr<ast::Stmt> callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) );
+
+			if ( callStmt ) {
+				// auto & callStmt = stmt.front();
+
+				try {
+					callStmt = callStmt->accept( *visitor );
+					if ( isCtor ) {
+						mutStmts->push_front( callStmt );
+					} else { // TODO: don't generate destructor function/object for intrinsic calls
+						// destructor statements should be added at the end
+						// function->get_statements()->push_back( callStmt );
+
+						// Optimization: do not need to call intrinsic destructors on members
+						if ( isIntrinsicSingleArgCallStmt( callStmt ) ) continue;
+
+						// __Destructor _dtor0 = { (void *)&b.a1, (void (*)(void *)_destroy_A };
+						std::list< ast::ptr<ast::Stmt> > stmtsToAdd;
+
+						static UniqueName memberDtorNamer = { "__memberDtor" };
+						assertf( global.dtorStruct, "builtin __Destructor not found." );
+						assertf( global.dtorDestroy, "builtin __destroy_Destructor not found." );
+
+						ast::Expr * thisExpr = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, thisParam ) ), new ast::PointerType( new ast::VoidType(), ast::CV::Qualifiers() ) );
+						ast::Expr * dtorExpr = new ast::VariableExpr(loc, getDtorFunc( thisParam, callStmt, stmtsToAdd ) );
+
+						// cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
+						auto dtorFtype = new ast::FunctionType();
+						dtorFtype->params.emplace_back( new ast::PointerType( new ast::VoidType() ) );
+						auto dtorType = new ast::PointerType( dtorFtype );
+
+						auto destructor = new ast::ObjectDecl(loc, memberDtorNamer.newName(), new ast::StructInstType( global.dtorStruct ), new ast::ListInit(loc, { new ast::SingleInit(loc, thisExpr ), new ast::SingleInit(loc, new ast::CastExpr( dtorExpr, dtorType ) ) } ) );
+						destructor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr( loc, global.dtorDestroy ) } ) );
+						mutStmts->push_front( new ast::DeclStmt(loc, destructor ) );
+						mutStmts->kids.splice( mutStmts->kids.begin(), stmtsToAdd );
+					}
+				} catch ( SemanticErrorException & error ) {
+					emit( funcDecl->location, "in ", function->name , ", field ", field->name, " not explicitly ", isCtor ? "constructed" : "destructed",  " and no ", isCtor ? "default constructor" : "destructor", " found" );
+				}
+			}
+		}
+		function->stmts = mutStmts;
+	}
+	if (! errors.isEmpty()) {
+		throw errors;
+	}
+	// return funcDecl;
+	return function;
+}
+
+/// true if expr is effectively just the 'this' parameter
+bool isThisExpression( const ast::Expr * expr, const ast::DeclWithType * thisParam ) {
+	// TODO: there are more complicated ways to pass 'this' to a constructor, e.g. &*, *&, etc.
+	if ( auto varExpr = dynamic_cast< const ast::VariableExpr * >( expr ) ) {
+		return varExpr->var == thisParam;
+	} else if ( auto castExpr = dynamic_cast< const ast::CastExpr * > ( expr ) ) {
+		return isThisExpression( castExpr->arg, thisParam );
+	}
+	return false;
+}
+
+/// returns a MemberExpr if expr is effectively just member access on the 'this' parameter, else nullptr
+const ast::MemberExpr * isThisMemberExpr( const ast::Expr * expr, const ast::DeclWithType * thisParam ) {
+	if ( auto memberExpr = dynamic_cast< const ast::MemberExpr * >( expr ) ) {
+		if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
+			return memberExpr;
+		}
+	} else if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
+		return isThisMemberExpr( castExpr->arg, thisParam );
+	}
+	return nullptr;
+}
+
+void GenStructMemberCalls::previsit( const ast::ApplicationExpr * appExpr ) {
+	if ( ! checkWarnings( function ) ) {
+		visit_children = false;
+		return;
+	}
+
+	std::string fname = getFunctionName( appExpr );
+	if ( fname == function->name ) {
+		// call to same kind of function
+		const ast::Expr * firstParam = appExpr->args.front();
+
+		if ( isThisExpression( firstParam, thisParam ) ) {
+			// if calling another constructor on thisParam, assume that function handles
+			// all members - if it doesn't a warning will appear in that function.
+			unhandled.clear();
+		} else if ( auto memberExpr = isThisMemberExpr( firstParam, thisParam ) ) {
+			// if first parameter is a member expression on the this parameter,
+			// then remove the member from unhandled set.
+			if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
+				unhandled.erase( memberExpr->member );
+			}
+		}
+	}
+}
+
+void GenStructMemberCalls::previsit( const ast::MemberExpr * memberExpr ) {
+	if ( ! checkWarnings( function ) || ! isCtor ) {
+		visit_children = false;
+		return;
+	}
+
+	if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
+		if ( unhandled.count( memberExpr->member ) ) {
+			// emit a warning because a member was used before it was constructed
+			usedUninit.insert( { memberExpr->member, memberExpr->location } );
+		}
+	}
+}
+
+template< typename... Params >
+void GenStructMemberCalls::emit( CodeLocation loc, const Params &... params ) {
+	SemanticErrorException err( loc, toString( params... ) );
+	errors.append( err );
+}
+
+const ast::Expr * GenStructMemberCalls::postvisit( const ast::UntypedExpr * untypedExpr ) {
+	// xxx - functions returning ast::ptr seems wrong...
+	auto res = ResolvExpr::findVoidExpression( untypedExpr, { symtab, transUnit().global } );
+	return res.release();
+}
+
+void InsertImplicitCalls::previsit(const ast::UniqueExpr * unqExpr) {
+	if (visitedIds.count(unqExpr->id)) visit_children = false;
+	else visitedIds.insert(unqExpr->id);
+}
+
+const ast::Expr * FixCtorExprs::postvisit( const ast::ConstructorExpr * ctorExpr ) {
+	const CodeLocation loc = ctorExpr->location;
+	static UniqueName tempNamer( "_tmp_ctor_expr" );
+	// xxx - is the size check necessary?
+	assert( ctorExpr->result && ctorExpr->result->size() == 1 );
+
+	// xxx - this can be TupleAssignExpr now. Need to properly handle this case.
+	// take possession of expr and env
+	ast::ptr<ast::ApplicationExpr> callExpr = ctorExpr->callExpr.strict_as<ast::ApplicationExpr>();
+	ast::ptr<ast::TypeSubstitution> env = ctorExpr->env;
+	// ctorExpr->set_callExpr( nullptr );
+	// ctorExpr->set_env( nullptr );
+
+	// xxx - ideally we would reuse the temporary generated from the copy constructor passes from within firstArg if it exists and not generate a temporary if it's unnecessary.
+	auto tmp = new ast::ObjectDecl(loc, tempNamer.newName(), callExpr->args.front()->result );
+	declsToAddBefore.push_back( tmp );
+
+	// build assignment and replace constructor's first argument with new temporary
+	auto mutCallExpr = callExpr.get_and_mutate();
+	const ast::Expr * firstArg = callExpr->args.front();
+	ast::Expr * assign = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ), { new ast::AddressExpr(loc, new ast::VariableExpr(loc, tmp ) ), new ast::AddressExpr( firstArg ) } );
+	firstArg = new ast::VariableExpr(loc, tmp );
+	mutCallExpr->args.front() = firstArg;
+
+	// resolve assignment and dispose of new env
+	auto resolved = ResolvExpr::findVoidExpression( assign, { symtab, transUnit().global } );
+	auto mut = resolved.get_and_mutate();
+	assertf(resolved.get() == mut, "newly resolved expression must be unique");
+	mut->env = nullptr;
+
+	// for constructor expr:
+	//   T x;
+	//   x{};
+	// results in:
+	//   T x;
+	//   T & tmp;
+	//   &tmp = &x, ?{}(tmp), tmp
+	ast::CommaExpr * commaExpr = new ast::CommaExpr(loc, resolved, new ast::CommaExpr(loc, mutCallExpr, new ast::VariableExpr(loc, tmp ) ) );
+	commaExpr->env = env;
+	return commaExpr;
+}
+
+} // namespace
+
+void fix( ast::TranslationUnit & translationUnit, bool inLibrary ) {
+	ast::Pass<SelfAssignChecker>::run( translationUnit );
+
+	// fixes StmtExpr to properly link to their resulting expression
+	ast::Pass<StmtExprResult>::run( translationUnit );
+
+	// fixes ConstructorInit for global variables. should happen before fixInitializers.
+	InitTweak::fixGlobalInit( translationUnit, inLibrary );
+
+	// must happen before ResolveCopyCtors because temporaries have to be inserted into the correct scope
+	ast::Pass<SplitExpressions>::run( translationUnit );
+
+	ast::Pass<InsertImplicitCalls>::run( translationUnit );
+
+	// Needs to happen before ResolveCopyCtors, because argument/return temporaries should not be considered in
+	// error checking branch statements
+	{
+		ast::Pass<LabelFinder> finder;
+		ast::Pass<InsertDtors>::run( translationUnit, finder );
+	}
+
+	ast::Pass<ResolveCopyCtors>::run( translationUnit );
+	FixInit::fixInitializers( translationUnit );
+	ast::Pass<GenStructMemberCalls>::run( translationUnit );
+
+	// Needs to happen after GenStructMemberCalls, since otherwise member constructors exprs
+	// don't have the correct form, and a member can be constructed more than once.
+	ast::Pass<FixCtorExprs>::run( translationUnit );
+}
+
+} // namespace InitTweak
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/InitTweak/FixInitNew.cpp
===================================================================
--- src/InitTweak/FixInitNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,1385 +1,0 @@
-#include "FixInit.h"
-
-#include <stddef.h>                    // for NULL
-#include <algorithm>                   // for set_difference, copy_if
-#include <cassert>                     // for assert, strict_dynamic_cast
-#include <iostream>                    // for operator<<, ostream, basic_ost...
-#include <iterator>                    // for insert_iterator, back_inserter
-#include <list>                        // for _List_iterator, list, list<>::...
-#include <map>                         // for _Rb_tree_iterator, _Rb_tree_co...
-#include <memory>                      // for allocator_traits<>::value_type
-#include <set>                         // for set, set<>::value_type
-#include <unordered_map>               // for unordered_map, unordered_map<>...
-#include <unordered_set>               // for unordered_set
-#include <utility>                     // for pair
-
-#include "AST/DeclReplacer.hpp"
-#include "AST/Expr.hpp"
-#include "AST/Inspect.hpp"             // for getFunction, getPointerBase, g...
-#include "AST/Node.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Print.hpp"
-#include "AST/SymbolTable.hpp"
-#include "AST/Type.hpp"
-#include "CodeGen/OperatorTable.h"     // for isConstructor, isCtorDtor, isD...
-#include "Common/SemanticError.h"      // for SemanticError
-#include "Common/ToString.hpp"         // for toCString
-#include "Common/UniqueName.h"         // for UniqueName
-#include "FixGlobalInit.h"             // for fixGlobalInit
-#include "GenInit.h"                   // for genCtorDtor
-#include "GenPoly/GenPoly.h"           // for getFunctionType
-#include "ResolvExpr/Resolver.h"       // for findVoidExpression
-#include "ResolvExpr/Unify.h"          // for typesCompatible
-#include "SymTab/GenImplicitCall.hpp"  // for genImplicitCall
-
-bool ctordtorp = false; // print all debug
-bool ctorp = false; // print ctor debug
-bool cpctorp = false; // print copy ctor debug
-bool dtorp = false; // print dtor debug
-#define PRINT( text ) if ( ctordtorp ) { text }
-#define CP_CTOR_PRINT( text ) if ( ctordtorp || cpctorp ) { text }
-#define DTOR_PRINT( text ) if ( ctordtorp || dtorp ) { text }
-
-namespace InitTweak {
-
-namespace {
-
-// Shallow copy the pointer list for return.
-std::vector<ast::ptr<ast::TypeDecl>> getGenericParams( const ast::Type * t ) {
-	if ( auto inst = dynamic_cast<const ast::StructInstType *>( t ) ) {
-		return inst->base->params;
-	}
-	if ( auto inst = dynamic_cast<const ast::UnionInstType *>( t ) ) {
-		return inst->base->params;
-	}
-	return {};
-}
-
-/// Given type T, generate type of default ctor/dtor, i.e. function type void (*) (T &).
-ast::FunctionDecl * genDefaultFunc(
-		const CodeLocation loc,
-		const std::string fname,
-		const ast::Type * paramType,
-		bool maybePolymorphic = true) {
-	std::vector<ast::ptr<ast::TypeDecl>> typeParams;
-	if ( maybePolymorphic ) typeParams = getGenericParams( paramType );
-	auto dstParam = new ast::ObjectDecl( loc,
-		"_dst",
-		new ast::ReferenceType( paramType ),
-		nullptr,
-		{},
-		ast::Linkage::Cforall
-	);
-	return new ast::FunctionDecl( loc,
-		fname,
-		std::move(typeParams),
-		{},
-		{dstParam},
-		{},
-		new ast::CompoundStmt(loc),
-		{},
-		ast::Linkage::Cforall
-	);
-}
-
-struct SelfAssignChecker {
-	void previsit( const ast::ApplicationExpr * appExpr );
-};
-
-struct StmtExprResult {
-	const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
-};
-
-/// wrap function application expressions as ImplicitCopyCtorExpr nodes so that it is easy to identify which
-/// function calls need their parameters to be copy constructed
-struct InsertImplicitCalls : public ast::WithShortCircuiting {
-	const ast::Expr * postvisit( const ast::ApplicationExpr * appExpr );
-
-	// only handles each UniqueExpr once
-	// if order of visit does not change, this should be safe
-	void previsit (const ast::UniqueExpr *);
-
-	std::unordered_set<decltype(ast::UniqueExpr::id)> visitedIds;
-};
-
-/// generate temporary ObjectDecls for each argument and return value of each ImplicitCopyCtorExpr,
-/// generate/resolve copy construction expressions for each, and generate/resolve destructors for both
-/// arguments and return value temporaries
-struct ResolveCopyCtors final : public ast::WithGuards, public ast::WithStmtsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithVisitorRef<ResolveCopyCtors>, public ast::WithConstTranslationUnit {
-	const ast::Expr * postvisit( const ast::ImplicitCopyCtorExpr * impCpCtorExpr );
-	const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
-	const ast::UniqueExpr * previsit( const ast::UniqueExpr * unqExpr );
-
-	/// handles distant mutations of environment manually.
-	/// WithConstTypeSubstitution cannot remember where the environment is from
-
-	/// MUST be called at start of overload previsit
-	void previsit( const ast::Expr * expr);
-	/// MUST be called at return of overload postvisit
-	const ast::Expr * postvisit(const ast::Expr * expr);
-
-	/// create and resolve ctor/dtor expression: fname(var, [cpArg])
-	const ast::Expr * makeCtorDtor( const std::string & fname, const ast::ObjectDecl * var, const ast::Expr * cpArg = nullptr );
-	/// true if type does not need to be copy constructed to ensure correctness
-	bool skipCopyConstruct( const ast::Type * type );
-	ast::ptr< ast::Expr > copyConstructArg( const ast::Expr * arg, const ast::ImplicitCopyCtorExpr * impCpCtorExpr, const ast::Type * formal );
-	ast::Expr * destructRet( const ast::ObjectDecl * ret, const ast::Expr * arg );
-private:
-	/// hack to implement WithTypeSubstitution while conforming to mutation safety.
-	ast::TypeSubstitution * env         = nullptr;
-	bool                    envModified = false;
-};
-
-/// collects constructed object decls - used as a base class
-struct ObjDeclCollector : public ast::WithGuards, public ast::WithShortCircuiting {
-	// use ordered data structure to maintain ordering for set_difference and for consistent error messages
-	typedef std::list< const ast::ObjectDecl * > ObjectSet;
-	void previsit( const ast::CompoundStmt *compoundStmt );
-	void previsit( const ast::DeclStmt *stmt );
-
-	// don't go into other functions
-	void previsit( const ast::FunctionDecl * ) { visit_children = false; }
-
-protected:
-	ObjectSet curVars;
-};
-
-// debug
-template<typename ObjectSet>
-struct PrintSet {
-	PrintSet( const ObjectSet & objs ) : objs( objs ) {}
-	const ObjectSet & objs;
-};
-template<typename ObjectSet>
-PrintSet<ObjectSet> printSet( const ObjectSet & objs ) { return PrintSet<ObjectSet>( objs ); }
-template<typename ObjectSet>
-std::ostream & operator<<( std::ostream & out, const PrintSet<ObjectSet> & set) {
-	out << "{ ";
-	for ( auto & obj : set.objs ) {
-		out << obj->name << ", " ;
-	} // for
-	out << " }";
-	return out;
-}
-
-struct LabelFinder final : public ObjDeclCollector {
-	typedef std::map< std::string, ObjectSet > LabelMap;
-	// map of Label -> live variables at that label
-	LabelMap vars;
-
-	typedef ObjDeclCollector Parent;
-	using Parent::previsit;
-	void previsit( const ast::Stmt * stmt );
-
-	void previsit( const ast::CompoundStmt *compoundStmt );
-	void previsit( const ast::DeclStmt *stmt );
-};
-
-/// insert destructor calls at the appropriate places.  must happen before CtorInit nodes are removed
-/// (currently by FixInit)
-struct InsertDtors final : public ObjDeclCollector, public ast::WithStmtsToAdd<> {
-	InsertDtors( ast::Pass<LabelFinder> & finder ) : finder( finder ), labelVars( finder.core.vars ) {}
-
-	typedef ObjDeclCollector Parent;
-	using Parent::previsit;
-
-	void previsit( const ast::FunctionDecl * funcDecl );
-
-	void previsit( const ast::BranchStmt * stmt );
-private:
-	void handleGoto( const ast::BranchStmt * stmt );
-
-	ast::Pass<LabelFinder> & finder;
-	LabelFinder::LabelMap & labelVars;
-};
-
-/// expand each object declaration to use its constructor after it is declared.
-struct FixInit : public ast::WithStmtsToAdd<> {
-	static void fixInitializers( ast::TranslationUnit &translationUnit );
-
-	const ast::DeclWithType * postvisit( const ast::ObjectDecl *objDecl );
-
-	std::list< ast::ptr< ast::Decl > > staticDtorDecls;
-};
-
-/// generate default/copy ctor and dtor calls for user-defined struct ctor/dtors
-/// for any member that is missing a corresponding ctor/dtor call.
-/// error if a member is used before constructed
-struct GenStructMemberCalls final : public ast::WithGuards, public ast::WithShortCircuiting, public ast::WithSymbolTable, public ast::WithVisitorRef<GenStructMemberCalls>, public ast::WithConstTranslationUnit {
-	void previsit( const ast::FunctionDecl * funcDecl );
-	const ast::DeclWithType * postvisit( const ast::FunctionDecl * funcDecl );
-
-	void previsit( const ast::MemberExpr * memberExpr );
-	void previsit( const ast::ApplicationExpr * appExpr );
-
-	/// Note: this post mutate used to be in a separate visitor. If this pass breaks, one place to examine is whether it is
-	/// okay for this part of the recursion to occur alongside the rest.
-	const ast::Expr * postvisit( const ast::UntypedExpr * expr );
-
-	SemanticErrorException errors;
-private:
-	template< typename... Params >
-	void emit( CodeLocation, const Params &... params );
-
-	ast::FunctionDecl * function = nullptr;
-	std::set< const ast::DeclWithType * > unhandled;
-	std::map< const ast::DeclWithType *, CodeLocation > usedUninit;
-	const ast::ObjectDecl * thisParam = nullptr;
-	bool isCtor = false; // true if current function is a constructor
-	const ast::StructDecl * structDecl = nullptr;
-};
-
-/// expands ConstructorExpr nodes into comma expressions, using a temporary for the first argument
-struct FixCtorExprs final : public ast::WithDeclsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithConstTranslationUnit {
-	const ast::Expr * postvisit( const ast::ConstructorExpr * ctorExpr );
-};
-
-/// add CompoundStmts around top-level expressions so that temporaries are destroyed in the correct places.
-struct SplitExpressions : public ast::WithShortCircuiting {
-	ast::Stmt * postvisit( const ast::ExprStmt * stmt );
-	void previsit( const ast::TupleAssignExpr * expr );
-};
-
-/// find and return the destructor used in `input`. If `input` is not a simple destructor call, generate a thunk
-/// that wraps the destructor, insert it into `stmtsToAdd` and return the new function declaration
-const ast::DeclWithType * getDtorFunc( const ast::ObjectDecl * objDecl, const ast::Stmt * input, std::list< ast::ptr<ast::Stmt> > & stmtsToAdd ) {
-	const CodeLocation loc = input->location;
-	// unwrap implicit statement wrapper
-	// Statement * dtor = input;
-	assert( input );
-	// std::list< const ast::Expr * > matches;
-	auto matches = collectCtorDtorCalls( input );
-
-	if ( dynamic_cast< const ast::ExprStmt * >( input ) ) {
-		// only one destructor call in the expression
-		if ( matches.size() == 1 ) {
-			auto func = getFunction( matches.front() );
-			assertf( func, "getFunction failed to find function in %s", toString( matches.front() ).c_str() );
-
-			// cleanup argument must be a function, not an object (including function pointer)
-			if ( auto dtorFunc = dynamic_cast< const ast::FunctionDecl * > ( func ) ) {
-				if ( dtorFunc->type->forall.empty() ) {
-					// simple case where the destructor is a monomorphic function call - can simply
-					// use that function as the cleanup function.
-					return func;
-				}
-			}
-		}
-	}
-
-	// otherwise the cleanup is more complicated - need to build a single argument cleanup function that
-	// wraps the more complicated code.
-	static UniqueName dtorNamer( "__cleanup_dtor" );
-	std::string name = dtorNamer.newName();
-	ast::FunctionDecl * dtorFunc = genDefaultFunc( loc, name, objDecl->type->stripReferences(), false );
-	stmtsToAdd.push_back( new ast::DeclStmt(loc, dtorFunc ) );
-
-	// the original code contains uses of objDecl - replace them with the newly generated 'this' parameter.
-	const ast::ObjectDecl * thisParam = getParamThis( dtorFunc );
-	const ast::Expr * replacement = new ast::VariableExpr( loc, thisParam );
-
-	auto base = replacement->result->stripReferences();
-	if ( dynamic_cast< const ast::ArrayType * >( base ) || dynamic_cast< const ast::TupleType * > ( base ) ) {
-		// need to cast away reference for array types, since the destructor is generated without the reference type,
-		// and for tuple types since tuple indexing does not work directly on a reference
-		replacement = new ast::CastExpr( replacement, base );
-	}
-	auto dtor = ast::DeclReplacer::replace( input, ast::DeclReplacer::ExprMap{ std::make_pair( objDecl, replacement ) } );
-	auto mutStmts = dtorFunc->stmts.get_and_mutate();
-	mutStmts->push_back(strict_dynamic_cast<const ast::Stmt *>( dtor ));
-	dtorFunc->stmts = mutStmts;
-
-	return dtorFunc;
-}
-
-void FixInit::fixInitializers( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<FixInit> fixer;
-
-	// can't use mutateAll, because need to insert declarations at top-level
-	// can't use DeclMutator, because sometimes need to insert IfStmt, etc.
-	SemanticErrorException errors;
-	for ( auto i = translationUnit.decls.begin(); i != translationUnit.decls.end(); ++i ) {
-		try {
-			// maybeAccept( *i, fixer ); translationUnit should never contain null
-			*i = (*i)->accept(fixer);
-			translationUnit.decls.splice( i, fixer.core.staticDtorDecls );
-		} catch( SemanticErrorException &e ) {
-			errors.append( e );
-		} // try
-	} // for
-	if ( ! errors.isEmpty() ) {
-		throw errors;
-	} // if
-}
-
-const ast::StmtExpr * StmtExprResult::previsit( const ast::StmtExpr * stmtExpr ) {
-	// we might loose the result expression here so add a pointer to trace back
-	assert( stmtExpr->result );
-	const ast::Type * result = stmtExpr->result;
-	if ( ! result->isVoid() ) {
-		auto mutExpr = mutate(stmtExpr);
-		const ast::CompoundStmt * body = mutExpr->stmts;
-		assert( ! body->kids.empty() );
-		mutExpr->resultExpr = body->kids.back().strict_as<ast::ExprStmt>();
-		return mutExpr;
-	}
-	return stmtExpr;
-}
-
-ast::Stmt * SplitExpressions::postvisit( const ast::ExprStmt * stmt ) {
-	// wrap each top-level ExprStmt in a block so that destructors for argument and return temporaries are destroyed
-	// in the correct places
-	ast::CompoundStmt * ret = new ast::CompoundStmt( stmt->location, { stmt } );
-	return ret;
-}
-
-void SplitExpressions::previsit( const ast::TupleAssignExpr * ) {
-	// don't do this within TupleAssignExpr, since it is already broken up into multiple expressions
-	visit_children = false;
-}
-
-// Relatively simple structural comparison for expressions, needed to determine
-// if two expressions are "the same" (used to determine if self assignment occurs)
-struct StructuralChecker {
-	// Strip all casts and then dynamic_cast.
-	template<typename T>
-	static const T * cast( const ast::Expr * expr ) {
-		// this might be too permissive. It's possible that only particular casts are relevant.
-		while ( auto cast = dynamic_cast< const ast::CastExpr * >( expr ) ) {
-			expr = cast->arg;
-		}
-		return dynamic_cast< const T * >( expr );
-	}
-
-	void previsit( const ast::Expr * ) {
-		// anything else does not qualify
-		result = false;
-	}
-
-	// ignore casts
-	void previsit( const ast::CastExpr * ) {}
-
-	void previsit( const ast::MemberExpr * memExpr ) {
-		if ( auto otherMember = cast< ast::MemberExpr >( other ) ) {
-			if ( otherMember->member == memExpr->member ) {
-				other = otherMember->aggregate;
-				return;
-			}
-		}
-		result = false;
-	}
-
-	void previsit( const ast::VariableExpr * varExpr ) {
-		if ( auto otherVar = cast< ast::VariableExpr >( other ) ) {
-			if ( otherVar->var == varExpr->var ) {
-				return;
-			}
-		}
-		result = false;
-	}
-
-	void previsit( const ast::AddressExpr * ) {
-		if ( auto addrExpr = cast< ast::AddressExpr >( other ) ) {
-			other = addrExpr->arg;
-			return;
-		}
-		result = false;
-	}
-
-	const ast::Expr * other;
-	bool result = true;
-	StructuralChecker( const ast::Expr * other ) : other(other) {}
-};
-
-bool structurallySimilar( const ast::Expr * e1, const ast::Expr * e2 ) {
-	return ast::Pass<StructuralChecker>::read( e1, e2 );
-}
-
-void SelfAssignChecker::previsit( const ast::ApplicationExpr * appExpr ) {
-	auto function = getFunction( appExpr );
-	// Doesn't use isAssignment, because ?+=?, etc. should not count as self-assignment.
-	if ( function->name == "?=?" && appExpr->args.size() == 2
-			// Check for structural similarity (same variable use, ignore casts, etc.
-			// (but does not look too deeply, anything looking like a function is off limits).
-			&& structurallySimilar( appExpr->args.front(), appExpr->args.back() ) ) {
-		SemanticWarning( appExpr->location, Warning::SelfAssignment, toCString( appExpr->args.front() ) );
-	}
-}
-
-const ast::Expr * InsertImplicitCalls::postvisit( const ast::ApplicationExpr * appExpr ) {
-	if ( auto function = appExpr->func.as<ast::VariableExpr>() ) {
-		if ( function->var->linkage.is_builtin ) {
-			// optimization: don't need to copy construct in order to call intrinsic functions
-			return appExpr;
-		} else if ( auto funcDecl = function->var.as<ast::DeclWithType>() ) {
-			auto ftype = dynamic_cast< const ast::FunctionType * >( GenPoly::getFunctionType( funcDecl->get_type() ) );
-			assertf( ftype, "Function call without function type: %s", toString( funcDecl ).c_str() );
-			if ( CodeGen::isConstructor( funcDecl->name ) && ftype->params.size() == 2 ) {
-				auto t1 = getPointerBase( ftype->params.front() );
-				auto t2 = ftype->params.back();
-				assert( t1 );
-
-				if ( ResolvExpr::typesCompatible( t1, t2 ) ) {
-					// optimization: don't need to copy construct in order to call a copy constructor
-					return appExpr;
-				} // if
-			} else if ( CodeGen::isDestructor( funcDecl->name ) ) {
-				// correctness: never copy construct arguments to a destructor
-				return appExpr;
-			} // if
-		} // if
-	} // if
-	CP_CTOR_PRINT( std::cerr << "InsertImplicitCalls: adding a wrapper " << appExpr << std::endl; )
-
-	// wrap each function call so that it is easy to identify nodes that have to be copy constructed
-	ast::ptr<ast::TypeSubstitution> tmp = appExpr->env;
-	auto mutExpr = mutate(appExpr);
-	mutExpr->env = nullptr;
-
-	auto expr = new ast::ImplicitCopyCtorExpr( appExpr->location, mutExpr );
-	// Move the type substitution to the new top-level. The substitution
-	// is needed to obtain the type of temporary variables so that copy
-	// constructor calls can be resolved.
-	expr->env = tmp;
-	return expr;
-}
-
-void ResolveCopyCtors::previsit(const ast::Expr * expr) {
-	if ( nullptr == expr->env ) {
-		return;
-	}
-	GuardValue( env ) = expr->env->clone();
-	GuardValue( envModified ) = false;
-}
-
-const ast::Expr * ResolveCopyCtors::postvisit(const ast::Expr * expr) {
-	// No local environment, skip.
-	if ( nullptr == expr->env ) {
-		return expr;
-	// Environment was modified, mutate and replace.
-	} else if ( envModified ) {
-		auto mutExpr = mutate(expr);
-		mutExpr->env = env;
-		return mutExpr;
-	// Environment was not mutated, delete the shallow copy before guard.
-	} else {
-		delete env;
-		return expr;
-	}
-}
-
-bool ResolveCopyCtors::skipCopyConstruct( const ast::Type * type ) { return ! isConstructable( type ); }
-
-const ast::Expr * ResolveCopyCtors::makeCtorDtor( const std::string & fname, const ast::ObjectDecl * var, const ast::Expr * cpArg ) {
-	assert( var );
-	assert( var->isManaged() );
-	assert( !cpArg || cpArg->isManaged() );
-	// arrays are not copy constructed, so this should always be an ExprStmt
-	ast::ptr< ast::Stmt > stmt = genCtorDtor(var->location, fname, var, cpArg );
-	assertf( stmt, "ResolveCopyCtors: genCtorDtor returned nullptr: %s / %s / %s", fname.c_str(), toString( var ).c_str(), toString( cpArg ).c_str() );
-	auto exprStmt = stmt.strict_as<ast::ImplicitCtorDtorStmt>()->callStmt.strict_as<ast::ExprStmt>();
-	ast::ptr<ast::Expr> untyped = exprStmt->expr; // take ownership of expr
-
-	// resolve copy constructor
-	// should only be one alternative for copy ctor and dtor expressions, since all arguments are fixed
-	// (VariableExpr and already resolved expression)
-	CP_CTOR_PRINT( std::cerr << "ResolvingCtorDtor " << untyped << std::endl; )
-	ast::ptr<ast::Expr> resolved = ResolvExpr::findVoidExpression(untyped, { symtab, transUnit().global } );
-	assert( resolved );
-	if ( resolved->env ) {
-		// Extract useful information and discard new environments. Keeping them causes problems in PolyMutator passes.
-		env->add( *resolved->env );
-		envModified = true;
-		auto mut = mutate(resolved.get());
-		assertf(mut == resolved.get(), "newly resolved expression must be unique");
-		mut->env = nullptr;
-	} // if
-	if ( auto assign = resolved.as<ast::TupleAssignExpr>() ) {
-		// fix newly generated StmtExpr
-		previsit( assign->stmtExpr );
-	}
-	return resolved.release();
-}
-
-ast::ptr<ast::Expr> ResolveCopyCtors::copyConstructArg(
-	const ast::Expr * arg, const ast::ImplicitCopyCtorExpr * impCpCtorExpr, const ast::Type * formal )
-{
-	static UniqueName tempNamer("_tmp_cp");
-	const CodeLocation loc = impCpCtorExpr->location;
-	// CP_CTOR_PRINT( std::cerr << "Type Substitution: " << *env << std::endl; )
-	assert( arg->result );
-	ast::ptr<ast::Type> result = arg->result;
-	if ( skipCopyConstruct( result ) ) return arg; // skip certain non-copyable types
-
-	// type may involve type variables, so apply type substitution to get temporary variable's actual type,
-	// since result type may not be substituted (e.g., if the type does not appear in the parameter list)
-	// Use applyFree so that types bound in function pointers are not substituted, e.g. in forall(dtype T) void (*)(T).
-
-	// xxx - this originally mutates arg->result in place. is it correct?
-	assert( env );
-	result = env->applyFree( result.get() ).node;
-	auto mutResult = result.get_and_mutate();
-	mutResult->set_const(false);
-
-	auto mutArg = mutate(arg);
-	mutArg->result = mutResult;
-
-	ast::ptr<ast::Expr> guard = mutArg;
-
-	ast::ptr<ast::ObjectDecl> tmp = new ast::ObjectDecl(loc, "__tmp", mutResult, nullptr );
-
-	// create and resolve copy constructor
-	CP_CTOR_PRINT( std::cerr << "makeCtorDtor for an argument" << std::endl; )
-	auto cpCtor = makeCtorDtor( "?{}", tmp, mutArg );
-
-	if ( auto appExpr = dynamic_cast< const ast::ApplicationExpr * >( cpCtor ) ) {
-		// if the chosen constructor is intrinsic, the copy is unnecessary, so
-		// don't create the temporary and don't call the copy constructor
-		auto function = appExpr->func.strict_as<ast::VariableExpr>();
-		if ( function->var->linkage == ast::Linkage::Intrinsic ) {
-			// arguments that need to be boxed need a temporary regardless of whether the copy constructor is intrinsic,
-			// so that the object isn't changed inside of the polymorphic function
-			if ( ! GenPoly::needsBoxing( formal, result, impCpCtorExpr->callExpr, env ) ) {
-				// xxx - should arg->result be mutated? see comment above.
-				return guard;
-			}
-		}
-	}
-
-	// set a unique name for the temporary once it's certain the call is necessary
-	auto mut = tmp.get_and_mutate();
-	assertf (mut == tmp, "newly created ObjectDecl must be unique");
-	mut->name = tempNamer.newName();
-
-	// replace argument to function call with temporary
-	stmtsToAddBefore.push_back( new ast::DeclStmt(loc, tmp ) );
-	arg = cpCtor;
-	return destructRet( tmp, arg );
-
-	// impCpCtorExpr->dtors.push_front( makeCtorDtor( "^?{}", tmp ) );
-}
-
-ast::Expr * ResolveCopyCtors::destructRet( const ast::ObjectDecl * ret, const ast::Expr * arg ) {
-	auto global = transUnit().global;
-	// TODO: refactor code for generating cleanup attribute, since it's common and reused in ~3-4 places
-	// check for existing cleanup attribute before adding another(?)
-	// need to add __Destructor for _tmp_cp variables as well
-
-	assertf( global.dtorStruct, "Destructor generation requires __Destructor definition." );
-	assertf( global.dtorStruct->members.size() == 2, "__Destructor definition does not have expected fields." );
-	assertf( global.dtorDestroy, "Destructor generation requires __destroy_Destructor." );
-
-	const CodeLocation & loc = ret->location;
-
-	// generate a __Destructor for ret that calls the destructor
-	auto res = makeCtorDtor( "^?{}", ret );
-	auto dtor = mutate(res);
-
-	// if the chosen destructor is intrinsic, elide the generated dtor handler
-	if ( arg && isIntrinsicCallExpr( dtor ) ) {
-		return new ast::CommaExpr(loc, arg, new ast::VariableExpr(loc, ret ) );
-	}
-
-	if ( ! dtor->env ) dtor->env = maybeClone( env );
-	auto dtorFunc = getDtorFunc( ret, new ast::ExprStmt(loc, dtor ), stmtsToAddBefore );
-
-	auto dtorStructType = new ast::StructInstType( global.dtorStruct );
-
-	// what does this do???
-	dtorStructType->params.push_back( new ast::TypeExpr(loc, new ast::VoidType() ) );
-
-	// cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
-	auto dtorFtype = new ast::FunctionType();
-	dtorFtype->params.push_back( new ast::PointerType(new ast::VoidType( ) ) );
-	auto dtorType = new ast::PointerType( dtorFtype );
-
-	static UniqueName namer( "_ret_dtor" );
-	auto retDtor = new ast::ObjectDecl(loc, namer.newName(), dtorStructType, new ast::ListInit(loc, { new ast::SingleInit(loc, ast::ConstantExpr::null(loc) ), new ast::SingleInit(loc, new ast::CastExpr( new ast::VariableExpr(loc, dtorFunc ), dtorType ) ) } ) );
-	retDtor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, global.dtorDestroy ) } ) );
-	stmtsToAddBefore.push_back( new ast::DeclStmt(loc, retDtor ) );
-
-	if ( arg ) {
-		auto member = new ast::MemberExpr(loc, global.dtorStruct->members.front().strict_as<ast::DeclWithType>(), new ast::VariableExpr(loc, retDtor ) );
-		auto object = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, ret ) ), new ast::PointerType(new ast::VoidType() ) );
-		ast::Expr * assign = createBitwiseAssignment( member, object );
-		return new ast::CommaExpr(loc, new ast::CommaExpr(loc, arg, assign ), new ast::VariableExpr(loc, ret ) );
-	}
-	return nullptr;
-	// impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", ret ) );
-}
-
-const ast::Expr * ResolveCopyCtors::postvisit( const ast::ImplicitCopyCtorExpr *impCpCtorExpr ) {
-	CP_CTOR_PRINT( std::cerr << "ResolveCopyCtors: " << impCpCtorExpr << std::endl; )
-
-	ast::ApplicationExpr * appExpr = mutate(impCpCtorExpr->callExpr.get());
-	const ast::ObjectDecl * returnDecl = nullptr;
-	const CodeLocation loc = appExpr->location;
-
-	// take each argument and attempt to copy construct it.
-	auto ftype = GenPoly::getFunctionType( appExpr->func->result );
-	assert( ftype );
-	auto & params = ftype->params;
-	auto iter = params.begin();
-	for ( auto & arg : appExpr->args ) {
-		const ast::Type * formal = nullptr;
-		if ( iter != params.end() ) { // does not copy construct C-style variadic arguments
-			// DeclarationWithType * param = *iter++;
-			formal = *iter++;
-		}
-
-		arg = copyConstructArg( arg, impCpCtorExpr, formal );
-	} // for
-
-	// each return value from the call needs to be connected with an ObjectDecl at the call site, which is
-	// initialized with the return value and is destructed later
-	// xxx - handle named return values?
-	const ast::Type * result = appExpr->result;
-	if ( ! result->isVoid() ) {
-		static UniqueName retNamer("_tmp_cp_ret");
-		auto subResult = env->apply( result ).node;
-		auto ret = new ast::ObjectDecl(loc, retNamer.newName(), subResult, nullptr );
-		auto mutType = mutate(ret->type.get());
-		mutType->set_const( false );
-		ret->type = mutType;
-		returnDecl = ret;
-		stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
-		CP_CTOR_PRINT( std::cerr << "makeCtorDtor for a return" << std::endl; )
-	} // for
-	CP_CTOR_PRINT( std::cerr << "after Resolving: " << impCpCtorExpr << std::endl; )
-	// ------------------------------------------------------
-
-	CP_CTOR_PRINT( std::cerr << "Coming out the back..." << impCpCtorExpr << std::endl; )
-
-	// detach fields from wrapper node so that it can be deleted without deleting too much
-
-	// xxx - actual env might be somewhere else, need to keep invariant
-
-	// deletion of wrapper should be handled by pass template now
-
-	// impCpCtorExpr->callExpr = nullptr;
-	assert (appExpr->env == nullptr);
-	appExpr->env = impCpCtorExpr->env;
-	// std::swap( impCpCtorExpr->env, appExpr->env );
-	// assert( impCpCtorExpr->env == nullptr );
-	// delete impCpCtorExpr;
-
-	if ( returnDecl ) {
-		ast::Expr * assign = createBitwiseAssignment( new ast::VariableExpr(loc, returnDecl ), appExpr );
-		if ( ! dynamic_cast< const ast::ReferenceType * >( result ) ) {
-			// destructing reference returns is bad because it can cause multiple destructor calls to the same object - the returned object is not a temporary
-			assign = destructRet( returnDecl, assign );
-			assert(assign);
-		} else {
-			assign = new ast::CommaExpr(loc, assign, new ast::VariableExpr(loc, returnDecl ) );
-		}
-		// move env from appExpr to retExpr
-		// std::swap( assign->env, appExpr->env );
-		assign->env = appExpr->env;
-		// actual env is handled by common routine that replaces WithTypeSubstitution
-		return postvisit((const ast::Expr *)assign);
-	} else {
-		return postvisit((const ast::Expr *)appExpr);
-	} // if
-}
-
-const ast::StmtExpr * ResolveCopyCtors::previsit( const ast::StmtExpr * _stmtExpr ) {
-	// function call temporaries should be placed at statement-level, rather than nested inside of a new statement expression,
-	// since temporaries can be shared across sub-expressions, e.g.
-	//   [A, A] f();       // decl
-	//   g([A] x, [A] y);  // decl
-	//   g(f());           // call
-	// f is executed once, so the return temporary is shared across the tuple constructors for x and y.
-	// Explicitly mutating children instead of mutating the inner compound statement forces the temporaries to be added
-	// to the outer context, rather than inside of the statement expression.
-
-	// call the common routine that replaces WithTypeSubstitution
-	previsit((const ast::Expr *) _stmtExpr);
-
-	visit_children = false;
-	const CodeLocation loc = _stmtExpr->location;
-
-	assert( env );
-
-	symtab.enterScope();
-	// visit all statements
-	auto stmtExpr = mutate(_stmtExpr);
-	auto mutStmts = mutate(stmtExpr->stmts.get());
-
-	auto & stmts = mutStmts->kids;
-	for ( auto & stmt : stmts ) {
-		stmt = stmt->accept( *visitor );
-	} // for
-	stmtExpr->stmts = mutStmts;
-	symtab.leaveScope();
-
-	assert( stmtExpr->result );
-	// const ast::Type * result = stmtExpr->result;
-	if ( ! stmtExpr->result->isVoid() ) {
-		static UniqueName retNamer("_tmp_stmtexpr_ret");
-
-		// result = result->clone();
-		auto result = env->apply( stmtExpr->result.get() ).node;
-		if ( ! InitTweak::isConstructable( result ) ) {
-			// delete result;
-			return stmtExpr;
-		}
-		auto mutResult = result.get_and_mutate();
-		mutResult->set_const(false);
-
-		// create variable that will hold the result of the stmt expr
-		auto ret = new ast::ObjectDecl(loc, retNamer.newName(), mutResult, nullptr );
-		stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
-
-		assertf(
-			stmtExpr->resultExpr,
-			"Statement-Expression should have a resulting expression at %s:%d",
-			stmtExpr->location.filename.c_str(),
-			stmtExpr->location.first_line
-		);
-
-		const ast::ExprStmt * last = stmtExpr->resultExpr;
-		// xxx - if this is non-unique, need to copy while making resultExpr ref
-		assertf(last->unique(), "attempt to modify weakly shared statement");
-		auto mutLast = mutate(last);
-		// above assertion means in-place mutation is OK
-		try {
-			mutLast->expr = makeCtorDtor( "?{}", ret, mutLast->expr );
-		} catch(...) {
-			std::cerr << "*CFA internal error: ";
-			std::cerr << "can't resolve implicit constructor";
-			std::cerr << " at " << stmtExpr->location.filename;
-			std::cerr << ":" << stmtExpr->location.first_line << std::endl;
-
-			abort();
-		}
-
-		// add destructors after current statement
-		stmtsToAddAfter.push_back( new ast::ExprStmt(loc, makeCtorDtor( "^?{}", ret ) ) );
-
-		// must have a non-empty body, otherwise it wouldn't have a result
-		assert( ! stmts.empty() );
-
-		// if there is a return decl, add a use as the last statement; will not have return decl on non-constructable returns
-		stmts.push_back( new ast::ExprStmt(loc, new ast::VariableExpr(loc, ret ) ) );
-	} // if
-
-	assert( stmtExpr->returnDecls.empty() );
-	assert( stmtExpr->dtors.empty() );
-
-	return stmtExpr;
-}
-
-// to prevent warnings ('_unq0' may be used uninitialized in this function),
-// insert an appropriate zero initializer for UniqueExpr temporaries.
-ast::Init * makeInit( const ast::Type * t, CodeLocation const & loc ) {
-	if ( auto inst = dynamic_cast< const ast::StructInstType * >( t ) ) {
-		// initizer for empty struct must be empty
-		if ( inst->base->members.empty() ) {
-			return new ast::ListInit( loc, {} );
-		}
-	} else if ( auto inst = dynamic_cast< const ast::UnionInstType * >( t ) ) {
-		// initizer for empty union must be empty
-		if ( inst->base->members.empty() ) {
-			return new ast::ListInit( loc, {} );
-		}
-	}
-
-	return new ast::ListInit( loc, {
-		new ast::SingleInit( loc, ast::ConstantExpr::from_int( loc, 0 ) )
-	} );
-}
-
-const ast::UniqueExpr * ResolveCopyCtors::previsit( const ast::UniqueExpr * unqExpr ) {
-	visit_children = false;
-	// xxx - hack to prevent double-handling of unique exprs, otherwise too many temporary variables and destructors are generated
-	static std::unordered_map< int, const ast::UniqueExpr * > unqMap;
-	auto mutExpr = mutate(unqExpr);
-	if ( ! unqMap.count( unqExpr->id ) ) {
-		// resolve expr and find its
-
-		auto impCpCtorExpr = mutExpr->expr.as<ast::ImplicitCopyCtorExpr>();
-		// PassVisitor<ResolveCopyCtors> fixer;
-
-		mutExpr->expr = mutExpr->expr->accept( *visitor );
-		// it should never be necessary to wrap a void-returning expression in a UniqueExpr - if this assumption changes, this needs to be rethought
-		assert( unqExpr->result );
-		if ( impCpCtorExpr ) {
-			auto comma = unqExpr->expr.strict_as<ast::CommaExpr>();
-			auto var = comma->arg2.strict_as<ast::VariableExpr>();
-			// note the variable used as the result from the call
-			mutExpr->var = var;
-		} else {
-			// expr isn't a call expr, so create a new temporary variable to use to hold the value of the unique expression
-			mutExpr->object = new ast::ObjectDecl( mutExpr->location, toString("_unq", mutExpr->id), mutExpr->result, makeInit( mutExpr->result, mutExpr->location ) );
-			mutExpr->var = new ast::VariableExpr( mutExpr->location, mutExpr->object );
-		}
-
-		unqMap[mutExpr->id] = mutExpr;
-	} else {
-		// take data from other UniqueExpr to ensure consistency
-		// delete unqExpr->get_expr();
-		mutExpr->expr = unqMap[mutExpr->id]->expr;
-		// delete unqExpr->result;
-		mutExpr->result = mutExpr->expr->result;
-	}
-	return mutExpr;
-}
-
-const ast::DeclWithType * FixInit::postvisit( const ast::ObjectDecl *_objDecl ) {
-	const CodeLocation loc = _objDecl->location;
-
-	// since this removes the init field from objDecl, it must occur after children are mutated (i.e. postvisit)
-	if ( ast::ptr<ast::ConstructorInit> ctorInit = _objDecl->init.as<ast::ConstructorInit>() ) {
-		auto objDecl = mutate(_objDecl);
-
-		// could this be non-unique?
-		if (objDecl != _objDecl) {
-			std::cerr << "FixInit: non-unique object decl " << objDecl->location << objDecl->name << std::endl;
-		}
-		// a decision should have been made by the resolver, so ctor and init are not both non-NULL
-		assert( ! ctorInit->ctor || ! ctorInit->init );
-		if ( const ast::Stmt * ctor = ctorInit->ctor ) {
-			if ( objDecl->storage.is_static ) {
-				addDataSectionAttribute(objDecl);
-				// originally wanted to take advantage of gcc nested functions, but
-				// we get memory errors with this approach. To remedy this, the static
-				// variable is hoisted when the destructor needs to be called.
-				//
-				// generate:
-				// static T __objName_static_varN;
-				// void __objName_dtor_atexitN() {
-				//   __dtor__...;
-				// }
-				// int f(...) {
-				//   ...
-				//   static bool __objName_uninitialized = true;
-				//   if (__objName_uninitialized) {
-				//     __ctor(__objName);
-				//     __objName_uninitialized = false;
-				//     atexit(__objName_dtor_atexitN);
-				//   }
-				//   ...
-				// }
-
-				static UniqueName dtorCallerNamer( "_dtor_atexit" );
-
-				// static bool __objName_uninitialized = true
-				auto boolType = new ast::BasicType( ast::BasicType::Kind::Bool );
-				auto boolInitExpr = new ast::SingleInit(loc, ast::ConstantExpr::from_int(loc, 1 ) );
-				auto isUninitializedVar = new ast::ObjectDecl(loc, objDecl->mangleName + "_uninitialized", boolType, boolInitExpr, ast::Storage::Static, ast::Linkage::Cforall);
-				isUninitializedVar->fixUniqueId();
-
-				// __objName_uninitialized = false;
-				auto setTrue = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ) );
-				setTrue->args.push_back( new ast::VariableExpr(loc, isUninitializedVar ) );
-				setTrue->args.push_back( ast::ConstantExpr::from_int(loc, 0 ) );
-
-				// generate body of if
-				auto initStmts = new ast::CompoundStmt(loc);
-				auto & body = initStmts->kids;
-				body.push_back( ctor );
-				body.push_back( new ast::ExprStmt(loc, setTrue ) );
-
-				// put it all together
-				auto ifStmt = new ast::IfStmt(loc, new ast::VariableExpr(loc, isUninitializedVar ), initStmts, 0 );
-				stmtsToAddAfter.push_back( new ast::DeclStmt(loc, isUninitializedVar ) );
-				stmtsToAddAfter.push_back( ifStmt );
-
-				const ast::Stmt * dtor = ctorInit->dtor;
-
-				// these should be automatically managed once reassigned
-				// objDecl->set_init( nullptr );
-				// ctorInit->set_ctor( nullptr );
-				// ctorInit->set_dtor( nullptr );
-				if ( dtor ) {
-					// if the object has a non-trivial destructor, have to
-					// hoist it and the object into the global space and
-					// call the destructor function with atexit.
-
-					// Statement * dtorStmt = dtor->clone();
-
-					// void __objName_dtor_atexitN(...) {...}
-					ast::FunctionDecl * dtorCaller = new ast::FunctionDecl(loc, objDecl->mangleName + dtorCallerNamer.newName(), {}, {}, {}, {}, new ast::CompoundStmt(loc, {dtor}), ast::Storage::Static, ast::Linkage::C );
-					dtorCaller->fixUniqueId();
-					// dtorCaller->stmts->push_back( dtor );
-
-					// atexit(dtor_atexit);
-					auto callAtexit = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "atexit" ) );
-					callAtexit->args.push_back( new ast::VariableExpr(loc, dtorCaller ) );
-
-					body.push_back( new ast::ExprStmt(loc, callAtexit ) );
-
-					// hoist variable and dtor caller decls to list of decls that will be added into global scope
-					staticDtorDecls.push_back( objDecl );
-					staticDtorDecls.push_back( dtorCaller );
-
-					// need to rename object uniquely since it now appears
-					// at global scope and there could be multiple function-scoped
-					// static variables with the same name in different functions.
-					// Note: it isn't sufficient to modify only the mangleName, because
-					// then subsequent SymbolTable passes can choke on seeing the object's name
-					// if another object has the same name and type. An unfortunate side-effect
-					// of renaming the object is that subsequent NameExprs may fail to resolve,
-					// but there shouldn't be any remaining past this point.
-					static UniqueName staticNamer( "_static_var" );
-					objDecl->name = objDecl->name + staticNamer.newName();
-					objDecl->mangleName = Mangle::mangle( objDecl );
-					objDecl->init = nullptr;
-
-					// xxx - temporary hack: need to return a declaration, but want to hoist the current object out of this scope
-					// create a new object which is never used
-					static UniqueName dummyNamer( "_dummy" );
-					auto dummy = new ast::ObjectDecl(loc, dummyNamer.newName(), new ast::PointerType(new ast::VoidType()), nullptr, ast::Storage::Static, ast::Linkage::Cforall, 0, { new ast::Attribute("unused") } );
-					// delete ctorInit;
-					return dummy;
-				} else {
-					objDecl->init = nullptr;
-					return objDecl;
-				}
-			} else {
-				auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * > ( ctor );
-				auto ctorStmt = implicit->callStmt.as<ast::ExprStmt>();
-				const ast::ApplicationExpr * ctorCall = nullptr;
-				if ( ctorStmt && (ctorCall = isIntrinsicCallExpr( ctorStmt->expr )) && ctorCall->args.size() == 2 ) {
-					// clean up intrinsic copy constructor calls by making them into SingleInits
-					const ast::Expr * ctorArg = ctorCall->args.back();
-					// ctorCall should be gone afterwards
-					auto mutArg = mutate(ctorArg);
-					mutArg->env = ctorCall->env;
-					// std::swap( ctorArg->env, ctorCall->env );
-					objDecl->init = new ast::SingleInit(loc, mutArg );
-
-					// ctorCall->args.pop_back();
-				} else {
-					stmtsToAddAfter.push_back( ctor );
-					objDecl->init = nullptr;
-					// ctorInit->ctor = nullptr;
-				}
-
-				const ast::Stmt * dtor = ctorInit->dtor;
-				if ( dtor ) {
-					auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * >( dtor );
-					const ast::Stmt * dtorStmt = implicit->callStmt;
-
-					// don't need to call intrinsic dtor, because it does nothing, but
-					// non-intrinsic dtors must be called
-					if ( ! isIntrinsicSingleArgCallStmt( dtorStmt ) ) {
-						// set dtor location to the object's location for error messages
-						auto dtorFunc = getDtorFunc( objDecl, dtorStmt, stmtsToAddBefore );
-						objDecl->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, dtorFunc ) } ) );
-						// ctorInit->dtor = nullptr;
-					} // if
-				}
-			} // if
-		} else if ( const ast::Init * init = ctorInit->init ) {
-			objDecl->init = init;
-			// ctorInit->init = nullptr;
-		} else {
-			// no constructor and no initializer, which is okay
-			objDecl->init = nullptr;
-		} // if
-		// delete ctorInit;
-		return objDecl;
-	} // if
-	return _objDecl;
-}
-
-void ObjDeclCollector::previsit( const ast::CompoundStmt * ) {
-	GuardValue( curVars );
-}
-
-void ObjDeclCollector::previsit( const ast::DeclStmt * stmt ) {
-	// keep track of all variables currently in scope
-	if ( auto objDecl = stmt->decl.as<ast::ObjectDecl>() ) {
-		curVars.push_back( objDecl );
-	} // if
-}
-
-void LabelFinder::previsit( const ast::Stmt * stmt ) {
-	// for each label, remember the variables in scope at that label.
-	for ( auto l : stmt->labels ) {
-		vars[l] = curVars;
-	} // for
-}
-
-void LabelFinder::previsit( const ast::CompoundStmt * stmt ) {
-	previsit( (const ast::Stmt *) stmt );
-	Parent::previsit( stmt );
-}
-
-void LabelFinder::previsit( const ast::DeclStmt * stmt ) {
-	previsit( (const ast::Stmt *)stmt );
-	Parent::previsit( stmt );
-}
-
-void InsertDtors::previsit( const ast::FunctionDecl * funcDecl ) {
-	// each function needs to have its own set of labels
-	GuardValue( labelVars );
-	labelVars.clear();
-	// LabelFinder does not recurse into FunctionDecl, so need to visit
-	// its children manually.
-	if (funcDecl->type) funcDecl->type->accept(finder);
-	// maybeAccept( funcDecl->type, finder );
-	if (funcDecl->stmts) funcDecl->stmts->accept(finder) ;
-
-	// all labels for this function have been collected, insert destructors as appropriate via implicit recursion.
-}
-
-// Handle break/continue/goto in the same manner as C++.  Basic idea: any objects that are in scope at the
-// BranchStmt but not at the labelled (target) statement must be destructed.  If there are any objects in scope
-// at the target location but not at the BranchStmt then those objects would be uninitialized so notify the user
-// of the error.  See C++ Reference 6.6 Jump Statements for details.
-void InsertDtors::handleGoto( const ast::BranchStmt * stmt ) {
-	// can't do anything for computed goto
-	if ( stmt->computedTarget ) return;
-
-	assertf( stmt->target.name != "", "BranchStmt missing a label: %s", toString( stmt ).c_str() );
-	// S_L = lvars = set of objects in scope at label definition
-	// S_G = curVars = set of objects in scope at goto statement
-	ObjectSet & lvars = labelVars[ stmt->target ];
-
-	DTOR_PRINT(
-		std::cerr << "at goto label: " << stmt->target.name << std::endl;
-		std::cerr << "S_G = " << printSet( curVars ) << std::endl;
-		std::cerr << "S_L = " << printSet( lvars ) << std::endl;
-	)
-
-
-	// std::set_difference requires that the inputs be sorted.
-	lvars.sort();
-	curVars.sort();
-
-	ObjectSet diff;
-	// S_L-S_G results in set of objects whose construction is skipped - it's an error if this set is non-empty
-	std::set_difference( lvars.begin(), lvars.end(), curVars.begin(), curVars.end(), std::inserter( diff, diff.begin() ) );
-	DTOR_PRINT(
-		std::cerr << "S_L-S_G = " << printSet( diff ) << std::endl;
-	)
-	if ( ! diff.empty() ) {
-		SemanticError( stmt, std::string("jump to label '") + stmt->target.name + "' crosses initialization of " + (*diff.begin())->name + " " );
-	} // if
-}
-
-void InsertDtors::previsit( const ast::BranchStmt * stmt ) {
-	switch( stmt->kind ) {
-	case ast::BranchStmt::Continue:
-	case ast::BranchStmt::Break:
-		// could optimize the break/continue case, because the S_L-S_G check is unnecessary (this set should
-		// always be empty), but it serves as a small sanity check.
-	case ast::BranchStmt::Goto:
-		handleGoto( stmt );
-		break;
-	default:
-		assert( false );
-	} // switch
-}
-
-bool checkWarnings( const ast::FunctionDecl * funcDecl ) {
-	// only check for warnings if the current function is a user-defined
-	// constructor or destructor
-	if ( ! funcDecl ) return false;
-	if ( ! funcDecl->stmts ) return false;
-	return CodeGen::isCtorDtor( funcDecl->name ) && ! funcDecl->linkage.is_overrideable;
-}
-
-void GenStructMemberCalls::previsit( const ast::FunctionDecl * funcDecl ) {
-	GuardValue( function );
-	GuardValue( unhandled );
-	GuardValue( usedUninit );
-	GuardValue( thisParam );
-	GuardValue( isCtor );
-	GuardValue( structDecl );
-	errors = SemanticErrorException();  // clear previous errors
-
-	// need to start with fresh sets
-	unhandled.clear();
-	usedUninit.clear();
-
-	function = mutate(funcDecl);
-	// could this be non-unique?
-	if (function != funcDecl) {
-		std::cerr << "GenStructMemberCalls: non-unique FunctionDecl " << funcDecl->location << funcDecl->name << std::endl;
-	}
-
-	isCtor = CodeGen::isConstructor( function->name );
-	if ( checkWarnings( function ) ) {
-		// const ast::FunctionType * type = function->type;
-		// assert( ! type->params.empty() );
-		thisParam = function->params.front().strict_as<ast::ObjectDecl>();
-		auto thisType = getPointerBase( thisParam->get_type() );
-		auto structType = dynamic_cast< const ast::StructInstType * >( thisType );
-		if ( structType ) {
-			structDecl = structType->base;
-			for ( auto & member : structDecl->members ) {
-				if ( auto field = member.as<ast::ObjectDecl>() ) {
-					// record all of the struct type's members that need to be constructed or
-					// destructed by the end of the function
-					unhandled.insert( field );
-				}
-			}
-		}
-	}
-}
-
-const ast::DeclWithType * GenStructMemberCalls::postvisit( const ast::FunctionDecl * funcDecl ) {
-	// remove the unhandled objects from usedUninit, because a call is inserted
-	// to handle them - only objects that are later constructed are used uninitialized.
-	std::map< const ast::DeclWithType *, CodeLocation > diff;
-	// need the comparator since usedUninit and unhandled have different types
-	struct comp_t {
-		typedef decltype(usedUninit)::value_type usedUninit_t;
-		typedef decltype(unhandled)::value_type unhandled_t;
-		bool operator()(usedUninit_t x, unhandled_t y) { return x.first < y; }
-		bool operator()(unhandled_t x, usedUninit_t y) { return x < y.first; }
-	} comp;
-	std::set_difference( usedUninit.begin(), usedUninit.end(), unhandled.begin(), unhandled.end(), std::inserter( diff, diff.begin() ), comp );
-	for ( auto p : diff ) {
-		auto member = p.first;
-		auto loc = p.second;
-		// xxx - make error message better by also tracking the location that the object is constructed at?
-		emit( loc, "in ", function->name, ", field ", member->name, " used before being constructed" );
-	}
-
-	const CodeLocation loc = funcDecl->location;
-
-	if ( ! unhandled.empty() ) {
-		auto mutStmts = function->stmts.get_and_mutate();
-		// need to explicitly re-add function parameters to the indexer in order to resolve copy constructors
-		auto guard = makeFuncGuard( [this]() { symtab.enterScope(); }, [this]() { symtab.leaveScope(); } );
-		symtab.addFunction( function );
-		auto global = transUnit().global;
-
-		// need to iterate through members in reverse in order for
-		// ctor/dtor statements to come out in the right order
-		for ( auto & member : reverseIterate( structDecl->members ) ) {
-			auto field = member.as<ast::ObjectDecl>();
-			// skip non-DWT members
-			if ( ! field ) continue;
-			// skip non-constructable members
-			if ( ! tryConstruct( field ) ) continue;
-			// skip handled members
-			if ( ! unhandled.count( field ) ) continue;
-
-			// insert and resolve default/copy constructor call for each field that's unhandled
-			// std::list< const ast::Stmt * > stmt;
-			ast::Expr * arg2 = nullptr;
-			if ( function->name == "?{}" && isCopyFunction( function ) ) {
-				// if copy ctor, need to pass second-param-of-this-function.field
-				// std::list< DeclarationWithType * > & params = function->get_functionType()->get_parameters();
-				assert( function->params.size() == 2 );
-				arg2 = new ast::MemberExpr(funcDecl->location, field, new ast::VariableExpr(funcDecl->location, function->params.back() ) );
-			}
-			InitExpander srcParam( arg2 );
-			// cast away reference type and construct field.
-			ast::Expr * thisExpr = new ast::CastExpr(funcDecl->location, new ast::VariableExpr(funcDecl->location, thisParam ), thisParam->get_type()->stripReferences());
-			ast::Expr * memberDest = new ast::MemberExpr(funcDecl->location, field, thisExpr );
-			ast::ptr<ast::Stmt> callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) );
-
-			if ( callStmt ) {
-				// auto & callStmt = stmt.front();
-
-				try {
-					callStmt = callStmt->accept( *visitor );
-					if ( isCtor ) {
-						mutStmts->push_front( callStmt );
-					} else { // TODO: don't generate destructor function/object for intrinsic calls
-						// destructor statements should be added at the end
-						// function->get_statements()->push_back( callStmt );
-
-						// Optimization: do not need to call intrinsic destructors on members
-						if ( isIntrinsicSingleArgCallStmt( callStmt ) ) continue;
-
-						// __Destructor _dtor0 = { (void *)&b.a1, (void (*)(void *)_destroy_A };
-						std::list< ast::ptr<ast::Stmt> > stmtsToAdd;
-
-						static UniqueName memberDtorNamer = { "__memberDtor" };
-						assertf( global.dtorStruct, "builtin __Destructor not found." );
-						assertf( global.dtorDestroy, "builtin __destroy_Destructor not found." );
-
-						ast::Expr * thisExpr = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, thisParam ) ), new ast::PointerType( new ast::VoidType(), ast::CV::Qualifiers() ) );
-						ast::Expr * dtorExpr = new ast::VariableExpr(loc, getDtorFunc( thisParam, callStmt, stmtsToAdd ) );
-
-						// cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
-						auto dtorFtype = new ast::FunctionType();
-						dtorFtype->params.emplace_back( new ast::PointerType( new ast::VoidType() ) );
-						auto dtorType = new ast::PointerType( dtorFtype );
-
-						auto destructor = new ast::ObjectDecl(loc, memberDtorNamer.newName(), new ast::StructInstType( global.dtorStruct ), new ast::ListInit(loc, { new ast::SingleInit(loc, thisExpr ), new ast::SingleInit(loc, new ast::CastExpr( dtorExpr, dtorType ) ) } ) );
-						destructor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr( loc, global.dtorDestroy ) } ) );
-						mutStmts->push_front( new ast::DeclStmt(loc, destructor ) );
-						mutStmts->kids.splice( mutStmts->kids.begin(), stmtsToAdd );
-					}
-				} catch ( SemanticErrorException & error ) {
-					emit( funcDecl->location, "in ", function->name , ", field ", field->name, " not explicitly ", isCtor ? "constructed" : "destructed",  " and no ", isCtor ? "default constructor" : "destructor", " found" );
-				}
-			}
-		}
-		function->stmts = mutStmts;
-	}
-	if (! errors.isEmpty()) {
-		throw errors;
-	}
-	// return funcDecl;
-	return function;
-}
-
-/// true if expr is effectively just the 'this' parameter
-bool isThisExpression( const ast::Expr * expr, const ast::DeclWithType * thisParam ) {
-	// TODO: there are more complicated ways to pass 'this' to a constructor, e.g. &*, *&, etc.
-	if ( auto varExpr = dynamic_cast< const ast::VariableExpr * >( expr ) ) {
-		return varExpr->var == thisParam;
-	} else if ( auto castExpr = dynamic_cast< const ast::CastExpr * > ( expr ) ) {
-		return isThisExpression( castExpr->arg, thisParam );
-	}
-	return false;
-}
-
-/// returns a MemberExpr if expr is effectively just member access on the 'this' parameter, else nullptr
-const ast::MemberExpr * isThisMemberExpr( const ast::Expr * expr, const ast::DeclWithType * thisParam ) {
-	if ( auto memberExpr = dynamic_cast< const ast::MemberExpr * >( expr ) ) {
-		if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
-			return memberExpr;
-		}
-	} else if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
-		return isThisMemberExpr( castExpr->arg, thisParam );
-	}
-	return nullptr;
-}
-
-void GenStructMemberCalls::previsit( const ast::ApplicationExpr * appExpr ) {
-	if ( ! checkWarnings( function ) ) {
-		visit_children = false;
-		return;
-	}
-
-	std::string fname = getFunctionName( appExpr );
-	if ( fname == function->name ) {
-		// call to same kind of function
-		const ast::Expr * firstParam = appExpr->args.front();
-
-		if ( isThisExpression( firstParam, thisParam ) ) {
-			// if calling another constructor on thisParam, assume that function handles
-			// all members - if it doesn't a warning will appear in that function.
-			unhandled.clear();
-		} else if ( auto memberExpr = isThisMemberExpr( firstParam, thisParam ) ) {
-			// if first parameter is a member expression on the this parameter,
-			// then remove the member from unhandled set.
-			if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
-				unhandled.erase( memberExpr->member );
-			}
-		}
-	}
-}
-
-void GenStructMemberCalls::previsit( const ast::MemberExpr * memberExpr ) {
-	if ( ! checkWarnings( function ) || ! isCtor ) {
-		visit_children = false;
-		return;
-	}
-
-	if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
-		if ( unhandled.count( memberExpr->member ) ) {
-			// emit a warning because a member was used before it was constructed
-			usedUninit.insert( { memberExpr->member, memberExpr->location } );
-		}
-	}
-}
-
-template< typename... Params >
-void GenStructMemberCalls::emit( CodeLocation loc, const Params &... params ) {
-	SemanticErrorException err( loc, toString( params... ) );
-	errors.append( err );
-}
-
-const ast::Expr * GenStructMemberCalls::postvisit( const ast::UntypedExpr * untypedExpr ) {
-	// xxx - functions returning ast::ptr seems wrong...
-	auto res = ResolvExpr::findVoidExpression( untypedExpr, { symtab, transUnit().global } );
-	return res.release();
-}
-
-void InsertImplicitCalls::previsit(const ast::UniqueExpr * unqExpr) {
-	if (visitedIds.count(unqExpr->id)) visit_children = false;
-	else visitedIds.insert(unqExpr->id);
-}
-
-const ast::Expr * FixCtorExprs::postvisit( const ast::ConstructorExpr * ctorExpr ) {
-	const CodeLocation loc = ctorExpr->location;
-	static UniqueName tempNamer( "_tmp_ctor_expr" );
-	// xxx - is the size check necessary?
-	assert( ctorExpr->result && ctorExpr->result->size() == 1 );
-
-	// xxx - this can be TupleAssignExpr now. Need to properly handle this case.
-	// take possession of expr and env
-	ast::ptr<ast::ApplicationExpr> callExpr = ctorExpr->callExpr.strict_as<ast::ApplicationExpr>();
-	ast::ptr<ast::TypeSubstitution> env = ctorExpr->env;
-	// ctorExpr->set_callExpr( nullptr );
-	// ctorExpr->set_env( nullptr );
-
-	// xxx - ideally we would reuse the temporary generated from the copy constructor passes from within firstArg if it exists and not generate a temporary if it's unnecessary.
-	auto tmp = new ast::ObjectDecl(loc, tempNamer.newName(), callExpr->args.front()->result );
-	declsToAddBefore.push_back( tmp );
-
-	// build assignment and replace constructor's first argument with new temporary
-	auto mutCallExpr = callExpr.get_and_mutate();
-	const ast::Expr * firstArg = callExpr->args.front();
-	ast::Expr * assign = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ), { new ast::AddressExpr(loc, new ast::VariableExpr(loc, tmp ) ), new ast::AddressExpr( firstArg ) } );
-	firstArg = new ast::VariableExpr(loc, tmp );
-	mutCallExpr->args.front() = firstArg;
-
-	// resolve assignment and dispose of new env
-	auto resolved = ResolvExpr::findVoidExpression( assign, { symtab, transUnit().global } );
-	auto mut = resolved.get_and_mutate();
-	assertf(resolved.get() == mut, "newly resolved expression must be unique");
-	mut->env = nullptr;
-
-	// for constructor expr:
-	//   T x;
-	//   x{};
-	// results in:
-	//   T x;
-	//   T & tmp;
-	//   &tmp = &x, ?{}(tmp), tmp
-	ast::CommaExpr * commaExpr = new ast::CommaExpr(loc, resolved, new ast::CommaExpr(loc, mutCallExpr, new ast::VariableExpr(loc, tmp ) ) );
-	commaExpr->env = env;
-	return commaExpr;
-}
-
-} // namespace
-
-void fix( ast::TranslationUnit & translationUnit, bool inLibrary ) {
-	ast::Pass<SelfAssignChecker>::run( translationUnit );
-
-	// fixes StmtExpr to properly link to their resulting expression
-	ast::Pass<StmtExprResult>::run( translationUnit );
-
-	// fixes ConstructorInit for global variables. should happen before fixInitializers.
-	InitTweak::fixGlobalInit( translationUnit, inLibrary );
-
-	// must happen before ResolveCopyCtors because temporaries have to be inserted into the correct scope
-	ast::Pass<SplitExpressions>::run( translationUnit );
-
-	ast::Pass<InsertImplicitCalls>::run( translationUnit );
-
-	// Needs to happen before ResolveCopyCtors, because argument/return temporaries should not be considered in
-	// error checking branch statements
-	{
-		ast::Pass<LabelFinder> finder;
-		ast::Pass<InsertDtors>::run( translationUnit, finder );
-	}
-
-	ast::Pass<ResolveCopyCtors>::run( translationUnit );
-	FixInit::fixInitializers( translationUnit );
-	ast::Pass<GenStructMemberCalls>::run( translationUnit );
-
-	// Needs to happen after GenStructMemberCalls, since otherwise member constructors exprs
-	// don't have the correct form, and a member can be constructed more than once.
-	ast::Pass<FixCtorExprs>::run( translationUnit );
-}
-
-} // namespace InitTweak
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/InitTweak/module.mk
===================================================================
--- src/InitTweak/module.mk	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/InitTweak/module.mk	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -24,6 +24,6 @@
 	InitTweak/FixGlobalInit.cc \
 	InitTweak/FixGlobalInit.h \
-	InitTweak/FixInit.h \
-	InitTweak/FixInitNew.cpp
+	InitTweak/FixInit.cpp \
+	InitTweak/FixInit.h
 
 SRCDEMANGLE += $(SRC_INITTWEAK)
Index: src/MakeLibCfa.cpp
===================================================================
--- src/MakeLibCfa.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/MakeLibCfa.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,147 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// MakeLibCfa.cpp --
+//
+// Author           : Henry Xue
+// Created On       : Fri Aug 27 15:50:14 2021
+// Last Modified By : Henry Xue
+// Last Modified On : Fri Aug 27 15:50:14 2021
+// Update Count     : 1
+//
+
+#include "MakeLibCfa.h"
+
+#include "AST/Copy.hpp"
+#include "AST/Fwd.hpp"
+#include "AST/Pass.hpp"
+#include "CodeGen/OperatorTable.h"
+#include "Common/UniqueName.h"
+
+namespace LibCfa {
+namespace {
+	struct MakeLibCfa {
+		const ast::DeclWithType * postvisit( const ast::FunctionDecl * funcDecl );
+		std::list< ast::ptr< ast::Decl > > newDecls;
+	};
+}
+
+void makeLibCfa( ast::TranslationUnit & prelude ) {
+	ast::Pass< MakeLibCfa > maker;
+	accept_all( prelude, maker );
+	prelude.decls.splice( prelude.decls.end(), maker.core.newDecls );
+}
+
+namespace {
+	struct TypeFinder {
+		void postvisit( const ast::TypeInstType * inst ) {
+			// if a type variable is seen, assume all zero_t/one_t in the parameter list
+			//  can be replaced with the equivalent 'general' pointer.
+			if ( type ) return;
+			if ( inst->kind == ast::TypeDecl::Ftype ) {
+				type = new ast::PointerType( new ast::FunctionType() );
+			} else {
+				type = new ast::PointerType( new ast::VoidType() );
+			}
+		}
+		ast::ptr< ast::Type > type;
+	};
+
+	struct ZeroOneReplacer {
+		ZeroOneReplacer( const ast::Type * t ) : type( t ) {}
+		ast::ptr< ast::Type > type;
+
+		const ast::Type * common( const ast::Type * t ) {
+			if ( ! type ) return t;
+			return type;
+		}
+
+		const ast::Type * postvisit( const ast::OneType * t ) { return common( t ); }
+		const ast::Type * postvisit( const ast::ZeroType * t ) { return common( t ); }
+	};
+
+	// const ast::Type * fixZeroOneType( ast::FunctionDecl * origFuncDecl ) {
+	// 	// find appropriate type to replace zero_t/one_t with
+	// 	ast::Pass< TypeFinder > finder;
+	// 	origFuncDecl->type->accept( finder );
+	// 	// replace zero_t/one_t in function type
+	// 	ast::Pass< ZeroOneReplacer > replacer( finder.core.type );
+	// 	//auto funcDecl = mutate( origFuncDecl );
+	// 	return origFuncDecl->type->accept( replacer );
+	// }
+
+	const ast::DeclWithType * MakeLibCfa::postvisit( const ast::FunctionDecl * origFuncDecl ) {
+		// don't change non-intrinsic functions
+		if ( origFuncDecl->linkage != ast::Linkage::Intrinsic ) return origFuncDecl;
+		// replace zero_t/one_t with void */void (*)(void)
+		auto mutDecl = mutate( origFuncDecl );
+		//mutDecl->type = fixZeroOneType( mutDecl );
+
+		// find appropriate type to replace zero_t/one_t with
+		ast::Pass< TypeFinder > finder;
+		mutDecl->type->accept( finder );
+		// replace zero_t/one_t in function type
+		ast::Pass< ZeroOneReplacer > replacer( finder.core.type );
+		mutDecl->type = mutDecl->type->accept( replacer );
+
+		// skip functions already defined
+		if ( mutDecl->has_body() ) return mutDecl;
+
+		const CodeLocation loc = mutDecl->location;
+		auto funcDecl = dynamic_cast<ast::FunctionDecl *>(ast::deepCopy( (ast::DeclWithType*)mutDecl ));
+		const CodeGen::OperatorInfo * opInfo;
+		opInfo = CodeGen::operatorLookup( funcDecl->name );
+		assert( opInfo );
+		assert( ! funcDecl->has_body() );
+		// build a recursive call - this is okay, as the call will actually be codegen'd using operator syntax
+		auto newExpr = new ast::UntypedExpr( loc, new ast::NameExpr( loc, funcDecl->name ) );
+		UniqueName paramNamer( "_p" );
+		auto param = funcDecl->params.begin();
+		assert( param != funcDecl->params.end() );
+
+		for ( ; param != funcDecl->params.end(); ++param ) {
+			// name each unnamed parameter
+			if ( (*param)->name == "" ) {
+				auto _param = param->get_and_mutate();
+				_param->name = paramNamer.newName() ;
+				_param->linkage = ast::Linkage::C;
+			}
+			// add parameter to the expression
+			newExpr->args.push_back( new ast::VariableExpr( loc, *param ) );
+		} // for
+
+		auto stmts = new ast::CompoundStmt( loc );;
+		newDecls.push_back( funcDecl );
+
+		ast::ptr< ast::Stmt > stmt;
+		switch ( opInfo->type ) {
+			case CodeGen::OT_INDEX:
+			case CodeGen::OT_CALL:
+			case CodeGen::OT_PREFIX:
+			case CodeGen::OT_POSTFIX:
+			case CodeGen::OT_INFIX:
+			case CodeGen::OT_PREFIXASSIGN:
+			case CodeGen::OT_POSTFIXASSIGN:
+			case CodeGen::OT_INFIXASSIGN:
+				// return the recursive call
+				stmt = new ast::ReturnStmt( loc, newExpr );
+				break;
+			case CodeGen::OT_CTOR:
+			case CodeGen::OT_DTOR:
+				// execute the recursive call
+				stmt = new ast::ExprStmt( loc, newExpr );
+				break;
+			case CodeGen::OT_CONSTANT:
+			case CodeGen::OT_LABELADDRESS:
+			// there are no intrinsic definitions of 0/1 or label addresses as functions
+			assert( false );
+		} // switch
+		stmts->push_back( stmt );
+		funcDecl->stmts = stmts;
+		return mutDecl;
+	}
+} // namespace
+} // namespace LibCfa
Index: c/MakeLibCfaNew.cpp
===================================================================
--- src/MakeLibCfaNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,147 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// MakeLibCfaNew.cpp --
-//
-// Author           : Henry Xue
-// Created On       : Fri Aug 27 15:50:14 2021
-// Last Modified By : Henry Xue
-// Last Modified On : Fri Aug 27 15:50:14 2021
-// Update Count     : 1
-//
-
-#include "MakeLibCfa.h"
-
-#include "AST/Copy.hpp"
-#include "AST/Fwd.hpp"
-#include "AST/Pass.hpp"
-#include "CodeGen/OperatorTable.h"
-#include "Common/UniqueName.h"
-
-namespace LibCfa {
-namespace {
-	struct MakeLibCfa {
-		const ast::DeclWithType * postvisit( const ast::FunctionDecl * funcDecl );
-		std::list< ast::ptr< ast::Decl > > newDecls;
-	};
-}
-
-void makeLibCfa( ast::TranslationUnit & prelude ) {
-	ast::Pass< MakeLibCfa > maker;
-	accept_all( prelude, maker );
-	prelude.decls.splice( prelude.decls.end(), maker.core.newDecls );
-}
-
-namespace {
-	struct TypeFinder {
-		void postvisit( const ast::TypeInstType * inst ) {
-			// if a type variable is seen, assume all zero_t/one_t in the parameter list
-			//  can be replaced with the equivalent 'general' pointer.
-			if ( type ) return;
-			if ( inst->kind == ast::TypeDecl::Ftype ) {
-				type = new ast::PointerType( new ast::FunctionType() );
-			} else {
-				type = new ast::PointerType( new ast::VoidType() );
-			}
-		}
-		ast::ptr< ast::Type > type;
-	};
-
-	struct ZeroOneReplacer {
-		ZeroOneReplacer( const ast::Type * t ) : type( t ) {}
-		ast::ptr< ast::Type > type;
-
-		const ast::Type * common( const ast::Type * t ) {
-			if ( ! type ) return t;
-			return type;
-		}
-
-		const ast::Type * postvisit( const ast::OneType * t ) { return common( t ); }
-		const ast::Type * postvisit( const ast::ZeroType * t ) { return common( t ); }
-	};
-
-	// const ast::Type * fixZeroOneType( ast::FunctionDecl * origFuncDecl ) {
-	// 	// find appropriate type to replace zero_t/one_t with
-	// 	ast::Pass< TypeFinder > finder;
-	// 	origFuncDecl->type->accept( finder );
-	// 	// replace zero_t/one_t in function type
-	// 	ast::Pass< ZeroOneReplacer > replacer( finder.core.type );
-	// 	//auto funcDecl = mutate( origFuncDecl );
-	// 	return origFuncDecl->type->accept( replacer );
-	// }
-
-	const ast::DeclWithType * MakeLibCfa::postvisit( const ast::FunctionDecl * origFuncDecl ) {
-		// don't change non-intrinsic functions
-		if ( origFuncDecl->linkage != ast::Linkage::Intrinsic ) return origFuncDecl;
-		// replace zero_t/one_t with void */void (*)(void)
-		auto mutDecl = mutate( origFuncDecl );
-		//mutDecl->type = fixZeroOneType( mutDecl );
-
-		// find appropriate type to replace zero_t/one_t with
-		ast::Pass< TypeFinder > finder;
-		mutDecl->type->accept( finder );
-		// replace zero_t/one_t in function type
-		ast::Pass< ZeroOneReplacer > replacer( finder.core.type );
-		mutDecl->type = mutDecl->type->accept( replacer );
-
-		// skip functions already defined
-		if ( mutDecl->has_body() ) return mutDecl;
-
-		const CodeLocation loc = mutDecl->location;
-		auto funcDecl = dynamic_cast<ast::FunctionDecl *>(ast::deepCopy( (ast::DeclWithType*)mutDecl ));
-		const CodeGen::OperatorInfo * opInfo;
-		opInfo = CodeGen::operatorLookup( funcDecl->name );
-		assert( opInfo );
-		assert( ! funcDecl->has_body() );
-		// build a recursive call - this is okay, as the call will actually be codegen'd using operator syntax
-		auto newExpr = new ast::UntypedExpr( loc, new ast::NameExpr( loc, funcDecl->name ) );
-		UniqueName paramNamer( "_p" );
-		auto param = funcDecl->params.begin();
-		assert( param != funcDecl->params.end() );
-
-		for ( ; param != funcDecl->params.end(); ++param ) {
-			// name each unnamed parameter
-			if ( (*param)->name == "" ) {
-				auto _param = param->get_and_mutate();
-				_param->name = paramNamer.newName() ;
-				_param->linkage = ast::Linkage::C;
-			}
-			// add parameter to the expression
-			newExpr->args.push_back( new ast::VariableExpr( loc, *param ) );
-		} // for
-
-		auto stmts = new ast::CompoundStmt( loc );;
-		newDecls.push_back( funcDecl );
-
-		ast::ptr< ast::Stmt > stmt;
-		switch ( opInfo->type ) {
-			case CodeGen::OT_INDEX:
-			case CodeGen::OT_CALL:
-			case CodeGen::OT_PREFIX:
-			case CodeGen::OT_POSTFIX:
-			case CodeGen::OT_INFIX:
-			case CodeGen::OT_PREFIXASSIGN:
-			case CodeGen::OT_POSTFIXASSIGN:
-			case CodeGen::OT_INFIXASSIGN:
-				// return the recursive call
-				stmt = new ast::ReturnStmt( loc, newExpr );
-				break;
-			case CodeGen::OT_CTOR:
-			case CodeGen::OT_DTOR:
-				// execute the recursive call
-				stmt = new ast::ExprStmt( loc, newExpr );
-				break;
-			case CodeGen::OT_CONSTANT:
-			case CodeGen::OT_LABELADDRESS:
-			// there are no intrinsic definitions of 0/1 or label addresses as functions
-			assert( false );
-		} // switch
-		stmts->push_back( stmt );
-		funcDecl->stmts = stmts;
-		return mutDecl;
-	}
-} // namespace
-} // namespace LibCfa
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/Makefile.am	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -22,5 +22,5 @@
 	CompilationState.cc \
 	CompilationState.h \
-	MakeLibCfaNew.cpp \
+	MakeLibCfa.cpp \
 	MakeLibCfa.h
 
Index: src/Tuples/TupleExpansion.cpp
===================================================================
--- src/Tuples/TupleExpansion.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/Tuples/TupleExpansion.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// TupleExpansionNew.cpp --
+// TupleExpansion.cpp --
 //
 // Author           : Henry Xue
Index: src/Validate/FindSpecialDecls.cpp
===================================================================
--- src/Validate/FindSpecialDecls.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
+++ src/Validate/FindSpecialDecls.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -0,0 +1,87 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// FindSpecialDecls.cpp -- Find special declarations used in the compiler.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Nov 10 13:51:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Nov 10 15:22:00 2021
+// Update Count     : 0
+//
+
+#include "Validate/FindSpecialDecls.h"
+
+#include "AST/Decl.hpp"
+#include "AST/Pass.hpp"
+#include "AST/TranslationUnit.hpp"
+
+// NOTE: currently, it is assumed that every special declaration occurs at the
+// top-level, so function bodies, aggregate bodies, object initializers, etc.
+// are not visited. If this assumption changes, e.g., with the introduction
+// of namespaces, remove the visit_children assignments.
+
+namespace Validate {
+
+namespace {
+
+struct FindDeclsCore : public ast::WithShortCircuiting {
+	ast::TranslationGlobal & global;
+	FindDeclsCore( ast::TranslationGlobal & g ) : global( g ) {}
+
+	void previsit( const ast::Decl * decl );
+	void previsit( const ast::FunctionDecl * decl );
+	void previsit( const ast::StructDecl * decl );
+};
+
+void FindDeclsCore::previsit( const ast::Decl * ) {
+	visit_children = false;
+}
+
+void FindDeclsCore::previsit( const ast::FunctionDecl * decl ) {
+	visit_children = false;
+	if ( !global.dereference && decl->name == "*?" ) {
+		const ast::FunctionType * type = decl->type.get();
+		if ( decl->linkage == ast::Linkage::Intrinsic && type->params.size() == 1 ) {
+			const ast::PointerType * ptrType = type->params.front().strict_as<ast::PointerType>();
+			ast::ptr<ast::Type> baseType = ptrType->base;
+			if ( baseType->qualifiers == ast::CV::Qualifiers() ) {
+				const ast::TypeInstType * inst = baseType.as<ast::TypeInstType>();
+				if ( inst || inst->kind != ast::TypeDecl::Ftype ) {
+					global.dereference = decl;
+				}
+			}
+		}
+	} else if ( !global.dtorDestroy && decl->name == "__destroy_Destructor" ) {
+		global.dtorDestroy = decl;
+	}
+}
+
+void FindDeclsCore::previsit( const ast::StructDecl * decl ) {
+	visit_children = false;
+	if ( !global.dtorStruct && decl->name == "__Destructor" ) {
+		global.dtorStruct = decl;
+	}
+}
+
+} // namespace
+
+// Fill the TranslationUnit's dereference, dtorStruct and dtorDestroy fields.
+void findGlobalDecls( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<FindDeclsCore>::run( translationUnit, translationUnit.global );
+
+	// TODO: conditionally generate 'fake' declarations for missing features,
+	// so that translation can proceed in the event that builtins, prelude,
+	// etc. are missing.
+}
+
+} // namespace Validate
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: c/Validate/FindSpecialDeclsNew.cpp
===================================================================
--- src/Validate/FindSpecialDeclsNew.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ 	(revision )
@@ -1,87 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FindSpecialDeclsNew.cpp -- Find special declarations used in the compiler.
-//
-// Author           : Andrew Beach
-// Created On       : Wed Nov 10 13:51:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Nov 10 15:22:00 2021
-// Update Count     : 0
-//
-
-#include "Validate/FindSpecialDecls.h"
-
-#include "AST/Decl.hpp"
-#include "AST/Pass.hpp"
-#include "AST/TranslationUnit.hpp"
-
-// NOTE: currently, it is assumed that every special declaration occurs at the
-// top-level, so function bodies, aggregate bodies, object initializers, etc.
-// are not visited. If this assumption changes, e.g., with the introduction
-// of namespaces, remove the visit_children assignments.
-
-namespace Validate {
-
-namespace {
-
-struct FindDeclsCore : public ast::WithShortCircuiting {
-	ast::TranslationGlobal & global;
-	FindDeclsCore( ast::TranslationGlobal & g ) : global( g ) {}
-
-	void previsit( const ast::Decl * decl );
-	void previsit( const ast::FunctionDecl * decl );
-	void previsit( const ast::StructDecl * decl );
-};
-
-void FindDeclsCore::previsit( const ast::Decl * ) {
-	visit_children = false;
-}
-
-void FindDeclsCore::previsit( const ast::FunctionDecl * decl ) {
-	visit_children = false;
-	if ( !global.dereference && decl->name == "*?" ) {
-		const ast::FunctionType * type = decl->type.get();
-		if ( decl->linkage == ast::Linkage::Intrinsic && type->params.size() == 1 ) {
-			const ast::PointerType * ptrType = type->params.front().strict_as<ast::PointerType>();
-			ast::ptr<ast::Type> baseType = ptrType->base;
-			if ( baseType->qualifiers == ast::CV::Qualifiers() ) {
-				const ast::TypeInstType * inst = baseType.as<ast::TypeInstType>();
-				if ( inst || inst->kind != ast::TypeDecl::Ftype ) {
-					global.dereference = decl;
-				}
-			}
-		}
-	} else if ( !global.dtorDestroy && decl->name == "__destroy_Destructor" ) {
-		global.dtorDestroy = decl;
-	}
-}
-
-void FindDeclsCore::previsit( const ast::StructDecl * decl ) {
-	visit_children = false;
-	if ( !global.dtorStruct && decl->name == "__Destructor" ) {
-		global.dtorStruct = decl;
-	}
-}
-
-} // namespace
-
-// Fill the TranslationUnit's dereference, dtorStruct and dtorDestroy fields.
-void findGlobalDecls( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<FindDeclsCore>::run( translationUnit, translationUnit.global );
-
-	// TODO: conditionally generate 'fake' declarations for missing features,
-	// so that translation can proceed in the event that builtins, prelude,
-	// etc. are missing.
-}
-
-} // namespace Validate
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Validate/FixReturnTypes.cpp
===================================================================
--- src/Validate/FixReturnTypes.cpp	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/Validate/FixReturnTypes.cpp	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -19,10 +19,6 @@
 #include "AST/Pass.hpp"
 #include "AST/Type.hpp"
-#include "CodeGen/CodeGeneratorNew.hpp"
+#include "CodeGen/CodeGenerator.hpp"
 #include "ResolvExpr/Unify.h"
-
-namespace ast {
-    class TranslationUnit;
-}
 
 namespace Validate {
Index: src/Validate/module.mk
===================================================================
--- src/Validate/module.mk	(revision 6009a5a8d82cef194788e78005da6e3c1bce16c3)
+++ src/Validate/module.mk	(revision 83fd57dbde2b629bbcc22cdd6694eccab69dd48e)
@@ -27,5 +27,5 @@
 	Validate/EnumAndPointerDecay.cpp \
 	Validate/EnumAndPointerDecay.hpp \
-	Validate/FindSpecialDeclsNew.cpp \
+	Validate/FindSpecialDecls.cpp \
 	Validate/FixQualifiedTypes.cpp \
 	Validate/FixQualifiedTypes.hpp \
