Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/AST/Decl.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -131,4 +131,5 @@
 	// declared type, derived from parameter declarations
 	ptr<FunctionType> type;
+	/// Null for the forward declaration of a function.
 	ptr<CompoundStmt> stmts;
 	std::vector< ptr<Expr> > withExprs;
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/AST/Pass.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -348,9 +348,11 @@
 
 	/// When this node is finished being visited, restore the value of a variable
+	/// You may assign to the return value to set the new value in the same statement.
 	template< typename T >
-	void GuardValue( T& val ) {
+	T& GuardValue( T& val ) {
 		at_cleanup( [ val ]( void * newVal ) {
 			* static_cast< T * >( newVal ) = val;
 		}, static_cast< void * >( & val ) );
+		return val;
 	}
 
@@ -394,4 +396,14 @@
 };
 
+/// Used to get a pointer to the wrapping TranslationUnit.
+struct WithConstTranslationUnit {
+	const TranslationUnit * translationUnit = nullptr;
+
+	const TranslationUnit & transUnit() const {
+		assertf( translationUnit, "WithConstTranslationUnit not set-up." );
+		return *translationUnit;
+	}
+};
+
 }
 
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/AST/Pass.impl.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -420,5 +420,11 @@
 template< typename core_t >
 inline void ast::accept_all( ast::TranslationUnit & unit, ast::Pass< core_t > & visitor ) {
-	return ast::accept_all( unit.decls, visitor );
+	if ( auto ptr = __pass::translation_unit::get_cptr( visitor.core, 0 ) ) {
+		ValueGuard<const TranslationUnit *> guard( *ptr );
+		*ptr = &unit;
+		return ast::accept_all( unit.decls, visitor );
+	} else {
+		return ast::accept_all( unit.decls, visitor );
+	}
 }
 
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/AST/Pass.proto.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -426,4 +426,18 @@
 	} // namespace forall
 
+	// For passes that need access to the global context. Sreaches `translationUnit`
+	namespace translation_unit {
+		template<typename core_t>
+		static inline auto get_cptr( core_t & core, int )
+				-> decltype( &core.translationUnit ) {
+			return &core.translationUnit;
+		}
+
+		template<typename core_t>
+		static inline const TranslationUnit ** get_cptr( core_t &, long ) {
+			return nullptr;
+		}
+	}
+
 	template<typename core_t>
 	static inline auto get_result( core_t & core, char ) -> decltype( core.result() ) {
Index: src/AST/Stmt.hpp
===================================================================
--- src/AST/Stmt.hpp	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/AST/Stmt.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -175,4 +175,5 @@
 class CaseStmt final : public Stmt {
 public:
+	/// Null for the default label.
 	ptr<Expr> cond;
 	std::vector<ptr<Stmt>> stmts;
Index: src/AST/TranslationUnit.hpp
===================================================================
--- src/AST/TranslationUnit.hpp	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/AST/TranslationUnit.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -26,8 +26,8 @@
 	std::list< ptr< Decl > > decls;
 
-	struct Globals {
+	struct Global {
 		std::map< UniqueId, Decl * > idMap;
 
-		const Type * sizeType;
+		ptr<Type> sizeType;
 		const FunctionDecl * dereference;
 		const StructDecl * dtorStruct;
Index: src/AST/porting.md
===================================================================
--- src/AST/porting.md	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/AST/porting.md	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -98,4 +98,5 @@
 	* `Initializer` => `ast::Init`
     * `Statement` => `ast::Stmt`
+    * `ReferenceToType` => `ast::BaseInstType`
 	* any field names should follow a similar renaming
   * because they don't really belong to `Type` (and for consistency with `Linkage::Spec`):
Index: src/CodeGen/FixMain.cc
===================================================================
--- src/CodeGen/FixMain.cc	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/CodeGen/FixMain.cc	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -22,4 +22,7 @@
 #include <string>                  // for operator<<
 
+#include "AST/Decl.hpp"
+#include "AST/Type.hpp"
+#include "Common/PassVisitor.h"
 #include "Common/SemanticError.h"  // for SemanticError
 #include "CodeGen/GenType.h"       // for GenType
@@ -29,6 +32,23 @@
 
 namespace CodeGen {
+
+namespace {
+
+struct FindMainCore {
+	FunctionDecl * main_signature = nullptr;
+
+	void previsit( FunctionDecl * decl ) {
+		if ( FixMain::isMain( decl ) ) {
+			if ( main_signature ) {
+				SemanticError( decl, "Multiple definition of main routine\n" );
+			}
+			main_signature = decl;
+		}
+	}
+};
+
+}
+
 	bool FixMain::replace_main = false;
-	std::unique_ptr<FunctionDecl> FixMain::main_signature = nullptr;
 
 	template<typename container>
@@ -37,16 +57,13 @@
 	}
 
-	void FixMain::registerMain(FunctionDecl* functionDecl)
-	{
-		if(main_signature) {
-			SemanticError(functionDecl, "Multiple definition of main routine\n");
-		}
-		main_signature.reset( functionDecl->clone() );
-	}
+	void FixMain::fix( std::list< Declaration * > & translationUnit,
+			std::ostream &os, const char* bootloader_filename ) {
+		PassVisitor< FindMainCore > main_finder;
+		acceptAll( translationUnit, main_finder );
+		FunctionDecl * main_signature = main_finder.pass.main_signature;
 
-	void FixMain::fix(std::ostream &os, const char* bootloader_filename) {
 		if( main_signature ) {
 			os << "static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return ";
-			main_signature->mangleName = SymTab::Mangler::mangle(main_signature.get());
+			main_signature->mangleName = SymTab::Mangler::mangle(main_signature);
 
 			os << main_signature->get_scopedMangleName() << "(";
@@ -65,3 +82,72 @@
 		}
 	}
+
+namespace {
+
+ObjectDecl * signedIntObj() {
+	return new ObjectDecl(
+		"", Type::StorageClasses(), LinkageSpec::Cforall, 0,
+		new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr );
+}
+
+ObjectDecl * charStarObj() {
+	return new ObjectDecl(
+		"", Type::StorageClasses(), LinkageSpec::Cforall, 0,
+		new PointerType( Type::Qualifiers(),
+			new PointerType( Type::Qualifiers(),
+				new BasicType( Type::Qualifiers(), BasicType::Char ) ) ),
+		nullptr );
+}
+
+std::string create_mangled_main_function_name( FunctionType * function_type ) {
+	std::unique_ptr<FunctionDecl> decl( new FunctionDecl(
+		"main", Type::StorageClasses(), LinkageSpec::Cforall,
+		function_type, nullptr ) );
+	return SymTab::Mangler::mangle( decl.get() );
+}
+
+std::string mangled_0_argument_main() {
+	FunctionType* main_type = new FunctionType( Type::Qualifiers(), true );
+	main_type->get_returnVals().push_back( signedIntObj() );
+	return create_mangled_main_function_name( main_type );
+}
+
+std::string mangled_2_argument_main() {
+	FunctionType* main_type = new FunctionType( Type::Qualifiers(), false );
+	main_type->get_returnVals().push_back( signedIntObj() );
+	main_type->get_parameters().push_back( signedIntObj() );
+	main_type->get_parameters().push_back( charStarObj() );
+	return create_mangled_main_function_name( main_type );
+}
+
+bool is_main( const std::string & mangled_name ) {
+	// This breaks if you move it out of the function.
+	static const std::string mangled_mains[] = {
+		mangled_0_argument_main(),
+		mangled_2_argument_main(),
+		//mangled_3_argument_main(),
+	};
+
+	for ( auto main_name : mangled_mains ) {
+		if ( main_name == mangled_name ) return true;
+	}
+	return false;
+}
+
+} // namespace
+
+bool FixMain::isMain( FunctionDecl * decl ) {
+	if ( std::string("main") != decl->name ) {
+		return false;
+	}
+	return is_main( SymTab::Mangler::mangle( decl, true, true ) );
+}
+
+bool FixMain::isMain( const ast::FunctionDecl * decl ) {
+	if ( std::string("main") != decl->name ) {
+		return false;
+	}
+	return is_main( Mangle::mangle( decl, Mangle::Type ) );
+}
+
 };
Index: src/CodeGen/FixMain.h
===================================================================
--- src/CodeGen/FixMain.h	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/CodeGen/FixMain.h	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -9,7 +9,7 @@
 // Author           : Thierry Delisle
 // Created On       : Thr Jan 12 14:11:09 2017
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sun Feb 16 03:24:32 2020
-// Update Count     : 5
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Oct 29 16:20:00 2021
+// Update Count     : 8
 //
 
@@ -18,27 +18,35 @@
 #include <iosfwd>
 #include <memory>
+#include <list>
 
 #include "SynTree/LinkageSpec.h"
 
+class Declaration;
 class FunctionDecl;
+namespace ast {
+	class FunctionDecl;
+}
 
 namespace CodeGen {
-	class FixMain {
-	  public :
-		static inline LinkageSpec::Spec mainLinkage() {
-			return replace_main ? LinkageSpec::Cforall : LinkageSpec::C;
-		}
-		
-		static inline void setReplaceMain(bool val) {
-			replace_main = val;
-		}
 
-		static void registerMain(FunctionDecl* val);
+class FixMain {
+public :
+	static inline LinkageSpec::Spec mainLinkage() {
+		return replace_main ? LinkageSpec::Cforall : LinkageSpec::C;
+	}
 
-		static void fix(std::ostream &os, const char* bootloader_filename);
+	static inline void setReplaceMain(bool val) {
+		replace_main = val;
+	}
 
-	  private:
-  		static bool replace_main;
-		static std::unique_ptr<FunctionDecl> main_signature;
-	};
+	static bool isMain(FunctionDecl* decl);
+	static bool isMain(const ast::FunctionDecl * decl);
+
+	static void fix( std::list< Declaration * > & decls,
+			std::ostream &os, const char* bootloader_filename );
+
+private:
+	static bool replace_main;
+};
+
 } // namespace CodeGen
Index: src/CodeGen/FixNames.cc
===================================================================
--- src/CodeGen/FixNames.cc	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/CodeGen/FixNames.cc	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -9,7 +9,7 @@
 // Author           : Richard C. Bilson
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Dec 13 23:39:14 2019
-// Update Count     : 21
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Oct 29 15:49:00 2021
+// Update Count     : 23
 //
 
@@ -19,4 +19,7 @@
 #include <string>                  // for string, operator!=, operator==
 
+#include "AST/Chain.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
 #include "Common/PassVisitor.h"
 #include "Common/SemanticError.h"  // for SemanticError
@@ -46,53 +49,4 @@
 	};
 
-	std::string mangle_main() {
-		FunctionType* main_type;
-		std::unique_ptr<FunctionDecl> mainDecl { new FunctionDecl( "main", Type::StorageClasses(), LinkageSpec::Cforall,
-																   main_type = new FunctionType( Type::Qualifiers(), true ), nullptr )
-				};
-		main_type->get_returnVals().push_back(
-			new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )
-		);
-
-		auto && name = SymTab::Mangler::mangle( mainDecl.get() );
-		// std::cerr << name << std::endl;
-		return std::move(name);
-	}
-	std::string mangle_main_args() {
-		FunctionType* main_type;
-		std::unique_ptr<FunctionDecl> mainDecl { new FunctionDecl( "main", Type::StorageClasses(), LinkageSpec::Cforall,
-																   main_type = new FunctionType( Type::Qualifiers(), false ), nullptr )
-				};
-		main_type->get_returnVals().push_back(
-			new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )
-		);
-
-		main_type->get_parameters().push_back(
-			new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), nullptr )
-		);
-
-		main_type->get_parameters().push_back(
-			new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0,
-			new PointerType( Type::Qualifiers(), new PointerType( Type::Qualifiers(), new BasicType( Type::Qualifiers(), BasicType::Char ) ) ),
-			nullptr )
-		);
-
-		auto&& name = SymTab::Mangler::mangle( mainDecl.get() );
-		// std::cerr << name << std::endl;
-		return std::move(name);
-	}
-
-	bool is_main(const std::string& name) {
-		static std::string mains[] = {
-			mangle_main(),
-			mangle_main_args()
-		};
-
-		for(const auto& m : mains) {
-			if( name == m ) return true;
-		}
-		return false;
-	}
-
 	void fixNames( std::list< Declaration* > & translationUnit ) {
 		PassVisitor<FixNames> fixer;
@@ -118,5 +72,5 @@
 		fixDWT( functionDecl );
 
-		if(is_main( SymTab::Mangler::mangle(functionDecl, true, true) )) {
+		if ( FixMain::isMain( functionDecl ) ) {
 			int nargs = functionDecl->get_functionType()->get_parameters().size();
 			if( !(nargs == 0 || nargs == 2 || nargs == 3) ) {
@@ -124,5 +78,4 @@
 			}
 			functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( new ConstantExpr( Constant::from_int( 0 ) ) ) );
-			CodeGen::FixMain::registerMain( functionDecl );
 		}
 	}
@@ -132,4 +85,56 @@
 		GuardAction( [this](){ scopeLevel--; } );
 	}
+
+/// Does work with the main function and scopeLevels.
+class FixNames_new : public ast::WithGuards {
+	int scopeLevel = 1;
+
+	bool shouldSetScopeLevel( const ast::DeclWithType * dwt ) {
+		return !dwt->name.empty() && dwt->linkage.is_mangled
+			&& dwt->scopeLevel != scopeLevel;
+	}
+public:
+	const ast::ObjectDecl *postvisit( const ast::ObjectDecl *objectDecl ) {
+		if ( shouldSetScopeLevel( objectDecl ) ) {
+			return ast::mutate_field( objectDecl, &ast::ObjectDecl::scopeLevel, scopeLevel );
+		}
+		return objectDecl;
+	}
+
+	const ast::FunctionDecl *postvisit( const ast::FunctionDecl *functionDecl ) {
+		// This store is used to ensure a maximum of one call to mutate.
+		ast::FunctionDecl * mutDecl = nullptr;
+
+		if ( shouldSetScopeLevel( functionDecl ) ) {
+			mutDecl = ast::mutate( functionDecl );
+			mutDecl->scopeLevel = scopeLevel;
+		}
+
+		if ( FixMain::isMain( functionDecl ) ) {
+			if ( !mutDecl ) { mutDecl = ast::mutate( functionDecl ); }
+
+			int nargs = mutDecl->params.size();
+			if ( 0 != nargs && 2 != nargs && 3 != nargs ) {
+				SemanticError( functionDecl, "Main expected to have 0, 2 or 3 arguments\n" );
+			}
+			ast::chain_mutate( mutDecl->stmts )->kids.push_back(
+				new ast::ReturnStmt(
+					mutDecl->location,
+					ast::ConstantExpr::from_int( mutDecl->location, 0 )
+				)
+			);
+		}
+		return mutDecl ? mutDecl : functionDecl;
+	}
+
+	void previsit( const ast::CompoundStmt * ) {
+		GuardValue( scopeLevel ) += 1;
+	}
+};
+
+void fixNames( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<FixNames_new>::run( translationUnit );
+}
+
 } // namespace CodeGen
 
Index: src/CodeGen/FixNames.h
===================================================================
--- src/CodeGen/FixNames.h	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/CodeGen/FixNames.h	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -9,7 +9,7 @@
 // Author           : Richard C. Bilson
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Jul 21 22:17:33 2017
-// Update Count     : 3
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Oct 26 13:47:00 2021
+// Update Count     : 4
 //
 
@@ -19,8 +19,12 @@
 
 class Declaration;
+namespace ast {
+	struct TranslationUnit;
+}
 
 namespace CodeGen {
 	/// mangles object and function names
 	void fixNames( std::list< Declaration* > & translationUnit );
+	void fixNames( ast::TranslationUnit & translationUnit );
 } // namespace CodeGen
 
Index: src/CodeTools/DeclStats.cc
===================================================================
--- src/CodeTools/DeclStats.cc	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/CodeTools/DeclStats.cc	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -156,6 +156,17 @@
 		/// number of counting bins for linkages
 		static const unsigned n_named_specs = 8;
-		/// map from total number of specs to bins
-		static const unsigned ind_for_linkage[16];
+		/// Mapping function from linkage to bin.
+		static unsigned linkage_index( LinkageSpec::Spec spec ) {
+			switch ( spec ) {
+			case LinkageSpec::Intrinsic:  return 0;
+			case LinkageSpec::C:          return 1;
+			case LinkageSpec::Cforall:    return 2;
+			case LinkageSpec::AutoGen:    return 3;
+			case LinkageSpec::Compiler:   return 4;
+			case LinkageSpec::BuiltinCFA: return 5;
+			case LinkageSpec::BuiltinC:   return 6;
+			default:                      return 7;
+			}
+		}
 
 		Stats for_linkage[n_named_specs];            ///< Stores separate stats per linkage
@@ -366,5 +377,5 @@
 			const std::string& mangleName = decl->get_mangleName().empty() ? decl->name : decl->get_mangleName();
 			if ( seen_names.insert( mangleName ).second ) {
-				Stats& stats = for_linkage[ ind_for_linkage[ decl->linkage ] ];
+				Stats& stats = for_linkage[ linkage_index( decl->linkage ) ];
 
 				++stats.n_decls;
@@ -527,7 +538,4 @@
 	};
 
-	const unsigned DeclStats::ind_for_linkage[]
-		= { 7, 7, 2, 1,   7, 7, 7, 3,   4, 7, 6, 5,   7, 7, 7, 0 };
-
 	void printDeclStats( std::list< Declaration * > &translationUnit ) {
 		PassVisitor<DeclStats> stats;
Index: src/Common/DeclStats.cpp
===================================================================
--- src/Common/DeclStats.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/Common/DeclStats.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,575 @@
+//
+// 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.
+//
+// DeclStats.cpp -- Print statistics about a translation unit's declarations.
+//
+// Author           : Andrew Beach
+// Created On       : Fri Oct  1 14:26:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Oct  8 11:24:00 2021
+// Update Count     : 0
+//
+
+#include "DeclStats.hpp"
+
+#include "AST/LinkageSpec.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Print.hpp"
+#include "Common/VectorMap.h"
+
+#include <iostream>
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+
+// Everything but printDeclStats at the bottom is hidden.
+namespace {
+
+template<typename T>
+void sum( T & l, const T & r ) { l += r; }
+
+void sum( VectorMap<unsigned> & l, const VectorMap<unsigned> & r ) {
+	l.reserve( r.size() );
+	for ( unsigned i = 0 ; i < r.size() ; ++i ) {
+		l[i] += r[i];
+	}
+}
+
+template<typename KeyT>
+void sum( std::map<KeyT, unsigned> & l, const std::map<KeyT, unsigned> & r ) {
+	for ( const auto & entry : r ) {
+		l[ entry.first ] += entry.second;
+	}
+}
+
+template<typename KeyT>
+void sum( std::unordered_map<KeyT, unsigned> & l,
+		const std::unordered_map<KeyT, unsigned> & r ) {
+	for ( const auto & entry : r ) {
+		l[ entry.first ] += entry.second;
+	}
+}
+
+/// Stores statistics on a single group of arguments or return values.
+struct ArgPackStats {
+	/// Count of decls with each number of elements.
+	VectorMap<unsigned> n;
+	/// Count of decls with each number of basic type elements.
+	VectorMap<unsigned> n_basic;
+	/// Count of decls with each number of generic type elements.
+	VectorMap<unsigned> n_generic;
+	/// Count of decls with each number of polymorphic elements.
+	VectorMap<unsigned> n_poly;
+	/// Count of decls with each number of non-generic compound types.
+	VectorMap<unsigned> n_compound;
+	/// Count of decls with each percentage of basic type elements.
+	std::map<unsigned, unsigned> p_basic;
+	/// Count of decls with each percentage of generic type elements.
+	std::map<unsigned, unsigned> p_generic;
+	/// Count of decls with each percentage of polymorphic elements.
+	std::map<unsigned, unsigned> p_poly;
+	/// Count of decls with each percentage of non-generic compound type elements.
+	std::map<unsigned, unsigned> p_compound;
+	/// Count of decls with each number of distinct types in the pack.
+	VectorMap<unsigned> n_types;
+	/// Count of decls with each percentage of new types in lists.
+	/// Types used in the parameter list that recur in the return list are not considered to be new.
+	std::map<unsigned, unsigned> p_new;
+
+	ArgPackStats& operator+=( const ArgPackStats& other ) {
+		sum(n, other.n);
+		sum(n_basic, other.n_basic);
+		sum(n_generic, other.n_generic);
+		sum(n_poly, other.n_poly);
+		sum(n_compound, other.n_compound);
+		sum(p_basic, other.p_basic);
+		sum(p_generic, other.p_generic);
+		sum(p_poly, other.p_poly);
+		sum(p_compound, other.p_compound);
+		sum(n_types, other.n_types);
+		sum(p_new, other.p_new);
+
+		return *this;
+	}
+};
+
+/// Collected statistics on a group of declarations.
+struct Stats {
+	/// Total number of declarations in these statistics.
+	unsigned n_decls;
+	/// Count of declarations with each number of assertion parameters.
+	VectorMap<unsigned> n_type_params;
+	/// Count of generic types with each number of type parameters.
+	VectorMap<unsigned> n_generic_params;
+	/// Count of maximum nesting depth of types.
+	VectorMap<unsigned> n_generic_nesting;
+	/// Count of declarations with each name.
+	std::unordered_map<std::string, unsigned> by_name;
+	/// Count of uses of each basic type.
+	std::unordered_map<std::string, unsigned> basic_type_names;
+	/// Count of uses of each generic type name (includes "*", "[]", "(*)", "[,]").
+	std::unordered_map<std::string, unsigned> generic_type_names;
+	/// Count of uses of each non-generic aggregate type.
+	std::unordered_map<std::string, unsigned> compound_type_names;
+	/// Count of decls using each basic type.
+	std::unordered_map<std::string, unsigned> basic_type_decls;
+	/// Count of decls using each generic type (includes "*", "[]", "(*)", "[,]").
+	std::unordered_map<std::string, unsigned> generic_type_decls;
+	/// Count of decls using each compound type.
+	std::unordered_map<std::string, unsigned> compound_type_decls;
+	/// Stats for the parameter lists.
+	ArgPackStats params;
+	/// Stats for the return lists.
+	ArgPackStats returns;
+
+	/// Count of declarations with each number of assertions.
+	std::map<unsigned, unsigned> n_assns;
+	/// Stats for the assertions' parameters.
+	ArgPackStats assn_params;
+	/// Stats for the assertions' return types.
+	ArgPackStats assn_returns;
+
+	Stats& operator+=( const Stats& other ) {
+		sum( n_decls, other.n_decls );
+		sum( n_type_params, other.n_type_params );
+		sum( n_generic_params, other.n_generic_params );
+		sum( n_generic_nesting, other.n_generic_nesting );
+		sum( by_name, other.by_name );
+		sum( basic_type_names, other.basic_type_names );
+		sum( generic_type_names, other.generic_type_names );
+		sum( compound_type_names, other.compound_type_names );
+		sum( basic_type_decls, other.basic_type_decls );
+		sum( generic_type_decls, other.generic_type_decls );
+		sum( compound_type_decls, other.compound_type_decls );
+		sum( params, other.params );
+		sum( returns, other.returns );
+		sum( n_assns, other.n_assns );
+		sum( assn_params, other.assn_params );
+		sum( assn_returns, other.assn_returns );
+
+		return *this;
+	}
+
+};
+
+void update_max( unsigned & max, unsigned value ) {
+	if ( max < value ) max = value;
+}
+
+// Where all unnamed specs are counted as one named spec group.
+constexpr unsigned num_named_specs = 8;
+
+unsigned linkage_index( ast::Linkage::Spec spec ) {
+	switch ( spec.val ) {
+	case ast::Linkage::Intrinsic.val:  return 0;
+	case ast::Linkage::C.val:          return 1;
+	case ast::Linkage::Cforall.val:    return 2;
+	case ast::Linkage::AutoGen.val:    return 3;
+	case ast::Linkage::Compiler.val:   return 4;
+	case ast::Linkage::BuiltinCFA.val: return 5;
+	case ast::Linkage::BuiltinC.val:   return 6;
+	default:                           return 7;
+	}
+}
+
+struct DeclStats : public ast::WithShortCircuiting {
+	/// Stores separate stats per linkage.
+	Stats by_linkage[num_named_specs];
+	/// Stores manglenames already seen to avoid double-counting.
+	std::unordered_set<std::string> seen_names;
+	/// Overall stats.
+	Stats total;
+	/// Count of expressions with (depth, fanout)
+	std::map<std::pair<unsigned, unsigned>, unsigned> exprs_by_fanout_at_depth;
+
+	/// Count that we have seen a named type.
+	void countType(
+			const std::string & name, unsigned & n,
+			std::unordered_map<std::string, unsigned> & names,
+			std::unordered_map<std::string, unsigned> & decls,
+			std::unordered_set<std::string> & elSeen ) {
+		++n;
+		++names[ name ];
+		if ( elSeen.insert( name ).second ) {
+			++decls[ name ];
+		}
+	}
+
+	/// Perform type analysis on a subtype.
+	void analyzeSubtype( const ast::Type * type, Stats & stats,
+			std::unordered_set<std::string> & elSeen, unsigned & n_poly,
+			bool & seen_poly, unsigned & max_depth, unsigned depth ) {
+		// This kind of gets in the way of grouping arguments.
+		unsigned ignored = 0;
+		analyzeType(
+			type, stats, elSeen, ignored, ignored, n_poly, ignored,
+			seen_poly, max_depth, depth + 1 );
+	}
+
+	/// Perform type analysis on each subtype.
+	void analyzeSubtypes(
+			const std::vector<ast::ptr<ast::Type>> & types, Stats & stats,
+			std::unordered_set<std::string> & elSeen, unsigned & n_poly,
+			bool & seen_poly, unsigned & max_depth, unsigned depth ) {
+		for ( const auto & type : types ) {
+			analyzeSubtype( type, stats, elSeen, n_poly, seen_poly, max_depth, depth );
+		}
+	}
+
+	/// Perform sub-type analysis on each subtype in an argument pack.
+	void analyzeSubPack(
+			const std::vector<ast::ptr<ast::Type>> & types, Stats & stats,
+			std::unordered_set<std::string> & elSeen, unsigned & n_poly,
+			bool & seen_poly, unsigned & max_depth, unsigned depth,
+			unsigned & n_subs ) {
+		// ... and count voids?
+		for ( const auto & type : types ) {
+			if ( type.as<ast::VoidType>() ) {
+				++n_subs;
+			}
+			// Could this be in `else`?
+			analyzeSubtype( type, stats, elSeen, n_poly, seen_poly, max_depth, depth );
+		}
+	}
+
+	/// Analyze and gather stats from a single type.
+	void analyzeType( const ast::ptr<ast::Type> & type, Stats & stats,
+			std::unordered_set<std::string> & elSeen,
+			unsigned & n_basic, unsigned & n_generic, unsigned & n_poly,
+			unsigned & n_agg, bool & seen_poly,
+			unsigned & max_depth, unsigned depth ) {
+		// Almost a visit, except it is only types.
+		if ( const ast::BasicType * t = type.as<ast::BasicType>() ) {
+			const std::string name = ast::BasicType::typeNames[ t->kind ];
+			countType( name, n_basic, stats.basic_type_names, stats.basic_type_decls, elSeen );
+			update_max( max_depth, depth );
+		} else if ( auto t = type.as<ast::PointerType>() ) {
+			static const std::string name = "*";
+			countType( name, n_generic, stats.generic_type_names, stats.generic_type_decls, elSeen );
+			analyzeSubtype( t->base, stats, elSeen, n_poly, seen_poly, max_depth, depth );
+			++stats.n_generic_params.at( 1 );
+		} else if ( auto t = type.as<ast::ArrayType>() ) {
+			static const std::string name = "[]";
+			countType( name, n_generic, stats.generic_type_names, stats.generic_type_decls, elSeen );
+			analyzeSubtype( t->base, stats, elSeen, n_poly, seen_poly, max_depth, depth );
+			++stats.n_generic_params.at( 1 );
+		} else if ( auto t = type.as<ast::ReferenceType>() ) {
+			static const std::string name = "&";
+			countType( name, n_generic, stats.generic_type_names, stats.generic_type_decls, elSeen );
+			analyzeSubtype( t->base, stats, elSeen, n_poly, seen_poly, max_depth, depth );
+			++stats.n_generic_params.at( 1 );
+		} else if ( auto t = type.as<ast::FunctionType>() ) {
+			static const std::string name = "(*)";
+			countType( name, n_generic, stats.generic_type_names, stats.generic_type_decls, elSeen );
+			unsigned n_subs = 0;
+			analyzeSubPack( t->returns, stats, elSeen, n_poly, seen_poly, max_depth, depth, n_subs );
+			analyzeSubPack( t->params, stats, elSeen, n_poly, seen_poly, max_depth, depth, n_subs );
+			++stats.n_generic_params.at( n_subs );
+		} else if ( auto t = type.as<ast::TypeInstType>() ) {
+			if ( !seen_poly ) {
+				++n_poly;
+				seen_poly = true;
+			}
+			countType( t->name, n_agg, stats.compound_type_names,
+					stats.compound_type_decls, elSeen );
+			update_max( max_depth, depth );
+		} else if ( auto t = type.as<ast::BaseInstType>() ) {
+			auto & params = t->params;
+			if ( params.empty() ) {
+				countType( t->name, n_agg, stats.compound_type_names,
+						stats.compound_type_decls, elSeen );
+				update_max( max_depth, depth );
+			} else {
+				countType( t->name, n_generic, stats.generic_type_names,
+						stats.generic_type_decls, elSeen );
+				++stats.n_generic_params.at( params.size() );
+			}
+		} else if ( auto t = type.as<ast::TupleType>() ) {
+			static const std::string name = "[,]";
+			countType( name, n_generic, stats.generic_type_names, stats.generic_type_decls, elSeen);
+			analyzeSubtypes( t->types, stats, elSeen, n_poly, seen_poly, max_depth, depth );
+			++stats.n_generic_params.at( t->size() );
+		} else if ( type.as<ast::VarArgsType>() ) {
+			static const std::string name = "...";
+			countType( name, n_agg, stats.compound_type_names, stats.compound_type_decls, elSeen );
+			update_max( max_depth, depth );
+		} else if ( type.as<ast::ZeroType>() ) {
+			static const std::string name = "0";
+			countType( name, n_basic, stats.basic_type_names, stats.basic_type_decls, elSeen );
+			update_max( max_depth, depth );
+		} else if ( type.as<ast::OneType>() ) {
+			static const std::string name = "1";
+			countType( name, n_basic, stats.basic_type_names, stats.basic_type_decls, elSeen );
+			update_max( max_depth, depth );
+		}
+	}
+
+	/// Update an ArgPackStats based on the list of types it repersents.
+	void analyzeArgPack(
+			const std::vector<ast::ptr<ast::Type>> & types,
+			Stats & stats,
+			ArgPackStats & packStats,
+			// What are these two used for?
+			std::unordered_set<std::string> & seen,
+			std::unordered_set<std::string> & elSeen ) {
+		std::unordered_set<std::string> type_names;
+		unsigned n = 0;
+		unsigned n_basic = 0;
+		unsigned n_generic = 0;
+		unsigned n_poly = 0;
+		unsigned n_compound = 0;
+		unsigned n_new = 0;
+
+		for ( auto & type : types ) {
+			n += type->size();
+
+			std::stringstream ss;
+			ast::print( ss, type );
+			type_names.insert( ss.str() );
+			if ( seen.insert( ss.str() ).second ) {
+				++n_new;
+			}
+
+			bool seen_poly = false;
+			unsigned max_depth = 0;
+			analyzeType(
+				type, stats, elSeen, n_basic, n_generic, n_poly, n_compound,
+				seen_poly, max_depth, 0
+			);
+			++stats.n_generic_nesting.at( max_depth );
+		}
+
+		++packStats.n.at( n );
+		++packStats.n_basic.at( n_basic );
+		++packStats.n_generic.at( n_generic );
+		++packStats.n_poly.at( n_poly );
+		++packStats.n_compound.at( n_compound );
+		if ( n > 0 ) {
+			++packStats.p_basic[ n_basic * 100 / n ];
+			++packStats.p_generic[ n_generic * 100 / n ];
+			++packStats.p_poly[ n_poly * 100 / n ];
+			++packStats.p_compound[ n_compound * 100 / n ];
+			if ( n > 1 ) {
+				++packStats.p_new[ (n_new - 1) * 100 / (n - 1) ];
+			}
+		}
+		++packStats.n_types.at( types.size() );
+	}
+
+	/// Perform type analysis on a function type, storing information in the
+	/// given ArgPackStats.
+	void analyzeFunctionType( const ast::FunctionType * type, Stats& stats,
+			ArgPackStats Stats::* param_pack,
+			ArgPackStats Stats::* return_pack ) {
+		// I still don't know what these are for.
+		std::unordered_set<std::string> seen;
+		std::unordered_set<std::string> elSeen;
+		analyzeArgPack( type->params, stats, stats.*param_pack, seen, elSeen );
+		analyzeArgPack( type->returns, stats, stats.*return_pack, seen, elSeen );
+	}
+
+	/// If the assertion is a function, return the function type.
+	static const ast::FunctionType * getAssertionFunctionType(
+			const ast::ptr<ast::DeclWithType> & assertion ) {
+		if ( auto * assertionObject = assertion.as<ast::ObjectDecl>() ) {
+			if ( auto * ptrTy = assertionObject->type.as<ast::PointerType>() ) {
+				return ptrTy->base.as<ast::FunctionType>();
+			} else {
+				return assertionObject->type.as<ast::FunctionType>();
+			}
+		} else if ( auto * assertionDecl = assertion.as<ast::FunctionDecl>() ) {
+			return assertionDecl->type;
+		}
+		return nullptr;
+	}
+
+	void analyzeFunctionDecl( const ast::FunctionDecl * decl ) {
+		Stats & stats = by_linkage[ linkage_index( decl->linkage ) ];
+
+		++stats.n_decls;
+		const ast::FunctionType * type = decl->type.get();
+		const ast::FunctionType::ForallList & forall = type->forall;
+		++stats.n_type_params.at( forall.size() );
+		unsigned num_assertions = 0;
+		for ( const ast::ptr<ast::TypeInstType> & instType : forall ) {
+			num_assertions += instType->base->assertions.size();
+			for ( const auto & assertion : instType->base->assertions ) {
+				if ( auto assertionType = getAssertionFunctionType( assertion ) ) {
+					analyzeFunctionType( assertionType, stats,
+							&Stats::assn_params, &Stats::assn_returns );
+				}
+			}
+		}
+		++stats.n_assns[ num_assertions ];
+		++stats.by_name[ decl->name ];
+		analyzeFunctionType( type, stats, &Stats::params, &Stats::returns );
+	}
+
+	void analyzeUntypedExpr( const ast::UntypedExpr * expr, unsigned depth ) {
+		unsigned fanout = expr->args.size();
+		++exprs_by_fanout_at_depth[ std::make_pair( depth, fanout ) ];
+
+		for ( const ast::ptr<ast::Expr> & arg : expr->args ) {
+			if ( const auto * untyped = arg.as<ast::UntypedExpr>() ) {
+				analyzeUntypedExpr( untyped, depth + 1 );
+			}
+		}
+	}
+
+public:
+	void previsit( const ast::UntypedExpr * expr ) {
+		visit_children = false;
+		analyzeUntypedExpr( expr, 0 );
+	}
+
+	void previsit( const ast::FunctionDecl * decl ) {
+		const std::string & mangleName = decl->mangleName;
+		const std::string & indexName = mangleName.empty() ? decl->name : mangleName;
+		if ( seen_names.insert( indexName ).second ) {
+			analyzeFunctionDecl( decl );
+		}
+	}
+
+private:
+
+	// Trying to avoid duplication by templates.
+	// I couldn't do it in all cases.
+	template<typename T, typename U>
+	using getter = std::function<U(T const &)>;
+
+	/// Print a single role, for every linkage and the totals.
+	void printRow( const std::string & name, getter<Stats, unsigned> extract ) {
+		std::cout << "\"" << name << "\",";
+		for ( const Stats & stats : by_linkage ) {
+			std::cout << "," << extract( stats );
+		}
+		std::cout << "," << extract( total ) << std::endl;
+	}
+
+	/// Print every row in a group of maps.
+	template<typename Func>
+	void printAllMap( const std::string & name, Func && extract ) {
+		// Get all rows from the total stats.
+		for ( auto & entry : extract( total ) ) {
+			auto & key = entry.first;
+			std::cout << "\"" << name << "\"," << key;
+			for ( const auto & stats : by_linkage ) {
+				const auto & map = extract( stats );
+				auto it = map.find( key );
+				if ( map.end() == it ) {
+					std::cout << ",0";
+				} else {
+					std::cout << "," << it->second;
+				}
+			}
+			std::cout << "," << entry.second << std::endl;
+		}
+	}
+
+	/// Accumalate information, then print every row in the remaining maps.
+	template<typename Func>
+	void printAllSparseHisto( const std::string & name, Func && extract ) {
+		std::map<unsigned, unsigned> histos[num_named_specs];
+		std::map<unsigned, unsigned> histo_total;
+
+		// Collect all data into the histograms.
+		for ( const auto & entry : extract( total ) ) {
+			++histo_total[ entry.second ];
+		}
+
+		for ( unsigned i = 0 ; i < num_named_specs ; ++i ) {
+			for ( const auto & entry : extract( by_linkage[i] ) ) {
+				++histos[ i ][ entry.second ];
+			}
+		}
+
+		// Get all rows from the total stats.
+		for ( const auto & entry : histo_total ) {
+			const unsigned & key = entry.first;
+			std::cout << "\"" << name << "\"," << key;
+			for ( unsigned i = 0 ; i < num_named_specs ; ++i ) {
+				auto it = histos[i].find( key );
+				if ( histos[i].end() == it ) {
+					std::cout << ",0";
+				} else {
+					std::cout << "," << it->second;
+				}
+			}
+			std::cout << "," << entry.second << std::endl;
+		}
+	}
+
+	void printAllPack( const std::string & name, ArgPackStats Stats::* field ) {
+		printAllMap("n_basic_" + name, [&field](const Stats& stats) { return (stats.*field).n_basic; });
+		printAllMap("n_generic_" + name, [&field](const Stats& stats) { return (stats.*field).n_generic; });
+		printAllMap("n_poly_" + name, [&field](const Stats& stats) { return (stats.*field).n_poly; });
+		printAllMap("n_compound_" + name, [&field](const Stats& stats) { return (stats.*field).n_compound; });
+		printAllMap("n_" + name, [&field](const Stats& stats) { return (stats.*field).n; });
+		printAllMap("%_basic_" + name, [&field](const Stats& stats) { return (stats.*field).p_basic; });
+		printAllMap("%_generic_" + name, [&field](const Stats& stats) { return (stats.*field).p_generic; });
+		printAllMap("%_poly_" + name, [&field](const Stats& stats) { return (stats.*field).p_poly; });
+		printAllMap("%_compound_" + name, [&field](const Stats& stats) { return (stats.*field).p_compound; });
+		printAllMap("n_distinct_types_" + name, [&field](const Stats& stats) { return (stats.*field).n_types; });
+		printAllMap("%_new_types_in_" + name, [&field](const Stats& stats) { return (stats.*field).p_new; });
+	}
+
+	static void printPairMap (
+			const std::string & name,
+			const std::map<std::pair<unsigned, unsigned>, unsigned> & map ) {
+		for ( const auto & entry : map ) {
+			const auto & key = entry.first;
+			std::cout << "\"" << name << "\"," << key.first << ','
+				<< key.second << ',' << entry.second << std::endl;
+		}
+	}
+
+public:
+	void print() {
+		for ( auto & stats : by_linkage ) {
+			total += stats;
+		}
+
+		std::cout << ",,\"intrinsic\",\"Cforall\",\"C\",\"autogen\",\"compiler\",\"builtinCFA\",\"builtinC\",\"other\",\"TOTAL\"" << std::endl;
+
+		printAllMap("n_type_params", [](const Stats& stats) { return stats.n_type_params; });
+		printAllMap("n_generic_params", [](const Stats& stats) { return stats.n_generic_params; });
+		printAllMap("n_generic_nesting", [](const Stats& stats) { return stats.n_generic_nesting; });
+		printRow("n_decls", [](const Stats& stats) { return stats.n_decls; });
+		printRow("unique_names", [](const Stats& stats) { return stats.by_name.size(); });
+		printAllSparseHisto("overloads", [](const Stats& stats) { return stats.by_name; });
+		printRow("basic_type_names", [](const Stats& stats) { return stats.basic_type_names.size(); });
+		printAllSparseHisto("basic_type_uses", [](const Stats& stats) { return stats.basic_type_names; });
+		printAllSparseHisto("decls_using_basic_type", [](const Stats& stats) { return stats.basic_type_decls; });
+		printRow("generic_type_names", [](const Stats& stats) { return stats.generic_type_names.size(); });
+		printAllSparseHisto("generic_type_uses", [](const Stats& stats) { return stats.generic_type_names; });
+		printAllSparseHisto("decls_using_generic_type", [](const Stats& stats) { return stats.generic_type_decls; });
+		printRow("compound_type_names", [](const Stats& stats) { return stats.compound_type_names.size(); });
+		printAllSparseHisto("compound_type_uses", [](const Stats& stats) { return stats.compound_type_names; });
+		printAllSparseHisto("decls_using_compound_type", [](const Stats& stats) { return stats.compound_type_decls; });
+		printAllPack("params", &Stats::params);
+		printAllPack("returns", &Stats::returns);
+		printAllMap("n_assns", [](const Stats& stats) { return stats.n_assns; });
+		printAllPack("assn_params", &Stats::assn_params);
+		printAllPack("assn_returns", &Stats::assn_returns);
+		std::cout << std::endl;
+
+		printPairMap( "exprs by depth+fanout", exprs_by_fanout_at_depth );
+	}
+};
+
+} // namespace
+
+void printDeclStats( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<DeclStats> stats;
+	accept_all( translationUnit, stats );
+	stats.core.print();
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/DeclStats.hpp
===================================================================
--- src/Common/DeclStats.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/Common/DeclStats.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,29 @@
+//
+// 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.
+//
+// DeclStats.hpp -- Print statistics about a translation unit's declarations.
+//
+// Author           : Andrew Beach
+// Created On       : Fri Oct  1 14:20:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Oct  1 14:28:00 2021
+// Update Count     : 0
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+/// Print statistics about a translation unit's declarations.
+void printDeclStats( ast::TranslationUnit &translationUnit );
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/ResolvProtoDump.cpp
===================================================================
--- src/Common/ResolvProtoDump.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/Common/ResolvProtoDump.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,808 @@
+//
+// 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.
+//
+// ResolvProtoDump.cpp -- Prints AST as instances for resolv-proto.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Oct  6 14:10:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Oct 18 11:23:00 2021
+// Update Count     : 0
+//
+
+#include "ResolvProtoDump.hpp"
+
+#include <cctype>
+#include <iostream>
+#include <set>
+#include <unordered_set>
+
+#include "AST/Copy.hpp"
+#include "AST/Pass.hpp"
+#include "AST/TranslationUnit.hpp"
+#include "AST/Type.hpp"
+#include "CodeGen/OperatorTable.h"
+#include "Common/utility.h"
+
+namespace {
+
+/// Add a prefix to an existing name.
+std::string add_prefix( const std::string & prefix, const char * added ) {
+	if ( prefix.empty() ) {
+		return std::string("$") + added;
+	} else {
+		return prefix + added;
+	}
+}
+
+/// Shortens operator names.
+std::string op_name( const std::string & name ) {
+	if ( name.compare( 0, 10, "_operator_" ) == 0 ) {
+		return name.substr( 10 );
+	} else if ( name.compare( "_constructor" ) == 0
+			|| name.compare( "_destructor" ) == 0 ) {
+		return name.substr( 1 );
+	} else if ( name.compare( 0, 11, "__operator_" ) == 0 ) {
+		return name.substr( 11 );
+	} else {
+		return name;
+	}
+}
+
+/// Get the resolv-proto names for operators.
+std::string rp_name( const std::string & name, std::string && pre = "" ) {
+	// Check for anonymous names.
+	if ( name.empty() ) {
+		return add_prefix( pre, "anon" );
+	}
+
+	// Replace operator names.
+	const CodeGen::OperatorInfo * opInfo = CodeGen::operatorLookup( name );
+	if ( nullptr != opInfo ) {
+		return add_prefix( pre, "" ) + op_name( opInfo->outputName );
+	}
+
+	// Replace return value prefix.
+	if ( name.compare( 0, 8, "_retval_" ) == 0 ) {
+		return add_prefix( pre, "rtn_" ) + op_name( name.substr( 8 ) );
+	}
+
+	// Default to just name, with first character in lowercase.
+	if ( std::isupper( name[0] ) ) {
+		std::string copy = name;
+		copy[0] = std::tolower( copy[0] );
+		return pre + copy;
+	}
+	return pre + name;
+}
+
+/// Normalise a type instance name.
+std::string ti_name( const std::string & name ) {
+	// Replace built-in names
+	if ( name == "char16_t" || name == "char32_t" || name == "wchar_t" ) {
+		return std::string("#") + name;
+	}
+
+	// Strip leadng underscores.
+	unsigned i = 0;
+	while ( i < name.size() && name[i] == '_' ) { ++i; }
+	if ( i == name.size() ) {
+		return "Anon";
+	}
+
+	std::string stripped = name.substr( i );
+	// Strip trailing generic from autogen names ()
+	static char generic[] = "_generic_";
+	static size_t n_generic = sizeof(generic) - 1;
+	if ( stripped.size() >= n_generic
+			&& stripped.substr( stripped.size() - n_generic ) == generic ) {
+		stripped.resize( stripped.size() - n_generic );
+	}
+
+	// Uppercase first character.
+	stripped[0] = std::toupper( stripped[0] );
+	return stripped;
+}
+
+std::vector<ast::ptr<ast::Type>> to_types(
+		const std::vector<ast::ptr<ast::Expr>> & data ) {
+	std::vector<ast::ptr<ast::Type>> ret_val;
+	ret_val.reserve( data.size() );
+	for ( auto entry : data ) {
+		if ( auto * typeExpr = entry.as<ast::TypeExpr>() ) {
+			ret_val.emplace_back( typeExpr->type );
+		}
+	}
+	return ret_val;
+}
+
+enum class septype { separated, terminated, preceded };
+
+template<typename V>
+void build(
+		V & visitor,
+		const std::vector<ast::ptr<ast::Type>> & types,
+		std::stringstream & ss,
+		septype mode );
+
+template<typename V>
+void buildAsTuple(
+		V & visitor, const std::vector<ast::ptr<ast::Type>> & types,
+		std::stringstream & ss );
+
+struct TypePrinter : public ast::WithShortCircuiting, ast::WithVisitorRef<TypePrinter> {
+	/// Accumulator for the printed type.
+	std::stringstream ss;
+	/// Closed type variables.
+	const std::unordered_set<std::string> & closed;
+	/// Depth of nesting from root type.
+	unsigned depth;
+
+	TypePrinter( const std::unordered_set<std::string> & closed ) :
+		ss(), closed(closed), depth(0)
+	{}
+
+	std::string result() const {
+		return ss.str();
+	}
+
+	// Basic type represented as an integer type.
+	// TODO: Maybe hard-code conversion graph and make named type.
+	void previsit( const ast::BasicType * type ) {
+		ss << (int)type->kind;
+	}
+
+	// Pointers (except function pointers) are represented as generic type.
+	void previsit( const ast::PointerType * type ) {
+		if ( nullptr == type->base.as<ast::FunctionType>() ) {
+			ss << "#$ptr<";
+			++depth;
+		}
+	}
+	void postvisit( const ast::PointerType * type ) {
+		if ( nullptr == type->base.as<ast::FunctionType>() ) {
+			--depth;
+			ss << '>';
+		}
+	}
+
+	// Arrays repersented as pointers.
+	void previsit( const ast::ArrayType * type ) {
+		ss << "#$ptr<";
+		++depth;
+		type->base->accept( *visitor );
+		--depth;
+		ss << '>';
+		visit_children = false;
+	}
+
+	// Ignore top-level references as they are mostly transparent to resolution.
+	void previsit( const ast::ReferenceType * ) {
+		if ( !atTopLevel() ) { ss << "#$ref<"; }
+		++depth;
+	}
+	void postvisit( const ast::ReferenceType * ) {
+		--depth;
+		if ( !atTopLevel() ) { ss << '>'; }
+	}
+
+	void previsit( const ast::FunctionType * type ) {
+		ss << '[';
+		++depth;
+		build( *visitor, type->returns, ss, septype::preceded );
+		ss << " : ";
+		build( *visitor, type->params, ss, septype::terminated );
+		--depth;
+		ss << ']';
+		visit_children = false;
+	}
+
+private:
+	bool atTopLevel() const {
+		return 0 == depth;
+	}
+
+	void handleAggregate( const ast::BaseInstType * type ) {
+		ss << '#' << type->name;
+		if ( !type->params.empty() ) {
+			ss << '<';
+			++depth;
+			build( *visitor, to_types( type->params ), ss, septype::separated );
+			--depth;
+			ss << '>';
+		}
+		visit_children = false;
+	}
+public:
+
+	void previsit( const ast::StructInstType * type ) {
+		handleAggregate( type );
+	}
+
+	void previsit( const ast::UnionInstType * type ) {
+		handleAggregate( type );
+	}
+
+	void previsit( const ast::EnumInstType * ) {
+		ss << (int)ast::BasicType::SignedInt;
+	}
+
+	void previsit( const ast::TypeInstType * type ) {
+		// Print closed variables as named types.
+		if ( closed.count( type->name ) ) {
+			ss << '#' << type->name;
+		// Otherwise normalize the name.
+		} else {
+			ss << ti_name( type->name );
+		}
+	}
+
+	void previsit( const ast::TupleType * tupleType ) {
+		++depth;
+		buildAsTuple( *visitor, tupleType->types, ss );
+		--depth;
+		visit_children = false;
+	}
+
+	void previsit( const ast::VarArgsType * ) {
+		if ( atTopLevel() ) ss << "#$varargs";
+	}
+
+	// TODO: Support 0 and 1 with their type names and conversions.
+	void previsit( const ast::ZeroType * ) {
+		ss << (int)ast::BasicType::SignedInt;
+	}
+
+	void previsit( const ast::OneType * ) {
+		ss << (int)ast::BasicType::SignedInt;
+	}
+
+	void previsit( const ast::VoidType * ) {
+		if ( !atTopLevel() ) {
+			ss << "#void";
+		}
+	}
+};
+
+struct ExprPrinter : public ast::WithShortCircuiting, ast::WithVisitorRef<ExprPrinter> {
+	// TODO: Change interface to generate multiple expression canditates.
+	/// Accumulator of the printed expression.
+	std::stringstream ss;
+	/// Set of closed type variables.
+	const std::unordered_set<std::string> & closed;
+
+	ExprPrinter( const std::unordered_set<std::string> & closed ) :
+		ss(), closed( closed )
+	{}
+
+	std::string result() const {
+		return ss.str();
+	}
+
+	void previsit( const ast::NameExpr * expr ) {
+		ss << '&' << rp_name( expr->name );
+	}
+
+	/// Handle already resolved variables as type constants.
+	void previsit( const ast::VariableExpr * expr ) {
+		ss << ast::Pass<TypePrinter>::read( expr->var->get_type(), closed );
+		visit_children = false;
+	}
+
+	void previsit( const ast::UntypedExpr * expr ) {
+		// TODO: Handle name extraction more generally.
+		const ast::NameExpr * name = expr->func.as<ast::NameExpr>();
+
+		// TODO: Incorporate function type into resolv-proto.
+		if ( !name ) {
+			expr->func->accept( *visitor );
+			visit_children = false;
+			return;
+		}
+
+		ss << rp_name( name->name );
+		if ( expr->args.empty() ) {
+			ss << "()";
+		} else {
+			ss << "( ";
+			auto it = expr->args.begin();
+			while (true) {
+				(*it)->accept( *visitor );
+				if ( ++it == expr->args.end() ) break;
+				ss << ' ';
+			}
+			ss << " )";
+		}
+		visit_children = false;
+	}
+
+	void previsit( const ast::ApplicationExpr * expr ) {
+		ss << ast::Pass<TypePrinter>::read( static_cast<const ast::Expr *>( expr ), closed );
+		visit_children = false;
+	}
+
+	void previsit( const ast::AddressExpr * expr ) {
+		ss << "$addr( ";
+		expr->arg->accept( *visitor );
+		ss << " )";
+		visit_children = false;
+	}
+
+	void previsit( const ast::CastExpr * expr ) {
+		ss << ast::Pass<TypePrinter>::read( expr->result.get(), closed );
+		visit_children = false;
+	}
+
+	/// Member access handled as function from aggregate to member.
+	void previsit( const ast::UntypedMemberExpr * expr ) {
+		// TODO: Handle name extraction more generally.
+		const ast::NameExpr * name = expr->member.as<ast::NameExpr>();
+
+		// TODO: Incorporate function type into resolve-proto.
+		if ( !name ) {
+			expr->member->accept( *visitor );
+			visit_children = false;
+			return;
+		}
+
+		ss << rp_name( name->name, "$field_" );
+		ss << "( ";
+		expr->aggregate->accept( *visitor );
+		ss << " )";
+		visit_children = false;
+	}
+
+	/// Constant expression replaced by its type.
+	void previsit( const ast::ConstantExpr * expr ) {
+		ss << ast::Pass<TypePrinter>::read( static_cast<const ast::Expr *>( expr ), closed );
+		visit_children = false;
+	}
+
+	/// sizeof, alignof, & offsetof are replaced by constant type.
+	// TODO: Extra expression to resolve argument.
+	void previsit( const ast::SizeofExpr * ) {
+		ss << (int)ast::BasicType::LongUnsignedInt;
+		visit_children = false;
+	}
+	void previsit( const ast::AlignofExpr * ) {
+		ss << (int)ast::BasicType::LongUnsignedInt;
+		visit_children = false;
+	}
+	void previsit( const ast::UntypedOffsetofExpr * ) {
+		ss << (int)ast::BasicType::LongUnsignedInt;
+		visit_children = false;
+	}
+
+	/// Logical expressions represented as operators.
+	void previsit( const ast::LogicalExpr * expr ) {
+		ss << ( (ast::AndExpr == expr->isAnd) ? "$and( " : "$or( " );
+		expr->arg1->accept( *visitor );
+		ss << ' ';
+		expr->arg2->accept( *visitor );
+		ss << " )";
+		visit_children = false;
+	}
+
+	/// Conditional expression represented as an operator.
+	void previsit( const ast::ConditionalExpr * expr ) {
+		ss << "$if( ";
+		expr->arg1->accept( *visitor );
+		ss << ' ';
+		expr->arg2->accept( *visitor );
+		ss << ' ';
+		expr->arg3->accept( *visitor );
+		ss << " )";
+		visit_children = false;
+	}
+
+	/// Comma expression represented as on operator.
+	void previsit( const ast::CommaExpr * expr ) {
+		ss << "$seq( ";
+		expr->arg1->accept( *visitor );
+		ss << ' ';
+		expr->arg2->accept( *visitor );
+		ss << " )";
+		visit_children = false;
+	}
+
+	// TODO: Handle ignored ImplicitCopyCtorExpr and below.
+};
+
+template<typename V>
+void build(
+		V & visitor,
+		const std::vector<ast::ptr<ast::Type>> & types,
+		std::stringstream & ss,
+		septype mode ) {
+	if ( types.empty() ) return;
+
+	if ( septype::preceded == mode ) { ss << ' '; }
+
+	auto it = types.begin();
+	(*it)->accept( visitor );
+
+	while ( ++it != types.end() ) {
+		ss << ' ';
+		(*it)->accept( visitor );
+	}
+
+	if ( septype::terminated == mode ) { ss << ' '; }
+}
+
+std::string buildType(
+		const std::string & name, const ast::Type * type,
+		const std::unordered_set<std::string> & closed );
+
+/// Build a string representing a function type.
+std::string buildFunctionType(
+		const std::string & name, const ast::FunctionType * type,
+		const std::unordered_set<std::string> & closed ) {
+	ast::Pass<TypePrinter> printer( closed );
+	std::stringstream & ss = printer.core.ss;
+
+	build( printer, type->returns, ss, septype::terminated );
+	ss << rp_name( name );
+	build( printer, type->params, ss, septype::preceded );
+	for ( const auto & assertion : type->assertions ) {
+		auto var = assertion->var;
+		ss << " | " << buildType( var->name, var->get_type(), closed );
+	}
+	return ss.str();
+}
+
+/// Build a description of a type.
+std::string buildType(
+		const std::string & name, const ast::Type * type,
+		const std::unordered_set<std::string> & closed ) {
+	const ast::Type * norefs = type->stripReferences();
+
+	if ( const auto & ptrType = dynamic_cast<const ast::PointerType *>( norefs ) ) {
+		if ( const auto & funcType = ptrType->base.as<ast::FunctionType>() ) {
+			return buildFunctionType( name, funcType, closed );
+		}
+	} else if ( const auto & funcType = dynamic_cast<const ast::FunctionType *>( norefs ) ) {
+		return buildFunctionType( name, funcType, closed );
+	}
+
+	std::stringstream ss;
+	ss << ast::Pass<TypePrinter>::read( norefs, closed );
+	ss << " &" << rp_name( name );
+	return ss.str();
+}
+
+/// Builds description of a field access.
+std::string buildAggregateDecl( const std::string & name,
+		const ast::AggregateDecl * agg, const ast::Type * type,
+		const std::unordered_set<std::string> & closed ) {
+	const ast::Type * norefs = type->stripReferences();
+	std::stringstream ss;
+
+	ss << ast::Pass<TypePrinter>::read( norefs, closed ) << ' ';
+	ss << rp_name( name, "$field_" );
+	ss << " #" << agg->name;
+	if ( !agg->params.empty() ) {
+		ss << '<';
+		auto it = agg->params.begin();
+		while (true) {
+			ss << ti_name( (*it)->name );
+			if ( ++it == agg->params.end() ) break;
+			ss << ' ';
+		}
+		ss << '>';
+	}
+	return ss.str();
+}
+
+template<typename V>
+void buildAsTuple(
+		V & visitor, const std::vector<ast::ptr<ast::Type>> & types,
+		std::stringstream & ss ) {
+	switch ( types.size() ) {
+	case 0:
+		ss << "#void";
+		break;
+	case 1:
+		types.front()->accept( visitor );
+		break;
+	default:
+		ss << "#$" << types.size() << '<';
+		build( visitor, types, ss, septype::separated );
+		ss << '>';
+		break;
+	}
+}
+
+/// Adds a return
+std::string buildReturn(
+		const ast::Type * returnType,
+		const ast::Expr * expr,
+		const std::unordered_set<std::string> & closed ) {
+	std::stringstream ss;
+	ss << "$constructor( ";
+	ss << ast::Pass<TypePrinter>::read( returnType, closed );
+	ss << ' ';
+	ss << ast::Pass<ExprPrinter>::read( expr, closed );
+	ss << " )";
+	return ss.str();
+}
+
+void buildInitComponent(
+		std::stringstream & out, const ast::Init * init,
+		const std::unordered_set<std::string> & closed ) {
+	if ( const auto * s = dynamic_cast<const ast::SingleInit *>( init ) ) {
+		out << ast::Pass<ExprPrinter>::read( s->value.get(), closed ) << ' ';
+	} else if ( const auto * l = dynamic_cast<const ast::ListInit *>( init ) ) {
+		for ( const auto & it : l->initializers ) {
+			buildInitComponent( out, it, closed );
+		}
+	}
+}
+
+/// Build a representation of an initializer.
+std::string buildInitializer(
+		const std::string & name, const ast::Init * init,
+		const std::unordered_set<std::string> & closed ) {
+	std::stringstream ss;
+	ss << "$constructor( &";
+	ss << rp_name( name );
+	ss << ' ';
+	buildInitComponent( ss, init, closed );
+	ss << ')';
+	return ss.str();
+}
+
+/// Visitor for collecting and printing resolver prototype output.
+class ProtoDump : public ast::WithShortCircuiting, ast::WithVisitorRef<ProtoDump> {
+	/// Declarations in this scope.
+	// Set is used for ordering of printing.
+	std::set<std::string> decls;
+	/// Expressions in this scope.
+	std::vector<std::string> exprs;
+	/// Sub-scopes
+	std::vector<ProtoDump> subs;
+	/// Closed type variables
+	std::unordered_set<std::string> closed;
+	/// Outer lexical scope
+	const ProtoDump * parent;
+	/// Return type for this scope
+	ast::ptr<ast::Type> returnType;
+
+	/// Is the declaration in this scope or a parent scope?
+	bool hasDecl( const std::string & decl ) const {
+		return decls.count( decl ) || (parent && parent->hasDecl( decl ));
+	}
+
+	/// Adds a declaration to this scope if it is new.
+	void addDecl( const std::string & decl ) {
+		if ( !hasDecl( decl ) ) decls.insert( decl );
+	}
+
+	/// Adds a new expression to this scope.
+	void addExpr( const std::string & expr ) {
+		if ( !expr.empty() ) exprs.emplace_back( expr );
+	}
+
+	/// Adds a new scope as a child scope.
+	void addSub( ast::Pass<ProtoDump> && pass ) {
+		subs.emplace_back( std::move( pass.core ) );
+	}
+
+	/// Adds all named declaration in a list to the local scope.
+	void addAll( const std::vector<ast::ptr<ast::DeclWithType>> & decls ) {
+		for ( auto decl : decls ) {
+			// Skip anonymous decls.
+			if ( decl->name.empty() ) continue;
+
+			if ( const auto & obj = decl.as<ast::ObjectDecl>() ) {
+				previsit( obj );
+			}
+		}
+	}
+
+public:
+	ProtoDump() :
+		parent( nullptr ), returnType( nullptr )
+	{}
+
+	ProtoDump( const ProtoDump * parent, const ast::Type * returnType ) :
+		closed( parent->closed ), parent( parent ), returnType( returnType )
+	{}
+
+	ProtoDump( const ProtoDump & other ) :
+		decls( other.decls ), exprs( other.exprs ), subs( other.subs ),
+		closed( other.closed ), parent( other.parent ),
+		returnType( other.returnType )
+	{}
+
+	ProtoDump( ProtoDump && ) = default;
+
+	ProtoDump & operator=( const ProtoDump & ) = delete;
+	ProtoDump & operator=( ProtoDump && ) = delete;
+
+	void previsit( const ast::ObjectDecl * decl ) {
+		// Add variable as declaration.
+		addDecl( buildType( decl->name, decl->type, closed ) );
+
+		// Add initializer as expression if applicable.
+		if ( decl->init ) {
+			addExpr( buildInitializer( decl->name, decl->init, closed ) );
+		}
+	}
+
+	void previsit( const ast::FunctionDecl * decl ) {
+		visit_children = false;
+
+		// Skips declarations with ftype parameters.
+		for ( const auto & typeDecl : decl->type->forall ) {
+			if ( ast::TypeDecl::Ftype == typeDecl->kind ) {
+				return;
+			}
+		}
+
+		// Add function as declaration.
+		// NOTE: I'm not sure why the assertions are only present on the
+		// declaration and not the function type. Is that an error?
+		ast::FunctionType * new_type = ast::shallowCopy( decl->type.get() );
+		for ( const ast::ptr<ast::DeclWithType> & assertion : decl->assertions ) {
+			new_type->assertions.push_back(
+				new ast::VariableExpr( assertion->location , assertion )
+			);
+		}
+		addDecl( buildFunctionType( decl->name, new_type, closed ) );
+		delete new_type;
+
+		// Add information body if available.
+		if ( !decl->stmts ) return;
+		const std::vector<ast::ptr<ast::Type>> & returns =
+				decl->type->returns;
+
+		// Add the return statement.
+		ast::ptr<ast::Type> retType = nullptr;
+		if ( 1 == returns.size() ) {
+			if ( !returns.front().as<ast::VoidType>() ) {
+				retType = returns.front();
+			}
+		} else if ( 1 < returns.size() ) {
+			retType = new ast::TupleType( copy( returns ) );
+		}
+		ast::Pass<ProtoDump> body( this, retType.get() );
+
+		// Handle the forall clause (type parameters and assertions).
+		for ( const ast::ptr<ast::TypeDecl> & typeDecl : decl->type_params ) {
+			// Add set of "closed" types to body so that it can print them as NamedType.
+			body.core.closed.insert( typeDecl->name );
+
+			// Add assertions to local scope as declarations as well.
+			for ( const ast::DeclWithType * assertion : typeDecl->assertions ) {
+				assertion->accept( body );
+			}
+		}
+
+		// NOTE: Related to the last NOTE; this is where the assertions are now.
+		for ( const ast::ptr<ast::DeclWithType> & assertion : decl->assertions ) {
+			assertion->accept( body );
+		}
+
+		// Add named parameters and returns to local scope.
+		body.core.addAll( decl->returns );
+		body.core.addAll( decl->params );
+
+		// Add contents of the function to a new scope.
+		decl->stmts->accept( body );
+
+		// Store sub-scope
+		addSub( std::move( body ) );
+	}
+
+private:
+	void addAggregateFields( const ast::AggregateDecl * agg ) {
+		for ( const auto & member : agg->members ) {
+			if ( const ast::ObjectDecl * obj = member.as<ast::ObjectDecl>() ) {
+				addDecl( buildAggregateDecl( obj->name, agg, obj->type, closed ) );
+			}
+		}
+		visit_children = false;
+	}
+
+public:
+
+	void previsit( const ast::StructDecl * decl ) {
+		addAggregateFields( decl );
+	}
+
+	void previsit( const ast::UnionDecl * decl ) {
+		addAggregateFields( decl );
+	}
+
+	void previsit( const ast::EnumDecl * decl ) {
+		for ( const auto & member : decl->members ) {
+			if ( const auto * obj = member.as<ast::ObjectDecl>() ) {
+				previsit( obj );
+			}
+		}
+
+		visit_children = false;
+	}
+
+	void previsit( const ast::ReturnStmt * stmt ) {
+		// Do nothing for void-returning functions or statements returning nothing.
+		if ( !returnType || !stmt->expr ) return;
+
+		// Otherwise constuct the return type from the expression.
+		addExpr( buildReturn( returnType.get(), stmt->expr, closed ) );
+		visit_children = false;
+	}
+
+	void previsit( const ast::AsmStmt * ) {
+		// Skip asm statements.
+		visit_children = false;
+	}
+
+	void previsit( const ast::Expr * expr ) {
+		addExpr( ast::Pass<ExprPrinter>::read( expr, closed ) );
+		visit_children = false;
+	}
+
+private:
+	/// Print the pesudo-declarations not in any scope.
+	void printGlobal( std::ostream & out ) const {
+		// &? Address of operator.
+		out << "#$ptr<T> $addr T" << std::endl;
+		const int intId = (int)ast::BasicType::SignedInt;
+		// ?&&? ?||? ?: Logical operators.
+		out << intId << " $and " << intId << ' ' << intId << std::endl;
+		out << intId << " $or " << intId << ' ' << intId << std::endl;
+		out << "T $if " << intId << " T T" << std::endl;
+		// ?,? Sequencing.
+		out << "T $seq X T" << std::endl;
+	}
+
+	/// Print everything in this scope and its child scopes.
+	void printLocal( std::ostream & out, unsigned indent ) const {
+		const std::string tab( indent, '\t' );
+
+		// Print Declarations:
+		for ( const std::string & decl : decls ) {
+			out << tab << decl << std::endl;
+		}
+
+		// Print Divider:
+		out << '\n' << tab << "%%\n" << std::endl;
+
+		// Print Top-Level Expressions:
+		for ( const std::string & expr : exprs ) {
+			out << tab << expr << std::endl;
+		}
+
+		// Print Children Scopes:
+		++indent;
+		for ( const ProtoDump & sub : subs ) {
+			out << tab << '{' << std::endl;
+			sub.printLocal( out, indent );
+			out << tab << '}' << std::endl;
+		}
+	}
+public:
+	/// Start printing, the collected information.
+	void print( std::ostream & out ) const {
+		printGlobal( out );
+		printLocal( out, 0 );
+	}
+};
+
+} // namespace
+
+void dumpAsResolverProto( ast::TranslationUnit & transUnit ) {
+	ast::Pass<ProtoDump> dump;
+	accept_all( transUnit, dump );
+	dump.core.print( std::cout );
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/ResolvProtoDump.hpp
===================================================================
--- src/Common/ResolvProtoDump.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/Common/ResolvProtoDump.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,29 @@
+//
+// 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.
+//
+// ResolvProtoDump.hpp -- Prints AST as instances for resolv-proto.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Oct  6 14:05:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Oct  6 14:29:00 2021
+// Update Count     : 0
+//
+
+#pragma once
+
+namespace ast {
+	struct TranslationUnit;
+}
+
+/// Prints AST as instances for resolv-proto.
+void dumpAsResolverProto( ast::TranslationUnit & transUnit );
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/module.mk
===================================================================
--- src/Common/module.mk	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/Common/module.mk	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -22,4 +22,6 @@
       Common/CompilerError.h \
       Common/Debug.h \
+      Common/DeclStats.hpp \
+      Common/DeclStats.cpp \
       Common/ErrorObjects.h \
       Common/Eval.cc \
@@ -33,4 +35,6 @@
       Common/PassVisitor.proto.h \
       Common/PersistentMap.h \
+      Common/ResolvProtoDump.hpp \
+      Common/ResolvProtoDump.cpp \
       Common/ScopedMap.h \
       Common/SemanticError.cc \
Index: src/Concurrency/Keywords.cc
===================================================================
--- src/Concurrency/Keywords.cc	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/Concurrency/Keywords.cc	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -93,4 +93,5 @@
 		ObjectDecl * addField( StructDecl * );
 		void addRoutines( ObjectDecl *, FunctionDecl * );
+		void addLockUnlockRoutines( StructDecl * );
 
 		virtual bool is_target( StructDecl * decl ) = 0;
@@ -322,4 +323,5 @@
 		StructDecl* dtor_guard_decl = nullptr;
 		StructDecl* thread_guard_decl = nullptr;
+		StructDecl* lock_guard_decl = nullptr;
 
 		static std::unique_ptr< Type > generic_func;
@@ -463,5 +465,4 @@
 	}
 
-
 	void ConcurrentSueKeyword::handle( StructDecl * decl ) {
 		if( ! decl->body ) return;
@@ -479,5 +480,9 @@
 		FunctionDecl * func = forwardDeclare( decl );
 		ObjectDecl * field = addField( decl );
+
+		// add get_.* routine
 		addRoutines( field, func );
+		// add lock/unlock routines to monitors for use by mutex stmt
+		addLockUnlockRoutines( decl );
 	}
 
@@ -612,4 +617,6 @@
 	}
 
+	// This function adds the get_.* routine body for coroutines, monitors etc
+	// 		after their corresponding struct has been made
 	void ConcurrentSueKeyword::addRoutines( ObjectDecl * field, FunctionDecl * func ) {
 		CompoundStmt * statement = new CompoundStmt();
@@ -634,4 +641,112 @@
 
 		declsToAddAfter.push_back( get_decl );
+	}
+
+	// Generates lock/unlock routines for monitors to be used by mutex stmts
+	void ConcurrentSueKeyword::addLockUnlockRoutines( StructDecl * decl ) {
+		// this routine will be called for all ConcurrentSueKeyword children so only continue if we are a monitor
+		if ( !decl->is_monitor() ) return;
+
+		FunctionType * lock_fn_type = new FunctionType( noQualifiers, false );
+		FunctionType * unlock_fn_type = new FunctionType( noQualifiers, false );
+
+		// create this ptr parameter for both routines
+		ObjectDecl * this_decl = new ObjectDecl(
+			"this",
+			noStorageClasses,
+			LinkageSpec::Cforall,
+			nullptr,
+			new ReferenceType(
+				noQualifiers,
+				new StructInstType(
+					noQualifiers,
+					decl
+				)
+			),
+			nullptr
+		);
+
+		lock_fn_type->get_parameters().push_back( this_decl->clone() );
+		unlock_fn_type->get_parameters().push_back( this_decl->clone() );
+		fixupGenerics(lock_fn_type, decl);
+		fixupGenerics(unlock_fn_type, decl);
+
+		delete this_decl;
+
+
+		//////////////////////////////////////////////////////////////////////
+		// The following generates this lock routine for all monitors
+		/*
+			void lock (monitor_t & this) {
+				lock(get_monitor(this));
+			}	
+		*/
+		FunctionDecl * lock_decl = new FunctionDecl(
+			"lock",
+			Type::Static,
+			LinkageSpec::Cforall,
+			lock_fn_type,
+			nullptr,
+			{ },
+			Type::Inline
+		);
+
+		UntypedExpr * get_monitor_lock =  new UntypedExpr (
+			new NameExpr( "get_monitor" ),
+			{ new VariableExpr( lock_fn_type->get_parameters().front() ) }
+		);
+
+		CompoundStmt * lock_statement = new CompoundStmt();
+		lock_statement->push_back(
+			new ExprStmt( 
+				new UntypedExpr (
+					new NameExpr( "lock" ),
+					{
+						get_monitor_lock
+					}
+				)
+			)
+		);
+		lock_decl->set_statements( lock_statement );
+
+		//////////////////////////////////////////////////////////////////
+		// The following generates this routine for all monitors
+		/*
+			void unlock (monitor_t & this) {
+				unlock(get_monitor(this));
+			}	
+		*/
+		FunctionDecl * unlock_decl = new FunctionDecl(
+			"unlock",
+			Type::Static,
+			LinkageSpec::Cforall,
+			unlock_fn_type,
+			nullptr,
+			{ },
+			Type::Inline
+		);
+
+		CompoundStmt * unlock_statement = new CompoundStmt();
+
+		UntypedExpr * get_monitor_unlock =  new UntypedExpr (
+			new NameExpr( "get_monitor" ),
+			{ new VariableExpr( unlock_fn_type->get_parameters().front() ) }
+		);
+
+		unlock_statement->push_back(
+			new ExprStmt( 
+				new UntypedExpr(
+					new NameExpr( "unlock" ),
+					{
+						get_monitor_unlock
+					}
+				)
+			)
+		);
+		unlock_decl->set_statements( unlock_statement );
+		
+		// pushes routines to declsToAddAfter to add at a later time
+		declsToAddAfter.push_back( lock_decl );
+		declsToAddAfter.push_back( unlock_decl );
 	}
 
@@ -937,4 +1052,8 @@
 			assert( !thread_guard_decl );
 			thread_guard_decl = decl;
+		} 
+		else if ( decl->name == "__mutex_stmt_lock_guard" && decl->body ) {
+			assert( !lock_guard_decl );
+			lock_guard_decl = decl;
 		}
 	}
@@ -1081,7 +1200,9 @@
 				new PointerType(
 					noQualifiers,
-					new StructInstType(
-						noQualifiers,
-						monitor_decl
+					//new TypeofType( noQualifiers, args.front()->clone() )
+					new TypeofType( noQualifiers, new UntypedExpr(
+							new NameExpr( "__get_type" ),
+							{ args.front()->clone() }
+						) 
 					)
 				),
@@ -1093,10 +1214,22 @@
 				map_range < std::list<Initializer*> > ( args, [](Expression * var ){
 					return new SingleInit( new UntypedExpr(
-						new NameExpr( "get_monitor" ),
-						{ var }
+							new NameExpr( "__get_ptr" ),
+							{ var }
 					) );
+					//return new SingleInit( new AddressExpr( var ) );
 				})
 			)
 		);
+
+		StructInstType * lock_guard_struct = new StructInstType( noQualifiers, lock_guard_decl );
+		TypeExpr * lock_type_expr = new TypeExpr( 
+			new TypeofType( noQualifiers, new UntypedExpr(
+				new NameExpr( "__get_type" ),
+				{ args.front()->clone() }
+				) 
+			) 
+		);
+
+		lock_guard_struct->parameters.push_back( lock_type_expr ) ;
 
 		// in reverse order :
@@ -1108,8 +1241,5 @@
 				LinkageSpec::Cforall,
 				nullptr,
-				new StructInstType(
-					noQualifiers,
-					guard_decl
-				),
+				lock_guard_struct,
 				new ListInit(
 					{
Index: src/ControlStruct/ExceptTranslate.cc
===================================================================
--- src/ControlStruct/ExceptTranslate.cc	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/ControlStruct/ExceptTranslate.cc	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// ExceptVisitor.cc --
+// ExceptTranslate.cc -- Conversion of exception control flow structures.
 //
 // Author           : Andrew Beach
Index: src/ControlStruct/ExceptTranslate.h
===================================================================
--- src/ControlStruct/ExceptTranslate.h	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/ControlStruct/ExceptTranslate.h	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -5,11 +5,11 @@
 // file "LICENCE" distributed with Cforall.
 //
-// ExceptTranslate.h --
+// ExceptTranslate.h -- Conversion of exception control flow structures.
 //
 // Author           : Andrew Beach
 // Created On       : Tus Jun 06 10:13:00 2017
 // Last Modified By : Andrew Beach
-// Last Modified On : Tus May 19 11:47:00 2020
-// Update Count     : 5
+// Last Modified On : Mon Nov  8 11:43:00 2020
+// Update Count     : 6
 //
 
@@ -19,7 +19,11 @@
 
 class Declaration;
+namespace ast {
+	class TranslationUnit;
+}
 
 namespace ControlStruct {
 	void translateThrows( std::list< Declaration *> & translationUnit );
+	void translateThrows( ast::TranslationUnit & transUnit );
 	/* Replaces all throw & throwResume statements with function calls.
 	 * These still need to be resolved, so call this before the reslover.
Index: src/ControlStruct/ExceptTranslateNew.cpp
===================================================================
--- src/ControlStruct/ExceptTranslateNew.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/ControlStruct/ExceptTranslateNew.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,142 @@
+//
+// 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 : Mon Nov  8 16:50:00 2021
+// Update Count     : 0
+//
+
+#include "ExceptTranslate.h"
+
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/TranslationUnit.hpp"
+
+namespace ControlStruct {
+
+namespace {
+
+class TranslateThrowsCore : 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::CatchStmt * 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::CatchStmt * 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);
+		}
+	}
+}
+
+} // namespace
+
+void translateThrows( ast::TranslationUnit & transUnit ) {
+	ast::Pass<TranslateThrowsCore>::run( transUnit );
+}
+
+} // namespace ControlStruct
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ControlStruct/FixLabels.cpp
===================================================================
--- src/ControlStruct/FixLabels.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/ControlStruct/FixLabels.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,118 @@
+//
+// 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.
+//
+// FixLabels.cpp -- Normalizes labels and handles multi-level exit labels.
+//
+// Author           : Andrew Beach
+// Created On       : Mon Nov  1 09:39:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Nov  8 10:53:00 2021
+// Update Count     : 3
+//
+
+#include "FixLabels.hpp"
+
+#include "AST/Label.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Stmt.hpp"
+#include "ControlStruct/MultiLevelExit.hpp"
+
+namespace ControlStruct {
+
+namespace {
+
+class FixLabelsCore final : public ast::WithGuards {
+	LabelToStmt labelTable;
+public:
+	FixLabelsCore() : labelTable() {}
+
+	void previsit( const ast::FunctionDecl * );
+	const ast::FunctionDecl * postvisit( const ast::FunctionDecl * );
+	void previsit( const ast::Stmt * );
+	void previsit( const ast::BranchStmt * );
+	void previsit( const ast::LabelAddressExpr * );
+
+	void setLabelsDef( const std::vector<ast::Label> &, const ast::Stmt * );
+	void setLabelsUsage( const ast::Label & );
+};
+
+void FixLabelsCore::previsit( const ast::FunctionDecl * ) {
+	GuardValue( labelTable ).clear();
+}
+
+const ast::FunctionDecl * FixLabelsCore::postvisit(
+		const ast::FunctionDecl * decl ) {
+	if ( nullptr == decl->stmts ) return decl;
+	for ( auto kvp : labelTable ) {
+		if ( nullptr == kvp.second ) {
+			SemanticError( kvp.first.location,
+				"Use of undefined label: " + kvp.first.name );
+		}
+	}
+	return ast::mutate_field( decl, &ast::FunctionDecl::stmts,
+		multiLevelExitUpdate( decl->stmts.get(), labelTable ) );
+}
+
+void FixLabelsCore::previsit( const ast::Stmt * stmt ) {
+	if ( !stmt->labels.empty() ) {
+		setLabelsDef( stmt->labels, stmt );
+	}
+}
+
+void FixLabelsCore::previsit( const ast::BranchStmt * stmt ) {
+	if ( !stmt->labels.empty() ) {
+		setLabelsDef( stmt->labels, stmt );
+	}
+	if ( !stmt->target.empty() ) {
+		setLabelsUsage( stmt->target );
+	}
+}
+
+void FixLabelsCore::previsit( const ast::LabelAddressExpr * expr ) {
+	assert( !expr->arg.empty() );
+	setLabelsUsage( expr->arg );
+}
+
+void FixLabelsCore::setLabelsDef(
+		const std::vector<ast::Label> & labels, const ast::Stmt * stmt ) {
+	assert( !labels.empty() );
+	assert( stmt );
+
+	for ( auto label : labels ) {
+		if ( labelTable.find( label ) == labelTable.end() ) {
+			// Make sure to only create the entry once.
+			labelTable[ label ] = stmt;
+		} else if ( nullptr != labelTable[ label ] ) {
+			// Duplicate definition, this is an error.
+			SemanticError( label.location,
+				"Duplicate definition of label: " + label.name );
+		} else {
+			// Perviously used, but not defined until now.
+			labelTable[ label ] = stmt;
+		}
+	}
+}
+
+// Label was used, if it is new add it to the table.
+void FixLabelsCore::setLabelsUsage( const ast::Label & label ) {
+	if ( labelTable.find( label ) == labelTable.end() ) {
+		labelTable[ label ] = nullptr;
+	}
+}
+
+} // namespace
+
+void fixLabels( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<FixLabelsCore>::run( translationUnit );
+}
+
+} // namespace ControlStruct
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ControlStruct/FixLabels.hpp
===================================================================
--- src/ControlStruct/FixLabels.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/ControlStruct/FixLabels.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,33 @@
+//
+// 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.
+//
+// FixLabels.hpp -- Normalizes labels and handles multi-level exit labels.
+//
+// Author           : Andrew Beach
+// Created On       : Mon Nov  1 09:36:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Nov  1 09:40:00 2021
+// Update Count     : 0
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace ControlStruct {
+
+/// normalizes label definitions and generates multi-level exit labels
+void fixLabels( ast::TranslationUnit & translationUnit );
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ControlStruct/LabelGenerator.cc
===================================================================
--- src/ControlStruct/LabelGenerator.cc	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/ControlStruct/LabelGenerator.cc	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Mar 11 22:23:20 2019
-// Update Count     : 15
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Nov  8 10:18:00 2021
+// Update Count     : 17
 //
 
@@ -19,4 +19,8 @@
 
 #include "LabelGenerator.h"
+
+#include "AST/Attribute.hpp"
+#include "AST/Label.hpp"
+#include "AST/Stmt.hpp"
 #include "SynTree/Attribute.h"  // for Attribute
 #include "SynTree/Label.h"      // for Label, operator<<
@@ -24,5 +28,7 @@
 
 namespace ControlStruct {
-	LabelGenerator * LabelGenerator::labelGenerator = 0;
+
+int LabelGenerator::current = 0;
+LabelGenerator * LabelGenerator::labelGenerator = nullptr;
 
 	LabelGenerator * LabelGenerator::getGenerator() {
@@ -43,4 +49,19 @@
 		return l;
 	}
+
+ast::Label LabelGenerator::newLabel(
+		const std::string & suffix, const ast::Stmt * stmt ) {
+	assert( stmt );
+
+	std::ostringstream os;
+	os << "__L" << current++ << "__" << suffix;
+	if ( stmt && !stmt->labels.empty() ) {
+		os << "_" << stmt->labels.front() << "__";
+	}
+	ast::Label ret_label( stmt->location, os.str() );
+	ret_label.attributes.push_back( new ast::Attribute( "unused" ) );
+	return ret_label;
+}
+
 } // namespace ControlStruct
 
Index: src/ControlStruct/LabelGenerator.h
===================================================================
--- src/ControlStruct/LabelGenerator.h	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/ControlStruct/LabelGenerator.h	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:20:14 2017
-// Update Count     : 6
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Nov  8 10:16:00 2021
+// Update Count     : 8
 //
 
@@ -21,18 +21,24 @@
 
 class Statement;
+namespace ast {
+	class Stmt;
+	class Label;
+}
 
 namespace ControlStruct {
-	class LabelGenerator {
-	  public:
-		static LabelGenerator *getGenerator();
-		Label newLabel(std::string suffix, Statement * stmt = nullptr);
-		void reset() { current = 0; }
-		void rewind() { current--; }
-	  protected:
-		LabelGenerator(): current(0) {}
-	  private:
-		int current;
-		static LabelGenerator *labelGenerator;
-	};
+
+class LabelGenerator {
+	static int current;
+	static LabelGenerator *labelGenerator;
+protected:
+	LabelGenerator() {}
+public:
+	static LabelGenerator *getGenerator();
+	static Label newLabel(std::string suffix, Statement * stmt = nullptr);
+	static ast::Label newLabel( const std::string&, const ast::Stmt * );
+	static void reset() { current = 0; }
+	static void rewind() { current--; }
+};
+
 } // namespace ControlStruct
 
Index: src/ControlStruct/MultiLevelExit.cpp
===================================================================
--- src/ControlStruct/MultiLevelExit.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/ControlStruct/MultiLevelExit.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,620 @@
+//
+// 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.
+//
+// MultiLevelExit.cpp -- Replaces CFA's local control flow with C's versions.
+//
+// Author           : Andrew Beach
+// Created On       : Mon Nov  1 13:48:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Nov  8 10:56:00 2021
+// Update Count     : 2
+//
+
+#include "MultiLevelExit.hpp"
+
+#include "AST/Pass.hpp"
+#include "AST/Stmt.hpp"
+#include "ControlStruct/LabelGenerator.h"
+
+#include <set>
+
+namespace ControlStruct {
+
+namespace {
+
+class Entry {
+public:
+	const ast::Stmt * stmt;
+private:
+	// Organized like a manual ADT. Avoids creating a bunch of dead data.
+	struct Target {
+		ast::Label label;
+		bool used = false;
+		Target( const ast::Label & label ) : label( label ) {}
+		Target() : label( CodeLocation() ) {}
+	};
+	Target firstTarget;
+	Target secondTarget;
+
+	enum Kind {
+		ForStmt, WhileStmt, CompoundStmt, IfStmt, CaseStmt, SwitchStmt, TryStmt
+	} kind;
+
+	bool fallDefaultValid = true;
+
+	static ast::Label & useTarget( Target & target ) {
+		target.used = true;
+		return target.label;
+	}
+
+public:
+	Entry( const ast::ForStmt * stmt, ast::Label breakExit, ast::Label contExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( ForStmt ) {}
+	Entry( const ast::WhileStmt * stmt, ast::Label breakExit, ast::Label contExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( WhileStmt ) {}
+	Entry( const ast::CompoundStmt *stmt, ast::Label breakExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( CompoundStmt ) {}
+	Entry( const ast::IfStmt *stmt, ast::Label breakExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( IfStmt ) {}
+	Entry( const ast::CaseStmt *stmt, ast::Label fallExit ) :
+		stmt( stmt ), firstTarget( fallExit ), secondTarget(), kind( CaseStmt ) {}
+	Entry( const ast::SwitchStmt *stmt, ast::Label breakExit, ast::Label fallDefaultExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget( fallDefaultExit ), kind( SwitchStmt ) {}
+	Entry( const ast::TryStmt *stmt, ast::Label breakExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( TryStmt ) {}
+
+	bool isContTarget() const { return kind <= WhileStmt; }
+	bool isBreakTarget() const { return CaseStmt != kind; }
+	bool isFallTarget() const { return CaseStmt == kind; }
+	bool isFallDefaultTarget() const { return SwitchStmt == kind; }
+
+	ast::Label useContExit() { assert( kind <= WhileStmt ); return useTarget(secondTarget); }
+	ast::Label useBreakExit() { assert( CaseStmt != kind ); return useTarget(firstTarget); }
+	ast::Label useFallExit() { assert( CaseStmt == kind );  return useTarget(firstTarget); }
+	ast::Label useFallDefaultExit() { assert( SwitchStmt == kind ); return useTarget(secondTarget); }
+
+	bool isContUsed() const { assert( kind <= WhileStmt ); return secondTarget.used; }
+	bool isBreakUsed() const { assert( CaseStmt != kind ); return firstTarget.used; }
+	bool isFallUsed() const { assert( CaseStmt == kind ); return firstTarget.used; }
+	bool isFallDefaultUsed() const { assert( SwitchStmt == kind ); return secondTarget.used; }
+	void seenDefault() { fallDefaultValid = false; }
+	bool isFallDefaultValid() const { return fallDefaultValid; }
+};
+
+// Helper predicates used in std::find_if calls (it doesn't take methods):
+bool isBreakTarget( const Entry & entry ) {
+	return entry.isBreakTarget();
+}
+
+bool isContinueTarget( const Entry & entry ) {
+	return entry.isContTarget();
+}
+
+bool isFallthroughTarget( const Entry & entry ) {
+	return entry.isFallTarget();
+}
+
+bool isFallthroughDefaultTarget( const Entry & entry ) {
+	return entry.isFallDefaultTarget();
+}
+
+struct MultiLevelExitCore final :
+		public ast::WithVisitorRef<MultiLevelExitCore>,
+		public ast::WithShortCircuiting, public ast::WithGuards {
+	MultiLevelExitCore( const LabelToStmt & lt );
+
+	void previsit( const ast::FunctionDecl * );
+
+	const ast::CompoundStmt * previsit( const ast::CompoundStmt * );
+	const ast::BranchStmt * postvisit( const ast::BranchStmt * );
+	void previsit( const ast::WhileStmt * );
+	const ast::WhileStmt * postvisit( const ast::WhileStmt * );
+	void previsit( const ast::ForStmt * );
+	const ast::ForStmt * postvisit( const ast::ForStmt * );
+	const ast::CaseStmt * previsit( const ast::CaseStmt * );
+	void previsit( const ast::IfStmt * );
+	const ast::IfStmt * postvisit( const ast::IfStmt * );
+	void previsit( const ast::SwitchStmt * );
+	const ast::SwitchStmt * postvisit( const ast::SwitchStmt * );
+	void previsit( const ast::ReturnStmt * );
+	void previsit( const ast::TryStmt * );
+	void postvisit( const ast::TryStmt * );
+	void previsit( const ast::FinallyStmt * );
+
+	const ast::Stmt * mutateLoop( const ast::Stmt * body, Entry& );
+
+	const LabelToStmt & target_table;
+	std::set<ast::Label> fallthrough_labels;
+	std::vector<Entry> enclosing_control_structures;
+	ast::Label break_label;
+	bool inFinally;
+
+	template<typename LoopNode>
+	void prehandleLoopStmt( const LoopNode * loopStmt );
+	template<typename LoopNode>
+	const LoopNode * posthandleLoopStmt( const LoopNode * loopStmt );
+
+	std::list<ast::ptr<ast::Stmt>> fixBlock(
+		const std::list<ast::ptr<ast::Stmt>> & kids, bool caseClause );
+
+	template<typename UnaryPredicate>
+	auto findEnclosingControlStructure( UnaryPredicate pred ) {
+		return std::find_if( enclosing_control_structures.rbegin(),
+			enclosing_control_structures.rend(), pred );
+	}
+};
+
+ast::NullStmt * labelledNullStmt(
+		const CodeLocation & cl, const ast::Label & label ) {
+	return new ast::NullStmt( cl, std::vector<ast::Label>{ label } );
+}
+
+MultiLevelExitCore::MultiLevelExitCore( const LabelToStmt & lt ) :
+	target_table( lt ), break_label( CodeLocation(), "" ),
+	inFinally( false )
+{}
+
+void MultiLevelExitCore::previsit( const ast::FunctionDecl * ) {
+	visit_children = false;
+}
+
+const ast::CompoundStmt * MultiLevelExitCore::previsit(
+		const ast::CompoundStmt * stmt ) {
+	visit_children = false;
+	bool isLabeled = !stmt->labels.empty();
+	if ( isLabeled ) {
+		ast::Label breakLabel = LabelGenerator::newLabel( "blockBreak", stmt );
+		enclosing_control_structures.emplace_back( stmt, breakLabel );
+		GuardAction( [this]() { enclosing_control_structures.pop_back(); } );
+	}
+
+	auto mutStmt = ast::mutate( stmt );
+	// A child statement may set the break label.
+	mutStmt->kids = std::move( fixBlock( stmt->kids, false ) );
+
+	if ( isLabeled ) {
+		assert( !enclosing_control_structures.empty() );
+		Entry & entry = enclosing_control_structures.back();
+		if ( !entry.useBreakExit().empty() ) {
+			break_label = entry.useBreakExit();
+		}
+	}
+	return mutStmt;
+}
+
+size_t getUnusedIndex(
+		const ast::Stmt * stmt, const ast::Label & originalTarget ) {
+	const size_t size = stmt->labels.size();
+
+	// If the label is empty, we can skip adding the unused attribute:
+	if ( originalTarget.empty() ) return size;
+
+	// Search for a label that matches the originalTarget.
+	for ( size_t i = 0 ; i < size ; ++i ) {
+		const ast::Label & label = stmt->labels[i];
+		if ( label == originalTarget ) {
+			for ( const ast::Attribute * attr : label.attributes ) {
+				if ( attr->name == "unused" ) return size;
+			}
+			return i;
+		}
+	}
+	assertf( false, "Could not find label '%s' on statement %s",
+		originalTarget.name.c_str(), toString( stmt ).c_str() );
+}
+
+const ast::Stmt * addUnused(
+		const ast::Stmt * stmt, const ast::Label & originalTarget ) {
+	size_t i = getUnusedIndex( stmt, originalTarget );
+	if ( i == stmt->labels.size() ) {
+		return stmt;
+	}
+	ast::Stmt * mutStmt = ast::mutate( stmt );
+	mutStmt->labels[i].attributes.push_back( new ast::Attribute( "unused" ) );
+	return mutStmt;
+}
+
+const ast::BranchStmt * MultiLevelExitCore::postvisit( const ast::BranchStmt * stmt ) {
+	std::vector<Entry>::reverse_iterator targetEntry =
+		enclosing_control_structures.rend();
+	switch ( stmt->kind ) {
+	case ast::BranchStmt::Goto:
+		return stmt;
+	case ast::BranchStmt::Continue:
+	case ast::BranchStmt::Break: {
+		bool isContinue = stmt->kind == ast::BranchStmt::Continue;
+		// Handle unlabeled break and continue.
+		if ( stmt->target.empty() ) {
+			if ( isContinue ) {
+				targetEntry = findEnclosingControlStructure( isContinueTarget );
+			} else {
+				if ( enclosing_control_structures.empty() ) {
+					SemanticError( stmt->location,
+						"'break' outside a loop, 'switch', or labelled block" );
+				}
+				targetEntry = findEnclosingControlStructure( isBreakTarget );
+			}
+		// Handle labeled break and continue.
+		} else {
+			// Lookup label in table to find attached control structure.
+			targetEntry = findEnclosingControlStructure(
+				[ targetStmt = target_table.at(stmt->target) ](auto entry){
+					return entry.stmt == targetStmt;
+				} );
+		}
+		// Ensure that selected target is valid.
+		if ( targetEntry == enclosing_control_structures.rend() || ( isContinue && !isContinueTarget( *targetEntry ) ) ) {
+			SemanticError(
+				stmt->location,
+				toString( (isContinue ? "'continue'" : "'break'"),
+					" target must be an enclosing ",
+					(isContinue ? "loop: " : "control structure: "),
+					stmt->originalTarget ) );
+		}
+		break;
+	}
+	case ast::BranchStmt::FallThrough: {
+		targetEntry = findEnclosingControlStructure( isFallthroughTarget );
+		// Check that target is valid.
+		if ( targetEntry == enclosing_control_structures.rend() ) {
+			SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
+		}
+		if ( !stmt->target.empty() ) {
+			// Labelled fallthrough: target must be a valid fallthough label.
+			if ( !fallthrough_labels.count( stmt->target ) ) {
+				SemanticError( stmt->location, toString( "'fallthrough' target must be a later case statement: ", stmt->originalTarget ) );
+			}
+			return new ast::BranchStmt(
+				stmt->location, ast::BranchStmt::Goto, stmt->originalTarget );
+		}
+		break;
+	}
+	case ast::BranchStmt::FallThroughDefault: {
+		targetEntry = findEnclosingControlStructure( isFallthroughDefaultTarget );
+
+		// Check that this is in a switch or choose statement.
+		if ( targetEntry == enclosing_control_structures.rend() ) {
+			SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
+		}
+
+		// Check that the switch or choose has a default clause.
+		auto switchStmt = strict_dynamic_cast< const ast::SwitchStmt * >(
+			targetEntry->stmt );
+		bool foundDefault = false;
+		for ( auto subStmt : switchStmt->stmts ) {
+			const ast::CaseStmt * caseStmt = subStmt.strict_as<ast::CaseStmt>();
+			if ( caseStmt->isDefault() ) {
+				foundDefault = true;
+				break;
+			}
+		}
+		if ( !foundDefault ) {
+			SemanticError( stmt->location, "'fallthrough default' must be enclosed in a 'switch' or 'choose' control structure with a 'default' clause" );
+		}
+		break;
+	}
+	default:
+		assert( false );
+	}
+
+	// Branch error checks: get the appropriate label name:
+	// (This label will always be replaced.)
+	ast::Label exitLabel( CodeLocation(), "" );
+	switch ( stmt->kind ) {
+	case ast::BranchStmt::Break:
+		assert( !targetEntry->useBreakExit().empty() );
+		exitLabel = targetEntry->useBreakExit();
+		break;
+	case ast::BranchStmt::Continue:
+		assert( !targetEntry->useContExit().empty() );
+		exitLabel = targetEntry->useContExit();
+		break;
+	case ast::BranchStmt::FallThrough:
+		assert( !targetEntry->useFallExit().empty() );
+		exitLabel = targetEntry->useFallExit();
+		break;
+	case ast::BranchStmt::FallThroughDefault:
+		assert( !targetEntry->useFallDefaultExit().empty() );
+		exitLabel = targetEntry->useFallDefaultExit();
+		// Check that fallthrough default comes before the default clause.
+		if ( !targetEntry->isFallDefaultValid() ) {
+			SemanticError( stmt->location,
+				"'fallthrough default' must precede the 'default' clause" );
+		}
+		break;
+	default:
+		assert(0);
+	}
+
+	// Add unused attribute to silence warnings.
+	targetEntry->stmt = addUnused( targetEntry->stmt, stmt->originalTarget );
+
+	// Replace this with a goto to make later passes more uniform.
+	return new ast::BranchStmt( stmt->location, ast::BranchStmt::Goto, exitLabel );
+}
+
+void MultiLevelExitCore::previsit( const ast::WhileStmt * stmt ) {
+	return prehandleLoopStmt( stmt );
+}
+
+const ast::WhileStmt * MultiLevelExitCore::postvisit( const ast::WhileStmt * stmt ) {
+	return posthandleLoopStmt( stmt );
+}
+
+void MultiLevelExitCore::previsit( const ast::ForStmt * stmt ) {
+	return prehandleLoopStmt( stmt );
+}
+
+const ast::ForStmt * MultiLevelExitCore::postvisit( const ast::ForStmt * stmt ) {
+	return posthandleLoopStmt( stmt );
+}
+
+// Mimic what the built-in push_front would do anyways. It is O(n).
+void push_front(
+		std::vector<ast::ptr<ast::Stmt>> & vec, const ast::Stmt * element ) {
+	vec.emplace_back( nullptr );
+	for ( size_t i = vec.size() - 1 ; 0 < i ; --i ) {
+		vec[ i ] = std::move( vec[ i - 1 ] );
+	}
+	vec[ 0 ] = element;
+}
+
+const ast::CaseStmt * MultiLevelExitCore::previsit( const ast::CaseStmt * stmt ) {
+	visit_children = false;
+
+	// If it is the default, mark the default as seen.
+	if ( stmt->isDefault() ) {
+		assert( !enclosing_control_structures.empty() );
+		enclosing_control_structures.back().seenDefault();
+	}
+
+	// The cond may not exist, but if it does update it now.
+	visitor->maybe_accept( stmt, &ast::CaseStmt::cond );
+
+	// Just save the mutated node for simplicity.
+	ast::CaseStmt * mutStmt = ast::mutate( stmt );
+
+	ast::Label fallLabel = LabelGenerator::newLabel( "fallThrough", stmt );
+	if ( !mutStmt->stmts.empty() ) {
+		// Ensure that the stack isn't corrupted by exceptions in fixBlock.
+		auto guard = makeFuncGuard(
+			[&](){ enclosing_control_structures.emplace_back( mutStmt, fallLabel ); },
+			[this](){ enclosing_control_structures.pop_back(); }
+		);
+
+		// These should already be in a block.
+		auto block = ast::mutate( mutStmt->stmts.front().strict_as<ast::CompoundStmt>() );
+		block->kids = fixBlock( block->kids, true );
+
+		// Add fallthrough label if necessary.
+		assert( !enclosing_control_structures.empty() );
+		Entry & entry = enclosing_control_structures.back();
+		if ( entry.isFallUsed() ) {
+			mutStmt->stmts.push_back(
+				labelledNullStmt( mutStmt->location, entry.useFallExit() ) );
+		}
+	}
+	assert( !enclosing_control_structures.empty() );
+	Entry & entry = enclosing_control_structures.back();
+	assertf( dynamic_cast< const ast::SwitchStmt * >( entry.stmt ),
+		"Control structure enclosing a case clause must be a switch, but is: %s",
+		toString( entry.stmt ).c_str() );
+	if ( mutStmt->isDefault() ) {
+		if ( entry.isFallDefaultUsed() ) {
+			// Add fallthrough default label if necessary.
+			push_front( mutStmt->stmts, labelledNullStmt(
+				stmt->location, entry.useFallDefaultExit()
+			) );
+		}
+	}
+	return mutStmt;
+}
+
+void MultiLevelExitCore::previsit( const ast::IfStmt * stmt ) {
+	bool labeledBlock = !stmt->labels.empty();
+	if ( labeledBlock ) {
+		ast::Label breakLabel = LabelGenerator::newLabel( "blockBreak", stmt );
+		enclosing_control_structures.emplace_back( stmt, breakLabel );
+		GuardAction( [this](){ enclosing_control_structures.pop_back(); } );
+	}
+}
+
+const ast::IfStmt * MultiLevelExitCore::postvisit( const ast::IfStmt * stmt ) {
+	bool labeledBlock = !stmt->labels.empty();
+	if ( labeledBlock ) {
+		auto this_label = enclosing_control_structures.back().useBreakExit();
+		if ( !this_label.empty() ) {
+			break_label = this_label;
+		}
+	}
+	return stmt;
+}
+
+bool isDefaultCase( const ast::ptr<ast::Stmt> & stmt ) {
+	const ast::CaseStmt * caseStmt = stmt.strict_as<ast::CaseStmt>();
+	return caseStmt->isDefault();
+}
+
+void MultiLevelExitCore::previsit( const ast::SwitchStmt * stmt ) {
+	ast::Label label = LabelGenerator::newLabel( "switchBreak", stmt );
+	auto it = std::find_if( stmt->stmts.rbegin(), stmt->stmts.rend(), isDefaultCase );
+
+	const ast::CaseStmt * defaultCase = it != stmt->stmts.rend()
+		? (it)->strict_as<ast::CaseStmt>() : nullptr;
+	ast::Label defaultLabel = defaultCase
+		? LabelGenerator::newLabel( "fallThroughDefault", defaultCase )
+		: ast::Label( stmt->location, "" );
+	enclosing_control_structures.emplace_back( stmt, label, defaultLabel );
+	GuardAction( [this]() { enclosing_control_structures.pop_back(); } );
+
+	// Collect valid labels for fallthrough. It starts with all labels at
+	// this level, then removed as we see them in traversal.
+	for ( const ast::Stmt * stmt : stmt->stmts ) {
+		auto * caseStmt = strict_dynamic_cast< const ast::CaseStmt * >( stmt );
+		if ( caseStmt->stmts.empty() ) continue;
+		auto block = caseStmt->stmts.front().strict_as<ast::CompoundStmt>();
+		for ( const ast::Stmt * stmt : block->kids ) {
+			for ( const ast::Label & l : stmt->labels ) {
+				fallthrough_labels.insert( l );
+			}
+		}
+	}
+}
+
+const ast::SwitchStmt * MultiLevelExitCore::postvisit( const ast::SwitchStmt * stmt ) {
+	assert( !enclosing_control_structures.empty() );
+	Entry & entry = enclosing_control_structures.back();
+	assert( entry.stmt == stmt );
+
+	// Only run if we need to generate the break label.
+	if ( entry.isBreakUsed() ) {
+		// To keep the switch statements uniform (all direct children of a
+		// SwitchStmt should be CastStmts), append the exit label and break
+		// to the last case, create a default case is there are no cases.
+		ast::SwitchStmt * mutStmt = ast::mutate( stmt );
+		if ( mutStmt->stmts.empty() ) {
+			mutStmt->stmts.push_back( new ast::CaseStmt(
+				mutStmt->location, nullptr, {} ));
+		}
+
+		auto caseStmt = mutStmt->stmts.back().strict_as<ast::CaseStmt>();
+		auto mutCase = ast::mutate( caseStmt );
+		mutStmt->stmts.back() = mutCase;
+
+		ast::Label label( mutCase->location, "breakLabel" );
+		auto branch = new ast::BranchStmt( mutCase->location, ast::BranchStmt::Break, label );
+		branch->labels.push_back( entry.useBreakExit() );
+		mutCase->stmts.push_back( branch );
+
+		return mutStmt;
+	}
+	return stmt;
+}
+
+void MultiLevelExitCore::previsit( const ast::ReturnStmt * stmt ) {
+	if ( inFinally ) {
+		SemanticError( stmt->location, "'return' may not appear in a finally clause" );
+	}
+}
+
+void MultiLevelExitCore::previsit( const ast::TryStmt * stmt ) {
+	bool isLabeled = !stmt->labels.empty();
+	if ( isLabeled ) {
+		ast::Label breakLabel = LabelGenerator::newLabel( "blockBreak", stmt );
+		enclosing_control_structures.emplace_back( stmt, breakLabel );
+		GuardAction([this](){ enclosing_control_structures.pop_back(); } );
+	}
+}
+
+void MultiLevelExitCore::postvisit( const ast::TryStmt * stmt ) {
+	bool isLabeled = !stmt->labels.empty();
+	if ( isLabeled ) {
+		auto this_label = enclosing_control_structures.back().useBreakExit();
+		if ( !this_label.empty() ) {
+			break_label = this_label;
+		}
+	}
+}
+
+void MultiLevelExitCore::previsit( const ast::FinallyStmt * ) {
+	GuardAction([this, old = std::move(enclosing_control_structures)](){
+		enclosing_control_structures = std::move(old);
+	});
+	enclosing_control_structures = std::vector<Entry>();
+	GuardValue( inFinally ) = true;
+}
+
+const ast::Stmt * MultiLevelExitCore::mutateLoop(
+		const ast::Stmt * body, Entry & entry ) {
+	if ( entry.isBreakUsed() ) {
+		break_label = entry.useBreakExit();
+	}
+
+	if ( entry.isContUsed() ) {
+		ast::CompoundStmt * new_body = new ast::CompoundStmt( body->location );
+		new_body->kids.push_back( body );
+		new_body->kids.push_back(
+			labelledNullStmt( body->location, entry.useContExit() ) );
+		return new_body;
+	}
+
+	return body;
+}
+
+template<typename LoopNode>
+void MultiLevelExitCore::prehandleLoopStmt( const LoopNode * loopStmt ) {
+	// Remember is loop before going onto mutate the body.
+	// The labels will be folded in if they are used.
+	ast::Label breakLabel = LabelGenerator::newLabel( "loopBreak", loopStmt );
+	ast::Label contLabel = LabelGenerator::newLabel( "loopContinue", loopStmt );
+	enclosing_control_structures.emplace_back( loopStmt, breakLabel, contLabel );
+	GuardAction( [this](){ enclosing_control_structures.pop_back(); } );
+}
+
+template<typename LoopNode>
+const LoopNode * MultiLevelExitCore::posthandleLoopStmt( const LoopNode * loopStmt ) {
+	assert( !enclosing_control_structures.empty() );
+	Entry & entry = enclosing_control_structures.back();
+	assert( entry.stmt == loopStmt );
+
+	// Now we check if the labels are used and add them if so.
+	return ast::mutate_field(
+		loopStmt, &LoopNode::body, mutateLoop( loopStmt->body, entry ) );
+}
+
+std::list<ast::ptr<ast::Stmt>> MultiLevelExitCore::fixBlock(
+		const std::list<ast::ptr<ast::Stmt>> & kids, bool is_case_clause ) {
+	// Unfortunately we can't use the automatic error collection.
+	SemanticErrorException errors;
+
+	std::list<ast::ptr<ast::Stmt>> ret;
+
+	// Manually visit each child.
+	for ( const ast::ptr<ast::Stmt> & kid : kids ) {
+		if ( is_case_clause ) {
+			// Once a label is seen, it's no longer a valid for fallthrough.
+			for ( const ast::Label & l : kid->labels ) {
+				fallthrough_labels.erase( l );
+			}
+		}
+
+		try {
+			ret.push_back( kid->accept( *visitor ) );
+		} catch ( SemanticErrorException & e ) {
+			errors.append( e );
+		}
+
+		if ( !break_label.empty() ) {
+			ret.push_back(
+				labelledNullStmt( ret.back()->location, break_label ) );
+			break_label = ast::Label( CodeLocation(), "" );
+		}
+	}
+
+	if ( !errors.isEmpty() ) {
+		throw errors;
+	}
+	return ret;
+}
+
+} // namespace
+
+const ast::CompoundStmt * multiLevelExitUpdate(
+    	const ast::CompoundStmt * stmt,
+		const LabelToStmt & labelTable ) {
+	// Must start in the body, so FunctionDecls can be a stopping point.
+	ast::Pass<MultiLevelExitCore> visitor( labelTable );
+	const ast::CompoundStmt * ret = stmt->accept( visitor );
+	return ret;
+}
+
+} // namespace ControlStruct
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ControlStruct/MultiLevelExit.hpp
===================================================================
--- src/ControlStruct/MultiLevelExit.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/ControlStruct/MultiLevelExit.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,40 @@
+//
+// 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.
+//
+// MultiLevelExit.hpp -- Replaces CFA's local control flow with C's versions.
+//
+// Author           : Andrew Beach
+// Created On       : Mon Nov  1 13:49:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Nov  8 10:53:00 2021
+// Update Count     : 3
+//
+
+#pragma once
+
+#include <map>
+
+namespace ast {
+	class CompoundStmt;
+	class Label;
+	class Stmt;
+}
+
+namespace ControlStruct {
+
+using LabelToStmt = std::map<ast::Label, const ast::Stmt *>;
+
+/// Mutate a function body to handle multi-level exits.
+const ast::CompoundStmt * multiLevelExitUpdate(
+	const ast::CompoundStmt *, const LabelToStmt & );
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ControlStruct/module.mk
===================================================================
--- src/ControlStruct/module.mk	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/ControlStruct/module.mk	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -18,4 +18,6 @@
 	ControlStruct/ExceptDecl.cc \
 	ControlStruct/ExceptDecl.h \
+	ControlStruct/FixLabels.cpp \
+	ControlStruct/FixLabels.hpp \
 	ControlStruct/ForExprMutator.cc \
 	ControlStruct/ForExprMutator.h \
@@ -26,8 +28,14 @@
 	ControlStruct/MLEMutator.cc \
 	ControlStruct/MLEMutator.h \
+	ControlStruct/MultiLevelExit.cpp \
+	ControlStruct/MultiLevelExit.hpp \
 	ControlStruct/Mutate.cc \
 	ControlStruct/Mutate.h
 
-SRC += $(SRC_CONTROLSTRUCT) ControlStruct/ExceptTranslate.cc ControlStruct/ExceptTranslate.h
+SRC += $(SRC_CONTROLSTRUCT) \
+	ControlStruct/ExceptTranslateNew.cpp \
+	ControlStruct/ExceptTranslate.cc \
+	ControlStruct/ExceptTranslate.h
+
 SRCDEMANGLE += $(SRC_CONTROLSTRUCT)
 
Index: src/InitTweak/GenInit.cc
===================================================================
--- src/InitTweak/GenInit.cc	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/InitTweak/GenInit.cc	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -9,7 +9,7 @@
 // Author           : Rob Schluntz
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Dec 13 23:15:10 2019
-// Update Count     : 184
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Oct 25 13:53:00 2021
+// Update Count     : 186
 //
 #include "GenInit.h"
@@ -24,4 +24,5 @@
 #include "AST/Decl.hpp"
 #include "AST/Init.hpp"
+#include "AST/Pass.hpp"
 #include "AST/Node.hpp"
 #include "AST/Stmt.hpp"
@@ -294,4 +295,133 @@
 	}
 
+namespace {
+
+#	warning Remove the _New suffix after the conversion is complete.
+	struct HoistArrayDimension_NoResolve_New final :
+			public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting,
+			public ast::WithGuards, public ast::WithConstTranslationUnit,
+			public ast::WithVisitorRef<HoistArrayDimension_NoResolve_New> {
+		void previsit( const ast::ObjectDecl * decl );
+		const ast::DeclWithType * postvisit( const ast::ObjectDecl * decl );
+		// Do not look for objects inside there declarations (and type).
+		void previsit( const ast::AggregateDecl * ) { visit_children = false; }
+		void previsit( const ast::NamedTypeDecl * ) { visit_children = false; }
+		void previsit( const ast::FunctionType * ) { visit_children = false; }
+
+		const ast::Type * hoist( const ast::Type * type );
+
+		ast::Storage::Classes storageClasses;
+	};
+
+	void HoistArrayDimension_NoResolve_New::previsit(
+			const ast::ObjectDecl * decl ) {
+		GuardValue( storageClasses ) = decl->storage;
+	}
+
+	const ast::DeclWithType * HoistArrayDimension_NoResolve_New::postvisit(
+			const ast::ObjectDecl * objectDecl ) {
+		return mutate_field( objectDecl, &ast::ObjectDecl::type,
+				hoist( objectDecl->type ) );
+	}
+
+	const ast::Type * HoistArrayDimension_NoResolve_New::hoist(
+			const ast::Type * type ) {
+		static UniqueName dimensionName( "_array_dim" );
+
+		if ( !isInFunction() || storageClasses.is_static ) {
+			return type;
+		}
+
+		if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
+			if ( nullptr == arrayType->dimension ) {
+				return type;
+			}
+
+			if ( !Tuples::maybeImpure( arrayType->dimension ) ) {
+				return type;
+			}
+
+			ast::ptr<ast::Type> dimType = transUnit().global.sizeType;
+			assert( dimType );
+			add_qualifiers( dimType, ast::CV::Qualifiers( ast::CV::Const ) );
+
+			ast::ObjectDecl * arrayDimension = new ast::ObjectDecl(
+				arrayType->dimension->location,
+				dimensionName.newName(),
+				dimType,
+				new ast::SingleInit(
+					arrayType->dimension->location,
+					arrayType->dimension
+				)
+			);
+
+			ast::ArrayType * mutType = ast::mutate( arrayType );
+			mutType->dimension = new ast::VariableExpr(
+					arrayDimension->location, arrayDimension );
+			declsToAddBefore.push_back( arrayDimension );
+
+			mutType->base = hoist( mutType->base );
+			return mutType;
+		}
+		return type;
+	}
+
+	struct ReturnFixer_New final :
+			public ast::WithStmtsToAdd<>, ast::WithGuards {
+		void previsit( const ast::FunctionDecl * decl );
+		const ast::ReturnStmt * previsit( const ast::ReturnStmt * stmt );
+	private:
+		const ast::FunctionDecl * funcDecl = nullptr;
+	};
+
+	void ReturnFixer_New::previsit( const ast::FunctionDecl * decl ) {
+		GuardValue( funcDecl ) = decl;
+	}
+
+	const ast::ReturnStmt * ReturnFixer_New::previsit(
+			const ast::ReturnStmt * stmt ) {
+		auto & returns = funcDecl->returns;
+		assert( returns.size() < 2 );
+		// Hands off if the function returns a reference.
+		// Don't allocate a temporary if the address is returned.
+		if ( stmt->expr && 1 == returns.size() ) {
+			ast::ptr<ast::DeclWithType> retDecl = returns.front();
+			if ( isConstructable( retDecl->get_type() ) ) {
+				// Explicitly construct the return value using the return
+				// expression and the retVal object.
+				assertf( "" != retDecl->name,
+					"Function %s has unnamed return value.\n",
+					funcDecl->name.c_str() );
+
+				auto retVal = retDecl.strict_as<ast::ObjectDecl>();
+				if ( auto varExpr = stmt->expr.as<ast::VariableExpr>() ) {
+					// Check if the return statement is already set up.
+					if ( varExpr->var == retVal ) return stmt;
+				}
+				ast::ptr<ast::Stmt> ctorStmt = genCtorDtor(
+					retVal->location, "?{}", retVal, stmt->expr );
+				assertf( ctorStmt,
+					"ReturnFixer: genCtorDtor returned nllptr: %s / %s",
+					toString( retVal ).c_str(),
+					toString( stmt->expr ).c_str() );
+					stmtsToAddBefore.push_back( ctorStmt );
+
+				// Return the retVal object.
+				ast::ReturnStmt * mutStmt = ast::mutate( stmt );
+				mutStmt->expr = new ast::VariableExpr(
+					stmt->location, retDecl );
+				return mutStmt;
+			}
+		}
+		return stmt;
+	}
+
+} // namespace
+
+	void genInit( ast::TranslationUnit & transUnit ) {
+		ast::Pass<HoistArrayDimension_NoResolve_New>::run( transUnit );
+		ast::Pass<ReturnFixer_New>::run( transUnit );
+	}
+
 	void CtorDtor::generateCtorDtor( std::list< Declaration * > & translationUnit ) {
 		PassVisitor<CtorDtor> ctordtor;
Index: src/InitTweak/GenInit.h
===================================================================
--- src/InitTweak/GenInit.h	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/InitTweak/GenInit.h	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:31:19 2017
-// Update Count     : 4
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Oct 22 16:08:00 2021
+// Update Count     : 6
 //
 
@@ -27,4 +27,5 @@
 	/// Adds return value temporaries and wraps Initializers in ConstructorInit nodes
 	void genInit( std::list< Declaration * > & translationUnit );
+	void genInit( ast::TranslationUnit & translationUnit );
 
 	/// Converts return statements into copy constructor calls on the hidden return variable
Index: src/MakeLibCfa.h
===================================================================
--- src/MakeLibCfa.h	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/MakeLibCfa.h	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -19,7 +19,11 @@
 
 class Declaration;
+namespace ast {
+	struct TranslationUnit;
+}
 
 namespace LibCfa {
 	void makeLibCfa( std::list< Declaration* > &prelude );
+	void makeLibCfa( ast::TranslationUnit & translationUnit );
 } // namespace LibCfa
 
Index: src/MakeLibCfaNew.cpp
===================================================================
--- src/MakeLibCfaNew.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/MakeLibCfaNew.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,146 @@
+//
+// 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/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 f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/Makefile.am	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -23,4 +23,5 @@
       CompilationState.h \
       MakeLibCfa.cc \
+	  MakeLibCfaNew.cpp \
 	MakeLibCfa.h
 
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/Parser/parser.yy	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -10,6 +10,6 @@
 // Created On       : Sat Sep  1 20:22:55 2001
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Tue Jul 20 22:03:04 2021
-// Update Count     : 5031
+// Last Modified On : Fri Oct 15 09:20:17 2021
+// Update Count     : 5163
 //
 
@@ -31,5 +31,5 @@
 // from ANSI90 to ANSI11 C are marked with the comment "C99/C11".
 
-// This grammar also has two levels of extensions. The first extensions cover most of the GCC C extensions All of the
+// This grammar also has two levels of extensions. The first extensions cover most of the GCC C extensions. All of the
 // syntactic extensions for GCC C are marked with the comment "GCC". The second extensions are for Cforall (CFA), which
 // fixes several of C's outstanding problems and extends C with many modern language concepts. All of the syntactic
@@ -69,5 +69,5 @@
 	// 2. String encodings are transformed into canonical form (one encoding at start) so the encoding can be found
 	//    without searching the string, e.g.: "abc" L"def" L"ghi" => L"abc" "def" "ghi". Multiple encodings must match,
-	//    i.e., u"a" U"b" L"c" is disallowed.
+	//    e.g., u"a" U"b" L"c" is disallowed.
 
 	if ( from[0] != '"' ) {								// encoding ?
@@ -185,4 +185,5 @@
 		type = new ExpressionNode( new CastExpr( maybeMoveBuild<Expression>(type), new BasicType( Type::Qualifiers(), BasicType::SignedInt ) ) );
 	} // if
+//	type = new ExpressionNode( build_func( new ExpressionNode( build_varref( new string( "__for_control_index_constraints__" ) ) ), type ) );
 	return new ForCtrl(
 		distAttr( DeclarationNode::newTypeof( type, true ), DeclarationNode::newName( index )->addInitializer( new InitializerNode( start ) ) ),
@@ -309,6 +310,5 @@
 %token ATassign											// @=
 
-%type<tok> identifier
-%type<tok> identifier_or_type_name  attr_name
+%type<tok> identifier					identifier_at				identifier_or_type_name		attr_name
 %type<tok> quasi_keyword
 %type<constant> string_literal
@@ -326,5 +326,5 @@
 %type<en> conditional_expression		constant_expression			assignment_expression		assignment_expression_opt
 %type<en> comma_expression				comma_expression_opt
-%type<en> argument_expression_list_opt	argument_expression			default_initializer_opt
+%type<en> argument_expression_list_opt	argument_expression_list	argument_expression			default_initializer_opt
 %type<ifctl> if_control_expression
 %type<fctl> for_control_expression		for_control_expression_list
@@ -558,4 +558,8 @@
 	IDENTIFIER
 	| quasi_keyword
+	;
+
+identifier_at:
+	identifier
 	| '@'												// CFA
 		{ Token tok = { new string( DeclarationNode::anonymous.newName() ), yylval.tok.loc }; $$ = tok; }
@@ -692,5 +696,9 @@
 	// empty
 		{ $$ = nullptr; }
-	| argument_expression
+	| argument_expression_list
+	;
+
+argument_expression_list:
+	argument_expression
 	| argument_expression_list_opt ',' argument_expression
 		{ $$ = (ExpressionNode *)($1->set_last( $3 )); }
@@ -730,5 +738,5 @@
 	| FLOATINGconstant fraction_constants_opt
 		{ $$ = new ExpressionNode( build_field_name_fraction_constants( build_field_name_FLOATINGconstant( *$1 ), $2 ) ); }
-	| identifier fraction_constants_opt
+	| identifier_at fraction_constants_opt				// CFA, allow anonymous fields
 		{
 			$$ = new ExpressionNode( build_field_name_fraction_constants( build_varref( $1 ), $2 ) );
@@ -1083,4 +1091,7 @@
 	comma_expression_opt ';'
 		{ $$ = new StatementNode( build_expr( $1 ) ); }
+	| MUTEX '(' ')' comma_expression ';'
+		{ $$ = new StatementNode( build_mutex( nullptr, new StatementNode( build_expr( $4 ) ) ) ); }
+		// { SemanticError( yylloc, "Mutex expression is currently unimplemented." ); $$ = nullptr; }
 	;
 
@@ -1181,16 +1192,22 @@
 
 iteration_statement:
-	WHILE '(' push if_control_expression ')' statement pop
-		{ $$ = new StatementNode( build_while( $4, maybe_build_compound( $6 ) ) ); }
-	| WHILE '(' ')' statement							// CFA => while ( 1 )
+	WHILE '(' ')' statement								// CFA => while ( 1 )
 		{ $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); }
-	| DO statement WHILE '(' comma_expression ')' ';'
-		{ $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); }
+	| WHILE '(' if_control_expression ')' statement		%prec THEN
+		{ $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ) ) ); }
+	| WHILE '(' if_control_expression ')' statement ELSE statement // CFA
+		{ SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
 	| DO statement WHILE '(' ')' ';'					// CFA => do while( 1 )
 		{ $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); }
-	| FOR '(' push for_control_expression_list ')' statement pop
-		{ $$ = new StatementNode( build_for( $4, maybe_build_compound( $6 ) ) ); }
+	| DO statement WHILE '(' comma_expression ')' ';'	%prec THEN
+		{ $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); }
+	| DO statement WHILE '(' comma_expression ')' ELSE statement // CFA
+		{ SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
 	| FOR '(' ')' statement								// CFA => for ( ;; )
 		{ $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 ) ) ); }
+	| FOR '(' for_control_expression_list ')' statement	%prec THEN
+	  	{ $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ) ) ); }
+	| FOR '(' for_control_expression_list ')' statement ELSE statement // CFA
+		{ SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
 	;
 
@@ -1338,12 +1355,10 @@
 with_statement:
 	WITH '(' tuple_expression_list ')' statement
-		{
-			$$ = new StatementNode( build_with( $3, $5 ) );
-		}
+		{ $$ = new StatementNode( build_with( $3, $5 ) ); }
 	;
 
 // If MUTEX becomes a general qualifier, there are shift/reduce conflicts, so change syntax to "with mutex".
 mutex_statement:
-	MUTEX '(' argument_expression_list_opt ')' statement
+	MUTEX '(' argument_expression_list ')' statement
 		{ $$ = new StatementNode( build_mutex( $3, $5 ) ); }
 	;
@@ -2445,4 +2460,5 @@
 	| simple_assignment_operator initializer	{ $$ = $1 == OperKinds::Assign ? $2 : $2->set_maybeConstructed( false ); }
 	| '=' VOID									{ $$ = new InitializerNode( true ); }
+	| '{' initializer_list_opt comma_opt '}'	{ $$ = new InitializerNode( $2, true ); }
 	;
 
@@ -2458,6 +2474,5 @@
 	| designation initializer					{ $$ = $2->set_designators( $1 ); }
 	| initializer_list_opt ',' initializer		{ $$ = (InitializerNode *)( $1->set_last( $3 ) ); }
-	| initializer_list_opt ',' designation initializer
-		{ $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); }
+	| initializer_list_opt ',' designation initializer { $$ = (InitializerNode *)($1->set_last( $4->set_designators( $3 ) )); }
 	;
 
@@ -2474,5 +2489,5 @@
 designation:
 	designator_list ':'									// C99, CFA uses ":" instead of "="
-	| identifier ':'									// GCC, field name
+	| identifier_at ':'									// GCC, field name
 		{ $$ = new ExpressionNode( build_varref( $1 ) ); }
 	;
@@ -2486,5 +2501,5 @@
 
 designator:
-	'.' identifier										// C99, field name
+	'.' identifier_at									// C99, field name
 		{ $$ = new ExpressionNode( build_varref( $2 ) ); }
 	| '[' push assignment_expression pop ']'			// C99, single array element
@@ -2918,5 +2933,5 @@
 
 paren_identifier:
-	identifier
+	identifier_at
 		{ $$ = DeclarationNode::newName( $1 ); }
 	| '(' paren_identifier ')'							// redundant parenthesis
Index: src/ResolvExpr/CandidatePrinter.cpp
===================================================================
--- src/ResolvExpr/CandidatePrinter.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/ResolvExpr/CandidatePrinter.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,62 @@
+//
+// 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.
+//
+// CandidatePrinter.cpp -- Print expression canditates.
+//
+// Author           : Andrew Beach
+// Created On       : Tue Nov  9  9:54:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Nov  9 15:47:00 2021
+// Update Count     : 0
+//
+
+#include "CandidatePrinter.hpp"
+
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Print.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/TranslationUnit.hpp"
+#include "ResolvExpr/CandidateFinder.hpp"
+
+#include <iostream>
+
+namespace ResolvExpr {
+
+namespace {
+
+class CandidatePrintCore : public ast::WithSymbolTable {
+	std::ostream & os;
+public:
+	CandidatePrintCore( std::ostream & os ) : os( os ) {}
+
+	void postvisit( const ast::ExprStmt * stmt ) {
+		ast::TypeEnvironment env;
+		CandidateFinder finder( symtab, env );
+		finder.find( stmt->expr, ResolvMode::withAdjustment() );
+		int count = 1;
+		os << "There are " << finder.candidates.size() << " candidates\n";
+		for ( const std::shared_ptr<Candidate> & cand : finder ) {
+			os << "Candidate " << count++ << " ==============\n";
+			ast::print( os, cand->expr->result.get() );
+			os << std::endl;
+		}
+	}
+};
+
+} // namespace
+
+void printCandidates( ast::TranslationUnit & transUnit ) {
+	ast::Pass<CandidatePrintCore>::run( transUnit, std::cout );
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/CandidatePrinter.hpp
===================================================================
--- src/ResolvExpr/CandidatePrinter.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
+++ src/ResolvExpr/CandidatePrinter.hpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+// CandidatePrinter.hpp -- Print expression canditates.
+//
+// Author           : Andrew Beach
+// Created On       : Tue Nov  9  9:49:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Nov  9 15:33:00 2021
+// Update Count     : 0
+//
+
+#pragma once
+
+namespace ast {
+    class TranslationUnit;
+}
+
+namespace ResolvExpr {
+
+void printCandidates( ast::TranslationUnit & transUnit );
+/* Traverse over the entire translation unit, printing candidates for each
+ * top level expression. See CandidateFinder.
+ */
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/module.mk
===================================================================
--- src/ResolvExpr/module.mk	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/ResolvExpr/module.mk	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -61,5 +61,9 @@
       ResolvExpr/WidenMode.h
 
+SRC += $(SRC_RESOLVEXPR) \
+	ResolvExpr/AlternativePrinter.cc \
+	ResolvExpr/AlternativePrinter.h \
+	ResolvExpr/CandidatePrinter.cpp \
+	ResolvExpr/CandidatePrinter.hpp
 
-SRC += $(SRC_RESOLVEXPR) ResolvExpr/AlternativePrinter.cc ResolvExpr/AlternativePrinter.h
 SRCDEMANGLE += $(SRC_RESOLVEXPR)
Index: src/Tuples/TupleExpansionNew.cpp
===================================================================
--- src/Tuples/TupleExpansionNew.cpp	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/Tuples/TupleExpansionNew.cpp	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -8,11 +8,9 @@
 //
 // Author           : Henry Xue
-// Created On       : Wed Aug 18 12:54:02 2021
+// Created On       : Mon Aug 23 15:36:09 2021
 // Last Modified By : Henry Xue
-// Last Modified On : Wed Aug 18 12:54:02 2021
+// Last Modified On : Mon Aug 23 15:36:09 2021
 // Update Count     : 1
 //
-
-// Currently not working due to unresolved issues with UniqueExpr
 
 #include "Tuples.h"
@@ -20,8 +18,55 @@
 namespace Tuples {
 namespace {
+	struct MemberTupleExpander final : public ast::WithShortCircuiting, public ast::WithVisitorRef< MemberTupleExpander > {
+		void previsit( const ast::UntypedMemberExpr * ) { visit_children = false; }
+        const ast::Expr * postvisit( const ast::UntypedMemberExpr * memberExpr );
+	};
 	struct UniqueExprExpander final : public ast::WithDeclsToAdd<> {
 		const ast::Expr * postvisit( const ast::UniqueExpr * unqExpr );
 		std::map< int, ast::ptr<ast::Expr> > decls; // not vector, because order added may not be increasing order
 	};
+} // namespace
+
+void expandMemberTuples( ast::TranslationUnit & translationUnit ) {
+	ast::Pass< MemberTupleExpander >::run( translationUnit );
+}
+
+namespace {
+	namespace {
+		/// given a expression representing the member and an expression representing the aggregate,
+		/// reconstructs a flattened UntypedMemberExpr with the right precedence
+		const ast::Expr * reconstructMemberExpr( const ast::Expr * member, const ast::Expr * aggr, const CodeLocation & loc ) {
+			if ( auto memberExpr = dynamic_cast< const ast::UntypedMemberExpr * >( member ) ) {
+				// construct a new UntypedMemberExpr with the correct structure , and recursively
+				// expand that member expression.
+				ast::Pass< MemberTupleExpander > expander;
+				auto inner = new ast::UntypedMemberExpr( loc, memberExpr->aggregate, aggr );
+				auto newMemberExpr = new ast::UntypedMemberExpr( loc, memberExpr->member, inner );
+				//delete memberExpr;
+				return newMemberExpr->accept( expander );
+			} else {
+				// not a member expression, so there is nothing to do but attach and return
+				return new ast::UntypedMemberExpr( loc, member, aggr );
+			}
+		}
+	}
+
+	const ast::Expr * MemberTupleExpander::postvisit( const ast::UntypedMemberExpr * memberExpr ) {
+		const CodeLocation loc = memberExpr->location;
+        if ( auto tupleExpr = memberExpr->member.as< ast::UntypedTupleExpr >() ) {
+			auto mutExpr = mutate( tupleExpr );
+			ast::ptr< ast::Expr > aggr = memberExpr->aggregate->accept( *visitor );
+			// aggregate expressions which might be impure must be wrapped in unique expressions
+			if ( Tuples::maybeImpureIgnoreUnique( memberExpr->aggregate ) ) aggr = new ast::UniqueExpr( loc, aggr );
+			for ( auto & expr : mutExpr->exprs ) {
+				expr = reconstructMemberExpr( expr, aggr, loc );
+			}
+			//delete aggr;
+			return mutExpr;
+		} else {
+			// there may be a tuple expr buried in the aggregate
+			return new ast::UntypedMemberExpr( loc, memberExpr->member, memberExpr->aggregate->accept( *visitor ) );
+		}
+	}
 } // namespace
 
Index: src/Tuples/Tuples.h
===================================================================
--- src/Tuples/Tuples.h	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/Tuples/Tuples.h	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Jun 18 09:36:00 2019
-// Update Count     : 18
+// Last Modified By : Henry Xue
+// Last Modified On : Mon Aug 23 15:36:09 2021
+// Update Count     : 19
 //
 
@@ -39,4 +39,5 @@
 	/// expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate
 	void expandMemberTuples( std::list< Declaration * > & translationUnit );
+	void expandMemberTuples( ast::TranslationUnit & translationUnit );
 
 	/// replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc.
Index: src/Tuples/module.mk
===================================================================
--- src/Tuples/module.mk	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/Tuples/module.mk	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -10,7 +10,7 @@
 ## Author           : Richard C. Bilson
 ## Created On       : Mon Jun  1 17:49:17 2015
-## Last Modified By : Peter A. Buhr
-## Last Modified On : Mon Jun  1 17:54:33 2015
-## Update Count     : 1
+## Last Modified By : Henry Xue
+## Last Modified On : Mon Aug 23 15:36:09 2021
+## Update Count     : 2
 ###############################################################################
 
Index: src/main.cc
===================================================================
--- src/main.cc	(revision f95634ee1f70e0dd4ea661aa832925cf8415519a)
+++ src/main.cc	(revision b7fd9daffd1af20c4c7cc80ac4660136917af20f)
@@ -9,7 +9,7 @@
 // Author           : Peter Buhr and Rob Schluntz
 // Created On       : Fri May 15 23:12:02 2015
-// Last Modified By : Henry Xue
-// Last Modified On : Tue Jul 20 04:27:35 2021
-// Update Count     : 658
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Nov  9 11:10:00 2021
+// Update Count     : 657
 //
 
@@ -43,4 +43,6 @@
 #include "Common/CodeLocationTools.hpp"     // for forceFillCodeLocations
 #include "Common/CompilerError.h"           // for CompilerError
+#include "Common/DeclStats.hpp"             // for printDeclStats
+#include "Common/ResolvProtoDump.hpp"       // for dumpAsResolverProto
 #include "Common/Stats.h"
 #include "Common/PassVisitor.h"
@@ -51,4 +53,5 @@
 #include "ControlStruct/ExceptDecl.h"       // for translateExcept
 #include "ControlStruct/ExceptTranslate.h"  // for translateEHM
+#include "ControlStruct/FixLabels.hpp"      // for fixLabels
 #include "ControlStruct/Mutate.h"           // for mutate
 #include "GenPoly/Box.h"                    // for box
@@ -62,4 +65,5 @@
 #include "Parser/TypedefTable.h"            // for TypedefTable
 #include "ResolvExpr/AlternativePrinter.h"  // for AlternativePrinter
+#include "ResolvExpr/CandidatePrinter.hpp"  // for printCandidates
 #include "ResolvExpr/Resolver.h"            // for resolve
 #include "SymTab/Validate.h"                // for validate
@@ -315,47 +319,6 @@
 		// add the assignment statement after the initialization of a type parameter
 		PASS( "Validate", SymTab::validate( translationUnit, symtabp ) );
-		if ( symtabp ) {
-			deleteAll( translationUnit );
-			return EXIT_SUCCESS;
-		} // if
-
-		if ( expraltp ) {
-			PassVisitor<ResolvExpr::AlternativePrinter> printer( cout );
-			acceptAll( translationUnit, printer );
-			return EXIT_SUCCESS;
-		} // if
-
-		if ( validp ) {
-			dump( translationUnit );
-			return EXIT_SUCCESS;
-		} // if
-
-		PASS( "Translate Throws", ControlStruct::translateThrows( translationUnit ) );
-		PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) );
-		PASS( "Fix Names", CodeGen::fixNames( translationUnit ) );
-		PASS( "Gen Init", InitTweak::genInit( translationUnit ) );
-		PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( translationUnit ) );
-		if ( libcfap ) {
-			// generate the bodies of cfa library functions
-			LibCfa::makeLibCfa( translationUnit );
-		} // if
-
-		if ( declstatsp ) {
-			CodeTools::printDeclStats( translationUnit );
-			deleteAll( translationUnit );
-			return EXIT_SUCCESS;
-		} // if
-
-		if ( bresolvep ) {
-			dump( translationUnit );
-			return EXIT_SUCCESS;
-		} // if
 
 		CodeTools::fillLocations( translationUnit );
-
-		if ( resolvprotop ) {
-			CodeTools::dumpAsResolvProto( translationUnit );
-			return EXIT_SUCCESS;
-		} // if
 
 		if( useNewAST ) {
@@ -365,4 +328,47 @@
 			}
 			auto transUnit = convert( move( translationUnit ) );
+
+			forceFillCodeLocations( transUnit );
+
+			if ( symtabp ) {
+				return EXIT_SUCCESS;
+			} // if
+
+			if ( expraltp ) {
+				ResolvExpr::printCandidates( transUnit );
+				return EXIT_SUCCESS;
+			} // if
+
+			if ( validp ) {
+				dump( move( transUnit ) );
+				return EXIT_SUCCESS;
+			} // if
+
+			PASS( "Translate Throws", ControlStruct::translateThrows( transUnit ) );
+			PASS( "Fix Labels", ControlStruct::fixLabels( transUnit ) );
+			PASS( "Fix Names", CodeGen::fixNames( transUnit ) );
+			PASS( "Gen Init", InitTweak::genInit( transUnit ) );
+			PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( transUnit ) );
+
+			if ( libcfap ) {
+				// Generate the bodies of cfa library functions.
+				LibCfa::makeLibCfa( transUnit );
+			} // if
+
+			if ( declstatsp ) {
+				printDeclStats( transUnit );
+				return EXIT_SUCCESS;
+			} // if
+
+			if ( bresolvep ) {
+				dump( move( transUnit ) );
+				return EXIT_SUCCESS;
+			} // if
+
+			if ( resolvprotop ) {
+				dumpAsResolverProto( transUnit );
+				return EXIT_SUCCESS;
+			} // if
+
 			PASS( "Resolve", ResolvExpr::resolve( transUnit ) );
 			if ( exprp ) {
@@ -385,4 +391,49 @@
 			translationUnit = convert( move( transUnit ) );
 		} else {
+			if ( symtabp ) {
+				deleteAll( translationUnit );
+				return EXIT_SUCCESS;
+			} // if
+
+			if ( expraltp ) {
+				PassVisitor<ResolvExpr::AlternativePrinter> printer( cout );
+				acceptAll( translationUnit, printer );
+				return EXIT_SUCCESS;
+			} // if
+
+			if ( validp ) {
+				dump( translationUnit );
+				return EXIT_SUCCESS;
+			} // if
+
+			PASS( "Translate Throws", ControlStruct::translateThrows( translationUnit ) );
+			PASS( "Fix Labels", ControlStruct::fixLabels( translationUnit ) );
+			PASS( "Fix Names", CodeGen::fixNames( translationUnit ) );
+			PASS( "Gen Init", InitTweak::genInit( translationUnit ) );
+			PASS( "Expand Member Tuples" , Tuples::expandMemberTuples( translationUnit ) );
+
+			if ( libcfap ) {
+				// Generate the bodies of cfa library functions.
+				LibCfa::makeLibCfa( translationUnit );
+			} // if
+
+			if ( declstatsp ) {
+				CodeTools::printDeclStats( translationUnit );
+				deleteAll( translationUnit );
+				return EXIT_SUCCESS;
+			} // if
+
+			if ( bresolvep ) {
+				dump( translationUnit );
+				return EXIT_SUCCESS;
+			} // if
+
+			CodeTools::fillLocations( translationUnit );
+
+			if ( resolvprotop ) {
+				CodeTools::dumpAsResolvProto( translationUnit );
+				return EXIT_SUCCESS;
+			} // if
+
 			PASS( "Resolve", ResolvExpr::resolve( translationUnit ) );
 			if ( exprp ) {
@@ -447,5 +498,6 @@
 		PASS( "Code Gen", CodeGen::generate( translationUnit, *output, ! genproto, prettycodegenp, true, linemarks ) );
 
-		CodeGen::FixMain::fix( *output, (PreludeDirector + "/bootloader.c").c_str() );
+		CodeGen::FixMain::fix( translationUnit, *output,
+				(PreludeDirector + "/bootloader.c").c_str() );
 		if ( output != &cout ) {
 			delete output;
