Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Decl.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,6 +20,6 @@
 #include <unordered_map>
 
-#include "Common/Eval.h"       // for eval
-#include "Common/SemanticError.h"
+#include "Common/Eval.hpp"     // for eval
+#include "Common/SemanticError.hpp"
 
 #include "Fwd.hpp"             // for UniqueId
Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Expr.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -27,9 +27,9 @@
 #include "Type.hpp"
 #include "TypeSubstitution.hpp"
-#include "Common/utility.h"
-#include "Common/SemanticError.h"
-#include "GenPoly/Lvalue.h"        // for referencesPermissable
-#include "ResolvExpr/Unify.h"      // for extractResultType
-#include "Tuples/Tuples.h"         // for makeTupleType
+#include "Common/Utility.hpp"
+#include "Common/SemanticError.hpp"
+#include "GenPoly/Lvalue.hpp"      // for referencesPermissable
+#include "ResolvExpr/Unify.hpp"    // for extractResultType
+#include "Tuples/Tuples.hpp"       // for makeTupleType
 
 namespace ast {
Index: src/AST/Inspect.cpp
===================================================================
--- src/AST/Inspect.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Inspect.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -24,5 +24,5 @@
 #include "AST/Stmt.hpp"
 #include "AST/Type.hpp"
-#include "CodeGen/OperatorTable.h"
+#include "CodeGen/OperatorTable.hpp"
 
 namespace ast {
Index: src/AST/Label.hpp
===================================================================
--- src/AST/Label.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Label.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -21,5 +21,5 @@
 
 #include "Node.hpp"
-#include "Common/CodeLocation.h"
+#include "Common/CodeLocation.hpp"
 
 namespace ast {
Index: src/AST/LinkageSpec.cpp
===================================================================
--- src/AST/LinkageSpec.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/LinkageSpec.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,6 +20,6 @@
 #include <string>
 
-#include "Common/CodeLocation.h"
-#include "Common/SemanticError.h"
+#include "Common/CodeLocation.hpp"
+#include "Common/SemanticError.hpp"
 
 namespace ast {
Index: src/AST/LinkageSpec.hpp
===================================================================
--- src/AST/LinkageSpec.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/LinkageSpec.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -19,5 +19,5 @@
 
 #include "Bitfield.hpp"
-#include "Common/CodeLocation.h"
+#include "Common/CodeLocation.hpp"
 
 namespace ast {
Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Node.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,5 +20,5 @@
 #include <iosfwd>
 
-#include "Common/ErrorObjects.h"  // for SemanticErrorException
+#include "Common/ErrorObjects.hpp"  // for SemanticErrorException
 
 namespace ast {
Index: src/AST/ParseNode.hpp
===================================================================
--- src/AST/ParseNode.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/ParseNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -18,5 +18,5 @@
 #include "Node.hpp"
 
-#include "Common/CodeLocation.h"
+#include "Common/CodeLocation.hpp"
 
 namespace ast {
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Pass.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -424,5 +424,5 @@
 }
 
-#include "Common/Stats.h"
+#include "Common/Stats.hpp"
 
 namespace ast {
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Pass.proto.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// Pass.impl.hpp --
+// Pass.proto.hpp --
 //
 // Author           : Thierry Delisle
@@ -18,6 +18,6 @@
 
 #include "Common/Iterate.hpp"
-#include "Common/Stats/Heap.h"
-#include "Common/utility.h"
+#include "Common/Stats/Heap.hpp"
+#include "Common/Utility.hpp"
 namespace ast {
 	template<typename core_t> class Pass;
Index: src/AST/Print.hpp
===================================================================
--- src/AST/Print.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Print.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -19,5 +19,5 @@
 
 #include "AST/Fwd.hpp"
-#include "Common/Indenter.h"
+#include "Common/Indenter.hpp"
 
 namespace ast {
Index: src/AST/Stmt.hpp
===================================================================
--- src/AST/Stmt.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Stmt.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -24,5 +24,5 @@
 #include "ParseNode.hpp"
 #include "Visitor.hpp"
-#include "Common/CodeLocation.h"
+#include "Common/CodeLocation.hpp"
 
 // Must be included in *all* AST classes; should be #undef'd at the end of the file
Index: src/AST/SymbolTable.cpp
===================================================================
--- src/AST/SymbolTable.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/SymbolTable.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -23,13 +23,13 @@
 #include "Inspect.hpp"
 #include "Type.hpp"
-#include "CodeGen/OperatorTable.h"         // for isCtorDtorAssign
-#include "Common/SemanticError.h"
-#include "Common/Stats/Counter.h"
-#include "GenPoly/GenPoly.h"
-#include "InitTweak/InitTweak.h"
-#include "ResolvExpr/Cost.h"
+#include "CodeGen/OperatorTable.hpp"       // for isCtorDtorAssign
+#include "Common/SemanticError.hpp"
+#include "Common/Stats/Counter.hpp"
+#include "GenPoly/GenPoly.hpp"
+#include "InitTweak/InitTweak.hpp"
+#include "ResolvExpr/Cost.hpp"
 #include "ResolvExpr/CandidateFinder.hpp"  // for referenceToRvalueConversion
-#include "ResolvExpr/Unify.h"
-#include "SymTab/Mangler.h"
+#include "ResolvExpr/Unify.hpp"
+#include "SymTab/Mangler.hpp"
 
 namespace ast {
Index: src/AST/SymbolTable.hpp
===================================================================
--- src/AST/SymbolTable.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/SymbolTable.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -21,6 +21,6 @@
 #include "Fwd.hpp"
 #include "Node.hpp"                // for ptr, readonly
-#include "Common/CodeLocation.h"
-#include "Common/PersistentMap.h"
+#include "Common/CodeLocation.hpp"
+#include "Common/PersistentMap.hpp"
 
 namespace ResolvExpr {
Index: src/AST/Type.cpp
===================================================================
--- src/AST/Type.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Type.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -23,6 +23,6 @@
 #include "Init.hpp"
 #include "Inspect.hpp"
-#include "Common/utility.h"      // for copy, move
-#include "Tuples/Tuples.h"       // for isTtype
+#include "Common/Utility.hpp"    // for copy, move
+#include "Tuples/Tuples.hpp"     // for isTtype
 
 namespace ast {
Index: src/AST/TypeEnvironment.cpp
===================================================================
--- src/AST/TypeEnvironment.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/TypeEnvironment.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -29,9 +29,9 @@
 #include "Print.hpp"
 #include "Type.hpp"
-#include "Common/Indenter.h"
-#include "ResolvExpr/typeops.h"    // for occurs
-#include "ResolvExpr/WidenMode.h"
-#include "ResolvExpr/Unify.h"      // for unifyInexact
-#include "Tuples/Tuples.h"         // for isTtype
+#include "Common/Indenter.hpp"
+#include "ResolvExpr/Typeops.hpp"    // for occurs
+#include "ResolvExpr/WidenMode.hpp"
+#include "ResolvExpr/Unify.hpp"      // for unifyInexact
+#include "Tuples/Tuples.hpp"         // for isTtype
 #include "CompilationState.hpp"
 
Index: src/AST/TypeEnvironment.hpp
===================================================================
--- src/AST/TypeEnvironment.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/TypeEnvironment.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -28,6 +28,6 @@
 #include "Type.hpp"
 #include "TypeSubstitution.hpp"
-#include "Common/Indenter.h"
-#include "ResolvExpr/WidenMode.h"
+#include "Common/Indenter.hpp"
+#include "ResolvExpr/WidenMode.hpp"
 
 namespace ast {
Index: src/AST/TypeSubstitution.hpp
===================================================================
--- src/AST/TypeSubstitution.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/TypeSubstitution.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// TypeSubstitution.h --
+// TypeSubstitution.hpp --
 //
 // Author           : Richard C. Bilson
@@ -16,15 +16,15 @@
 #pragma once
 
-#include <cassert>                 // for assert
-#include <list>                    // for list<>::iterator, _List_iterator
+#include <cassert>                   // for assert
+#include <list>                      // for list<>::iterator, _List_iterator
 #include <unordered_map>
 #include <unordered_set>
-#include <string>                  // for string, operator!=
-#include <utility>                 // for pair
+#include <string>                    // for string, operator!=
+#include <utility>                   // for pair
 
-#include "Fwd.hpp"        // for UniqueId
+#include "Fwd.hpp"                   // for UniqueId
 #include "ParseNode.hpp"
 #include "Type.hpp"
-#include "Common/SemanticError.h"  // for SemanticError
+#include "Common/SemanticError.hpp"  // for SemanticError
 #include "Visitor.hpp"
 #include "Decl.hpp"
Index: src/AST/Util.cpp
===================================================================
--- src/AST/Util.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/AST/Util.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,6 +20,6 @@
 #include "Pass.hpp"
 #include "TranslationUnit.hpp"
-#include "Common/utility.h"
-#include "GenPoly/ScopedSet.h"
+#include "Common/Utility.hpp"
+#include "GenPoly/ScopedSet.hpp"
 
 #include <vector>
Index: src/BasicTypes-gen.cpp
===================================================================
--- src/BasicTypes-gen.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/BasicTypes-gen.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -326,5 +326,5 @@
 
 
-	#define ConversionCost TOP_SRCDIR "src/ResolvExpr/ConversionCost.cc"
+	#define ConversionCost TOP_SRCDIR "src/ResolvExpr/ConversionCost.cpp"
 	resetInput( file, ConversionCost, buffer, code, str );
 
@@ -405,5 +405,5 @@
 
 
-	#define CommonType TOP_SRCDIR "src/ResolvExpr/CommonType.cc"
+	#define CommonType TOP_SRCDIR "src/ResolvExpr/CommonType.cpp"
 	resetInput( file, CommonType, buffer, code, str );
 
@@ -446,5 +446,5 @@
 
 
-	#define ManglerCommon TOP_SRCDIR "src/SymTab/ManglerCommon.cc"
+	#define ManglerCommon TOP_SRCDIR "src/SymTab/ManglerCommon.cpp"
 	resetInput( file, ManglerCommon, buffer, code, str );
 
Index: src/CodeGen/CodeGenerator.cpp
===================================================================
--- src/CodeGen/CodeGenerator.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/CodeGen/CodeGenerator.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -17,8 +17,8 @@
 
 #include "AST/Print.hpp"
-#include "OperatorTable.h"           // for OperatorInfo, operatorLookup
-#include "CodeGen/GenType.h"         // for genType
+#include "OperatorTable.hpp"         // for OperatorInfo, operatorLookup
+#include "CodeGen/GenType.hpp"       // for genType
 #include "Common/ToString.hpp"       // for toString
-#include "Common/UniqueName.h"       // for UniqueName
+#include "Common/UniqueName.hpp"     // for UniqueName
 
 namespace CodeGen {
Index: src/CodeGen/CodeGenerator.hpp
===================================================================
--- src/CodeGen/CodeGenerator.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/CodeGen/CodeGenerator.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,6 +20,6 @@
 #include "AST/Fwd.hpp"
 #include "AST/Pass.hpp"          // for WithGuards, WithShortCircuiting, ...
-#include "CodeGen/Options.h"     // for Options
-#include "Common/Indenter.h"     // for Indenter
+#include "CodeGen/Options.hpp"   // for Options
+#include "Common/Indenter.hpp"   // for Indenter
 
 
Index: src/CodeGen/FixMain.cc
===================================================================
--- src/CodeGen/FixMain.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,162 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FixMain.cc -- Tools to change a Cforall main into a C main.
-//
-// Author           : Thierry Delisle
-// Created On       : Thr Jan 12 14:11:09 2017
-// Last Modified By :
-// Last Modified On :
-// Update Count     : 1
-//
-
-#include "FixMain.h"
-
-#include <cassert>                 // for assert, assertf
-#include <fstream>                 // for operator<<, basic_ostream::operator<<
-#include <list>                    // for list
-#include <string>                  // for operator<<
-
-#include "AST/Decl.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-#include "AST/Vector.hpp"
-#include "Common/SemanticError.h"  // for SemanticError
-#include "CodeGen/GenType.h"       // for GenType
-#include "SymTab/Mangler.h"
-
-namespace CodeGen {
-
-namespace {
-
-struct FindMainCore final {
-	ast::FunctionDecl const * main_declaration = nullptr;
-
-	void previsit( ast::FunctionDecl const * decl ) {
-		if ( isMain( decl ) ) {
-			if ( main_declaration ) {
-				SemanticError( decl, "Multiple definition of main routine" );
-			}
-			main_declaration = decl;
-		}
-	}
-};
-
-std::string genTypeAt( const ast::vector<ast::Type> & types, size_t at ) {
-	return genType( types[at], "", Options( false, false, false, false ) );
-}
-
-ast::ObjectDecl * makeIntObj(){
-	return new ast::ObjectDecl( CodeLocation(), "",
-		new ast::BasicType( ast::BasicKind::SignedInt ) );
-}
-
-ast::ObjectDecl * makeCharStarStarObj() {
-	return new ast::ObjectDecl( CodeLocation(), "",
-		new ast::PointerType(
-			new ast::PointerType(
-				new ast::BasicType( ast::BasicKind::Char ) ) ) );
-}
-
-std::string getMangledNameOfMain(
-		ast::vector<ast::DeclWithType> && params, ast::ArgumentFlag isVarArgs ) {
-	ast::ptr<ast::FunctionDecl> decl = new ast::FunctionDecl(
-		CodeLocation(),
-		"main",
-		ast::vector<ast::TypeDecl>(),
-		ast::vector<ast::DeclWithType>(),
-		std::move( params ),
-		{ makeIntObj() },
-		nullptr,
-		ast::Storage::Classes(),
-		ast::Linkage::Spec(),
-		ast::vector<ast::Attribute>(),
-		ast::Function::Specs(),
-		isVarArgs
-	);
-	return Mangle::mangle( decl.get() );
-}
-
-std::string getMangledNameOf0ParameterMain() {
-	return getMangledNameOfMain( {}, ast::VariableArgs );
-}
-
-std::string getMangledNameOf2ParameterMain() {
-	return getMangledNameOfMain( {
-		makeIntObj(),
-		makeCharStarStarObj(),
-	}, ast::FixedArgs );
-}
-
-bool is_main( const std::string & mangled_name ) {
-	// This breaks if you move it out of the function.
-	static const std::string mangled_mains[] = {
-		getMangledNameOf0ParameterMain(),
-		getMangledNameOf2ParameterMain(),
-		//getMangledNameOf3ParameterMain(),
-	};
-
-	for ( auto main_name : mangled_mains ) {
-		if ( main_name == mangled_name ) return true;
-	}
-	return false;
-}
-
-struct FixLinkageCore final {
-	ast::Linkage::Spec const spec;
-	FixLinkageCore( ast::Linkage::Spec spec ) : spec( spec ) {}
-
-	ast::FunctionDecl const * previsit( ast::FunctionDecl const * decl ) {
-		if ( decl->name != "main" ) return decl;
-		return ast::mutate_field( decl, &ast::FunctionDecl::linkage, spec );
-	}
-};
-
-} // namespace
-
-bool isMain( const ast::FunctionDecl * decl ) {
-	if ( std::string("main") != decl->name ) {
-		return false;
-	}
-	return is_main( Mangle::mangle( decl, Mangle::Type ) );
-}
-
-void fixMainLinkage( ast::TranslationUnit & translationUnit,
-		bool replace_main ) {
-	ast::Linkage::Spec const spec =
-		( replace_main ) ? ast::Linkage::Cforall : ast::Linkage::C;
-	ast::Pass<FixLinkageCore>::run( translationUnit, spec );
-}
-
-void fixMainInvoke( ast::TranslationUnit & translationUnit,
-		std::ostream &os, const char * bootloader_filename ) {
-
-	ast::Pass<FindMainCore> main_finder;
-	ast::accept_all( translationUnit, main_finder );
-	if ( nullptr == main_finder.core.main_declaration ) return;
-
-	ast::FunctionDecl * main_declaration =
-		ast::mutate( main_finder.core.main_declaration );
-
-	main_declaration->mangleName = Mangle::mangle( main_declaration );
-
-	os << "static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return ";
-	os << main_declaration->scopedMangleName() << "(";
-	const auto& params = main_declaration->type->params;
-	switch ( params.size() ) {
-		case 3: os << "(" << genTypeAt(params, 0) << ")argc, (" << genTypeAt(params, 1) << ")argv, (" << genTypeAt(params, 2) << ")envp"; break;
-		case 2: os << "(" << genTypeAt(params, 0) << ")argc, (" << genTypeAt(params, 1) << ")argv"; break;
-		case 0: break;
-		default : assert(false);
-	}
-	os << "); }\n";
-
-	std::ifstream bootloader( bootloader_filename, std::ios::in );
-	assertf( bootloader.is_open(), "cannot open bootloader.c\n" );
-	os << bootloader.rdbuf();
-}
-
-} // namespace CodeGen
Index: src/CodeGen/FixMain.cpp
===================================================================
--- src/CodeGen/FixMain.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/FixMain.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,162 @@
+//
+// 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.
+//
+// FixMain.cpp -- Tools to change a Cforall main into a C main.
+//
+// Author           : Thierry Delisle
+// Created On       : Thr Jan 12 14:11:09 2017
+// Last Modified By :
+// Last Modified On :
+// Update Count     : 1
+//
+
+#include "FixMain.hpp"
+
+#include <cassert>                   // for assert, assertf
+#include <fstream>                   // for operator<<, basic_ostream::oper...
+#include <list>                      // for list
+#include <string>                    // for operator<<
+
+#include "AST/Decl.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+#include "AST/Vector.hpp"
+#include "Common/SemanticError.hpp"  // for SemanticError
+#include "CodeGen/GenType.hpp"       // for GenType
+#include "SymTab/Mangler.hpp"
+
+namespace CodeGen {
+
+namespace {
+
+struct FindMainCore final {
+	ast::FunctionDecl const * main_declaration = nullptr;
+
+	void previsit( ast::FunctionDecl const * decl ) {
+		if ( isMain( decl ) ) {
+			if ( main_declaration ) {
+				SemanticError( decl, "Multiple definition of main routine" );
+			}
+			main_declaration = decl;
+		}
+	}
+};
+
+std::string genTypeAt( const ast::vector<ast::Type> & types, size_t at ) {
+	return genType( types[at], "", Options( false, false, false, false ) );
+}
+
+ast::ObjectDecl * makeIntObj(){
+	return new ast::ObjectDecl( CodeLocation(), "",
+		new ast::BasicType( ast::BasicKind::SignedInt ) );
+}
+
+ast::ObjectDecl * makeCharStarStarObj() {
+	return new ast::ObjectDecl( CodeLocation(), "",
+		new ast::PointerType(
+			new ast::PointerType(
+				new ast::BasicType( ast::BasicKind::Char ) ) ) );
+}
+
+std::string getMangledNameOfMain(
+		ast::vector<ast::DeclWithType> && params, ast::ArgumentFlag isVarArgs ) {
+	ast::ptr<ast::FunctionDecl> decl = new ast::FunctionDecl(
+		CodeLocation(),
+		"main",
+		ast::vector<ast::TypeDecl>(),
+		ast::vector<ast::DeclWithType>(),
+		std::move( params ),
+		{ makeIntObj() },
+		nullptr,
+		ast::Storage::Classes(),
+		ast::Linkage::Spec(),
+		ast::vector<ast::Attribute>(),
+		ast::Function::Specs(),
+		isVarArgs
+	);
+	return Mangle::mangle( decl.get() );
+}
+
+std::string getMangledNameOf0ParameterMain() {
+	return getMangledNameOfMain( {}, ast::VariableArgs );
+}
+
+std::string getMangledNameOf2ParameterMain() {
+	return getMangledNameOfMain( {
+		makeIntObj(),
+		makeCharStarStarObj(),
+	}, ast::FixedArgs );
+}
+
+bool is_main( const std::string & mangled_name ) {
+	// This breaks if you move it out of the function.
+	static const std::string mangled_mains[] = {
+		getMangledNameOf0ParameterMain(),
+		getMangledNameOf2ParameterMain(),
+		//getMangledNameOf3ParameterMain(),
+	};
+
+	for ( auto main_name : mangled_mains ) {
+		if ( main_name == mangled_name ) return true;
+	}
+	return false;
+}
+
+struct FixLinkageCore final {
+	ast::Linkage::Spec const spec;
+	FixLinkageCore( ast::Linkage::Spec spec ) : spec( spec ) {}
+
+	ast::FunctionDecl const * previsit( ast::FunctionDecl const * decl ) {
+		if ( decl->name != "main" ) return decl;
+		return ast::mutate_field( decl, &ast::FunctionDecl::linkage, spec );
+	}
+};
+
+} // namespace
+
+bool isMain( const ast::FunctionDecl * decl ) {
+	if ( std::string("main") != decl->name ) {
+		return false;
+	}
+	return is_main( Mangle::mangle( decl, Mangle::Type ) );
+}
+
+void fixMainLinkage( ast::TranslationUnit & translationUnit,
+		bool replace_main ) {
+	ast::Linkage::Spec const spec =
+		( replace_main ) ? ast::Linkage::Cforall : ast::Linkage::C;
+	ast::Pass<FixLinkageCore>::run( translationUnit, spec );
+}
+
+void fixMainInvoke( ast::TranslationUnit & translationUnit,
+		std::ostream &os, const char * bootloader_filename ) {
+
+	ast::Pass<FindMainCore> main_finder;
+	ast::accept_all( translationUnit, main_finder );
+	if ( nullptr == main_finder.core.main_declaration ) return;
+
+	ast::FunctionDecl * main_declaration =
+		ast::mutate( main_finder.core.main_declaration );
+
+	main_declaration->mangleName = Mangle::mangle( main_declaration );
+
+	os << "static inline int invoke_main(int argc, char* argv[], char* envp[]) { (void)argc; (void)argv; (void)envp; return ";
+	os << main_declaration->scopedMangleName() << "(";
+	const auto& params = main_declaration->type->params;
+	switch ( params.size() ) {
+		case 3: os << "(" << genTypeAt(params, 0) << ")argc, (" << genTypeAt(params, 1) << ")argv, (" << genTypeAt(params, 2) << ")envp"; break;
+		case 2: os << "(" << genTypeAt(params, 0) << ")argc, (" << genTypeAt(params, 1) << ")argv"; break;
+		case 0: break;
+		default : assert(false);
+	}
+	os << "); }\n";
+
+	std::ifstream bootloader( bootloader_filename, std::ios::in );
+	assertf( bootloader.is_open(), "cannot open bootloader.c\n" );
+	os << bootloader.rdbuf();
+}
+
+} // namespace CodeGen
Index: src/CodeGen/FixMain.h
===================================================================
--- src/CodeGen/FixMain.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,37 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FixMain.h -- Tools to change a Cforall main into a C main.
-//
-// Author           : Thierry Delisle
-// Created On       : Thr Jan 12 14:11:09 2017
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Oct 29 16:20:00 2021
-// Update Count     : 8
-//
-
-#pragma once
-
-#include <iosfwd>
-
-namespace ast {
-	class FunctionDecl;
-	class TranslationUnit;
-}
-
-namespace CodeGen {
-
-/// Is this function a program main function?
-bool isMain( const ast::FunctionDecl * decl );
-
-/// Adjust the linkage of main functions.
-void fixMainLinkage( ast::TranslationUnit & transUnit, bool replaceMain );
-
-/// Add a wrapper around to run the Cforall main.
-void fixMainInvoke( ast::TranslationUnit & transUnit,
-		std::ostream & os, const char * bootloaderFilename );
-
-} // namespace CodeGen
Index: src/CodeGen/FixMain.hpp
===================================================================
--- src/CodeGen/FixMain.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/FixMain.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,37 @@
+//
+// 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.
+//
+// FixMain.hpp -- Tools to change a Cforall main into a C main.
+//
+// Author           : Thierry Delisle
+// Created On       : Thr Jan 12 14:11:09 2017
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Oct 29 16:20:00 2021
+// Update Count     : 8
+//
+
+#pragma once
+
+#include <iosfwd>
+
+namespace ast {
+	class FunctionDecl;
+	class TranslationUnit;
+}
+
+namespace CodeGen {
+
+/// Is this function a program main function?
+bool isMain( const ast::FunctionDecl * decl );
+
+/// Adjust the linkage of main functions.
+void fixMainLinkage( ast::TranslationUnit & transUnit, bool replaceMain );
+
+/// Add a wrapper around to run the Cforall main.
+void fixMainInvoke( ast::TranslationUnit & transUnit,
+		std::ostream & os, const char * bootloaderFilename );
+
+} // namespace CodeGen
Index: src/CodeGen/FixNames.cc
===================================================================
--- src/CodeGen/FixNames.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,97 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FixNames.cc -- Adjustments to typed declarations.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Thu Dec 14 16:16:51 2023
-// Update Count     : 25
-//
-
-#include "FixNames.h"
-
-#include <memory>                  // for unique_ptr
-#include <string>                  // for string, operator!=, operator==
-
-#include "AST/Chain.hpp"
-#include "AST/Expr.hpp"
-#include "AST/Pass.hpp"
-#include "Common/SemanticError.h"  // for SemanticError
-#include "FixMain.h"               // for FixMain
-#include "SymTab/Mangler.h"        // for Mangler
-#include "CompilationState.hpp"
-
-namespace CodeGen {
-
-namespace {
-
-/// Does work with the main function and scopeLevels.
-class FixNames final {
-	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 ) {
-		if ( isMain( functionDecl ) ) {
-			auto mutDecl = ast::mutate( functionDecl );
-
-			if ( shouldSetScopeLevel( mutDecl ) ) {
-				mutDecl->scopeLevel = scopeLevel;
-			}
-
-			int nargs = mutDecl->params.size();
-			if ( 0 != nargs && 2 != nargs && 3 != nargs ) {
-				SemanticError( functionDecl, "Main expected to have 0, 2 or 3 arguments" );
-			}
-			ast::chain_mutate( mutDecl->stmts )->kids.push_back(
-				new ast::ReturnStmt(
-					mutDecl->location,
-					ast::ConstantExpr::from_int( mutDecl->location, 0 )
-				)
-			);
-
-			return mutDecl;
-		} else if ( shouldSetScopeLevel( functionDecl ) ) {
-			return ast::mutate_field( functionDecl, &ast::FunctionDecl::scopeLevel, scopeLevel );
-		} else {
-			return functionDecl;
-		}
-	}
-
-	void previsit( const ast::CompoundStmt * ) {
-		scopeLevel += 1;
-	}
-
-	void postvisit( const ast::CompoundStmt * ) {
-		scopeLevel -= 1;
-	}
-};
-
-} // namespace
-
-void fixNames( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<FixNames>::run( translationUnit );
-}
-
-} // namespace CodeGen
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/CodeGen/FixNames.cpp
===================================================================
--- src/CodeGen/FixNames.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/FixNames.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,97 @@
+//
+// 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.
+//
+// FixNames.cpp -- Adjustments to typed declarations.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Thu Dec 14 16:16:51 2023
+// Update Count     : 25
+//
+
+#include "FixNames.hpp"
+
+#include <memory>                    // for unique_ptr
+#include <string>                    // for string, operator!=, operator==
+
+#include "AST/Chain.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
+#include "Common/SemanticError.hpp"  // for SemanticError
+#include "FixMain.hpp"               // for FixMain
+#include "SymTab/Mangler.hpp"        // for Mangler
+#include "CompilationState.hpp"
+
+namespace CodeGen {
+
+namespace {
+
+/// Does work with the main function and scopeLevels.
+class FixNames final {
+	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 ) {
+		if ( isMain( functionDecl ) ) {
+			auto mutDecl = ast::mutate( functionDecl );
+
+			if ( shouldSetScopeLevel( mutDecl ) ) {
+				mutDecl->scopeLevel = scopeLevel;
+			}
+
+			int nargs = mutDecl->params.size();
+			if ( 0 != nargs && 2 != nargs && 3 != nargs ) {
+				SemanticError( functionDecl, "Main expected to have 0, 2 or 3 arguments" );
+			}
+			ast::chain_mutate( mutDecl->stmts )->kids.push_back(
+				new ast::ReturnStmt(
+					mutDecl->location,
+					ast::ConstantExpr::from_int( mutDecl->location, 0 )
+				)
+			);
+
+			return mutDecl;
+		} else if ( shouldSetScopeLevel( functionDecl ) ) {
+			return ast::mutate_field( functionDecl, &ast::FunctionDecl::scopeLevel, scopeLevel );
+		} else {
+			return functionDecl;
+		}
+	}
+
+	void previsit( const ast::CompoundStmt * ) {
+		scopeLevel += 1;
+	}
+
+	void postvisit( const ast::CompoundStmt * ) {
+		scopeLevel -= 1;
+	}
+};
+
+} // namespace
+
+void fixNames( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<FixNames>::run( translationUnit );
+}
+
+} // namespace CodeGen
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/CodeGen/FixNames.h
===================================================================
--- src/CodeGen/FixNames.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,33 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FixNames.h -- Adjustments to typed declarations.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Oct 26 13:47:00 2021
-// Update Count     : 4
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace CodeGen {
-
-/// Sets scope levels and fills in main's default return.
-void fixNames( ast::TranslationUnit & translationUnit );
-
-} // namespace CodeGen
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/CodeGen/FixNames.hpp
===================================================================
--- src/CodeGen/FixNames.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/FixNames.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -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.
+//
+// FixNames.hpp -- Adjustments to typed declarations.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Oct 26 13:47:00 2021
+// Update Count     : 4
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace CodeGen {
+
+/// Sets scope levels and fills in main's default return.
+void fixNames( ast::TranslationUnit & translationUnit );
+
+} // namespace CodeGen
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/CodeGen/GenType.cc
===================================================================
--- src/CodeGen/GenType.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,369 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// GenType.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri May 20 11:18:00 2022
-// Update Count     : 24
-//
-#include "GenType.h"
-
-#include <cassert>                // for assert, assertf
-#include <list>                   // for _List_iterator, _List_const_iterator
-#include <sstream>                // for operator<<, ostringstream, basic_os...
-
-#include "AST/Print.hpp"          // for print
-#include "AST/Vector.hpp"         // for vector
-#include "CodeGenerator.hpp"      // for CodeGenerator
-#include "Common/UniqueName.h"    // for UniqueName
-
-namespace CodeGen {
-
-namespace {
-
-struct GenType final :
-		public ast::WithShortCircuiting,
-		public ast::WithVisitorRef<GenType> {
-	std::string result;
-	GenType( const std::string &typeString, const Options &options );
-
-	void previsit( ast::Node const * );
-	void postvisit( ast::Node const * );
-
-	void postvisit( ast::FunctionType const * type );
-	void postvisit( ast::VoidType const * type );
-	void postvisit( ast::BasicType const * type );
-	void postvisit( ast::PointerType const * type );
-	void postvisit( ast::ArrayType const * type );
-	void postvisit( ast::ReferenceType const * type );
-	void postvisit( ast::StructInstType const * type );
-	void postvisit( ast::UnionInstType const * type );
-	void postvisit( ast::EnumInstType const * type );
-	void postvisit( ast::EnumAttrType const * type );
-	void postvisit( ast::TypeInstType const * type );
-	void postvisit( ast::TupleType const * type );
-	void postvisit( ast::VarArgsType const * type );
-	void postvisit( ast::ZeroType const * type );
-	void postvisit( ast::OneType const * type );
-	void postvisit( ast::GlobalScopeType const * type );
-	void postvisit( ast::TraitInstType const * type );
-	void postvisit( ast::TypeofType const * type );
-	void postvisit( ast::VTableType const * type );
-	void postvisit( ast::QualifiedType const * type );
-
-private:
-	void handleQualifiers( ast::Type const *type );
-	std::string handleGeneric( ast::BaseInstType const * type );
-	void genArray( const ast::CV::Qualifiers &qualifiers, ast::Type const *base, ast::Expr const *dimension, bool isVarLen, bool isStatic );
-	std::string genParamList( const ast::vector<ast::Type> & );
-
-	Options options;
-};
-
-GenType::GenType( const std::string &typeString, const Options &options ) : result( typeString ), options( options ) {}
-
-void GenType::previsit( ast::Node const * ) {
-	// Turn off automatic recursion for all nodes, to allow each visitor to
-	// precisely control the order in which its children are visited.
-	visit_children = false;
-}
-
-void GenType::postvisit( ast::Node const * node ) {
-	std::stringstream ss;
-	ast::print( ss, node );
-	assertf( false, "Unhandled node reached in GenType: %s", ss.str().c_str() );
-}
-
-void GenType::postvisit( ast::VoidType const * type ) {
-	result = "void " + result;
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::BasicType const * type ) {
-	ast::BasicKind kind = type->kind;
-	assert( 0 <= kind && kind < ast::BasicKind::NUMBER_OF_BASIC_TYPES );
-	result = std::string( ast::BasicType::typeNames[kind] ) + " " + result;
-	handleQualifiers( type );
-}
-
-void GenType::genArray( const ast::CV::Qualifiers & qualifiers, ast::Type const * base, ast::Expr const *dimension, bool isVarLen, bool isStatic ) {
-	std::ostringstream os;
-	if ( result != "" ) {
-		if ( result[ 0 ] == '*' ) {
-			os << "(" << result << ")";
-		} else {
-			os << result;
-		}
-	}
-	os << "[";
-	if ( isStatic ) {
-		os << "static ";
-	}
-	if ( qualifiers.is_const ) {
-		os << "const ";
-	}
-	if ( qualifiers.is_volatile ) {
-		os << "volatile ";
-	}
-	if ( qualifiers.is_restrict ) {
-		os << "__restrict ";
-	}
-	if ( qualifiers.is_atomic ) {
-		os << "_Atomic ";
-	}
-	if ( dimension != 0 ) {
-		ast::Pass<CodeGenerator>::read( dimension, os, options );
-	} else if ( isVarLen ) {
-		// no dimension expression on a VLA means it came in with the * token
-		os << "*";
-	}
-	os << "]";
-
-	result = os.str();
-
-	base->accept( *visitor );
-}
-
-void GenType::postvisit( ast::PointerType const * type ) {
-	if ( type->isStatic || type->isVarLen || type->dimension ) {
-		genArray( type->qualifiers, type->base, type->dimension, type->isVarLen, type->isStatic );
-	} else {
-		handleQualifiers( type );
-		if ( result[ 0 ] == '?' ) {
-			result = "* " + result;
-		} else {
-			result = "*" + result;
-		}
-		type->base->accept( *visitor );
-	}
-}
-
-void GenType::postvisit( ast::ArrayType const * type ) {
-	genArray( type->qualifiers, type->base, type->dimension, type->isVarLen, type->isStatic );
-}
-
-void GenType::postvisit( ast::ReferenceType const * type ) {
-	assertf( !options.genC, "Reference types should not reach code generation." );
-	handleQualifiers( type );
-	result = "&" + result;
-	type->base->accept( *visitor );
-}
-
-void GenType::postvisit( ast::FunctionType const * type ) {
-	std::ostringstream os;
-
-	if ( result != "" ) {
-		if ( result[ 0 ] == '*' ) {
-			os << "(" << result << ")";
-		} else {
-			os << result;
-		}
-	}
-
-	if ( type->params.empty() ) {
-		if ( type->isVarArgs ) {
-			os << "()";
-		} else {
-			os << "(void)";
-		}
-	} else {
-		os << "(" ;
-
-		os << genParamList( type->params );
-
-		if ( type->isVarArgs ) {
-			os << ", ...";
-		}
-		os << ")";
-	}
-
-	result = os.str();
-
-	if ( type->returns.size() == 0 ) {
-		result = "void " + result;
-	} else {
-		type->returns.front()->accept( *visitor );
-	}
-
-	// Add forall clause.
-	if( !type->forall.empty() && !options.genC ) {
-		//assertf( !options.genC, "FunctionDecl type parameters should not reach code generation." );
-		std::ostringstream os;
-		ast::Pass<CodeGenerator> cg( os, options );
-		os << "forall(";
-		cg.core.genCommaList( type->forall );
-		os << ")" << std::endl;
-		result = os.str() + result;
-	}
-}
-
-std::string GenType::handleGeneric( ast::BaseInstType const * type ) {
-	if ( !type->params.empty() ) {
-		std::ostringstream os;
-		ast::Pass<CodeGenerator> cg( os, options );
-		os << "(";
-		cg.core.genCommaList( type->params );
-		os << ") ";
-		return os.str();
-	}
-	return "";
-}
-
-void GenType::postvisit( ast::StructInstType const * type )  {
-	result = type->name + handleGeneric( type ) + " " + result;
-	if ( options.genC ) result = "struct " + result;
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::UnionInstType const * type ) {
-	result = type->name + handleGeneric( type ) + " " + result;
-	if ( options.genC ) result = "union " + result;
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::EnumInstType const * type ) {
-	// if ( type->base && type->base->base ) {
-	// 	result = genType( type->base->base, result, options );
-	// } else {
-		result = type->name + " " + result;
-		if ( options.genC ) {
-			result = "enum " + result;
-		}
-	// }
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::EnumAttrType const * type ) {
-	postvisit( type->instance );
-}
-
-void GenType::postvisit( ast::TypeInstType const * type ) {
-	assertf( !options.genC, "TypeInstType should not reach code generation." );
-	result = type->name + " " + result;
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::TupleType const * type ) {
-	assertf( !options.genC, "TupleType should not reach code generation." );
-	unsigned int i = 0;
-	std::ostringstream os;
-	os << "[";
-	for ( ast::ptr<ast::Type> const & t : type->types ) {
-		i++;
-		os << genType( t, "", options ) << (i == type->size() ? "" : ", ");
-	}
-	os << "] ";
-	result = os.str() + result;
-}
-
-void GenType::postvisit( ast::VarArgsType const * type ) {
-	result = "__builtin_va_list " + result;
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::ZeroType const * type ) {
-	// Ideally these wouldn't hit codegen at all, but should be safe to make them ints.
-	result = (options.pretty ? "zero_t " : "long int ") + result;
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::OneType const * type ) {
-	// Ideally these wouldn't hit codegen at all, but should be safe to make them ints.
-	result = (options.pretty ? "one_t " : "long int ") + result;
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::GlobalScopeType const * type ) {
-	assertf( !options.genC, "GlobalScopeType should not reach code generation." );
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::TraitInstType const * type ) {
-	assertf( !options.genC, "TraitInstType should not reach code generation." );
-	result = type->name + " " + result;
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::TypeofType const * type ) {
-	std::ostringstream os;
-	os << "typeof(";
-	ast::Pass<CodeGenerator>::read( type->expr.get(), os, options );
-	os << ") " << result;
-	result = os.str();
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::VTableType const * type ) {
-	assertf( !options.genC, "Virtual table types should not reach code generation." );
-	std::ostringstream os;
-	os << "vtable(" << genType( type->base, "", options ) << ") " << result;
-	result = os.str();
-	handleQualifiers( type );
-}
-
-void GenType::postvisit( ast::QualifiedType const * type ) {
-	assertf( !options.genC, "QualifiedType should not reach code generation." );
-	std::ostringstream os;
-	os << genType( type->parent, "", options ) << "." << genType( type->child, "", options ) << result;
-	result = os.str();
-	handleQualifiers( type );
-}
-
-void GenType::handleQualifiers( ast::Type const * type ) {
-	if ( type->is_const() ) {
-		result = "const " + result;
-	}
-	if ( type->is_volatile() ) {
-		result = "volatile " + result;
-	}
-	if ( type->is_restrict() ) {
-		result = "__restrict " + result;
-	}
-	if ( type->is_atomic() ) {
-		result = "_Atomic " + result;
-	}
-}
-
-std::string GenType::genParamList( const ast::vector<ast::Type> & range ) {
-	auto cur = range.begin();
-	auto end = range.end();
-	if ( cur == end ) return "";
-	std::ostringstream oss;
-	UniqueName param( "__param_" );
-	while ( true ) {
-		oss << genType( *cur++, options.genC ? param.newName() : "", options );
-		if ( cur == end ) break;
-		oss << ", ";
-	}
-	return oss.str();
-}
-
-} // namespace
-
-std::string genType( ast::Type const * type, const std::string & base, const Options & options ) {
-	std::ostringstream os;
-	if ( !type->attributes.empty() ) {
-		ast::Pass<CodeGenerator> cg( os, options );
-		cg.core.genAttributes( type->attributes );
-	}
-
-	return os.str() + ast::Pass<GenType>::read( type, base, options );
-}
-
-std::string genTypeNoAttr( ast::Type const * type, const std::string & base, const Options & options ) {
-	return ast::Pass<GenType>::read( type, base, options );
-}
-
-} // namespace CodeGen
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/CodeGen/GenType.cpp
===================================================================
--- src/CodeGen/GenType.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/GenType.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,370 @@
+//
+// 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.
+//
+// GenType.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri May 20 11:18:00 2022
+// Update Count     : 24
+//
+
+#include "GenType.hpp"
+
+#include <cassert>                // for assert, assertf
+#include <list>                   // for _List_iterator, _List_const_iterator
+#include <sstream>                // for operator<<, ostringstream, basic_os...
+
+#include "AST/Print.hpp"          // for print
+#include "AST/Vector.hpp"         // for vector
+#include "CodeGenerator.hpp"      // for CodeGenerator
+#include "Common/UniqueName.hpp"  // for UniqueName
+
+namespace CodeGen {
+
+namespace {
+
+struct GenType final :
+		public ast::WithShortCircuiting,
+		public ast::WithVisitorRef<GenType> {
+	std::string result;
+	GenType( const std::string &typeString, const Options &options );
+
+	void previsit( ast::Node const * );
+	void postvisit( ast::Node const * );
+
+	void postvisit( ast::FunctionType const * type );
+	void postvisit( ast::VoidType const * type );
+	void postvisit( ast::BasicType const * type );
+	void postvisit( ast::PointerType const * type );
+	void postvisit( ast::ArrayType const * type );
+	void postvisit( ast::ReferenceType const * type );
+	void postvisit( ast::StructInstType const * type );
+	void postvisit( ast::UnionInstType const * type );
+	void postvisit( ast::EnumInstType const * type );
+	void postvisit( ast::EnumAttrType const * type );
+	void postvisit( ast::TypeInstType const * type );
+	void postvisit( ast::TupleType const * type );
+	void postvisit( ast::VarArgsType const * type );
+	void postvisit( ast::ZeroType const * type );
+	void postvisit( ast::OneType const * type );
+	void postvisit( ast::GlobalScopeType const * type );
+	void postvisit( ast::TraitInstType const * type );
+	void postvisit( ast::TypeofType const * type );
+	void postvisit( ast::VTableType const * type );
+	void postvisit( ast::QualifiedType const * type );
+
+private:
+	void handleQualifiers( ast::Type const *type );
+	std::string handleGeneric( ast::BaseInstType const * type );
+	void genArray( const ast::CV::Qualifiers &qualifiers, ast::Type const *base, ast::Expr const *dimension, bool isVarLen, bool isStatic );
+	std::string genParamList( const ast::vector<ast::Type> & );
+
+	Options options;
+};
+
+GenType::GenType( const std::string &typeString, const Options &options ) : result( typeString ), options( options ) {}
+
+void GenType::previsit( ast::Node const * ) {
+	// Turn off automatic recursion for all nodes, to allow each visitor to
+	// precisely control the order in which its children are visited.
+	visit_children = false;
+}
+
+void GenType::postvisit( ast::Node const * node ) {
+	std::stringstream ss;
+	ast::print( ss, node );
+	assertf( false, "Unhandled node reached in GenType: %s", ss.str().c_str() );
+}
+
+void GenType::postvisit( ast::VoidType const * type ) {
+	result = "void " + result;
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::BasicType const * type ) {
+	ast::BasicKind kind = type->kind;
+	assert( 0 <= kind && kind < ast::BasicKind::NUMBER_OF_BASIC_TYPES );
+	result = std::string( ast::BasicType::typeNames[kind] ) + " " + result;
+	handleQualifiers( type );
+}
+
+void GenType::genArray( const ast::CV::Qualifiers & qualifiers, ast::Type const * base, ast::Expr const *dimension, bool isVarLen, bool isStatic ) {
+	std::ostringstream os;
+	if ( result != "" ) {
+		if ( result[ 0 ] == '*' ) {
+			os << "(" << result << ")";
+		} else {
+			os << result;
+		}
+	}
+	os << "[";
+	if ( isStatic ) {
+		os << "static ";
+	}
+	if ( qualifiers.is_const ) {
+		os << "const ";
+	}
+	if ( qualifiers.is_volatile ) {
+		os << "volatile ";
+	}
+	if ( qualifiers.is_restrict ) {
+		os << "__restrict ";
+	}
+	if ( qualifiers.is_atomic ) {
+		os << "_Atomic ";
+	}
+	if ( dimension != 0 ) {
+		ast::Pass<CodeGenerator>::read( dimension, os, options );
+	} else if ( isVarLen ) {
+		// no dimension expression on a VLA means it came in with the * token
+		os << "*";
+	}
+	os << "]";
+
+	result = os.str();
+
+	base->accept( *visitor );
+}
+
+void GenType::postvisit( ast::PointerType const * type ) {
+	if ( type->isStatic || type->isVarLen || type->dimension ) {
+		genArray( type->qualifiers, type->base, type->dimension, type->isVarLen, type->isStatic );
+	} else {
+		handleQualifiers( type );
+		if ( result[ 0 ] == '?' ) {
+			result = "* " + result;
+		} else {
+			result = "*" + result;
+		}
+		type->base->accept( *visitor );
+	}
+}
+
+void GenType::postvisit( ast::ArrayType const * type ) {
+	genArray( type->qualifiers, type->base, type->dimension, type->isVarLen, type->isStatic );
+}
+
+void GenType::postvisit( ast::ReferenceType const * type ) {
+	assertf( !options.genC, "Reference types should not reach code generation." );
+	handleQualifiers( type );
+	result = "&" + result;
+	type->base->accept( *visitor );
+}
+
+void GenType::postvisit( ast::FunctionType const * type ) {
+	std::ostringstream os;
+
+	if ( result != "" ) {
+		if ( result[ 0 ] == '*' ) {
+			os << "(" << result << ")";
+		} else {
+			os << result;
+		}
+	}
+
+	if ( type->params.empty() ) {
+		if ( type->isVarArgs ) {
+			os << "()";
+		} else {
+			os << "(void)";
+		}
+	} else {
+		os << "(" ;
+
+		os << genParamList( type->params );
+
+		if ( type->isVarArgs ) {
+			os << ", ...";
+		}
+		os << ")";
+	}
+
+	result = os.str();
+
+	if ( type->returns.size() == 0 ) {
+		result = "void " + result;
+	} else {
+		type->returns.front()->accept( *visitor );
+	}
+
+	// Add forall clause.
+	if( !type->forall.empty() && !options.genC ) {
+		//assertf( !options.genC, "FunctionDecl type parameters should not reach code generation." );
+		std::ostringstream os;
+		ast::Pass<CodeGenerator> cg( os, options );
+		os << "forall(";
+		cg.core.genCommaList( type->forall );
+		os << ")" << std::endl;
+		result = os.str() + result;
+	}
+}
+
+std::string GenType::handleGeneric( ast::BaseInstType const * type ) {
+	if ( !type->params.empty() ) {
+		std::ostringstream os;
+		ast::Pass<CodeGenerator> cg( os, options );
+		os << "(";
+		cg.core.genCommaList( type->params );
+		os << ") ";
+		return os.str();
+	}
+	return "";
+}
+
+void GenType::postvisit( ast::StructInstType const * type )  {
+	result = type->name + handleGeneric( type ) + " " + result;
+	if ( options.genC ) result = "struct " + result;
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::UnionInstType const * type ) {
+	result = type->name + handleGeneric( type ) + " " + result;
+	if ( options.genC ) result = "union " + result;
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::EnumInstType const * type ) {
+	// if ( type->base && type->base->base ) {
+	// 	result = genType( type->base->base, result, options );
+	// } else {
+		result = type->name + " " + result;
+		if ( options.genC ) {
+			result = "enum " + result;
+		}
+	// }
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::EnumAttrType const * type ) {
+	postvisit( type->instance );
+}
+
+void GenType::postvisit( ast::TypeInstType const * type ) {
+	assertf( !options.genC, "TypeInstType should not reach code generation." );
+	result = type->name + " " + result;
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::TupleType const * type ) {
+	assertf( !options.genC, "TupleType should not reach code generation." );
+	unsigned int i = 0;
+	std::ostringstream os;
+	os << "[";
+	for ( ast::ptr<ast::Type> const & t : type->types ) {
+		i++;
+		os << genType( t, "", options ) << (i == type->size() ? "" : ", ");
+	}
+	os << "] ";
+	result = os.str() + result;
+}
+
+void GenType::postvisit( ast::VarArgsType const * type ) {
+	result = "__builtin_va_list " + result;
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::ZeroType const * type ) {
+	// Ideally these wouldn't hit codegen at all, but should be safe to make them ints.
+	result = (options.pretty ? "zero_t " : "long int ") + result;
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::OneType const * type ) {
+	// Ideally these wouldn't hit codegen at all, but should be safe to make them ints.
+	result = (options.pretty ? "one_t " : "long int ") + result;
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::GlobalScopeType const * type ) {
+	assertf( !options.genC, "GlobalScopeType should not reach code generation." );
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::TraitInstType const * type ) {
+	assertf( !options.genC, "TraitInstType should not reach code generation." );
+	result = type->name + " " + result;
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::TypeofType const * type ) {
+	std::ostringstream os;
+	os << "typeof(";
+	ast::Pass<CodeGenerator>::read( type->expr.get(), os, options );
+	os << ") " << result;
+	result = os.str();
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::VTableType const * type ) {
+	assertf( !options.genC, "Virtual table types should not reach code generation." );
+	std::ostringstream os;
+	os << "vtable(" << genType( type->base, "", options ) << ") " << result;
+	result = os.str();
+	handleQualifiers( type );
+}
+
+void GenType::postvisit( ast::QualifiedType const * type ) {
+	assertf( !options.genC, "QualifiedType should not reach code generation." );
+	std::ostringstream os;
+	os << genType( type->parent, "", options ) << "." << genType( type->child, "", options ) << result;
+	result = os.str();
+	handleQualifiers( type );
+}
+
+void GenType::handleQualifiers( ast::Type const * type ) {
+	if ( type->is_const() ) {
+		result = "const " + result;
+	}
+	if ( type->is_volatile() ) {
+		result = "volatile " + result;
+	}
+	if ( type->is_restrict() ) {
+		result = "__restrict " + result;
+	}
+	if ( type->is_atomic() ) {
+		result = "_Atomic " + result;
+	}
+}
+
+std::string GenType::genParamList( const ast::vector<ast::Type> & range ) {
+	auto cur = range.begin();
+	auto end = range.end();
+	if ( cur == end ) return "";
+	std::ostringstream oss;
+	UniqueName param( "__param_" );
+	while ( true ) {
+		oss << genType( *cur++, options.genC ? param.newName() : "", options );
+		if ( cur == end ) break;
+		oss << ", ";
+	}
+	return oss.str();
+}
+
+} // namespace
+
+std::string genType( ast::Type const * type, const std::string & base, const Options & options ) {
+	std::ostringstream os;
+	if ( !type->attributes.empty() ) {
+		ast::Pass<CodeGenerator> cg( os, options );
+		cg.core.genAttributes( type->attributes );
+	}
+
+	return os.str() + ast::Pass<GenType>::read( type, base, options );
+}
+
+std::string genTypeNoAttr( ast::Type const * type, const std::string & base, const Options & options ) {
+	return ast::Pass<GenType>::read( type, base, options );
+}
+
+} // namespace CodeGen
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/CodeGen/GenType.h
===================================================================
--- src/CodeGen/GenType.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,37 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// GenType.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sun Feb 16 04:11:40 2020
-// Update Count     : 5
-//
-
-#pragma once
-
-#include <string>  // for string
-
-#include "CodeGen/Options.h" // for Options
-
-namespace ast {
-	class Type;
-}
-
-namespace CodeGen {
-
-std::string genType( ast::Type const * type, const std::string & base, const Options & options );
-std::string genTypeNoAttr( ast::Type const * type, const std::string & base, const Options & options );
-
-} // namespace CodeGen
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/CodeGen/GenType.hpp
===================================================================
--- src/CodeGen/GenType.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/GenType.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,37 @@
+//
+// 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.
+//
+// GenType.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sun Feb 16 04:11:40 2020
+// Update Count     : 5
+//
+
+#pragma once
+
+#include <string>  // for string
+
+#include "CodeGen/Options.hpp" // for Options
+
+namespace ast {
+	class Type;
+}
+
+namespace CodeGen {
+
+std::string genType( ast::Type const * type, const std::string & base, const Options & options );
+std::string genTypeNoAttr( ast::Type const * type, const std::string & base, const Options & options );
+
+} // namespace CodeGen
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/CodeGen/Generate.cc
===================================================================
--- src/CodeGen/Generate.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,75 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Generate.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sun Feb 16 03:01:51 2020
-// Update Count     : 9
-//
-#include "Generate.h"
-
-#include <iostream>                  // for ostream, endl, operator<<
-#include <list>                      // for list
-#include <string>                    // for operator<<
-
-#include "CodeGenerator.hpp"         // for CodeGenerator, doSemicolon, ...
-#include "GenType.h"                 // for genPrettyType
-
-using namespace std;
-
-namespace CodeGen {
-
-namespace {
-	bool shouldClean( ast::Decl const * decl ) {
-		return dynamic_cast<ast::TraitDecl const *>( decl );
-	}
-
-	/// Removes various nodes that should not exist in CodeGen.
-	struct TreeCleaner final {
-		ast::CompoundStmt const * previsit( ast::CompoundStmt const * stmt ) {
-			auto mutStmt = ast::mutate( stmt );
-			erase_if( mutStmt->kids, []( ast::Stmt const * stmt ){
-				auto declStmt = dynamic_cast<ast::DeclStmt const *>( stmt );
-				return ( declStmt ) ? shouldClean( declStmt->decl ) : false;
-			} );
-			return mutStmt;
-		}
-
-		ast::Stmt const * postvisit( ast::ImplicitCtorDtorStmt const * stmt ) {
-			return stmt->callStmt;
-		}
-	};
-} // namespace
-
-void generate( ast::TranslationUnit & translationUnit, std::ostream & os, bool doIntrinsics,
-		bool pretty, bool generateC, bool lineMarks, bool printExprTypes ) {
-	erase_if( translationUnit.decls, shouldClean );
-	ast::Pass<TreeCleaner>::run( translationUnit );
-
-	ast::Pass<CodeGenerator> cgv( os,
-			Options( pretty, generateC, lineMarks, printExprTypes ) );
-	for ( auto & decl : translationUnit.decls ) {
-		if ( decl->linkage.is_generatable && (doIntrinsics || !decl->linkage.is_builtin ) ) {
-			cgv.core.updateLocation( decl );
-			decl->accept( cgv );
-			if ( doSemicolon( decl ) ) {
-				os << ";";
-			}
-			os << cgv.core.endl;
-		}
-	}
-}
-
-} // namespace CodeGen
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/CodeGen/Generate.cpp
===================================================================
--- src/CodeGen/Generate.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/Generate.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,75 @@
+//
+// 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.
+//
+// Generate.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sun Feb 16 03:01:51 2020
+// Update Count     : 9
+//
+#include "Generate.hpp"
+
+#include <iostream>                  // for ostream, endl, operator<<
+#include <list>                      // for list
+#include <string>                    // for operator<<
+
+#include "CodeGenerator.hpp"         // for CodeGenerator, doSemicolon, ...
+#include "GenType.hpp"               // for genPrettyType
+
+using namespace std;
+
+namespace CodeGen {
+
+namespace {
+	bool shouldClean( ast::Decl const * decl ) {
+		return dynamic_cast<ast::TraitDecl const *>( decl );
+	}
+
+	/// Removes various nodes that should not exist in CodeGen.
+	struct TreeCleaner final {
+		ast::CompoundStmt const * previsit( ast::CompoundStmt const * stmt ) {
+			auto mutStmt = ast::mutate( stmt );
+			erase_if( mutStmt->kids, []( ast::Stmt const * stmt ){
+				auto declStmt = dynamic_cast<ast::DeclStmt const *>( stmt );
+				return ( declStmt ) ? shouldClean( declStmt->decl ) : false;
+			} );
+			return mutStmt;
+		}
+
+		ast::Stmt const * postvisit( ast::ImplicitCtorDtorStmt const * stmt ) {
+			return stmt->callStmt;
+		}
+	};
+} // namespace
+
+void generate( ast::TranslationUnit & translationUnit, std::ostream & os, bool doIntrinsics,
+		bool pretty, bool generateC, bool lineMarks, bool printExprTypes ) {
+	erase_if( translationUnit.decls, shouldClean );
+	ast::Pass<TreeCleaner>::run( translationUnit );
+
+	ast::Pass<CodeGenerator> cgv( os,
+			Options( pretty, generateC, lineMarks, printExprTypes ) );
+	for ( auto & decl : translationUnit.decls ) {
+		if ( decl->linkage.is_generatable && (doIntrinsics || !decl->linkage.is_builtin ) ) {
+			cgv.core.updateLocation( decl );
+			decl->accept( cgv );
+			if ( doSemicolon( decl ) ) {
+				os << ";";
+			}
+			os << cgv.core.endl;
+		}
+	}
+}
+
+} // namespace CodeGen
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/CodeGen/Generate.h
===================================================================
--- src/CodeGen/Generate.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,41 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Generate.h --
-//
-// 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:16:35 2017
-// Update Count     : 2
-//
-
-#pragma once
-
-#include <iostream>  // for ostream
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace CodeGen {
-
-/// Generates all code in transUnit and writing it to the os.
-/// doIntrinsics: Should intrinsic functions be printed?
-/// pretty: Format output nicely (e.g., uses unmangled names, etc.).
-/// generateC: Make sure the output only consists of C code (allows some assertions, etc.)
-/// lineMarks: Output line marks (processed line directives) in the output.
-/// printExprTypes: Print the types of expressions in comments.
-void generate( ast::TranslationUnit & transUnit, std::ostream &os, bool doIntrinsics,
-		bool pretty, bool generateC, bool lineMarks, bool printExprTypes );
-
-} // namespace CodeGen
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/CodeGen/Generate.hpp
===================================================================
--- src/CodeGen/Generate.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/Generate.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,41 @@
+//
+// 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.
+//
+// Generate.hpp --
+//
+// 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:16:35 2017
+// Update Count     : 2
+//
+
+#pragma once
+
+#include <iostream>  // for ostream
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace CodeGen {
+
+/// Generates all code in transUnit and writing it to the os.
+/// doIntrinsics: Should intrinsic functions be printed?
+/// pretty: Format output nicely (e.g., uses unmangled names, etc.).
+/// generateC: Make sure the output only consists of C code (allows some assertions, etc.)
+/// lineMarks: Output line marks (processed line directives) in the output.
+/// printExprTypes: Print the types of expressions in comments.
+void generate( ast::TranslationUnit & transUnit, std::ostream &os, bool doIntrinsics,
+		bool pretty, bool generateC, bool lineMarks, bool printExprTypes );
+
+} // namespace CodeGen
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/CodeGen/LinkOnce.cc
===================================================================
--- src/CodeGen/LinkOnce.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,86 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// LinkOnce.cc -- Translate the cfa_linkonce attribute.
-//
-// Author           : Andrew Beach
-// Created On       : Thur May 13 10:10:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Oct  4 10:52:00 2023
-// Update Count     : 1
-//
-
-#include "LinkOnce.h"
-
-#include <algorithm>
-
-#include "AST/Attribute.hpp"
-#include "AST/Decl.hpp"
-#include "AST/Expr.hpp"
-#include "AST/Pass.hpp"
-
-namespace CodeGen {
-
-namespace {
-
-bool is_cfa_linkonce( ast::Attribute const * attr ) {
-	return "cfa_linkonce" == attr->name;
-}
-
-bool is_section_attribute( ast::Attribute const * attr ) {
-	return "section" == attr->name;
-}
-
-struct LinkOnceCore : public ast::WithShortCircuiting {
-	void previsit( ast::Decl const * ) {
-		visit_children = false;
-	}
-
-	ast::DeclWithType const * postvisit( ast::DeclWithType const * decl ) {
-		// Check to see if we have to mutate, because should be uncommon.
-		{
-			auto & attributes = decl->attributes;
-			auto found = std::find_if( attributes.begin(), attributes.end(),
-					is_cfa_linkonce );
-			if ( attributes.end() == found ) return decl;
-		}
-		auto mutDecl = mutate( decl );
-		auto & attributes = mutDecl->attributes;
-
-		// Remove all conflicting section attributes.
-		erase_if( attributes, is_section_attribute );
-
-		// Get the attribute, and overwrite it as a section attribute.
-		auto found = std::find_if( attributes.begin(), attributes.end(),
-				is_cfa_linkonce );
-		assert( attributes.end() != found );
-		ast::Attribute * attribute = found->get_and_mutate();
-		assert( attribute->params.empty() );
-		assert( !decl->mangleName.empty() );
-
-		attribute->name = "section";
-		attribute->params.push_back(
-			ast::ConstantExpr::from_string( mutDecl->location,
-				".gnu.linkonce." + decl->mangleName
-			)
-		);
-
-		// Unconditionnaly add "visibility(default)" to anything with
-		// .gnu.linkonce visibility is a mess otherwise.
-		attributes.push_back( new ast::Attribute( "visibility", {
-			ast::ConstantExpr::from_string( mutDecl->location, "default" )
-		} ) );
-		return mutDecl;
-	}
-};
-
-} // namespace
-
-void translateLinkOnce( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<LinkOnceCore>::run( translationUnit );
-}
-
-} // namespace CodeGen
Index: src/CodeGen/LinkOnce.cpp
===================================================================
--- src/CodeGen/LinkOnce.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/LinkOnce.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,86 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// LinkOnce.cpp -- Translate the cfa_linkonce attribute.
+//
+// Author           : Andrew Beach
+// Created On       : Thur May 13 10:10:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Oct  4 10:52:00 2023
+// Update Count     : 1
+//
+
+#include "LinkOnce.hpp"
+
+#include <algorithm>
+
+#include "AST/Attribute.hpp"
+#include "AST/Decl.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
+
+namespace CodeGen {
+
+namespace {
+
+bool is_cfa_linkonce( ast::Attribute const * attr ) {
+	return "cfa_linkonce" == attr->name;
+}
+
+bool is_section_attribute( ast::Attribute const * attr ) {
+	return "section" == attr->name;
+}
+
+struct LinkOnceCore : public ast::WithShortCircuiting {
+	void previsit( ast::Decl const * ) {
+		visit_children = false;
+	}
+
+	ast::DeclWithType const * postvisit( ast::DeclWithType const * decl ) {
+		// Check to see if we have to mutate, because should be uncommon.
+		{
+			auto & attributes = decl->attributes;
+			auto found = std::find_if( attributes.begin(), attributes.end(),
+					is_cfa_linkonce );
+			if ( attributes.end() == found ) return decl;
+		}
+		auto mutDecl = mutate( decl );
+		auto & attributes = mutDecl->attributes;
+
+		// Remove all conflicting section attributes.
+		erase_if( attributes, is_section_attribute );
+
+		// Get the attribute, and overwrite it as a section attribute.
+		auto found = std::find_if( attributes.begin(), attributes.end(),
+				is_cfa_linkonce );
+		assert( attributes.end() != found );
+		ast::Attribute * attribute = found->get_and_mutate();
+		assert( attribute->params.empty() );
+		assert( !decl->mangleName.empty() );
+
+		attribute->name = "section";
+		attribute->params.push_back(
+			ast::ConstantExpr::from_string( mutDecl->location,
+				".gnu.linkonce." + decl->mangleName
+			)
+		);
+
+		// Unconditionnaly add "visibility(default)" to anything with
+		// .gnu.linkonce visibility is a mess otherwise.
+		attributes.push_back( new ast::Attribute( "visibility", {
+			ast::ConstantExpr::from_string( mutDecl->location, "default" )
+		} ) );
+		return mutDecl;
+	}
+};
+
+} // namespace
+
+void translateLinkOnce( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<LinkOnceCore>::run( translationUnit );
+}
+
+} // namespace CodeGen
Index: src/CodeGen/LinkOnce.h
===================================================================
--- src/CodeGen/LinkOnce.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,35 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// LinkOnce.h -- Translate the cfa_linkonce attribute.
-//
-// Author           : Andrew Beach
-// Created On       : Thur May 13 10:06:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Oct  4 10:52:00 2023
-// Update Count     : 1
-//
-
-#pragma once
-
-// This could either be an early step in code-generation or a step of the
-// Cforall to C lowering. It could also be part of attribute handling but
-// for now its almost the only attribute we handle.
-
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace CodeGen {
-
-void translateLinkOnce( ast::TranslationUnit & translationUnit );
-/* Convert the cfa_linkonce attribute on top level declaration into
- * a special section declaration (.gnu.linkonce) so that it may be defined
- * multiple times (same name and same type, must have the same value).
- */
-
-}
Index: src/CodeGen/LinkOnce.hpp
===================================================================
--- src/CodeGen/LinkOnce.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/LinkOnce.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,35 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// LinkOnce.hpp -- Translate the cfa_linkonce attribute.
+//
+// Author           : Andrew Beach
+// Created On       : Thur May 13 10:06:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Oct  4 10:52:00 2023
+// Update Count     : 1
+//
+
+#pragma once
+
+// This could either be an early step in code-generation or a step of the
+// Cforall to C lowering. It could also be part of attribute handling but
+// for now its almost the only attribute we handle.
+
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace CodeGen {
+
+void translateLinkOnce( ast::TranslationUnit & translationUnit );
+/* Convert the cfa_linkonce attribute on top level declaration into
+ * a special section declaration (.gnu.linkonce) so that it may be defined
+ * multiple times (same name and same type, must have the same value).
+ */
+
+}
Index: src/CodeGen/OperatorTable.cc
===================================================================
--- src/CodeGen/OperatorTable.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,140 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// OperatorTable.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Nov  3 16:00:00 2023
-// Update Count     : 56
-//
-
-#include "OperatorTable.h"
-
-#include <cassert>         // for assert
-#include <unordered_map>   // for unordered_map
-
-namespace CodeGen {
-
-static const OperatorInfo tableValues[] = {
-	//  inputName symbol  outputName                     friendlyName                  type
-	{	"?[?]",   "",     "_operator_index",             "Index",                      OT_INDEX          },
-	{	"?{}",    "=",    "_constructor",                "Constructor",                OT_CTOR           },
-	{	"^?{}",   "",     "_destructor",                 "Destructor",                 OT_DTOR           },
-	{	"?()",    "",     "_operator_call",              "Call Operator",              OT_CALL           },
-	{	"?++",    "++",   "_operator_postincr",          "Postfix Increment",          OT_POSTFIXASSIGN  },
-	{	"?--",    "--",   "_operator_postdecr",          "Postfix Decrement",          OT_POSTFIXASSIGN  },
-	{	"*?",     "*",    "_operator_deref",             "Dereference",                OT_PREFIX         },
-	{	"+?",     "+",    "_operator_unaryplus",         "Plus",                       OT_PREFIX         },
-	{	"-?",     "-",    "_operator_unaryminus",        "Minus",                      OT_PREFIX         },
-	{	"~?",     "~",    "_operator_bitnot",            "Bitwise Not",                OT_PREFIX         },
-	{	"!?",     "!",    "_operator_lognot",            "Logical Not",                OT_PREFIX         },
-	{	"++?",    "++",   "_operator_preincr",           "Prefix Increment",           OT_PREFIXASSIGN   },
-	{	"--?",    "--",   "_operator_predecr",           "Prefix Decrement",           OT_PREFIXASSIGN   },
-	{	"?\\?",   "\\",   "_operator_exponential",       "Exponentiation",             OT_INFIX          },
-	{	"?*?",    "*",    "_operator_multiply",          "Multiplication",             OT_INFIX          },
-	{	"?/?",    "/",    "_operator_divide",            "Division",                   OT_INFIX          },
-	{	"?%?",    "%",    "_operator_modulus",           "Modulo",                     OT_INFIX          },
-	{	"?+?",    "+",    "_operator_add",               "Addition",                   OT_INFIX          },
-	{	"?-?",    "-",    "_operator_subtract",          "Substraction",               OT_INFIX          },
-	{	"?<<?",   "<<",   "_operator_shiftleft",         "Shift Left",                 OT_INFIX          },
-	{	"?>>?",   ">>",   "_operator_shiftright",        "Shift Right",                OT_INFIX          },
-	{	"?<?",    "<",    "_operator_less",              "Less-than",                  OT_INFIX          },
-	{	"?>?",    ">",    "_operator_greater",           "Greater-than",               OT_INFIX          },
-	{	"?<=?",   "<=",   "_operator_lessequal",         "Less-than-or-Equal",         OT_INFIX          },
-	{	"?>=?",   ">=",   "_operator_greaterequal",      "Greater-than-or-Equal",      OT_INFIX          },
-	{	"?==?",   "==",   "_operator_equal",             "Equality",                   OT_INFIX          },
-	{	"?!=?",   "!=",   "_operator_notequal",          "Not-Equal",                  OT_INFIX          },
-	{	"?&?",    "&",    "_operator_bitand",            "Bitwise And",                OT_INFIX          },
-	{	"?^?",    "^",    "_operator_bitxor",            "Bitwise Xor",                OT_INFIX          },
-	{	"?|?",    "|",    "_operator_bitor",             "Bitwise Or",                 OT_INFIX          },
-	{	"?=?",    "=",    "_operator_assign",            "Assignment",                 OT_INFIXASSIGN    },
-	{	"?\\=?",  "\\=",  "_operator_expassign",         "Exponentiation Assignment",  OT_INFIXASSIGN    },
-	{	"?*=?",   "*=",   "_operator_multassign",        "Multiplication Assignment",  OT_INFIXASSIGN    },
-	{	"?/=?",   "/=",   "_operator_divassign",         "Division Assignment",        OT_INFIXASSIGN    },
-	{	"?%=?",   "%=",   "_operator_modassign",         "Modulo Assignment",          OT_INFIXASSIGN    },
-	{	"?+=?",   "+=",   "_operator_addassign",         "Addition Assignment",        OT_INFIXASSIGN    },
-	{	"?-=?",   "-=",   "_operator_subassign",         "Substrction Assignment",     OT_INFIXASSIGN    },
-	{	"?<<=?",  "<<=",  "_operator_shiftleftassign",   "Shift Left Assignment",      OT_INFIXASSIGN    },
-	{	"?>>=?",  ">>=",  "_operator_shiftrightassign",  "Shift Right Assignment",     OT_INFIXASSIGN    },
-	{	"?&=?",   "&=",   "_operator_bitandassign",      "Bitwise And Assignment",     OT_INFIXASSIGN    },
-	{	"?^=?",   "^=",   "_operator_bitxorassign",      "Bitwise Xor Assignment",     OT_INFIXASSIGN    },
-	{	"?|=?",   "|=",   "_operator_bitorassign",       "Bitwise Or Assignment",      OT_INFIXASSIGN    },
-}; // tableValues
-
-enum { numOps = sizeof( tableValues ) / sizeof( OperatorInfo ) };
-
-const OperatorInfo * operatorLookup( const std::string & inputName ) {
-	// Static information set up:
-	static std::unordered_map<std::string, const OperatorInfo *> inputTable;
-	if ( inputTable.empty() ) for ( const OperatorInfo & op : tableValues ) {
-		inputTable[ op.inputName ] = &op;
-	}
-
-	if ( inputName.find_first_of( "?^*+-!", 0, 1 ) == std::string::npos ) return nullptr; // prefilter
-	const OperatorInfo * ret = inputTable.find( inputName )->second;
-	// This can only happen if an invalid identifier name has been used.
-	assert( ret );
-	return ret;
-}
-
-bool isOperator( const std::string & inputName ) {
-	return operatorLookup( inputName ) != nullptr;
-}
-
-std::string operatorFriendlyName( const std::string & inputName ) {
-	const OperatorInfo * info = operatorLookup( inputName );
-	if ( info ) return info->friendlyName;
-	return "";
-}
-
-// This is only used in the demangler, so it is smaller (and only maybe slow).
-const OperatorInfo * operatorLookupByOutput( const std::string & outputName ) {
-	if ( '_' != outputName[0] ) return nullptr;
-	for ( const OperatorInfo & op : tableValues ) {
-		if ( outputName == op.outputName ) {
-			return &op;
-		}
-	}
-	return nullptr;
-}
-
-bool isConstructor( const std::string & inputName ) {
-	const OperatorInfo * info = operatorLookup( inputName );
-	if ( info ) return info->type == OT_CTOR;
-	return false;
-}
-
-bool isDestructor( const std::string & inputName ) {
-	const OperatorInfo * info = operatorLookup( inputName );
-	if ( info ) return info->type == OT_DTOR;
-	return false;
-}
-
-bool isCtorDtor( const std::string & inputName ) {
-	const OperatorInfo * info = operatorLookup( inputName );
-	if ( info ) return info->type <= OT_CONSTRUCTOR;
-	return false;
-}
-
-bool isAssignment( const std::string & inputName ) {
-	const OperatorInfo * info = operatorLookup( inputName );
-	if ( info ) return info->type > OT_CONSTRUCTOR && info->type <= OT_ASSIGNMENT;
-	return false;
-}
-
-bool isCtorDtorAssign( const std::string & inputName ) {
-	const OperatorInfo * info = operatorLookup( inputName );
-	if ( info ) return info->type <= OT_ASSIGNMENT;
-	return false;
-}
-
-} // namespace CodeGen
-
-// Local Variables: //
-// tab-width: 4 //
-// End: //
Index: src/CodeGen/OperatorTable.cpp
===================================================================
--- src/CodeGen/OperatorTable.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/OperatorTable.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,140 @@
+//
+// 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.
+//
+// OperatorTable.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Nov  3 16:00:00 2023
+// Update Count     : 56
+//
+
+#include "OperatorTable.hpp"
+
+#include <cassert>         // for assert
+#include <unordered_map>   // for unordered_map
+
+namespace CodeGen {
+
+static const OperatorInfo tableValues[] = {
+	//  inputName symbol  outputName                     friendlyName                  type
+	{	"?[?]",   "",     "_operator_index",             "Index",                      OT_INDEX          },
+	{	"?{}",    "=",    "_constructor",                "Constructor",                OT_CTOR           },
+	{	"^?{}",   "",     "_destructor",                 "Destructor",                 OT_DTOR           },
+	{	"?()",    "",     "_operator_call",              "Call Operator",              OT_CALL           },
+	{	"?++",    "++",   "_operator_postincr",          "Postfix Increment",          OT_POSTFIXASSIGN  },
+	{	"?--",    "--",   "_operator_postdecr",          "Postfix Decrement",          OT_POSTFIXASSIGN  },
+	{	"*?",     "*",    "_operator_deref",             "Dereference",                OT_PREFIX         },
+	{	"+?",     "+",    "_operator_unaryplus",         "Plus",                       OT_PREFIX         },
+	{	"-?",     "-",    "_operator_unaryminus",        "Minus",                      OT_PREFIX         },
+	{	"~?",     "~",    "_operator_bitnot",            "Bitwise Not",                OT_PREFIX         },
+	{	"!?",     "!",    "_operator_lognot",            "Logical Not",                OT_PREFIX         },
+	{	"++?",    "++",   "_operator_preincr",           "Prefix Increment",           OT_PREFIXASSIGN   },
+	{	"--?",    "--",   "_operator_predecr",           "Prefix Decrement",           OT_PREFIXASSIGN   },
+	{	"?\\?",   "\\",   "_operator_exponential",       "Exponentiation",             OT_INFIX          },
+	{	"?*?",    "*",    "_operator_multiply",          "Multiplication",             OT_INFIX          },
+	{	"?/?",    "/",    "_operator_divide",            "Division",                   OT_INFIX          },
+	{	"?%?",    "%",    "_operator_modulus",           "Modulo",                     OT_INFIX          },
+	{	"?+?",    "+",    "_operator_add",               "Addition",                   OT_INFIX          },
+	{	"?-?",    "-",    "_operator_subtract",          "Substraction",               OT_INFIX          },
+	{	"?<<?",   "<<",   "_operator_shiftleft",         "Shift Left",                 OT_INFIX          },
+	{	"?>>?",   ">>",   "_operator_shiftright",        "Shift Right",                OT_INFIX          },
+	{	"?<?",    "<",    "_operator_less",              "Less-than",                  OT_INFIX          },
+	{	"?>?",    ">",    "_operator_greater",           "Greater-than",               OT_INFIX          },
+	{	"?<=?",   "<=",   "_operator_lessequal",         "Less-than-or-Equal",         OT_INFIX          },
+	{	"?>=?",   ">=",   "_operator_greaterequal",      "Greater-than-or-Equal",      OT_INFIX          },
+	{	"?==?",   "==",   "_operator_equal",             "Equality",                   OT_INFIX          },
+	{	"?!=?",   "!=",   "_operator_notequal",          "Not-Equal",                  OT_INFIX          },
+	{	"?&?",    "&",    "_operator_bitand",            "Bitwise And",                OT_INFIX          },
+	{	"?^?",    "^",    "_operator_bitxor",            "Bitwise Xor",                OT_INFIX          },
+	{	"?|?",    "|",    "_operator_bitor",             "Bitwise Or",                 OT_INFIX          },
+	{	"?=?",    "=",    "_operator_assign",            "Assignment",                 OT_INFIXASSIGN    },
+	{	"?\\=?",  "\\=",  "_operator_expassign",         "Exponentiation Assignment",  OT_INFIXASSIGN    },
+	{	"?*=?",   "*=",   "_operator_multassign",        "Multiplication Assignment",  OT_INFIXASSIGN    },
+	{	"?/=?",   "/=",   "_operator_divassign",         "Division Assignment",        OT_INFIXASSIGN    },
+	{	"?%=?",   "%=",   "_operator_modassign",         "Modulo Assignment",          OT_INFIXASSIGN    },
+	{	"?+=?",   "+=",   "_operator_addassign",         "Addition Assignment",        OT_INFIXASSIGN    },
+	{	"?-=?",   "-=",   "_operator_subassign",         "Substrction Assignment",     OT_INFIXASSIGN    },
+	{	"?<<=?",  "<<=",  "_operator_shiftleftassign",   "Shift Left Assignment",      OT_INFIXASSIGN    },
+	{	"?>>=?",  ">>=",  "_operator_shiftrightassign",  "Shift Right Assignment",     OT_INFIXASSIGN    },
+	{	"?&=?",   "&=",   "_operator_bitandassign",      "Bitwise And Assignment",     OT_INFIXASSIGN    },
+	{	"?^=?",   "^=",   "_operator_bitxorassign",      "Bitwise Xor Assignment",     OT_INFIXASSIGN    },
+	{	"?|=?",   "|=",   "_operator_bitorassign",       "Bitwise Or Assignment",      OT_INFIXASSIGN    },
+}; // tableValues
+
+enum { numOps = sizeof( tableValues ) / sizeof( OperatorInfo ) };
+
+const OperatorInfo * operatorLookup( const std::string & inputName ) {
+	// Static information set up:
+	static std::unordered_map<std::string, const OperatorInfo *> inputTable;
+	if ( inputTable.empty() ) for ( const OperatorInfo & op : tableValues ) {
+		inputTable[ op.inputName ] = &op;
+	}
+
+	if ( inputName.find_first_of( "?^*+-!", 0, 1 ) == std::string::npos ) return nullptr; // prefilter
+	const OperatorInfo * ret = inputTable.find( inputName )->second;
+	// This can only happen if an invalid identifier name has been used.
+	assert( ret );
+	return ret;
+}
+
+bool isOperator( const std::string & inputName ) {
+	return operatorLookup( inputName ) != nullptr;
+}
+
+std::string operatorFriendlyName( const std::string & inputName ) {
+	const OperatorInfo * info = operatorLookup( inputName );
+	if ( info ) return info->friendlyName;
+	return "";
+}
+
+// This is only used in the demangler, so it is smaller (and only maybe slow).
+const OperatorInfo * operatorLookupByOutput( const std::string & outputName ) {
+	if ( '_' != outputName[0] ) return nullptr;
+	for ( const OperatorInfo & op : tableValues ) {
+		if ( outputName == op.outputName ) {
+			return &op;
+		}
+	}
+	return nullptr;
+}
+
+bool isConstructor( const std::string & inputName ) {
+	const OperatorInfo * info = operatorLookup( inputName );
+	if ( info ) return info->type == OT_CTOR;
+	return false;
+}
+
+bool isDestructor( const std::string & inputName ) {
+	const OperatorInfo * info = operatorLookup( inputName );
+	if ( info ) return info->type == OT_DTOR;
+	return false;
+}
+
+bool isCtorDtor( const std::string & inputName ) {
+	const OperatorInfo * info = operatorLookup( inputName );
+	if ( info ) return info->type <= OT_CONSTRUCTOR;
+	return false;
+}
+
+bool isAssignment( const std::string & inputName ) {
+	const OperatorInfo * info = operatorLookup( inputName );
+	if ( info ) return info->type > OT_CONSTRUCTOR && info->type <= OT_ASSIGNMENT;
+	return false;
+}
+
+bool isCtorDtorAssign( const std::string & inputName ) {
+	const OperatorInfo * info = operatorLookup( inputName );
+	if ( info ) return info->type <= OT_ASSIGNMENT;
+	return false;
+}
+
+} // namespace CodeGen
+
+// Local Variables: //
+// tab-width: 4 //
+// End: //
Index: src/CodeGen/OperatorTable.h
===================================================================
--- src/CodeGen/OperatorTable.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,75 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// OperatorTable.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Nov  3 14:53:00 2023
-// Update Count     : 27
-//
-
-#pragma once
-
-#include <string>
-
-namespace CodeGen {
-
-enum OperatorType {
-	OT_CTOR,
-	OT_DTOR,
-	OT_CONSTRUCTOR = OT_DTOR,
-	OT_PREFIXASSIGN,
-	OT_POSTFIXASSIGN,
-	OT_INFIXASSIGN,
-	OT_ASSIGNMENT = OT_INFIXASSIGN,
-	OT_CALL,
-	OT_PREFIX,
-	OT_INFIX,
-	OT_POSTFIX,
-	OT_INDEX,
-	OT_LABELADDRESS,
-	OT_CONSTANT
-};
-
-struct OperatorInfo {
-	// The Cforall special function name.
-	std::string inputName;
-	// The string used when the operator is used as an operator.
-	std::string symbol;
-	// The base name used in the mangled name.
-	std::string outputName;
-	// Human-readable name of the operator.
-	std::string friendlyName;
-	// The type of operator shows how it is used as an operator.
-	OperatorType type;
-};
-
-// Look up the operator (by inputName), return nullptr if no such operator.
-const OperatorInfo * operatorLookup( const std::string & inputName );
-// Is there an operator with this name?
-bool isOperator( const std::string & inputName );
-// Get the friendlyName of the operator with the inputName
-std::string operatorFriendlyName( const std::string & inputName );
-// Get the OperatorInfo with the given outputName, if one exists.
-const OperatorInfo * operatorLookupByOutput( const std::string & outputName );
-
-// Is the operator a constructor, destructor or any form of assignment.
-// (Last two are "or" combinations of the first three.)
-bool isConstructor( const std::string & );
-bool isDestructor( const std::string & );
-bool isAssignment( const std::string & );
-bool isCtorDtor( const std::string & );
-bool isCtorDtorAssign( const std::string & );
-
-} // namespace CodeGen
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/CodeGen/OperatorTable.hpp
===================================================================
--- src/CodeGen/OperatorTable.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/OperatorTable.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,75 @@
+//
+// 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.
+//
+// OperatorTable.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Nov  3 14:53:00 2023
+// Update Count     : 27
+//
+
+#pragma once
+
+#include <string>
+
+namespace CodeGen {
+
+enum OperatorType {
+	OT_CTOR,
+	OT_DTOR,
+	OT_CONSTRUCTOR = OT_DTOR,
+	OT_PREFIXASSIGN,
+	OT_POSTFIXASSIGN,
+	OT_INFIXASSIGN,
+	OT_ASSIGNMENT = OT_INFIXASSIGN,
+	OT_CALL,
+	OT_PREFIX,
+	OT_INFIX,
+	OT_POSTFIX,
+	OT_INDEX,
+	OT_LABELADDRESS,
+	OT_CONSTANT
+};
+
+struct OperatorInfo {
+	// The Cforall special function name.
+	std::string inputName;
+	// The string used when the operator is used as an operator.
+	std::string symbol;
+	// The base name used in the mangled name.
+	std::string outputName;
+	// Human-readable name of the operator.
+	std::string friendlyName;
+	// The type of operator shows how it is used as an operator.
+	OperatorType type;
+};
+
+// Look up the operator (by inputName), return nullptr if no such operator.
+const OperatorInfo * operatorLookup( const std::string & inputName );
+// Is there an operator with this name?
+bool isOperator( const std::string & inputName );
+// Get the friendlyName of the operator with the inputName
+std::string operatorFriendlyName( const std::string & inputName );
+// Get the OperatorInfo with the given outputName, if one exists.
+const OperatorInfo * operatorLookupByOutput( const std::string & outputName );
+
+// Is the operator a constructor, destructor or any form of assignment.
+// (Last two are "or" combinations of the first three.)
+bool isConstructor( const std::string & );
+bool isDestructor( const std::string & );
+bool isAssignment( const std::string & );
+bool isCtorDtor( const std::string & );
+bool isCtorDtorAssign( const std::string & );
+
+} // namespace CodeGen
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/CodeGen/Options.h
===================================================================
--- src/CodeGen/Options.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,37 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Options.h --
-//
-// Author           : Andrew Beach
-// Created On       : Tue Apr 30 11:36:00 2019
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Feb 15 18:37:06 2020
-// Update Count     : 3
-//
-
-#pragma once
-
-struct Options {
-	// External Options: Same thoughout a pass.
-	bool pretty;
-	bool genC;
-	bool lineMarks;
-	bool printExprTypes;
-
-	// Internal Options: Changed on some recurisive calls.
-	bool anonymousUnused = false;
-
-	Options(bool pretty, bool genC, bool lineMarks, bool printExprTypes) :
-		pretty(pretty), genC(genC), lineMarks(lineMarks), printExprTypes(printExprTypes)
-		{}
-};
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/CodeGen/Options.hpp
===================================================================
--- src/CodeGen/Options.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/CodeGen/Options.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,37 @@
+//
+// 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.
+//
+// Options.h --
+//
+// Author           : Andrew Beach
+// Created On       : Tue Apr 30 11:36:00 2019
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Feb 15 18:37:06 2020
+// Update Count     : 3
+//
+
+#pragma once
+
+struct Options {
+	// External Options: Same thoughout a pass.
+	bool pretty;
+	bool genC;
+	bool lineMarks;
+	bool printExprTypes;
+
+	// Internal Options: Changed on some recurisive calls.
+	bool anonymousUnused = false;
+
+	Options(bool pretty, bool genC, bool lineMarks, bool printExprTypes) :
+		pretty(pretty), genC(genC), lineMarks(lineMarks), printExprTypes(printExprTypes)
+		{}
+};
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/CodeGen/module.mk
===================================================================
--- src/CodeGen/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/CodeGen/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -18,19 +18,19 @@
 	CodeGen/CodeGenerator.cpp \
 	CodeGen/CodeGenerator.hpp \
-	CodeGen/GenType.cc \
-	CodeGen/GenType.h \
-	CodeGen/OperatorTable.cc \
-	CodeGen/OperatorTable.h
+	CodeGen/GenType.cpp \
+	CodeGen/GenType.hpp \
+	CodeGen/OperatorTable.cpp \
+	CodeGen/OperatorTable.hpp
 
 SRC += $(SRC_CODEGEN) \
-	CodeGen/Generate.cc \
-	CodeGen/Generate.h \
-	CodeGen/FixMain.cc \
-	CodeGen/FixMain.h \
-	CodeGen/FixNames.cc \
-	CodeGen/FixNames.h \
-	CodeGen/LinkOnce.cc \
-	CodeGen/LinkOnce.h \
-	CodeGen/Options.h
+	CodeGen/Generate.cpp \
+	CodeGen/Generate.hpp \
+	CodeGen/FixMain.cpp \
+	CodeGen/FixMain.hpp \
+	CodeGen/FixNames.cpp \
+	CodeGen/FixNames.hpp \
+	CodeGen/LinkOnce.cpp \
+	CodeGen/LinkOnce.hpp \
+	CodeGen/Options.hpp
 
 SRCDEMANGLE += $(SRC_CODEGEN)
Index: src/Common/Assert.cc
===================================================================
--- src/Common/Assert.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,55 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Assert.cc --
-//
-// Author           : Peter A. Buhr
-// Created On       : Thu Aug 18 13:26:59 2016
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Nov 20 22:57:18 2023
-// Update Count     : 11
-//
-
-#include <cstdarg>  // for va_end, va_list, va_start
-#include <cstdio>   // for fprintf, stderr, vfprintf
-#include <cstdlib>  // for abort
-
-extern const char * __progname;							// global name of running executable (argv[0])
-
-#define CFA_ASSERT_FMT "*CFA assertion error* \"%s\" from program \"%s\" in \"%s\" at line %d in file \"%s\""
-
-// called by macro assert in assert.h
-void __assert_fail( const char *assertion, const char *file, unsigned int line, const char *function ) {
-	fprintf( stderr, CFA_ASSERT_FMT ".\n", assertion, __progname, function, line, file );
-	abort();
-}
-
-// called by macro assertf
-void __assert_fail_f( const char *assertion, const char *file, unsigned int line, const char *function, const char *fmt, ... ) {
-	fprintf( stderr, CFA_ASSERT_FMT ": ", assertion, __progname, function, line, file );
-	va_list args;
-	va_start( args, fmt );
-	vfprintf( stderr, fmt, args );
-	va_end( args );
-	fprintf( stderr, "\n" );
-	abort();
-}
-
-void abort(const char *fmt, ... ) noexcept __attribute__((noreturn, format(printf, 1, 2)));
-void abort(const char *fmt, ... ) noexcept {
-	va_list args;
-	va_start( args, fmt );
-	vfprintf( stderr, fmt, args );
-	va_end( args );
-	fprintf( stderr, "\n" );
-	abort();
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End:  //
Index: src/Common/Assert.cpp
===================================================================
--- src/Common/Assert.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Assert.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,55 @@
+//
+// 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.
+//
+// Assert.cc --
+//
+// Author           : Peter A. Buhr
+// Created On       : Thu Aug 18 13:26:59 2016
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Nov 20 22:57:18 2023
+// Update Count     : 11
+//
+
+#include <cstdarg>  // for va_end, va_list, va_start
+#include <cstdio>   // for fprintf, stderr, vfprintf
+#include <cstdlib>  // for abort
+
+extern const char * __progname;							// global name of running executable (argv[0])
+
+#define CFA_ASSERT_FMT "*CFA assertion error* \"%s\" from program \"%s\" in \"%s\" at line %d in file \"%s\""
+
+// called by macro assert in assert.h
+void __assert_fail( const char *assertion, const char *file, unsigned int line, const char *function ) {
+	fprintf( stderr, CFA_ASSERT_FMT ".\n", assertion, __progname, function, line, file );
+	abort();
+}
+
+// called by macro assertf
+void __assert_fail_f( const char *assertion, const char *file, unsigned int line, const char *function, const char *fmt, ... ) {
+	fprintf( stderr, CFA_ASSERT_FMT ": ", assertion, __progname, function, line, file );
+	va_list args;
+	va_start( args, fmt );
+	vfprintf( stderr, fmt, args );
+	va_end( args );
+	fprintf( stderr, "\n" );
+	abort();
+}
+
+void abort(const char *fmt, ... ) noexcept __attribute__((noreturn, format(printf, 1, 2)));
+void abort(const char *fmt, ... ) noexcept {
+	va_list args;
+	va_start( args, fmt );
+	vfprintf( stderr, fmt, args );
+	va_end( args );
+	fprintf( stderr, "\n" );
+	abort();
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End:  //
Index: src/Common/CodeLocation.h
===================================================================
--- src/Common/CodeLocation.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,75 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// CodeLocation.h --
-//
-// Author           : Andrew Beach
-// Created On       : Thr Aug 17 11:23:00 2017
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Aug 28 12:46:01 2017
-// Update Count     : 2
-//
-
-#pragma once
-
-#include <iostream>
-#include <string>
-
-struct CodeLocation {
-	int first_line = -1, first_column = -1, last_line = -1, last_column = -1;
-	std::string filename = "";
-
-	/// Create a new unset CodeLocation.
-	CodeLocation() = default;
-
-	/// Create a new CodeLocation with the given values.
-	CodeLocation( const char* filename, int lineno )
-		: first_line( lineno )
-		, filename(filename ? filename : "")
-	{}
-
-	CodeLocation( const CodeLocation& rhs ) = default;
-	CodeLocation( CodeLocation&& rhs ) = default;
-	CodeLocation& operator=( const CodeLocation & ) = default;
-	CodeLocation& operator=( CodeLocation && ) = default;
-
-	bool isSet () const {
-		return -1 != first_line;
-	}
-
-	bool isUnset () const {
-		return !isSet();
-	}
-
-	bool startsBefore( CodeLocation const & other ) const {
-		if( filename < other.filename ) return true;
-		if( filename > other.filename ) return false;
-
-		if( first_line < other.first_line ) return true;
-		if( first_line > other.first_line ) return false;
-
-		if( last_line < other.last_line ) return true;
-		return false;
-	}
-
-	bool followedBy( CodeLocation const & other, int seperation ) const {
-		return (first_line + seperation == other.first_line &&
-		        filename == other.filename);
-	}
-
-	bool operator==( CodeLocation const & other ) const {
-		return followedBy( other, 0 );
-	}
-
-	bool operator!=( CodeLocation const & other ) const {
-		return !(*this == other);
-	}
-};
-
-inline std::ostream & operator<<( std::ostream & out, const CodeLocation & location ) {
-	// Column number ":1" allows IDEs to parse the error message and position the cursor in the source text.
-	return location.isSet() ? out << location.filename << ":" << location.first_line << ":1 " : out;
-}
Index: src/Common/CodeLocation.hpp
===================================================================
--- src/Common/CodeLocation.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/CodeLocation.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,75 @@
+//
+// 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.
+//
+// CodeLocation.hpp --
+//
+// Author           : Andrew Beach
+// Created On       : Thr Aug 17 11:23:00 2017
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Aug 28 12:46:01 2017
+// Update Count     : 2
+//
+
+#pragma once
+
+#include <iostream>
+#include <string>
+
+struct CodeLocation {
+	int first_line = -1, first_column = -1, last_line = -1, last_column = -1;
+	std::string filename = "";
+
+	/// Create a new unset CodeLocation.
+	CodeLocation() = default;
+
+	/// Create a new CodeLocation with the given values.
+	CodeLocation( const char* filename, int lineno )
+		: first_line( lineno )
+		, filename(filename ? filename : "")
+	{}
+
+	CodeLocation( const CodeLocation& rhs ) = default;
+	CodeLocation( CodeLocation&& rhs ) = default;
+	CodeLocation& operator=( const CodeLocation & ) = default;
+	CodeLocation& operator=( CodeLocation && ) = default;
+
+	bool isSet () const {
+		return -1 != first_line;
+	}
+
+	bool isUnset () const {
+		return !isSet();
+	}
+
+	bool startsBefore( CodeLocation const & other ) const {
+		if( filename < other.filename ) return true;
+		if( filename > other.filename ) return false;
+
+		if( first_line < other.first_line ) return true;
+		if( first_line > other.first_line ) return false;
+
+		if( last_line < other.last_line ) return true;
+		return false;
+	}
+
+	bool followedBy( CodeLocation const & other, int seperation ) const {
+		return (first_line + seperation == other.first_line &&
+		        filename == other.filename);
+	}
+
+	bool operator==( CodeLocation const & other ) const {
+		return followedBy( other, 0 );
+	}
+
+	bool operator!=( CodeLocation const & other ) const {
+		return !(*this == other);
+	}
+};
+
+inline std::ostream & operator<<( std::ostream & out, const CodeLocation & location ) {
+	// Column number ":1" allows IDEs to parse the error message and position the cursor in the source text.
+	return location.isSet() ? out << location.filename << ":" << location.first_line << ":1 " : out;
+}
Index: src/Common/CodeLocationTools.cpp
===================================================================
--- src/Common/CodeLocationTools.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Common/CodeLocationTools.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,5 +20,5 @@
 #include "AST/Pass.hpp"
 #include "AST/TranslationUnit.hpp"
-#include "Common/CodeLocation.h"
+#include "Common/CodeLocation.hpp"
 
 namespace {
Index: src/Common/DebugMalloc.cc
===================================================================
--- src/Common/DebugMalloc.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,71 +1,0 @@
-#if 0
-#include <dlfcn.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-bool recursion = false;
-
-static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
-
-static union {
-    void *addr;
-    unsigned char bytes[sizeof(void *)];
-};
-
-struct Mallocmsg {
-    const char start[9];
-    char addr[16];
-    const char sep[3];
-    char size[16];
-    const char end[1];
-} mallocmsg = {
-    'm', 'a', 'l', 'l', 'o', 'c', ' ', '0', 'x',
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    ' ', '0', 'x',
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    '\n'
-};
-
-void * malloc( size_t size ) {
-    if ( recursion ) { write( STDERR_FILENO, "recursion\n", 10 ); abort(); }
-    recursion = true;
-    __typeof__( ::malloc ) *libc_malloc = (__typeof__( ::malloc ) *)dlsym(RTLD_NEXT, "malloc");
-    addr = (void *)size;
-    for ( int i = 0, j = 7; i < 16; i += 2, j -= 1 ) {
-    	mallocmsg.size[i] = hex[bytes[j] >> 4];
-    	mallocmsg.size[i + 1] = hex[bytes[j] & 0x0f];
-    } // for
-    addr = libc_malloc( size );
-    for ( int i = 0, j = 7; i < 16; i += 2, j -= 1 ) {
-	mallocmsg.addr[i] = hex[bytes[j] >> 4];
-	mallocmsg.addr[i + 1] = hex[bytes[j] & 0x0f];
-    } // for
-    write( STDERR_FILENO, &mallocmsg, sizeof(mallocmsg) );
-    recursion = false;
-    return addr;
-}
-
-struct Freemsg {
-    const char start[7];
-    char addr[16];
-    const char end[1];
-} freemsg = {
-    'f', 'r', 'e', 'e', ' ', '0', 'x',
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    '\n'
-};
-
-void free( void * x ) {
-    if ( recursion ) { write( STDERR_FILENO, "recursion\n", 10 ); abort(); }
-    recursion = true;
-    __typeof__( ::free ) *libc_free = (__typeof__( ::free ) *)dlsym(RTLD_NEXT, "free");
-    addr = x;
-    for ( int i = 0, j = 7; i < 16; i += 2, j -= 1 ) {
-	freemsg.addr[i] = hex[bytes[j] >> 4];
-	freemsg.addr[i + 1] = hex[bytes[j] & 0x0f];
-    } // for
-    write( STDERR_FILENO, &freemsg, sizeof(freemsg) );
-    recursion = false;
-    libc_free( addr );
-}
-#endif // 0
Index: src/Common/DebugMalloc.cpp
===================================================================
--- src/Common/DebugMalloc.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/DebugMalloc.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,71 @@
+#if 0
+#include <dlfcn.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+bool recursion = false;
+
+static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+static union {
+    void *addr;
+    unsigned char bytes[sizeof(void *)];
+};
+
+struct Mallocmsg {
+    const char start[9];
+    char addr[16];
+    const char sep[3];
+    char size[16];
+    const char end[1];
+} mallocmsg = {
+    'm', 'a', 'l', 'l', 'o', 'c', ' ', '0', 'x',
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    ' ', '0', 'x',
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    '\n'
+};
+
+void * malloc( size_t size ) {
+    if ( recursion ) { write( STDERR_FILENO, "recursion\n", 10 ); abort(); }
+    recursion = true;
+    __typeof__( ::malloc ) *libc_malloc = (__typeof__( ::malloc ) *)dlsym(RTLD_NEXT, "malloc");
+    addr = (void *)size;
+    for ( int i = 0, j = 7; i < 16; i += 2, j -= 1 ) {
+    	mallocmsg.size[i] = hex[bytes[j] >> 4];
+    	mallocmsg.size[i + 1] = hex[bytes[j] & 0x0f];
+    } // for
+    addr = libc_malloc( size );
+    for ( int i = 0, j = 7; i < 16; i += 2, j -= 1 ) {
+	mallocmsg.addr[i] = hex[bytes[j] >> 4];
+	mallocmsg.addr[i + 1] = hex[bytes[j] & 0x0f];
+    } // for
+    write( STDERR_FILENO, &mallocmsg, sizeof(mallocmsg) );
+    recursion = false;
+    return addr;
+}
+
+struct Freemsg {
+    const char start[7];
+    char addr[16];
+    const char end[1];
+} freemsg = {
+    'f', 'r', 'e', 'e', ' ', '0', 'x',
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    '\n'
+};
+
+void free( void * x ) {
+    if ( recursion ) { write( STDERR_FILENO, "recursion\n", 10 ); abort(); }
+    recursion = true;
+    __typeof__( ::free ) *libc_free = (__typeof__( ::free ) *)dlsym(RTLD_NEXT, "free");
+    addr = x;
+    for ( int i = 0, j = 7; i < 16; i += 2, j -= 1 ) {
+	freemsg.addr[i] = hex[bytes[j] >> 4];
+	freemsg.addr[i + 1] = hex[bytes[j] & 0x0f];
+    } // for
+    write( STDERR_FILENO, &freemsg, sizeof(freemsg) );
+    recursion = false;
+    libc_free( addr );
+}
+#endif // 0
Index: src/Common/DeclStats.cpp
===================================================================
--- src/Common/DeclStats.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Common/DeclStats.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -19,5 +19,5 @@
 #include "AST/Pass.hpp"
 #include "AST/Print.hpp"
-#include "Common/VectorMap.h"
+#include "Common/VectorMap.hpp"
 
 #include <iostream>
Index: src/Common/ErrorObjects.h
===================================================================
--- src/Common/ErrorObjects.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,47 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ErrorObjects.h --
-//
-// Author           : Thierry Delisle
-// Created On       : Wed Feb 28 15:16:47 2018
-// Last Modified By :
-// Last Modified On :
-// Update Count     :
-//
-
-#pragma once
-
-
-#include <exception>	// for exception
-#include <iostream>	// for ostream
-#include <list>		// for list
-#include <string>		// for string
-#include <unistd.h>	// for isatty
-
-#include "CodeLocation.h"								// for CodeLocation, toString
-
-struct error {
-	CodeLocation location;
-	std::string description;
-
-	error() = default;
-	error( CodeLocation loc, const std::string & str ) : location( loc ), description( str ) {}
-};
-
-class SemanticErrorException : public std::exception {
-  public:
-	SemanticErrorException() = default;
-	SemanticErrorException( CodeLocation location, std::string error );
-	~SemanticErrorException() throw() {}
-
-	void append( SemanticErrorException & other );
-	void append( CodeLocation location, const std::string & );
-	bool isEmpty() const;
-	void print();
-  private:
-	std::list< error > errors;
-};
Index: src/Common/ErrorObjects.hpp
===================================================================
--- src/Common/ErrorObjects.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/ErrorObjects.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+// ErrorObjects.hpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Wed Feb 28 15:16:47 2018
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#pragma once
+
+
+#include <exception>	// for exception
+#include <iostream>	// for ostream
+#include <list>		// for list
+#include <string>		// for string
+#include <unistd.h>	// for isatty
+
+#include "CodeLocation.hpp"								// for CodeLocation, toString
+
+struct error {
+	CodeLocation location;
+	std::string description;
+
+	error() = default;
+	error( CodeLocation loc, const std::string & str ) : location( loc ), description( str ) {}
+};
+
+class SemanticErrorException : public std::exception {
+  public:
+	SemanticErrorException() = default;
+	SemanticErrorException( CodeLocation location, std::string error );
+	~SemanticErrorException() throw() {}
+
+	void append( SemanticErrorException & other );
+	void append( CodeLocation location, const std::string & );
+	bool isEmpty() const;
+	void print();
+  private:
+	std::list< error > errors;
+};
Index: src/Common/Eval.cc
===================================================================
--- src/Common/Eval.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,217 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Eval.cc -- Evaluate parts of the ast at compile time.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Aug  6 12:11:59 2022
-// Update Count     : 119
-//
-
-#include "Eval.h"
-
-#include <utility> // for pair
-
-#include "AST/Inspect.hpp"
-#include "CodeGen/OperatorTable.h"						// access: OperatorInfo
-#include "AST/Pass.hpp"
-#include "InitTweak/InitTweak.h"
-
-struct EvalNew : public ast::WithShortCircuiting {
-	Evaluation result = { 0, true, true };
-
-	void previsit( const ast::Node * ) { visit_children = false; }
-	void postvisit( const ast::Node * ) { result.isEvaluableInGCC = result.hasKnownValue = false; }
-
-	void postvisit( const ast::UntypedExpr * ) {
-		assertf( false, "UntypedExpr in constant expression evaluation" ); // FIX ME, resolve variable
-	}
-
-	void postvisit( const ast::ConstantExpr * expr ) {	// only handle int constants
-		result.knownValue = expr->intValue();
-		result.hasKnownValue = true;
-		result.isEvaluableInGCC = true;
-	}
-
-	void postvisit( const ast::SizeofExpr * ) {
-		result.hasKnownValue = false;
-		result.isEvaluableInGCC = true;
-	}
-
-	void postvisit( const ast::AlignofExpr * ) {
-		result.hasKnownValue = false;
-		result.isEvaluableInGCC = true;
-	}
-
-	void postvisit( const ast::OffsetofExpr * ) {
-		result.hasKnownValue = false;
-		result.isEvaluableInGCC = true;
-	}
-
-	void postvisit( const ast::LogicalExpr * expr ) {
-		Evaluation arg1, arg2;
-		arg1 = eval( expr->arg1 );
-		result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
-		if ( ! result.isEvaluableInGCC ) return;
-		arg2 = eval( expr->arg2 );
-		result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
-		if ( ! result.isEvaluableInGCC ) return;
-
-		result.hasKnownValue &= arg1.hasKnownValue;
-		result.hasKnownValue &= arg2.hasKnownValue;
-		if ( ! result.hasKnownValue ) return;
-
-		if ( expr->isAnd ) {
-			result.knownValue = arg1.knownValue && arg2.knownValue;
-		} else {
-			result.knownValue = arg1.knownValue || arg2.knownValue;
-		} // if
-	}
-
-	void postvisit( const ast::ConditionalExpr * expr ) {
-		Evaluation arg1, arg2, arg3;
-		arg1 = eval( expr->arg1 );
-		result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
-		if ( ! result.isEvaluableInGCC ) return;
-		arg2 = eval( expr->arg2 );
-		result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
-		if ( ! result.isEvaluableInGCC ) return;
-		arg3 = eval( expr->arg3 );
-		result.isEvaluableInGCC &= arg3.isEvaluableInGCC;
-		if ( ! result.isEvaluableInGCC ) return;
-
-		result.hasKnownValue &= arg1.hasKnownValue;
-		result.hasKnownValue &= arg2.hasKnownValue;
-		result.hasKnownValue &= arg3.hasKnownValue;
-		if ( ! result.hasKnownValue ) return;
-
-		result.knownValue = arg1.knownValue ? arg2.knownValue : arg3.knownValue;
-	}
-
-	void postvisit( const ast::CastExpr * expr ) {		
-		// cfa-cc generates a cast before every constant and many other places, e.g., (int)3, 
-		// so we must use the value from the cast argument, even though we lack any basis for evaluating wraparound effects, etc
-		result = eval(expr->arg);
-	}
-
-	void postvisit( const ast::VariableExpr * expr ) {
-		result.hasKnownValue = false;
-		result.isEvaluableInGCC = false;
-		if ( const ast::EnumInstType * inst = dynamic_cast<const ast::EnumInstType *>(expr->result.get()) ) {
-			if ( const ast::EnumDecl * decl = inst->base ) {
-				result.isEvaluableInGCC = true;
-				result.hasKnownValue = decl->valueOf( expr->var, result.knownValue ); // result.knownValue filled by valueOf
-			}
-		}
-	}
-
-	void postvisit( const ast::ApplicationExpr * expr ) {
-		const ast::DeclWithType * function = ast::getFunction(expr);
-		if ( ! function || function->linkage != ast::Linkage::Intrinsic ) { 
-			result.isEvaluableInGCC = false;
-			result.hasKnownValue = false;
-			return;
-		}
-		const std::string & fname = function->name;
-		assertf( expr->args.size() == 1 || expr->args.size() == 2, "Intrinsic function with %zd arguments: %s", expr->args.size(), fname.c_str() );
-
-		if ( expr->args.size() == 1 ) {
-			// pre/postfix operators ++ and -- => assignment, which is not constant
-			Evaluation arg1;
-			arg1 = eval(expr->args.front());
-			result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
-			if ( ! result.isEvaluableInGCC ) return;
-
-			result.hasKnownValue &= arg1.hasKnownValue;
-			if ( ! result.hasKnownValue ) return;
-
-			if (fname == "+?") {
-				result.knownValue = arg1.knownValue;
-			} else if (fname == "-?") {
-				result.knownValue = -arg1.knownValue;
-			} else if (fname == "~?") {
-				result.knownValue = ~arg1.knownValue;
-			} else if (fname == "!?") {
-				result.knownValue = ! arg1.knownValue;
-			} else {
-				result.isEvaluableInGCC = false;
-				result.hasKnownValue = false;
-			} // if
-		} else { // => expr->args.size() == 2
-			// infix assignment operators => assignment, which is not constant
-			Evaluation arg1, arg2;
-			arg1 = eval(expr->args.front());
-			result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
-			if ( ! result.isEvaluableInGCC ) return;
-			arg2 = eval(expr->args.back());
-			result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
-			if ( ! result.isEvaluableInGCC ) return;
-
-			result.hasKnownValue &= arg1.hasKnownValue;
-			result.hasKnownValue &= arg2.hasKnownValue;
-			if ( ! result.hasKnownValue ) return;
-
-			if (fname == "?+?") {
-				result.knownValue = arg1.knownValue + arg2.knownValue;
-			} else if (fname == "?-?") {
-				result.knownValue = arg1.knownValue - arg2.knownValue;
-			} else if (fname == "?*?") {
-				result.knownValue = arg1.knownValue * arg2.knownValue;
-			} else if (fname == "?/?") {
-				if ( arg2.knownValue ) result.knownValue = arg1.knownValue / arg2.knownValue;
-			} else if (fname == "?%?") {
-				if ( arg2.knownValue ) result.knownValue = arg1.knownValue % arg2.knownValue;
-			} else if (fname == "?<<?") {
-				result.knownValue = arg1.knownValue << arg2.knownValue;
-			} else if (fname == "?>>?") {
-				result.knownValue = arg1.knownValue >> arg2.knownValue;
-			} else if (fname == "?<?") {
-				result.knownValue = arg1.knownValue < arg2.knownValue;
-			} else if (fname == "?>?") {
-				result.knownValue = arg1.knownValue > arg2.knownValue;
-			} else if (fname == "?<=?") {
-				result.knownValue = arg1.knownValue <= arg2.knownValue;
-			} else if (fname == "?>=?") {
-				result.knownValue = arg1.knownValue >= arg2.knownValue;
-			} else if (fname == "?==?") {
-				result.knownValue = arg1.knownValue == arg2.knownValue;
-			} else if (fname == "?!=?") {
-				result.knownValue = arg1.knownValue != arg2.knownValue;
-			} else if (fname == "?&?") {
-				result.knownValue = arg1.knownValue & arg2.knownValue;
-			} else if (fname == "?^?") {
-				result.knownValue = arg1.knownValue ^ arg2.knownValue;
-			} else if (fname == "?|?") {
-				result.knownValue = arg1.knownValue | arg2.knownValue;
-			} else {
-				result.isEvaluableInGCC = false;
-				result.hasKnownValue = false;
-			}
-		} // if
-		// TODO: implement other intrinsic functions
-	}
-};
-
-Evaluation eval( const ast::Expr * expr ) {
-	if ( expr ) {
-
-		return ast::Pass<EvalNew>::read(expr);
-		// Evaluation ret = ast::Pass<EvalNew>::read(expr);
-		// ret.knownValue = 777;
-		// return ret;
-
-	} else {
-		return { 0, false, false };
-	}
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/Eval.cpp
===================================================================
--- src/Common/Eval.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Eval.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,217 @@
+//
+// 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.
+//
+// Eval.cpp -- Evaluate parts of the ast at compile time.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Aug  6 12:11:59 2022
+// Update Count     : 119
+//
+
+#include "Eval.hpp"
+
+#include <utility> // for pair
+
+#include "AST/Inspect.hpp"
+#include "CodeGen/OperatorTable.hpp"						// access: OperatorInfo
+#include "AST/Pass.hpp"
+#include "InitTweak/InitTweak.hpp"
+
+struct EvalNew : public ast::WithShortCircuiting {
+	Evaluation result = { 0, true, true };
+
+	void previsit( const ast::Node * ) { visit_children = false; }
+	void postvisit( const ast::Node * ) { result.isEvaluableInGCC = result.hasKnownValue = false; }
+
+	void postvisit( const ast::UntypedExpr * ) {
+		assertf( false, "UntypedExpr in constant expression evaluation" ); // FIX ME, resolve variable
+	}
+
+	void postvisit( const ast::ConstantExpr * expr ) {	// only handle int constants
+		result.knownValue = expr->intValue();
+		result.hasKnownValue = true;
+		result.isEvaluableInGCC = true;
+	}
+
+	void postvisit( const ast::SizeofExpr * ) {
+		result.hasKnownValue = false;
+		result.isEvaluableInGCC = true;
+	}
+
+	void postvisit( const ast::AlignofExpr * ) {
+		result.hasKnownValue = false;
+		result.isEvaluableInGCC = true;
+	}
+
+	void postvisit( const ast::OffsetofExpr * ) {
+		result.hasKnownValue = false;
+		result.isEvaluableInGCC = true;
+	}
+
+	void postvisit( const ast::LogicalExpr * expr ) {
+		Evaluation arg1, arg2;
+		arg1 = eval( expr->arg1 );
+		result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
+		if ( ! result.isEvaluableInGCC ) return;
+		arg2 = eval( expr->arg2 );
+		result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
+		if ( ! result.isEvaluableInGCC ) return;
+
+		result.hasKnownValue &= arg1.hasKnownValue;
+		result.hasKnownValue &= arg2.hasKnownValue;
+		if ( ! result.hasKnownValue ) return;
+
+		if ( expr->isAnd ) {
+			result.knownValue = arg1.knownValue && arg2.knownValue;
+		} else {
+			result.knownValue = arg1.knownValue || arg2.knownValue;
+		} // if
+	}
+
+	void postvisit( const ast::ConditionalExpr * expr ) {
+		Evaluation arg1, arg2, arg3;
+		arg1 = eval( expr->arg1 );
+		result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
+		if ( ! result.isEvaluableInGCC ) return;
+		arg2 = eval( expr->arg2 );
+		result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
+		if ( ! result.isEvaluableInGCC ) return;
+		arg3 = eval( expr->arg3 );
+		result.isEvaluableInGCC &= arg3.isEvaluableInGCC;
+		if ( ! result.isEvaluableInGCC ) return;
+
+		result.hasKnownValue &= arg1.hasKnownValue;
+		result.hasKnownValue &= arg2.hasKnownValue;
+		result.hasKnownValue &= arg3.hasKnownValue;
+		if ( ! result.hasKnownValue ) return;
+
+		result.knownValue = arg1.knownValue ? arg2.knownValue : arg3.knownValue;
+	}
+
+	void postvisit( const ast::CastExpr * expr ) {		
+		// cfa-cc generates a cast before every constant and many other places, e.g., (int)3, 
+		// so we must use the value from the cast argument, even though we lack any basis for evaluating wraparound effects, etc
+		result = eval(expr->arg);
+	}
+
+	void postvisit( const ast::VariableExpr * expr ) {
+		result.hasKnownValue = false;
+		result.isEvaluableInGCC = false;
+		if ( const ast::EnumInstType * inst = dynamic_cast<const ast::EnumInstType *>(expr->result.get()) ) {
+			if ( const ast::EnumDecl * decl = inst->base ) {
+				result.isEvaluableInGCC = true;
+				result.hasKnownValue = decl->valueOf( expr->var, result.knownValue ); // result.knownValue filled by valueOf
+			}
+		}
+	}
+
+	void postvisit( const ast::ApplicationExpr * expr ) {
+		const ast::DeclWithType * function = ast::getFunction(expr);
+		if ( ! function || function->linkage != ast::Linkage::Intrinsic ) { 
+			result.isEvaluableInGCC = false;
+			result.hasKnownValue = false;
+			return;
+		}
+		const std::string & fname = function->name;
+		assertf( expr->args.size() == 1 || expr->args.size() == 2, "Intrinsic function with %zd arguments: %s", expr->args.size(), fname.c_str() );
+
+		if ( expr->args.size() == 1 ) {
+			// pre/postfix operators ++ and -- => assignment, which is not constant
+			Evaluation arg1;
+			arg1 = eval(expr->args.front());
+			result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
+			if ( ! result.isEvaluableInGCC ) return;
+
+			result.hasKnownValue &= arg1.hasKnownValue;
+			if ( ! result.hasKnownValue ) return;
+
+			if (fname == "+?") {
+				result.knownValue = arg1.knownValue;
+			} else if (fname == "-?") {
+				result.knownValue = -arg1.knownValue;
+			} else if (fname == "~?") {
+				result.knownValue = ~arg1.knownValue;
+			} else if (fname == "!?") {
+				result.knownValue = ! arg1.knownValue;
+			} else {
+				result.isEvaluableInGCC = false;
+				result.hasKnownValue = false;
+			} // if
+		} else { // => expr->args.size() == 2
+			// infix assignment operators => assignment, which is not constant
+			Evaluation arg1, arg2;
+			arg1 = eval(expr->args.front());
+			result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
+			if ( ! result.isEvaluableInGCC ) return;
+			arg2 = eval(expr->args.back());
+			result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
+			if ( ! result.isEvaluableInGCC ) return;
+
+			result.hasKnownValue &= arg1.hasKnownValue;
+			result.hasKnownValue &= arg2.hasKnownValue;
+			if ( ! result.hasKnownValue ) return;
+
+			if (fname == "?+?") {
+				result.knownValue = arg1.knownValue + arg2.knownValue;
+			} else if (fname == "?-?") {
+				result.knownValue = arg1.knownValue - arg2.knownValue;
+			} else if (fname == "?*?") {
+				result.knownValue = arg1.knownValue * arg2.knownValue;
+			} else if (fname == "?/?") {
+				if ( arg2.knownValue ) result.knownValue = arg1.knownValue / arg2.knownValue;
+			} else if (fname == "?%?") {
+				if ( arg2.knownValue ) result.knownValue = arg1.knownValue % arg2.knownValue;
+			} else if (fname == "?<<?") {
+				result.knownValue = arg1.knownValue << arg2.knownValue;
+			} else if (fname == "?>>?") {
+				result.knownValue = arg1.knownValue >> arg2.knownValue;
+			} else if (fname == "?<?") {
+				result.knownValue = arg1.knownValue < arg2.knownValue;
+			} else if (fname == "?>?") {
+				result.knownValue = arg1.knownValue > arg2.knownValue;
+			} else if (fname == "?<=?") {
+				result.knownValue = arg1.knownValue <= arg2.knownValue;
+			} else if (fname == "?>=?") {
+				result.knownValue = arg1.knownValue >= arg2.knownValue;
+			} else if (fname == "?==?") {
+				result.knownValue = arg1.knownValue == arg2.knownValue;
+			} else if (fname == "?!=?") {
+				result.knownValue = arg1.knownValue != arg2.knownValue;
+			} else if (fname == "?&?") {
+				result.knownValue = arg1.knownValue & arg2.knownValue;
+			} else if (fname == "?^?") {
+				result.knownValue = arg1.knownValue ^ arg2.knownValue;
+			} else if (fname == "?|?") {
+				result.knownValue = arg1.knownValue | arg2.knownValue;
+			} else {
+				result.isEvaluableInGCC = false;
+				result.hasKnownValue = false;
+			}
+		} // if
+		// TODO: implement other intrinsic functions
+	}
+};
+
+Evaluation eval( const ast::Expr * expr ) {
+	if ( expr ) {
+
+		return ast::Pass<EvalNew>::read(expr);
+		// Evaluation ret = ast::Pass<EvalNew>::read(expr);
+		// ret.knownValue = 777;
+		// return ret;
+
+	} else {
+		return { 0, false, false };
+	}
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/Eval.h
===================================================================
--- src/Common/Eval.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,38 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Eval.h -- Evaluate parts of the ast at compile time.
-//
-// Author           : Andrew Beach
-// Created On       : Fri Feb 17 11:41:00 2023
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Feb 17 11:41:00 2023
-// Update Count     : 0
-//
-
-#pragma once
-
-#include <utility>                 // for pair
-
-class Expression;
-namespace ast {
-	class Expr;
-}
-
-struct Evaluation {
-	long long int knownValue;
-	bool hasKnownValue;
-	bool isEvaluableInGCC;
-};
-
-/// Evaluates expr as a long long int.
-Evaluation eval(const ast::Expr * expr);
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/Eval.hpp
===================================================================
--- src/Common/Eval.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Eval.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,38 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Eval.hpp -- Evaluate parts of the ast at compile time.
+//
+// Author           : Andrew Beach
+// Created On       : Fri Feb 17 11:41:00 2023
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Feb 17 11:41:00 2023
+// Update Count     : 0
+//
+
+#pragma once
+
+#include <utility>                 // for pair
+
+class Expression;
+namespace ast {
+	class Expr;
+}
+
+struct Evaluation {
+	long long int knownValue;
+	bool hasKnownValue;
+	bool isEvaluableInGCC;
+};
+
+/// Evaluates expr as a long long int.
+Evaluation eval(const ast::Expr * expr);
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/Examine.cc
===================================================================
--- src/Common/Examine.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,70 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Examine.cc -- Helpers for examining AST code.
-//
-// Author           : Andrew Beach
-// Created On       : Wed Sept 2 14:02 2020
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Dec 10 10:27 2021
-// Update Count     : 1
-//
-
-#include "Common/Examine.h"
-
-#include "AST/Type.hpp"
-#include "CodeGen/OperatorTable.h"
-#include "InitTweak/InitTweak.h"
-
-namespace {
-
-// getTypeofThis but does some extra checks used in this module.
-const ast::Type * getTypeofThisSolo( const ast::FunctionDecl * func ) {
-	if ( 1 != func->params.size() ) {
-		return nullptr;
-	}
-	auto ref = func->type->params.front().as<ast::ReferenceType>();
-	return (ref) ? ref->base : nullptr;
-}
-
-}
-
-const ast::DeclWithType * isMainFor(
-		const ast::FunctionDecl * func, ast::AggregateDecl::Aggregate kind ) {
-	if ( "main" != func->name ) return nullptr;
-	if ( 1 != func->params.size() ) return nullptr;
-
-	auto param = func->params.front();
-
-	auto type = dynamic_cast<const ast::ReferenceType *>( param->get_type() );
-	if ( !type ) return nullptr;
-
-	auto obj = type->base.as<ast::StructInstType>();
-	if ( !obj ) return nullptr;
-
-	if ( kind != obj->base->kind ) return nullptr;
-
-	return param;
-}
-
-namespace {
-
-const ast::Type * getDestructorParam( const ast::FunctionDecl * func ) {
-	if ( !CodeGen::isDestructor( func->name ) ) return nullptr;
-	//return InitTweak::getParamThis( func )->type;
-	return getTypeofThisSolo( func );
-}
-
-}
-
-bool isDestructorFor(
-		const ast::FunctionDecl * func, const ast::StructDecl * type_decl ) {
-	if ( const ast::Type * type = getDestructorParam( func ) ) {
-		auto stype = dynamic_cast<const ast::StructInstType *>( type );
-		return stype && stype->base.get() == type_decl;
-	}
-	return false;
-}
Index: src/Common/Examine.cpp
===================================================================
--- src/Common/Examine.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Examine.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,69 @@
+//
+// 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.
+//
+// Examine.cpp -- Helpers for examining AST code.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Sept 2 14:02 2020
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Dec 10 10:27 2021
+// Update Count     : 1
+//
+
+#include "Common/Examine.hpp"
+
+#include "AST/Type.hpp"
+#include "CodeGen/OperatorTable.hpp"
+#include "InitTweak/InitTweak.hpp"
+
+namespace {
+
+// getTypeofThis but does some extra checks used in this module.
+const ast::Type * getTypeofThisSolo( const ast::FunctionDecl * func ) {
+	if ( 1 != func->params.size() ) {
+		return nullptr;
+	}
+	auto ref = func->type->params.front().as<ast::ReferenceType>();
+	return (ref) ? ref->base : nullptr;
+}
+
+}
+
+const ast::DeclWithType * isMainFor(
+		const ast::FunctionDecl * func, ast::AggregateDecl::Aggregate kind ) {
+	if ( "main" != func->name ) return nullptr;
+	if ( 1 != func->params.size() ) return nullptr;
+
+	auto param = func->params.front();
+
+	auto type = dynamic_cast<const ast::ReferenceType *>( param->get_type() );
+	if ( !type ) return nullptr;
+
+	auto obj = type->base.as<ast::StructInstType>();
+	if ( !obj ) return nullptr;
+
+	if ( kind != obj->base->kind ) return nullptr;
+
+	return param;
+}
+
+namespace {
+
+const ast::Type * getDestructorParam( const ast::FunctionDecl * func ) {
+	if ( !CodeGen::isDestructor( func->name ) ) return nullptr;
+	return getTypeofThisSolo( func );
+}
+
+}
+
+bool isDestructorFor(
+		const ast::FunctionDecl * func, const ast::StructDecl * type_decl ) {
+	if ( const ast::Type * type = getDestructorParam( func ) ) {
+		auto stype = dynamic_cast<const ast::StructInstType *>( type );
+		return stype && stype->base.get() == type_decl;
+	}
+	return false;
+}
Index: src/Common/Examine.h
===================================================================
--- src/Common/Examine.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,25 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Examine.h -- Helpers for examining AST code.
-//
-// Author           : Andrew Beach
-// Created On       : Wed Sept 2 13:57 2020
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Dec 10 10:28 2021
-// Update Count     : 1
-//
-
-#include "AST/Decl.hpp"
-
-/// Check if this is a main function for a type of an aggregate kind.
-const ast::DeclWithType * isMainFor(
-	const ast::FunctionDecl * func, ast::AggregateDecl::Aggregate kind );
-// Returns a pointer to the parameter if true, nullptr otherwise.
-
-/// Check if this function is a destructor for the given structure.
-bool isDestructorFor(
-	const ast::FunctionDecl * func, const ast::StructDecl * type );
Index: src/Common/Examine.hpp
===================================================================
--- src/Common/Examine.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Examine.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,25 @@
+//
+// 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.
+//
+// Examine.hpp -- Helpers for examining AST code.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Sept 2 13:57 2020
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Dec 10 10:28 2021
+// Update Count     : 1
+//
+
+#include "AST/Decl.hpp"
+
+/// Check if this is a main function for a type of an aggregate kind.
+const ast::DeclWithType * isMainFor(
+	const ast::FunctionDecl * func, ast::AggregateDecl::Aggregate kind );
+// Returns a pointer to the parameter if true, nullptr otherwise.
+
+/// Check if this function is a destructor for the given structure.
+bool isDestructorFor(
+	const ast::FunctionDecl * func, const ast::StructDecl * type );
Index: src/Common/FilterCombos.h
===================================================================
--- src/Common/FilterCombos.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,103 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FilterCombos.h --
-//
-// Author           : Aaron B. Moss
-// Created On       : Mon Jul 23 16:05:00 2018
-// Last Modified By : Aaron B. Moss
-// Last Modified On : Mon Jul 23 16:05:00 2018
-// Update Count     : 1
-//
-
-#pragma once
-
-#include <vector>
-
-/// Type of index vector for combinations
-typedef std::vector<unsigned> Indices;
-
-/// Combo iterator that simply collects values into a vector, marking all values as valid.
-/// Prefer combos in typeops.h to use of IntoVectorComboIter with filterCombos
-/// @param T	The element type of the vector.
-template<typename T>
-class IntoVectorComboIter {
-	std::vector<T> crnt_combo;
-public:
-	/// Outputs a vector of T
-	using OutType = std::vector<T>;
-
-	/// Adds the element to the current combination, always returning true for valid.
-	bool append( const T& x ) {
-		crnt_combo.push_back( x );
-		return true;
-	}
-
-	/// Removes the last element of the current combination.
-	void backtrack() { crnt_combo.pop_back(); }
-
-	/// Returns a copy of the current combination.
-	OutType finalize() { return crnt_combo; }
-};
-
-/// Filters combinations from qs by the given combo iterator. If the iterator rejects some prefix 
-/// of a combination, it will not accept any combination with that prefix.
-/// qs should not be empty
-template<typename Q, typename ComboIter>
-auto filterCombos( const std::vector<Q>& qs, ComboIter&& iter )
-		-> std::vector<typename ComboIter::OutType> {
-    unsigned n = qs.size();  // number of queues to combine
-
-	std::vector<typename ComboIter::OutType> out;  // filtered output
-	for ( auto& q : qs ) if ( q.empty() ) return out;  // return empty if any empty queue
-
-	Indices inds;
-	inds.reserve( n );
-	inds.push_back( 0 );
-
-	while (true) {
-		unsigned i = inds.size() - 1;
-
-		// iterate or keep successful match
-		if ( iter.append( qs[i][inds.back()] ) ) {
-			if ( i + 1 == n ) {
-				// keep successful match of entire combination and continue iterating final place
-				out.push_back( iter.finalize() );
-				iter.backtrack();
-			} else {
-				// try to extend successful prefix
-				inds.push_back( 0 );
-				continue;
-			}
-		}
-
-		// move to next combo
-		++inds.back();
-		if ( inds.back() < qs[i].size() ) continue;
-		
-		// Otherwise done with the current row.
-		// At this point, an invalid prefix is stored in inds and iter is in a state looking at 
-		// all but the final value in inds. Now backtrack to next prefix:
-		inds.pop_back();
-		while ( ! inds.empty() ) {
-			// try the next value at the previous index
-			++inds.back();
-			iter.backtrack();
-			if ( inds.back() < qs[inds.size()-1].size() ) break;
-			// if the previous index is finished, backtrack out
-			inds.pop_back();
-		}
-
-		// Done if backtracked the entire array
-		if ( inds.empty() ) return out;
-	}
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/FilterCombos.hpp
===================================================================
--- src/Common/FilterCombos.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/FilterCombos.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,103 @@
+//
+// 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.
+//
+// FilterCombos.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Mon Jul 23 16:05:00 2018
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Mon Jul 23 16:05:00 2018
+// Update Count     : 1
+//
+
+#pragma once
+
+#include <vector>
+
+/// Type of index vector for combinations
+typedef std::vector<unsigned> Indices;
+
+/// Combo iterator that simply collects values into a vector, marking all values as valid.
+/// Prefer combos in typeops.h to use of IntoVectorComboIter with filterCombos
+/// @param T	The element type of the vector.
+template<typename T>
+class IntoVectorComboIter {
+	std::vector<T> crnt_combo;
+public:
+	/// Outputs a vector of T
+	using OutType = std::vector<T>;
+
+	/// Adds the element to the current combination, always returning true for valid.
+	bool append( const T& x ) {
+		crnt_combo.push_back( x );
+		return true;
+	}
+
+	/// Removes the last element of the current combination.
+	void backtrack() { crnt_combo.pop_back(); }
+
+	/// Returns a copy of the current combination.
+	OutType finalize() { return crnt_combo; }
+};
+
+/// Filters combinations from qs by the given combo iterator. If the iterator rejects some prefix 
+/// of a combination, it will not accept any combination with that prefix.
+/// qs should not be empty
+template<typename Q, typename ComboIter>
+auto filterCombos( const std::vector<Q>& qs, ComboIter&& iter )
+		-> std::vector<typename ComboIter::OutType> {
+    unsigned n = qs.size();  // number of queues to combine
+
+	std::vector<typename ComboIter::OutType> out;  // filtered output
+	for ( auto& q : qs ) if ( q.empty() ) return out;  // return empty if any empty queue
+
+	Indices inds;
+	inds.reserve( n );
+	inds.push_back( 0 );
+
+	while (true) {
+		unsigned i = inds.size() - 1;
+
+		// iterate or keep successful match
+		if ( iter.append( qs[i][inds.back()] ) ) {
+			if ( i + 1 == n ) {
+				// keep successful match of entire combination and continue iterating final place
+				out.push_back( iter.finalize() );
+				iter.backtrack();
+			} else {
+				// try to extend successful prefix
+				inds.push_back( 0 );
+				continue;
+			}
+		}
+
+		// move to next combo
+		++inds.back();
+		if ( inds.back() < qs[i].size() ) continue;
+		
+		// Otherwise done with the current row.
+		// At this point, an invalid prefix is stored in inds and iter is in a state looking at 
+		// all but the final value in inds. Now backtrack to next prefix:
+		inds.pop_back();
+		while ( ! inds.empty() ) {
+			// try the next value at the previous index
+			++inds.back();
+			iter.backtrack();
+			if ( inds.back() < qs[inds.size()-1].size() ) break;
+			// if the previous index is finished, backtrack out
+			inds.pop_back();
+		}
+
+		// Done if backtracked the entire array
+		if ( inds.empty() ) return out;
+	}
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/Indenter.cc
===================================================================
--- src/Common/Indenter.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,24 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Indenter.cc --
-//
-// Author           : Andrew Beach
-// Created On       : Fri May 13 14:03:00 2022
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri May 13 14:03:00 2022
-// Update Count     : 0
-//
-
-#include "Indenter.h"
-
-unsigned Indenter::tabsize = 2;
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/Indenter.cpp
===================================================================
--- src/Common/Indenter.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Indenter.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,24 @@
+//
+// 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.
+//
+// Indenter.cpp --
+//
+// Author           : Andrew Beach
+// Created On       : Fri May 13 14:03:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri May 13 14:03:00 2022
+// Update Count     : 0
+//
+
+#include "Indenter.hpp"
+
+unsigned Indenter::tabsize = 2;
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/Indenter.h
===================================================================
--- src/Common/Indenter.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,39 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Indenter.h --
-//
-// Author           : Rob Schluntz
-// Created On       : Fri Jun 30 16:55:23 2017
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri May 13 14:10:00 2022
-// Update Count     : 2
-//
-
-#pragma once
-
-#include <ostream>
-
-struct Indenter {
-	static unsigned tabsize;  ///< default number of spaces in one level of indentation
-
-	unsigned int indent;      ///< number of spaces to indent
-	unsigned int amt;         ///< spaces in one level of indentation
-
-	Indenter( unsigned int indent = 0, unsigned int amt = tabsize )
-	: indent( indent ), amt( amt ) {}
-
-	Indenter & operator+=(int nlevels) { indent += nlevels; return *this; }
-	Indenter & operator-=(int nlevels) { indent -= nlevels; return *this; }
-	Indenter operator+(int nlevels) { Indenter indenter = *this; return indenter += nlevels; }
-	Indenter operator-(int nlevels) { Indenter indenter = *this; return indenter -= nlevels; }
-	Indenter & operator++() { return *this += 1; }
-	Indenter & operator--() { return *this -= 1; }
-};
-
-inline std::ostream & operator<<( std::ostream & out, const Indenter & indent ) {
-	return out << std::string(indent.indent * indent.amt, ' ');
-}
Index: src/Common/Indenter.hpp
===================================================================
--- src/Common/Indenter.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Indenter.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,39 @@
+//
+// 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.
+//
+// Indenter.hpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Fri Jun 30 16:55:23 2017
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri May 13 14:10:00 2022
+// Update Count     : 2
+//
+
+#pragma once
+
+#include <ostream>
+
+struct Indenter {
+	static unsigned tabsize;  ///< default number of spaces in one level of indentation
+
+	unsigned int indent;      ///< number of spaces to indent
+	unsigned int amt;         ///< spaces in one level of indentation
+
+	Indenter( unsigned int indent = 0, unsigned int amt = tabsize )
+	: indent( indent ), amt( amt ) {}
+
+	Indenter & operator+=(int nlevels) { indent += nlevels; return *this; }
+	Indenter & operator-=(int nlevels) { indent -= nlevels; return *this; }
+	Indenter operator+(int nlevels) { Indenter indenter = *this; return indenter += nlevels; }
+	Indenter operator-(int nlevels) { Indenter indenter = *this; return indenter -= nlevels; }
+	Indenter & operator++() { return *this += 1; }
+	Indenter & operator--() { return *this -= 1; }
+};
+
+inline std::ostream & operator<<( std::ostream & out, const Indenter & indent ) {
+	return out << std::string(indent.indent * indent.amt, ' ');
+}
Index: src/Common/PersistentMap.h
===================================================================
--- src/Common/PersistentMap.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,292 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// PersistentMap.h --
-//
-// Author           : Aaron B. Moss
-// Created On       : Thu Mar  7 15:50:00 2019
-// Last Modified By : Aaron B. Moss
-// Last Modified On : Thu Mar  7 15:50:00 2019
-// Update Count     : 1
-//
-
-#pragma once
-
-#include <cassert>        // for assertf
-#include <cstddef>        // for size_t
-#include <functional>     // for hash, equal_to
-#include <memory>         // for shared_ptr, enable_shared_from_this, make_shared
-#include <unordered_map>  // for unordered_map
-#include <utility>        // for forward, move
-
-/// Wraps a hash table in a persistent data structure, using a technique based
-/// on the persistent array in Conchon & Filliatre "A Persistent Union-Find
-/// Data Structure"
-
-template<typename Key, typename Val,
-		typename Hash = std::hash<Key>, typename Eq = std::equal_to<Key>>
-class PersistentMap
-	: public std::enable_shared_from_this<PersistentMap<Key, Val, Hash, Eq>> {
-public:
-	/// Type of this class
-	using Self = PersistentMap<Key, Val, Hash, Eq>;
-	/// Type of pointer to this class
-	using Ptr = std::shared_ptr<Self>;
-
-	/// Types of version nodes
-	enum Mode {
-		BASE,  ///< Root node of version tree
-		REM,   ///< Key removal node
-		INS,   ///< Key update node
-		UPD    ///< Key update node
-	};
-
-private:
-	using Base = std::unordered_map<Key, Val, Hash, Eq>;
-
-	/// Insertion/update node
-	struct Ins {
-		Ptr base;  ///< Modified map
-		Key key;   ///< Key inserted
-		Val val;   ///< Value stored
-
-		template<typename P, typename K, typename V>
-		Ins(P&& p, K&& k, V&& v)
-		: base(std::forward<P>(p)), key(std::forward<K>(k)), val(std::forward<V>(v)) {}
-	};
-
-	/// Removal node
-	struct Rem {
-		Ptr base;  ///< Modified map
-		Key key;   ///< Key removed
-
-		template<typename P, typename K>
-		Rem(P&& p, K&& k) : base(std::forward<P>(p)), key(std::forward<K>(k)) {}
-	};
-
-	/// Underlying storage
-	union Data {
-		char def;
-		Base base;
-		Ins ins;
-		Rem rem;
-
-		Data() : def('\0') {}
-		~Data() {}
-	} data;
-
-	/// Type of node
-	mutable Mode mode;
-
-	/// get mutable reference as T
-	template<typename T>
-	T& as() { return reinterpret_cast<T&>(data); }
-
-	/// get const reference as T
-	template<typename T>
-	const T& as() const { return reinterpret_cast<const T&>(data); }
-
-	/// get rvalue reference as T
-	template<typename T>
-	T&& take_as() { return std::move(as<T>()); }
-
-	/// initialize as T
-	template<typename T, typename... Args>
-	void init( Args&&... args ) {
-		new( &as<T>() ) T { std::forward<Args>(args)... };
-	}
-
-	/// reset as current mode
-	void reset() {
-		switch( mode ) {
-			case BASE:          as<Base>().~Base(); break;
-			case REM:           as<Rem>().~Rem();   break;
-			case INS: case UPD: as<Ins>().~Ins();   break;
-		}
-	}
-
-	/// reset as base
-	void reset_as_base() {
-		as<Base>().~Base();
-	}
-
-public:
-	using key_type = typename Base::key_type;
-	using mapped_type = typename Base::mapped_type;
-	using value_type = typename Base::value_type;
-	using size_type = typename Base::size_type;
-	using difference_type = typename Base::difference_type;
-	using iterator = typename Base::const_iterator;
-
-	PersistentMap() : data(), mode(BASE) { init<Base>(); }
-
-	PersistentMap( Base&& b ) : data(), mode(BASE) { init<Base>(std::move(b)); }
-
-	PersistentMap( const Self& o ) = delete;
-
-	Self& operator= ( const Self& o ) = delete;
-
-	~PersistentMap() { reset(); }
-
-	/// Create a pointer to a new, empty persistent map
-	static Ptr new_ptr() { return std::make_shared<Self>(); }
-
-	/// reroot persistent map at current node
-	void reroot() const {
-		// recursive base case
-		if ( mode == BASE ) return;
-
-		// reroot base
-		Self* mut_this = const_cast<Self*>(this);
-		Ptr base = ( mode == REM ) ? mut_this->as<Rem>().base : mut_this->as<Ins>().base;
-		base->reroot();
-
-		// remove map from base
-		Base base_map = base->template take_as<Base>();
-		base->reset_as_base();
-
-		// switch base to inverse of self and mutate base map
-		switch ( mode ) {
-			case REM: {
-				Rem& self = mut_this->as<Rem>();
-				auto it = base_map.find( self.key );
-
-				base->template init<Ins>(
-						mut_this->shared_from_this(), std::move(self.key), std::move(it->second) );
-				base->mode = INS;
-
-				base_map.erase( it );
-				break;
-			}
-			case INS: {
-				Ins& self = mut_this->as<Ins>();
-
-				base->template init<Rem>( mut_this->shared_from_this(), self.key );
-				base->mode = REM;
-
-				base_map.emplace( std::move(self.key), std::move(self.val) );
-				break;
-			}
-			case UPD: {
-				Ins& self = mut_this->as<Ins>();
-				auto it = base_map.find( self.key );
-
-				base->template init<Ins>(
-						mut_this->shared_from_this(), std::move(self.key), std::move(it->second) );
-				base->mode = UPD;
-
-				it->second = std::move(self.val);
-				break;
-			}
-			case BASE: assertf(false, "unreachable"); break;
-		}
-
-		// set base map into self
-		mut_this->reset();
-		mut_this->init<Base>( std::move(base_map) );
-		mode = BASE;
-	}
-
-private:
-	/// the base after rerooting at the current node
-	const Base& rerooted() const {
-		reroot();
-		return as<Base>();
-	}
-
-public:
-	/// true iff the map is empty
-	bool empty() const { return rerooted().empty(); }
-
-	/// number of entries in map
-	size_type size() const { return rerooted().size(); }
-
-	/// begin iterator for map; may be invalidated by calls to non-iteration functions
-	/// or functions on other maps in the same tree
-	iterator begin() const { return rerooted().begin(); }
-
-	/// end iterator for map; may be invalidated by calls to non-iteration functions
-	/// or functions on other maps in the same tree
-	iterator end() const { return rerooted().end(); }
-
-	/// underlying map iterator for value
-	iterator find(const Key& k) const { return rerooted().find( k ); }
-
-	/// check if value is present
-	size_type count(const Key& k) const { return rerooted().count( k ); }
-
-	/// get value; undefined behaviour if not present
-	const Val& get(const Key& k) const {
-		const Base& self = rerooted();
-		auto it = self.find( k );
-		return it->second;
-	}
-
-	/// get value; returns default if not present
-	template<typename V>
-	Val get_or_default(const Key& k, V&& d) const {
-		const Base& self = rerooted();
-		auto it = self.find( k );
-		if ( it == self.end() ) return d;
-		else return it->second;
-	}
-
-	/// set value, storing new map in output variable
-	template<typename K, typename V>
-	Ptr set(K&& k, V&& v) {
-		reroot();
-
-		// transfer map to new node
-		Ptr ret = std::make_shared<Self>( take_as<Base>() );
-		reset_as_base();
-		Base& base_map = ret->template as<Base>();
-
-		// check if this is update or insert
-		auto it = base_map.find( k );
-		if ( it == base_map.end() ) {
-			// set self to REM node and insert into base
-			init<Rem>( ret, k );
-			mode = REM;
-
-			base_map.emplace_hint( it, std::forward<K>(k), std::forward<V>(v) );
-		} else {
-			// set self to UPD node and modify base
-			init<Ins>( ret, std::forward<K>(k), std::move(it->second) );
-			mode = UPD;
-
-			it->second = std::forward<V>(v);
-		}
-
-		return ret;
-	}
-
-	/// remove value, storing new map in output variable; does nothing if key not in map
-	Ptr erase(const Key& k) {
-		reroot();
-
-		// exit early if key does not exist in map
-		if ( ! as<Base>().count( k ) ) return this->shared_from_this();
-
-		// transfer map to new node
-		Ptr ret = std::make_shared<Self>( take_as<Base>() );
-		reset_as_base();
-		Base& base_map = ret->template as<Base>();
-
-		// set self to INS node and remove from base
-		init<Ins>( ret, k, base_map[k] );
-		mode = INS;
-
-		base_map.erase( k );
-
-		return ret;
-	}
-};
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/PersistentMap.hpp
===================================================================
--- src/Common/PersistentMap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/PersistentMap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,292 @@
+//
+// 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.
+//
+// PersistentMap.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Thu Mar  7 15:50:00 2019
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Thu Mar  7 15:50:00 2019
+// Update Count     : 1
+//
+
+#pragma once
+
+#include <cassert>        // for assertf
+#include <cstddef>        // for size_t
+#include <functional>     // for hash, equal_to
+#include <memory>         // for shared_ptr, enable_shared_from_this, make_shared
+#include <unordered_map>  // for unordered_map
+#include <utility>        // for forward, move
+
+/// Wraps a hash table in a persistent data structure, using a technique based
+/// on the persistent array in Conchon & Filliatre "A Persistent Union-Find
+/// Data Structure"
+
+template<typename Key, typename Val,
+		typename Hash = std::hash<Key>, typename Eq = std::equal_to<Key>>
+class PersistentMap
+	: public std::enable_shared_from_this<PersistentMap<Key, Val, Hash, Eq>> {
+public:
+	/// Type of this class
+	using Self = PersistentMap<Key, Val, Hash, Eq>;
+	/// Type of pointer to this class
+	using Ptr = std::shared_ptr<Self>;
+
+	/// Types of version nodes
+	enum Mode {
+		BASE,  ///< Root node of version tree
+		REM,   ///< Key removal node
+		INS,   ///< Key update node
+		UPD    ///< Key update node
+	};
+
+private:
+	using Base = std::unordered_map<Key, Val, Hash, Eq>;
+
+	/// Insertion/update node
+	struct Ins {
+		Ptr base;  ///< Modified map
+		Key key;   ///< Key inserted
+		Val val;   ///< Value stored
+
+		template<typename P, typename K, typename V>
+		Ins(P&& p, K&& k, V&& v)
+		: base(std::forward<P>(p)), key(std::forward<K>(k)), val(std::forward<V>(v)) {}
+	};
+
+	/// Removal node
+	struct Rem {
+		Ptr base;  ///< Modified map
+		Key key;   ///< Key removed
+
+		template<typename P, typename K>
+		Rem(P&& p, K&& k) : base(std::forward<P>(p)), key(std::forward<K>(k)) {}
+	};
+
+	/// Underlying storage
+	union Data {
+		char def;
+		Base base;
+		Ins ins;
+		Rem rem;
+
+		Data() : def('\0') {}
+		~Data() {}
+	} data;
+
+	/// Type of node
+	mutable Mode mode;
+
+	/// get mutable reference as T
+	template<typename T>
+	T& as() { return reinterpret_cast<T&>(data); }
+
+	/// get const reference as T
+	template<typename T>
+	const T& as() const { return reinterpret_cast<const T&>(data); }
+
+	/// get rvalue reference as T
+	template<typename T>
+	T&& take_as() { return std::move(as<T>()); }
+
+	/// initialize as T
+	template<typename T, typename... Args>
+	void init( Args&&... args ) {
+		new( &as<T>() ) T { std::forward<Args>(args)... };
+	}
+
+	/// reset as current mode
+	void reset() {
+		switch( mode ) {
+			case BASE:          as<Base>().~Base(); break;
+			case REM:           as<Rem>().~Rem();   break;
+			case INS: case UPD: as<Ins>().~Ins();   break;
+		}
+	}
+
+	/// reset as base
+	void reset_as_base() {
+		as<Base>().~Base();
+	}
+
+public:
+	using key_type = typename Base::key_type;
+	using mapped_type = typename Base::mapped_type;
+	using value_type = typename Base::value_type;
+	using size_type = typename Base::size_type;
+	using difference_type = typename Base::difference_type;
+	using iterator = typename Base::const_iterator;
+
+	PersistentMap() : data(), mode(BASE) { init<Base>(); }
+
+	PersistentMap( Base&& b ) : data(), mode(BASE) { init<Base>(std::move(b)); }
+
+	PersistentMap( const Self& o ) = delete;
+
+	Self& operator= ( const Self& o ) = delete;
+
+	~PersistentMap() { reset(); }
+
+	/// Create a pointer to a new, empty persistent map
+	static Ptr new_ptr() { return std::make_shared<Self>(); }
+
+	/// reroot persistent map at current node
+	void reroot() const {
+		// recursive base case
+		if ( mode == BASE ) return;
+
+		// reroot base
+		Self* mut_this = const_cast<Self*>(this);
+		Ptr base = ( mode == REM ) ? mut_this->as<Rem>().base : mut_this->as<Ins>().base;
+		base->reroot();
+
+		// remove map from base
+		Base base_map = base->template take_as<Base>();
+		base->reset_as_base();
+
+		// switch base to inverse of self and mutate base map
+		switch ( mode ) {
+			case REM: {
+				Rem& self = mut_this->as<Rem>();
+				auto it = base_map.find( self.key );
+
+				base->template init<Ins>(
+						mut_this->shared_from_this(), std::move(self.key), std::move(it->second) );
+				base->mode = INS;
+
+				base_map.erase( it );
+				break;
+			}
+			case INS: {
+				Ins& self = mut_this->as<Ins>();
+
+				base->template init<Rem>( mut_this->shared_from_this(), self.key );
+				base->mode = REM;
+
+				base_map.emplace( std::move(self.key), std::move(self.val) );
+				break;
+			}
+			case UPD: {
+				Ins& self = mut_this->as<Ins>();
+				auto it = base_map.find( self.key );
+
+				base->template init<Ins>(
+						mut_this->shared_from_this(), std::move(self.key), std::move(it->second) );
+				base->mode = UPD;
+
+				it->second = std::move(self.val);
+				break;
+			}
+			case BASE: assertf(false, "unreachable"); break;
+		}
+
+		// set base map into self
+		mut_this->reset();
+		mut_this->init<Base>( std::move(base_map) );
+		mode = BASE;
+	}
+
+private:
+	/// the base after rerooting at the current node
+	const Base& rerooted() const {
+		reroot();
+		return as<Base>();
+	}
+
+public:
+	/// true iff the map is empty
+	bool empty() const { return rerooted().empty(); }
+
+	/// number of entries in map
+	size_type size() const { return rerooted().size(); }
+
+	/// begin iterator for map; may be invalidated by calls to non-iteration functions
+	/// or functions on other maps in the same tree
+	iterator begin() const { return rerooted().begin(); }
+
+	/// end iterator for map; may be invalidated by calls to non-iteration functions
+	/// or functions on other maps in the same tree
+	iterator end() const { return rerooted().end(); }
+
+	/// underlying map iterator for value
+	iterator find(const Key& k) const { return rerooted().find( k ); }
+
+	/// check if value is present
+	size_type count(const Key& k) const { return rerooted().count( k ); }
+
+	/// get value; undefined behaviour if not present
+	const Val& get(const Key& k) const {
+		const Base& self = rerooted();
+		auto it = self.find( k );
+		return it->second;
+	}
+
+	/// get value; returns default if not present
+	template<typename V>
+	Val get_or_default(const Key& k, V&& d) const {
+		const Base& self = rerooted();
+		auto it = self.find( k );
+		if ( it == self.end() ) return d;
+		else return it->second;
+	}
+
+	/// set value, storing new map in output variable
+	template<typename K, typename V>
+	Ptr set(K&& k, V&& v) {
+		reroot();
+
+		// transfer map to new node
+		Ptr ret = std::make_shared<Self>( take_as<Base>() );
+		reset_as_base();
+		Base& base_map = ret->template as<Base>();
+
+		// check if this is update or insert
+		auto it = base_map.find( k );
+		if ( it == base_map.end() ) {
+			// set self to REM node and insert into base
+			init<Rem>( ret, k );
+			mode = REM;
+
+			base_map.emplace_hint( it, std::forward<K>(k), std::forward<V>(v) );
+		} else {
+			// set self to UPD node and modify base
+			init<Ins>( ret, std::forward<K>(k), std::move(it->second) );
+			mode = UPD;
+
+			it->second = std::forward<V>(v);
+		}
+
+		return ret;
+	}
+
+	/// remove value, storing new map in output variable; does nothing if key not in map
+	Ptr erase(const Key& k) {
+		reroot();
+
+		// exit early if key does not exist in map
+		if ( ! as<Base>().count( k ) ) return this->shared_from_this();
+
+		// transfer map to new node
+		Ptr ret = std::make_shared<Self>( take_as<Base>() );
+		reset_as_base();
+		Base& base_map = ret->template as<Base>();
+
+		// set self to INS node and remove from base
+		init<Ins>( ret, k, base_map[k] );
+		mode = INS;
+
+		base_map.erase( k );
+
+		return ret;
+	}
+};
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/ResolvProtoDump.cpp
===================================================================
--- src/Common/ResolvProtoDump.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Common/ResolvProtoDump.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -26,5 +26,5 @@
 #include "AST/TranslationUnit.hpp"
 #include "AST/Type.hpp"
-#include "CodeGen/OperatorTable.h"
+#include "CodeGen/OperatorTable.hpp"
 
 namespace {
Index: src/Common/ScopedMap.h
===================================================================
--- src/Common/ScopedMap.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,357 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ScopedMap.h --
-//
-// Author           : Aaron B. Moss
-// Created On       : Wed Dec 2 11:37:00 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Tue Feb 15 08:41:28 2022
-// Update Count     : 5
-//
-
-#pragma once
-
-#include <cassert>
-#include <iterator>
-#include <map>
-#include <utility>
-#include <vector>
-
-/// Default (empty) ScopedMap note type
-struct EmptyNote {};
-
-/// A map where the items are placed into nested scopes;
-/// inserted items are placed into the innermost scope, lookup looks from the innermost scope outward.
-/// Scopes may be annotated with a value; the annotation defaults to empty
-template<typename Key, typename Value, typename Note = EmptyNote>
-class ScopedMap {
-	typedef std::map< Key, Value > MapType;
-	struct Scope {
-		MapType map;
-		Note note;
-
-		template<typename N>
-		Scope(N && n) : map(), note(std::forward<N>(n)) {}
-
-		Scope() = default;
-		Scope(const Scope &) = default;
-		Scope(Scope &&) = default;
-		Scope & operator= (const Scope &) = default;
-		Scope & operator= (Scope &&) = default;
-	};
-	typedef std::vector< Scope > ScopeList;
-
-	/// Scoped list of maps.
-	ScopeList scopes;
-public:
-	typedef typename MapType::key_type key_type;
-	typedef typename MapType::mapped_type mapped_type;
-	typedef typename MapType::value_type value_type;
-	typedef typename ScopeList::size_type size_type;
-	typedef typename ScopeList::difference_type difference_type;
-	typedef typename MapType::reference reference;
-	typedef typename MapType::const_reference const_reference;
-	typedef typename MapType::pointer pointer;
-	typedef typename MapType::const_pointer const_pointer;
-
-	// Both iterator types are complete bidrectional iterators, see below.
-	class iterator;
-	class const_iterator;
-
-	/// Starts a new scope
-	void beginScope() {
-		scopes.emplace_back();
-	}
-
-	// Starts a new scope with the given note
-	template<typename N>
-	void beginScope( N && n ) {
-		scopes.emplace_back( std::forward<N>(n) );
-	}
-
-	/// Ends a scope; invalidates any iterators pointing to elements of that scope
-	void endScope() {
-		scopes.pop_back();
-		assert( ! scopes.empty() );
-	}
-
-	/// Default constructor initializes with one scope
-	ScopedMap() : scopes() { beginScope(); }
-
-	/// Constructs with a given note on the outermost scope
-	template<typename N>
-	ScopedMap( N && n ) : scopes() { beginScope(std::forward<N>(n)); }
-
-	iterator begin() { return iterator(scopes, scopes.back().map.begin(), currentScope()).next_valid(); }
-	const_iterator begin() const { return const_iterator(scopes, scopes.back().map.begin(), currentScope()).next_valid(); }
-	const_iterator cbegin() const { return const_iterator(scopes, scopes.back().map.begin(), currentScope()).next_valid(); }
-	iterator end() { return iterator(scopes, scopes[0].map.end(), 0); }
-	const_iterator end() const { return const_iterator(scopes, scopes[0].map.end(), 0); }
-	const_iterator cend() const { return const_iterator(scopes, scopes[0].map.end(), 0); }
-
-	/// Gets the index of the current scope (counted from 1)
-	size_type currentScope() const { return scopes.size() - 1; }
-
-	/// Gets the note at the given scope
-	Note & getNote() { return scopes.back().note; }
-	const Note & getNote() const { return scopes.back().note; }
-	Note & getNote( size_type i ) { return scopes[i].note; }
-	const Note & getNote( size_type i ) const { return scopes[i].note; }
-
-	/// Finds the given key in the outermost scope it occurs; returns end() for none such
-	iterator find( const Key & key ) {
-		for ( size_type i = scopes.size() - 1; ; --i ) {
-			typename MapType::iterator val = scopes[i].map.find( key );
-			if ( val != scopes[i].map.end() ) return iterator( scopes, val, i );
-			if ( i == 0 ) break;
-		}
-		return end();
-	}
-	const_iterator find( const Key & key ) const {
-			return const_iterator( const_cast< ScopedMap< Key, Value, Note >* >(this)->find( key ) );
-	}
-
-	/// Finds the given key in the provided scope; returns end() for none such
-	iterator findAt( size_type scope, const Key & key ) {
-		typename MapType::iterator val = scopes[scope].map.find( key );
-		if ( val != scopes[scope].map.end() ) return iterator( scopes, val, scope );
-		return end();
-	}
-	const_iterator findAt( size_type scope, const Key & key ) const {
-		return const_iterator( const_cast< ScopedMap< Key, Value, Note >* >(this)->findAt( scope, key ) );
-	}
-
-	/// Finds the given key in the outermost scope inside the given scope where it occurs
-	iterator findNext( const_iterator & it, const Key & key ) {
-		if ( it.level == 0 ) return end();
-		for ( size_type i = it.level - 1; ; --i ) {
-			typename MapType::iterator val = scopes[i].map.find( key );
-			if ( val != scopes[i].map.end() ) return iterator( scopes, val, i );
-			if ( i == 0 ) break;
-		}
-		return end();
-	}
-	const_iterator findNext( const_iterator & it, const Key & key ) const {
-			return const_iterator( const_cast< ScopedMap< Key, Value, Note >* >(this)->findNext( it, key ) );
-	}
-
-	/// Inserts the given key-value pair into the outermost scope
-	template< typename value_type_t >
-	std::pair< iterator, bool > insert( value_type_t && value ) {
-		std::pair< typename MapType::iterator, bool > res = scopes.back().map.insert( std::forward<value_type_t>( value ) );
-		return std::make_pair( iterator(scopes, std::move( res.first ), scopes.size()-1), std::move( res.second ) );
-	}
-
-	template< typename value_t >
-	std::pair< iterator, bool > insert( const Key & key, value_t && value ) { return insert( std::make_pair( key, std::forward<value_t>( value ) ) ); }
-
-	template< typename value_type_t >
-	std::pair< iterator, bool > insertAt( size_type scope, value_type_t && value ) {
-		std::pair< typename MapType::iterator, bool > res = scopes.at(scope).map.insert( std::forward<value_type_t>( value ) );
-		return std::make_pair( iterator(scopes, std::move( res.first ), scope), std::move( res.second ) );
-	}
-
-	template< typename value_t >
-	std::pair< iterator, bool > insertAt( size_type scope, const Key & key, value_t && value ) {
-		return insertAt( scope, std::make_pair( key, std::forward<value_t>( value ) ) );
-	}
-
-	Value & operator[] ( const Key & key ) {
-		iterator slot = find( key );
-		if ( slot != end() ) return slot->second;
-		return insert( key, Value() ).first->second;
-	}
-
-	/// Erases element with key in the innermost scope that has it.
-	size_type erase( const Key & key ) {
-		for ( auto it = scopes.rbegin() ; it != scopes.rend() ; ++it ) {
-			size_type i = it->map.erase( key );
-			if ( 0 != i ) return i;
-		}
-		return 0;
-	}
-
-	size_type count( const Key & key ) const {
-		size_type c = 0;
-		auto it = find( key );
-		auto end = cend();
-
-		while(it != end) {
-			c++;
-			it = findNext(it, key);
-		}
-
-		return c;
-	}
-
-	bool contains( const Key & key ) const {
-		return find( key ) != cend();
-	}
-};
-
-template<typename Key, typename Value, typename Note>
-class ScopedMap<Key, Value, Note>::iterator :
-		public std::iterator< std::bidirectional_iterator_tag, value_type > {
-	friend class ScopedMap;
-	friend class const_iterator;
-	typedef typename MapType::iterator wrapped_iterator;
-	typedef typename ScopeList::size_type size_type;
-
-	/// Checks if this iterator points to a valid item
-	bool is_valid() const {
-		return it != (*scopes)[level].map.end();
-	}
-
-	/// Increments on invalid
-	iterator & next_valid() {
-		if ( ! is_valid() ) { ++(*this); }
-		return *this;
-	}
-
-	/// Decrements on invalid
-	iterator & prev_valid() {
-		if ( ! is_valid() ) { --(*this); }
-		return *this;
-	}
-
-	iterator(ScopeList & _scopes, const wrapped_iterator & _it, size_type inLevel)
-		: scopes(&_scopes), it(_it), level(inLevel) {}
-public:
-	iterator(const iterator & that) : scopes(that.scopes), it(that.it), level(that.level) {}
-	iterator & operator= (const iterator & that) {
-		scopes = that.scopes; level = that.level; it = that.it;
-		return *this;
-	}
-
-	reference operator* () { return *it; }
-	pointer operator-> () const { return it.operator->(); }
-
-	iterator & operator++ () {
-		if ( it == (*scopes)[level].map.end() ) {
-			if ( level == 0 ) return *this;
-			--level;
-			it = (*scopes)[level].map.begin();
-		} else {
-			++it;
-		}
-		return next_valid();
-	}
-	iterator operator++ (int) { iterator tmp = *this; ++(*this); return tmp; }
-
-	iterator & operator-- () {
-		// may fail if this is the begin iterator; allowed by STL spec
-		if ( it == (*scopes)[level].map.begin() ) {
-			++level;
-			it = (*scopes)[level].map.end();
-		}
-		--it;
-		return prev_valid();
-	}
-	iterator operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
-
-	bool operator== (const iterator & that) const {
-		return scopes == that.scopes && level == that.level && it == that.it;
-	}
-	bool operator!= (const iterator & that) const { return !( *this == that ); }
-
-	size_type get_level() const { return level; }
-
-	Note & get_note() { return (*scopes)[level].note; }
-	const Note & get_note() const { return (*scopes)[level].note; }
-
-private:
-	ScopeList *scopes;
-	wrapped_iterator it;
-	size_type level;
-};
-
-template<typename Key, typename Value, typename Note>
-class ScopedMap<Key, Value, Note>::const_iterator :
-		public std::iterator< std::bidirectional_iterator_tag, value_type > {
-	friend class ScopedMap;
-	typedef typename ScopedMap::MapType::iterator wrapped_iterator;
-	typedef typename ScopedMap::MapType::const_iterator wrapped_const_iterator;
-	typedef typename ScopedMap::ScopeList scope_list;
-	typedef typename scope_list::size_type size_type;
-
-	/// Checks if this iterator points to a valid item
-	bool is_valid() const {
-		return it != (*scopes)[level].map.end();
-	}
-
-	/// Increments on invalid
-	const_iterator & next_valid() {
-		if ( ! is_valid() ) { ++(*this); }
-		return *this;
-	}
-
-	/// Decrements on invalid
-	const_iterator & prev_valid() {
-		if ( ! is_valid() ) { --(*this); }
-		return *this;
-	}
-
-	const_iterator(scope_list const & _scopes, const wrapped_const_iterator & _it, size_type inLevel)
-		: scopes(&_scopes), it(_it), level(inLevel) {}
-public:
-	const_iterator(const iterator & that) : scopes(that.scopes), it(that.it), level(that.level) {}
-	const_iterator(const const_iterator & that) : scopes(that.scopes), it(that.it), level(that.level) {}
-	const_iterator & operator= (const iterator & that) {
-		scopes = that.scopes; level = that.level; it = that.it;
-		return *this;
-	}
-	const_iterator & operator= (const const_iterator & that) {
-		scopes = that.scopes; level = that.level; it = that.it;
-		return *this;
-	}
-
-	const_reference operator* () { return *it; }
-	const_pointer operator-> () { return it.operator->(); }
-
-	const_iterator & operator++ () {
-		if ( it == (*scopes)[level].map.end() ) {
-			if ( level == 0 ) return *this;
-			--level;
-			it = (*scopes)[level].map.begin();
-		} else {
-			++it;
-		}
-		return next_valid();
-	}
-	const_iterator operator++ (int) { const_iterator tmp = *this; ++(*this); return tmp; }
-
-	const_iterator & operator-- () {
-		// may fail if this is the begin iterator; allowed by STL spec
-		if ( it == (*scopes)[level].map.begin() ) {
-			++level;
-			it = (*scopes)[level].map.end();
-		}
-		--it;
-		return prev_valid();
-	}
-	const_iterator operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
-
-	bool operator== (const const_iterator & that) const {
-		return scopes == that.scopes && level == that.level && it == that.it;
-	}
-	bool operator!= (const const_iterator & that) const { return !( *this == that ); }
-
-	size_type get_level() const { return level; }
-
-	const Note & get_note() const { return (*scopes)[level].note; }
-
-private:
-	scope_list const *scopes;
-	wrapped_const_iterator it;
-	size_type level;
-};
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/ScopedMap.hpp
===================================================================
--- src/Common/ScopedMap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/ScopedMap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,357 @@
+//
+// 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.
+//
+// ScopedMap.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Wed Dec 2 11:37:00 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Tue Feb 15 08:41:28 2022
+// Update Count     : 5
+//
+
+#pragma once
+
+#include <cassert>
+#include <iterator>
+#include <map>
+#include <utility>
+#include <vector>
+
+/// Default (empty) ScopedMap note type
+struct EmptyNote {};
+
+/// A map where the items are placed into nested scopes;
+/// inserted items are placed into the innermost scope, lookup looks from the innermost scope outward.
+/// Scopes may be annotated with a value; the annotation defaults to empty
+template<typename Key, typename Value, typename Note = EmptyNote>
+class ScopedMap {
+	typedef std::map< Key, Value > MapType;
+	struct Scope {
+		MapType map;
+		Note note;
+
+		template<typename N>
+		Scope(N && n) : map(), note(std::forward<N>(n)) {}
+
+		Scope() = default;
+		Scope(const Scope &) = default;
+		Scope(Scope &&) = default;
+		Scope & operator= (const Scope &) = default;
+		Scope & operator= (Scope &&) = default;
+	};
+	typedef std::vector< Scope > ScopeList;
+
+	/// Scoped list of maps.
+	ScopeList scopes;
+public:
+	typedef typename MapType::key_type key_type;
+	typedef typename MapType::mapped_type mapped_type;
+	typedef typename MapType::value_type value_type;
+	typedef typename ScopeList::size_type size_type;
+	typedef typename ScopeList::difference_type difference_type;
+	typedef typename MapType::reference reference;
+	typedef typename MapType::const_reference const_reference;
+	typedef typename MapType::pointer pointer;
+	typedef typename MapType::const_pointer const_pointer;
+
+	// Both iterator types are complete bidrectional iterators, see below.
+	class iterator;
+	class const_iterator;
+
+	/// Starts a new scope
+	void beginScope() {
+		scopes.emplace_back();
+	}
+
+	// Starts a new scope with the given note
+	template<typename N>
+	void beginScope( N && n ) {
+		scopes.emplace_back( std::forward<N>(n) );
+	}
+
+	/// Ends a scope; invalidates any iterators pointing to elements of that scope
+	void endScope() {
+		scopes.pop_back();
+		assert( ! scopes.empty() );
+	}
+
+	/// Default constructor initializes with one scope
+	ScopedMap() : scopes() { beginScope(); }
+
+	/// Constructs with a given note on the outermost scope
+	template<typename N>
+	ScopedMap( N && n ) : scopes() { beginScope(std::forward<N>(n)); }
+
+	iterator begin() { return iterator(scopes, scopes.back().map.begin(), currentScope()).next_valid(); }
+	const_iterator begin() const { return const_iterator(scopes, scopes.back().map.begin(), currentScope()).next_valid(); }
+	const_iterator cbegin() const { return const_iterator(scopes, scopes.back().map.begin(), currentScope()).next_valid(); }
+	iterator end() { return iterator(scopes, scopes[0].map.end(), 0); }
+	const_iterator end() const { return const_iterator(scopes, scopes[0].map.end(), 0); }
+	const_iterator cend() const { return const_iterator(scopes, scopes[0].map.end(), 0); }
+
+	/// Gets the index of the current scope (counted from 1)
+	size_type currentScope() const { return scopes.size() - 1; }
+
+	/// Gets the note at the given scope
+	Note & getNote() { return scopes.back().note; }
+	const Note & getNote() const { return scopes.back().note; }
+	Note & getNote( size_type i ) { return scopes[i].note; }
+	const Note & getNote( size_type i ) const { return scopes[i].note; }
+
+	/// Finds the given key in the outermost scope it occurs; returns end() for none such
+	iterator find( const Key & key ) {
+		for ( size_type i = scopes.size() - 1; ; --i ) {
+			typename MapType::iterator val = scopes[i].map.find( key );
+			if ( val != scopes[i].map.end() ) return iterator( scopes, val, i );
+			if ( i == 0 ) break;
+		}
+		return end();
+	}
+	const_iterator find( const Key & key ) const {
+			return const_iterator( const_cast< ScopedMap< Key, Value, Note >* >(this)->find( key ) );
+	}
+
+	/// Finds the given key in the provided scope; returns end() for none such
+	iterator findAt( size_type scope, const Key & key ) {
+		typename MapType::iterator val = scopes[scope].map.find( key );
+		if ( val != scopes[scope].map.end() ) return iterator( scopes, val, scope );
+		return end();
+	}
+	const_iterator findAt( size_type scope, const Key & key ) const {
+		return const_iterator( const_cast< ScopedMap< Key, Value, Note >* >(this)->findAt( scope, key ) );
+	}
+
+	/// Finds the given key in the outermost scope inside the given scope where it occurs
+	iterator findNext( const_iterator & it, const Key & key ) {
+		if ( it.level == 0 ) return end();
+		for ( size_type i = it.level - 1; ; --i ) {
+			typename MapType::iterator val = scopes[i].map.find( key );
+			if ( val != scopes[i].map.end() ) return iterator( scopes, val, i );
+			if ( i == 0 ) break;
+		}
+		return end();
+	}
+	const_iterator findNext( const_iterator & it, const Key & key ) const {
+			return const_iterator( const_cast< ScopedMap< Key, Value, Note >* >(this)->findNext( it, key ) );
+	}
+
+	/// Inserts the given key-value pair into the outermost scope
+	template< typename value_type_t >
+	std::pair< iterator, bool > insert( value_type_t && value ) {
+		std::pair< typename MapType::iterator, bool > res = scopes.back().map.insert( std::forward<value_type_t>( value ) );
+		return std::make_pair( iterator(scopes, std::move( res.first ), scopes.size()-1), std::move( res.second ) );
+	}
+
+	template< typename value_t >
+	std::pair< iterator, bool > insert( const Key & key, value_t && value ) { return insert( std::make_pair( key, std::forward<value_t>( value ) ) ); }
+
+	template< typename value_type_t >
+	std::pair< iterator, bool > insertAt( size_type scope, value_type_t && value ) {
+		std::pair< typename MapType::iterator, bool > res = scopes.at(scope).map.insert( std::forward<value_type_t>( value ) );
+		return std::make_pair( iterator(scopes, std::move( res.first ), scope), std::move( res.second ) );
+	}
+
+	template< typename value_t >
+	std::pair< iterator, bool > insertAt( size_type scope, const Key & key, value_t && value ) {
+		return insertAt( scope, std::make_pair( key, std::forward<value_t>( value ) ) );
+	}
+
+	Value & operator[] ( const Key & key ) {
+		iterator slot = find( key );
+		if ( slot != end() ) return slot->second;
+		return insert( key, Value() ).first->second;
+	}
+
+	/// Erases element with key in the innermost scope that has it.
+	size_type erase( const Key & key ) {
+		for ( auto it = scopes.rbegin() ; it != scopes.rend() ; ++it ) {
+			size_type i = it->map.erase( key );
+			if ( 0 != i ) return i;
+		}
+		return 0;
+	}
+
+	size_type count( const Key & key ) const {
+		size_type c = 0;
+		auto it = find( key );
+		auto end = cend();
+
+		while(it != end) {
+			c++;
+			it = findNext(it, key);
+		}
+
+		return c;
+	}
+
+	bool contains( const Key & key ) const {
+		return find( key ) != cend();
+	}
+};
+
+template<typename Key, typename Value, typename Note>
+class ScopedMap<Key, Value, Note>::iterator :
+		public std::iterator< std::bidirectional_iterator_tag, value_type > {
+	friend class ScopedMap;
+	friend class const_iterator;
+	typedef typename MapType::iterator wrapped_iterator;
+	typedef typename ScopeList::size_type size_type;
+
+	/// Checks if this iterator points to a valid item
+	bool is_valid() const {
+		return it != (*scopes)[level].map.end();
+	}
+
+	/// Increments on invalid
+	iterator & next_valid() {
+		if ( ! is_valid() ) { ++(*this); }
+		return *this;
+	}
+
+	/// Decrements on invalid
+	iterator & prev_valid() {
+		if ( ! is_valid() ) { --(*this); }
+		return *this;
+	}
+
+	iterator(ScopeList & _scopes, const wrapped_iterator & _it, size_type inLevel)
+		: scopes(&_scopes), it(_it), level(inLevel) {}
+public:
+	iterator(const iterator & that) : scopes(that.scopes), it(that.it), level(that.level) {}
+	iterator & operator= (const iterator & that) {
+		scopes = that.scopes; level = that.level; it = that.it;
+		return *this;
+	}
+
+	reference operator* () { return *it; }
+	pointer operator-> () const { return it.operator->(); }
+
+	iterator & operator++ () {
+		if ( it == (*scopes)[level].map.end() ) {
+			if ( level == 0 ) return *this;
+			--level;
+			it = (*scopes)[level].map.begin();
+		} else {
+			++it;
+		}
+		return next_valid();
+	}
+	iterator operator++ (int) { iterator tmp = *this; ++(*this); return tmp; }
+
+	iterator & operator-- () {
+		// may fail if this is the begin iterator; allowed by STL spec
+		if ( it == (*scopes)[level].map.begin() ) {
+			++level;
+			it = (*scopes)[level].map.end();
+		}
+		--it;
+		return prev_valid();
+	}
+	iterator operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
+
+	bool operator== (const iterator & that) const {
+		return scopes == that.scopes && level == that.level && it == that.it;
+	}
+	bool operator!= (const iterator & that) const { return !( *this == that ); }
+
+	size_type get_level() const { return level; }
+
+	Note & get_note() { return (*scopes)[level].note; }
+	const Note & get_note() const { return (*scopes)[level].note; }
+
+private:
+	ScopeList *scopes;
+	wrapped_iterator it;
+	size_type level;
+};
+
+template<typename Key, typename Value, typename Note>
+class ScopedMap<Key, Value, Note>::const_iterator :
+		public std::iterator< std::bidirectional_iterator_tag, value_type > {
+	friend class ScopedMap;
+	typedef typename ScopedMap::MapType::iterator wrapped_iterator;
+	typedef typename ScopedMap::MapType::const_iterator wrapped_const_iterator;
+	typedef typename ScopedMap::ScopeList scope_list;
+	typedef typename scope_list::size_type size_type;
+
+	/// Checks if this iterator points to a valid item
+	bool is_valid() const {
+		return it != (*scopes)[level].map.end();
+	}
+
+	/// Increments on invalid
+	const_iterator & next_valid() {
+		if ( ! is_valid() ) { ++(*this); }
+		return *this;
+	}
+
+	/// Decrements on invalid
+	const_iterator & prev_valid() {
+		if ( ! is_valid() ) { --(*this); }
+		return *this;
+	}
+
+	const_iterator(scope_list const & _scopes, const wrapped_const_iterator & _it, size_type inLevel)
+		: scopes(&_scopes), it(_it), level(inLevel) {}
+public:
+	const_iterator(const iterator & that) : scopes(that.scopes), it(that.it), level(that.level) {}
+	const_iterator(const const_iterator & that) : scopes(that.scopes), it(that.it), level(that.level) {}
+	const_iterator & operator= (const iterator & that) {
+		scopes = that.scopes; level = that.level; it = that.it;
+		return *this;
+	}
+	const_iterator & operator= (const const_iterator & that) {
+		scopes = that.scopes; level = that.level; it = that.it;
+		return *this;
+	}
+
+	const_reference operator* () { return *it; }
+	const_pointer operator-> () { return it.operator->(); }
+
+	const_iterator & operator++ () {
+		if ( it == (*scopes)[level].map.end() ) {
+			if ( level == 0 ) return *this;
+			--level;
+			it = (*scopes)[level].map.begin();
+		} else {
+			++it;
+		}
+		return next_valid();
+	}
+	const_iterator operator++ (int) { const_iterator tmp = *this; ++(*this); return tmp; }
+
+	const_iterator & operator-- () {
+		// may fail if this is the begin iterator; allowed by STL spec
+		if ( it == (*scopes)[level].map.begin() ) {
+			++level;
+			it = (*scopes)[level].map.end();
+		}
+		--it;
+		return prev_valid();
+	}
+	const_iterator operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
+
+	bool operator== (const const_iterator & that) const {
+		return scopes == that.scopes && level == that.level && it == that.it;
+	}
+	bool operator!= (const const_iterator & that) const { return !( *this == that ); }
+
+	size_type get_level() const { return level; }
+
+	const Note & get_note() const { return (*scopes)[level].note; }
+
+private:
+	scope_list const *scopes;
+	wrapped_const_iterator it;
+	size_type level;
+};
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/SemanticError.cc
===================================================================
--- src/Common/SemanticError.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,195 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// SemanticError.cc --
-//
-// Author           : Thierry Delisle
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Thu Dec 14 13:45:28 2023
-// Update Count     : 34
-//
-
-#include <cstdarg>
-#include <cstdio>										// for fileno, stderr
-#include <cstring>
-#include <unistd.h>										// for isatty
-#include <iostream>										// for basic_ostream, operator<<, ostream
-#include <list>											// for list, _List_iterator
-#include <string>										// for string, operator<<, operator+, to_string
-#include <vector>
-
-using namespace std;
-
-#include "Common/utility.h"								// for to_string, CodeLocation (ptr only)
-#include "SemanticError.h"
-
-//-----------------------------------------------------------------------------
-// Severity Handling
-vector<Severity> & get_severities() {
-	static vector<Severity> severities;
-	if(severities.empty()) {
-		severities.reserve((size_t)Warning::NUMBER_OF_WARNINGS);
-		for ( const auto w : WarningFormats ) {
-			severities.push_back( w.default_severity );
-		} // for
-	}
-	return severities;
-}
-
-void SemanticWarning_SuppressAll() {
-	for( auto & s : get_severities() ) {
-		s = Severity::Suppress;
-	}
-}
-
-void SemanticWarning_EnableAll() {
-	for( auto & s : get_severities() ) {
-		s = Severity::Warn;
-	}
-}
-
-void SemanticWarning_WarningAsError() {
-	for( auto & s : get_severities() ) {
-		if(s == Severity::Warn) s = Severity::Error;
-	}
-}
-
-void SemanticWarning_Set(const char * const name, Severity s) {
-	size_t idx = 0;
-	for ( const auto & w : WarningFormats ) {
-		if ( strcmp( name, w.name ) == 0 ) {
-			get_severities()[idx] = s;
-			break;
-		}
-		idx++;
-	}
-}
-
-//-----------------------------------------------------------------------------
-// Semantic Error
-
-bool SemanticErrorThrow = false;
-
-SemanticErrorException::SemanticErrorException( CodeLocation location, string error ) {
-	append( location, error );
-}
-
-void SemanticErrorException::append( SemanticErrorException &other ) {
-	errors.splice( errors.end(), other.errors );
-}
-
-void SemanticErrorException::append( CodeLocation location, const string & msg ) {
-	errors.emplace_back( location, msg );
-}
-
-bool SemanticErrorException::isEmpty() const {
-	return errors.empty();
-}
-
-void SemanticErrorException::print() {
-//	using to_string;
-
-	errors.sort([](const error & lhs, const error & rhs) -> bool {
-		if(lhs.location.startsBefore(rhs.location)) return true;
-		if(rhs.location.startsBefore(lhs.location)) return false;
-
-		return lhs.description < rhs.description;
-	});
-
-	for( auto err : errors ) {
-		cerr << ErrorHelpers::bold() << err.location << ErrorHelpers::error_str() << ErrorHelpers::reset_font() << err.description << endl;
-	}
-}
-
-void SemanticError( CodeLocation location, const char * fmt, ... ) {
-	char msg[2048];										// worst-case error-message buffer
-	va_list args;
-	va_start( args, fmt );
-	vsnprintf( msg, sizeof(msg), fmt, args );			// always null terminated, but may be truncated
-	va_end( args );
-
-	SemanticErrorThrow = true;
-	throw SemanticErrorException( location, msg );		// convert msg to string
-}
-
-void SemanticWarning( CodeLocation location, Warning warning, ... ) {
-	Severity severity = get_severities()[(int)warning];
-
-	switch ( severity ) {
-	case Severity::Suppress :
-		break;
-	case Severity::Warn :
-	case Severity::Error :
-		{
-			char msg[2048];								// worst-case error-message buffer
-			va_list args;
-			va_start( args, warning );
-			vsnprintf( msg, sizeof(msg), WarningFormats[(int)warning].message, args ); // always null terminated, but may be truncated
-			va_end( args );
-
-			if ( severity == Severity::Warn ) {
-				cerr << ErrorHelpers::bold() << location << ErrorHelpers::warning_str() << ErrorHelpers::reset_font() << msg << endl;
-			} else {
-				SemanticError( location, string( msg ) );
-			}
-		}
-		break;
-	case Severity::Critical :
-		assertf(false, "Critical errors not implemented yet");
-		break;
-	}
-}
-
-//-----------------------------------------------------------------------------
-// Helpers
-namespace ErrorHelpers {
-	Colors colors = Colors::Auto;
-
-	static inline bool with_colors() {
-		return colors == Colors::Auto ? isatty( STDERR_FILENO ) : bool(colors);
-	}
-
-	const string & error_str() {
-		static string str = with_colors() ? "\e[31merror:\e[39m " : "error: ";
-		return str;
-	}
-
-	const string & warning_str() {
-		static string str = with_colors() ? "\e[95mwarning:\e[39m " : "warning: ";
-		return str;
-	}
-
-	const string & bold_ttycode() {
-		static string str = with_colors() ? "\e[1m" : "";
-		return str;
-	}
-
-	const string & reset_font_ttycode() {
-		static string str = with_colors() ? "\e[0m" : "";
-		return str;
-	}
-
-	string make_bold( const string & str ) {
-		return bold_ttycode() + str + reset_font_ttycode();
-	}
-
-	ostream & operator<<(ostream & os, bold) {
-		os << bold_ttycode();
-		return os;
-	}
-
-	ostream & operator<<(ostream & os, reset_font) {
-		os << reset_font_ttycode();
-		return os;
-	}
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/SemanticError.cpp
===================================================================
--- src/Common/SemanticError.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/SemanticError.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,196 @@
+//
+// 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.
+//
+// SemanticError.cpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Thu Dec 14 13:45:28 2023
+// Update Count     : 34
+//
+
+#include "SemanticError.hpp"
+
+#include <cstdarg>
+#include <cstdio>										// for fileno, stderr
+#include <cstring>
+#include <unistd.h>										// for isatty
+#include <iostream>										// for basic_ostream, operator<<, ostream
+#include <list>											// for list, _List_iterator
+#include <string>										// for string, operator<<, operator+, to_string
+#include <vector>
+
+#include "Common/Utility.hpp"							// for to_string, CodeLocation (ptr only)
+
+using namespace std;
+
+//-----------------------------------------------------------------------------
+// Severity Handling
+vector<Severity> & get_severities() {
+	static vector<Severity> severities;
+	if(severities.empty()) {
+		severities.reserve((size_t)Warning::NUMBER_OF_WARNINGS);
+		for ( const auto w : WarningFormats ) {
+			severities.push_back( w.default_severity );
+		} // for
+	}
+	return severities;
+}
+
+void SemanticWarning_SuppressAll() {
+	for( auto & s : get_severities() ) {
+		s = Severity::Suppress;
+	}
+}
+
+void SemanticWarning_EnableAll() {
+	for( auto & s : get_severities() ) {
+		s = Severity::Warn;
+	}
+}
+
+void SemanticWarning_WarningAsError() {
+	for( auto & s : get_severities() ) {
+		if(s == Severity::Warn) s = Severity::Error;
+	}
+}
+
+void SemanticWarning_Set(const char * const name, Severity s) {
+	size_t idx = 0;
+	for ( const auto & w : WarningFormats ) {
+		if ( strcmp( name, w.name ) == 0 ) {
+			get_severities()[idx] = s;
+			break;
+		}
+		idx++;
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Semantic Error
+
+bool SemanticErrorThrow = false;
+
+SemanticErrorException::SemanticErrorException( CodeLocation location, string error ) {
+	append( location, error );
+}
+
+void SemanticErrorException::append( SemanticErrorException &other ) {
+	errors.splice( errors.end(), other.errors );
+}
+
+void SemanticErrorException::append( CodeLocation location, const string & msg ) {
+	errors.emplace_back( location, msg );
+}
+
+bool SemanticErrorException::isEmpty() const {
+	return errors.empty();
+}
+
+void SemanticErrorException::print() {
+//	using to_string;
+
+	errors.sort([](const error & lhs, const error & rhs) -> bool {
+		if(lhs.location.startsBefore(rhs.location)) return true;
+		if(rhs.location.startsBefore(lhs.location)) return false;
+
+		return lhs.description < rhs.description;
+	});
+
+	for( auto err : errors ) {
+		cerr << ErrorHelpers::bold() << err.location << ErrorHelpers::error_str() << ErrorHelpers::reset_font() << err.description << endl;
+	}
+}
+
+void SemanticError( CodeLocation location, const char * fmt, ... ) {
+	char msg[2048];										// worst-case error-message buffer
+	va_list args;
+	va_start( args, fmt );
+	vsnprintf( msg, sizeof(msg), fmt, args );			// always null terminated, but may be truncated
+	va_end( args );
+
+	SemanticErrorThrow = true;
+	throw SemanticErrorException( location, msg );		// convert msg to string
+}
+
+void SemanticWarning( CodeLocation location, Warning warning, ... ) {
+	Severity severity = get_severities()[(int)warning];
+
+	switch ( severity ) {
+	case Severity::Suppress :
+		break;
+	case Severity::Warn :
+	case Severity::Error :
+		{
+			char msg[2048];								// worst-case error-message buffer
+			va_list args;
+			va_start( args, warning );
+			vsnprintf( msg, sizeof(msg), WarningFormats[(int)warning].message, args ); // always null terminated, but may be truncated
+			va_end( args );
+
+			if ( severity == Severity::Warn ) {
+				cerr << ErrorHelpers::bold() << location << ErrorHelpers::warning_str() << ErrorHelpers::reset_font() << msg << endl;
+			} else {
+				SemanticError( location, string( msg ) );
+			}
+		}
+		break;
+	case Severity::Critical :
+		assertf(false, "Critical errors not implemented yet");
+		break;
+	}
+}
+
+//-----------------------------------------------------------------------------
+// Helpers
+namespace ErrorHelpers {
+	Colors colors = Colors::Auto;
+
+	static inline bool with_colors() {
+		return colors == Colors::Auto ? isatty( STDERR_FILENO ) : bool(colors);
+	}
+
+	const string & error_str() {
+		static string str = with_colors() ? "\e[31merror:\e[39m " : "error: ";
+		return str;
+	}
+
+	const string & warning_str() {
+		static string str = with_colors() ? "\e[95mwarning:\e[39m " : "warning: ";
+		return str;
+	}
+
+	const string & bold_ttycode() {
+		static string str = with_colors() ? "\e[1m" : "";
+		return str;
+	}
+
+	const string & reset_font_ttycode() {
+		static string str = with_colors() ? "\e[0m" : "";
+		return str;
+	}
+
+	string make_bold( const string & str ) {
+		return bold_ttycode() + str + reset_font_ttycode();
+	}
+
+	ostream & operator<<(ostream & os, bold) {
+		os << bold_ttycode();
+		return os;
+	}
+
+	ostream & operator<<(ostream & os, reset_font) {
+		os << reset_font_ttycode();
+		return os;
+	}
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/SemanticError.h
===================================================================
--- src/Common/SemanticError.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,133 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// SemanticError.h --
-//
-// Author           : Thierry Delisle
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Thu Dec 14 13:48:07 2023
-// Update Count     : 72
-//
-
-#pragma once
-
-#include "ErrorObjects.h"
-#include "AST/Node.hpp"
-#include "AST/ParseNode.hpp"
-#include <cstring>
-
-//-----------------------------------------------------------------------------
-// Errors
-
-extern bool SemanticErrorThrow;
-
-__attribute__((noreturn, format(printf, 2, 3))) void SemanticError( CodeLocation location, const char fmt[], ... );
-
-__attribute__((noreturn)) static inline void SemanticError( CodeLocation location, std::string error ) {
-	SemanticErrorThrow = true;
-	throw SemanticErrorException( location, error );
-}
-
-__attribute__((noreturn)) static inline void SemanticError( const ast::ParseNode * obj, const std::string & error ) {
-	SemanticError( obj->location, toString( error, obj ) );
-}
-
-__attribute__((noreturn)) static inline void SemanticError( CodeLocation location, const ast::Node * obj, const std::string & error ) {
-	SemanticError( location, toString( error, obj ) );
-}
-
-//-----------------------------------------------------------------------------
-// Warnings
-
-enum class Severity {
-	Suppress,
-	Warn,
-	Error,
-	Critical
-};
-
-struct WarningData {
-	const char * const name;
-	const Severity default_severity;
-	const char * const message;
-};
-
-constexpr WarningData WarningFormats[] = {
-	{"self-assign"              , Severity::Warn, "self assignment of expression: %s"                          },
-	{"reference-conversion"     , Severity::Warn, "rvalue to reference conversion of rvalue: %s"               },
-	{"qualifiers-zero_t-one_t"  , Severity::Warn, "questionable use of type qualifier(s) with %s"              },
-	{"aggregate-forward-decl"   , Severity::Warn, "forward declaration of nested aggregate: %s"                },
-	{"superfluous-decl"         , Severity::Warn, "declaration does not allocate storage: %s"                  },
-	{"superfluous-else"         , Severity::Warn, "else clause never executed for empty loop conditional"      },
-	{"gcc-attributes"           , Severity::Warn, "invalid attribute: %s"                                      },
-	{"c++-like-copy"            , Severity::Warn, "Constructor from reference is not a valid copy constructor" },
-	{"depreciated-trait-syntax" , Severity::Warn, "trait type-parameters are now specified using the forall clause" },
-};
-
-enum class Warning {
-	SelfAssignment,
-	RvalueToReferenceConversion,
-	BadQualifiersZeroOne,
-	AggrForwardDecl,
-	SuperfluousDecl,
-	SuperfluousElse,
-	GccAttributes,
-	CppCopy,
-	DeprecTraitSyntax,
-	NUMBER_OF_WARNINGS, // MUST be last warning
-};
-
-static_assert(
-	(sizeof(WarningFormats) / sizeof(WarningFormats[0])) == ((unsigned long)Warning::NUMBER_OF_WARNINGS),
-	"Each warning format should have a corresponding warning enum value"
-);
-
-void SemanticWarning( CodeLocation loc, Warning warn, ... );
-
-void SemanticWarning_SuppressAll();
-void SemanticWarning_EnableAll();
-void SemanticWarning_WarningAsError();
-void SemanticWarning_Set(const char * const name, Severity s);
-
-// SKULLDUGGERY: cfa.cc is built before SemanticError.cc but needs this routine.
-static inline bool SemanticWarning_Exist(const char * const name) {
-	for ( const auto & w : WarningFormats ) {
-		if ( std::strcmp( name, w.name ) == 0 ) return true;
-	}
-	return false;
-}
-
-//-----------------------------------------------------------------------------
-// Helpers
-namespace ErrorHelpers {
-	enum class Colors {
-		Never = false,
-		Always = true,
-		Auto,
-	};
-
-	extern Colors colors;
-
-	const std::string & error_str();
-	const std::string & warning_str();
-	const std::string & bold_ttycode();
-	const std::string & reset_font_ttycode();
-
-	std::string make_bold( const std::string & str );
-
-	struct bold {};
-	std::ostream & operator<<(std::ostream & os, bold);
-
-	struct reset_font {};
-	std::ostream & operator<<(std::ostream & os, reset_font);
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/SemanticError.hpp
===================================================================
--- src/Common/SemanticError.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/SemanticError.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,133 @@
+//
+// 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.
+//
+// SemanticError.hpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Thu Dec 14 13:48:07 2023
+// Update Count     : 72
+//
+
+#pragma once
+
+#include "ErrorObjects.hpp"
+#include "AST/Node.hpp"
+#include "AST/ParseNode.hpp"
+#include <cstring>
+
+//-----------------------------------------------------------------------------
+// Errors
+
+extern bool SemanticErrorThrow;
+
+__attribute__((noreturn, format(printf, 2, 3))) void SemanticError( CodeLocation location, const char fmt[], ... );
+
+__attribute__((noreturn)) static inline void SemanticError( CodeLocation location, std::string error ) {
+	SemanticErrorThrow = true;
+	throw SemanticErrorException( location, error );
+}
+
+__attribute__((noreturn)) static inline void SemanticError( const ast::ParseNode * obj, const std::string & error ) {
+	SemanticError( obj->location, toString( error, obj ) );
+}
+
+__attribute__((noreturn)) static inline void SemanticError( CodeLocation location, const ast::Node * obj, const std::string & error ) {
+	SemanticError( location, toString( error, obj ) );
+}
+
+//-----------------------------------------------------------------------------
+// Warnings
+
+enum class Severity {
+	Suppress,
+	Warn,
+	Error,
+	Critical
+};
+
+struct WarningData {
+	const char * const name;
+	const Severity default_severity;
+	const char * const message;
+};
+
+constexpr WarningData WarningFormats[] = {
+	{"self-assign"              , Severity::Warn, "self assignment of expression: %s"                          },
+	{"reference-conversion"     , Severity::Warn, "rvalue to reference conversion of rvalue: %s"               },
+	{"qualifiers-zero_t-one_t"  , Severity::Warn, "questionable use of type qualifier(s) with %s"              },
+	{"aggregate-forward-decl"   , Severity::Warn, "forward declaration of nested aggregate: %s"                },
+	{"superfluous-decl"         , Severity::Warn, "declaration does not allocate storage: %s"                  },
+	{"superfluous-else"         , Severity::Warn, "else clause never executed for empty loop conditional"      },
+	{"gcc-attributes"           , Severity::Warn, "invalid attribute: %s"                                      },
+	{"c++-like-copy"            , Severity::Warn, "Constructor from reference is not a valid copy constructor" },
+	{"depreciated-trait-syntax" , Severity::Warn, "trait type-parameters are now specified using the forall clause" },
+};
+
+enum class Warning {
+	SelfAssignment,
+	RvalueToReferenceConversion,
+	BadQualifiersZeroOne,
+	AggrForwardDecl,
+	SuperfluousDecl,
+	SuperfluousElse,
+	GccAttributes,
+	CppCopy,
+	DeprecTraitSyntax,
+	NUMBER_OF_WARNINGS, // MUST be last warning
+};
+
+static_assert(
+	(sizeof(WarningFormats) / sizeof(WarningFormats[0])) == ((unsigned long)Warning::NUMBER_OF_WARNINGS),
+	"Each warning format should have a corresponding warning enum value"
+);
+
+void SemanticWarning( CodeLocation loc, Warning warn, ... );
+
+void SemanticWarning_SuppressAll();
+void SemanticWarning_EnableAll();
+void SemanticWarning_WarningAsError();
+void SemanticWarning_Set(const char * const name, Severity s);
+
+// SKULLDUGGERY: cfa.cc is built before SemanticError.cc but needs this routine.
+static inline bool SemanticWarning_Exist(const char * const name) {
+	for ( const auto & w : WarningFormats ) {
+		if ( std::strcmp( name, w.name ) == 0 ) return true;
+	}
+	return false;
+}
+
+//-----------------------------------------------------------------------------
+// Helpers
+namespace ErrorHelpers {
+	enum class Colors {
+		Never = false,
+		Always = true,
+		Auto,
+	};
+
+	extern Colors colors;
+
+	const std::string & error_str();
+	const std::string & warning_str();
+	const std::string & bold_ttycode();
+	const std::string & reset_font_ttycode();
+
+	std::string make_bold( const std::string & str );
+
+	struct bold {};
+	std::ostream & operator<<(std::ostream & os, bold);
+
+	struct reset_font {};
+	std::ostream & operator<<(std::ostream & os, reset_font);
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/Stats.h
===================================================================
--- src/Common/Stats.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,49 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Stats.h --
-//
-// Author           : Thierry Delisle
-// Created On       : Thu Feb 28 11::27:10 2019
-// Last Modified By :
-// Last Modified On :
-// Update Count     :
-//
-
-#pragma once
-
-// Entry point for compiler analytics.
-/*
-The compiler currently supports 3 times of analytics:
-	 - generic counters
-	 - heap statistics
-	 - timiing statistics
-
-These can be enabled using the --stats option, to which a comma seperated list of options can be passed.
-For more details see Stats.cc
-
-Counters:
-	The counters are a generic tree of counters that print in a 2-column output format.
-	They can count maximums, averages, totals, etc.
-
-	Currently all counters are under the same enable block, this could be changed if needed.
-
-Heap:
-	Measures the total calls malloc and free as the peak number of allocations per pass
-
-Timing:
-	Comming soon
-*/
-
-
-#include "Common/Stats/Counter.h"
-#include "Common/Stats/Heap.h"
-#include "Common/Stats/Time.h"
-
-namespace Stats {
-	void parse_params(const char * const params);
-	void print();
-}
Index: src/Common/Stats.hpp
===================================================================
--- src/Common/Stats.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,49 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Stats.hpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Thu Feb 28 11::27:10 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#pragma once
+
+// Entry point for compiler analytics.
+/*
+The compiler currently supports 3 times of analytics:
+	 - generic counters
+	 - heap statistics
+	 - timiing statistics
+
+These can be enabled using the --stats option, to which a comma seperated list of options can be passed.
+For more details see Stats.cc
+
+Counters:
+	The counters are a generic tree of counters that print in a 2-column output format.
+	They can count maximums, averages, totals, etc.
+
+	Currently all counters are under the same enable block, this could be changed if needed.
+
+Heap:
+	Measures the total calls malloc and free as the peak number of allocations per pass
+
+Timing:
+	Comming soon
+*/
+
+
+#include "Common/Stats/Counter.hpp"
+#include "Common/Stats/Heap.hpp"
+#include "Common/Stats/Time.hpp"
+
+namespace Stats {
+	void parse_params(const char * const params);
+	void print();
+}
Index: src/Common/Stats/Base.h
===================================================================
--- src/Common/Stats/Base.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,86 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Heap.h --
-//
-// Author           : Thierry Delisle
-// Created On       : Fri Mar 03 14:53:53 2019
-// Last Modified By :
-// Last Modified On :
-// Update Count     :
-//
-
-#pragma once
-
-#include <cstdint>
-#include <iostream>
-
-namespace Stats {
-	namespace Base {
-		class TreeImpl;
-
-		struct TreeTop {
-			TreeImpl * head = nullptr;
-			TreeImpl * tail = nullptr;
-
-			inline void append(TreeImpl * node);
-		};
-
-		template<typename func_t>
-		void ForAll(TreeTop & range, std::size_t level, func_t func, bool destroy = false);
-
-		class TreeImpl {
-		public:
-			virtual void print(std::ostream &) = 0;
-
-			const char * const name;
-			TreeImpl(const char * const name) : name(name) {}
-
-		protected:
-			virtual ~TreeImpl() = default;
-
-			TreeImpl * next = nullptr;
-			TreeTop children;
-
-			friend struct TreeTop;
-
-			template<typename func_t>
-			friend void ForAll(TreeTop & range, std::size_t level, func_t func, bool destroy);
-		};
-
-		void TreeTop::append(TreeImpl * node) {
-			if(!head) { head = node; }
-			else      { tail->next = node;}
-			tail = node;
-		}
-
-		template<typename func_t>
-		inline void ForAll(TreeTop & range, std::size_t level, func_t func, bool destroy) {
-			auto it = range.head;
-			while(it) {
-				auto next = it->next;
-				func(it, level);
-				ForAll(it->children, level + 1, func);
-				if(destroy) delete it;
-				it = next;
-			}
-		}
-
-		template<TreeTop & top>
-		class Tree : public TreeImpl {
-		public:
-			Tree(const char * const name) : TreeImpl{name} {
-				top.append(this);
-			}
-
-			Tree(const char * const name, Tree * parent) : TreeImpl{name} {
-				parent->children.append(this);
-			}
-		protected:
-			virtual ~Tree() = default;
-		};
-	}
-}
Index: src/Common/Stats/Base.hpp
===================================================================
--- src/Common/Stats/Base.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/Base.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,86 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Heap.h --
+//
+// Author           : Thierry Delisle
+// Created On       : Fri Mar 03 14:53:53 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#pragma once
+
+#include <cstdint>
+#include <iostream>
+
+namespace Stats {
+	namespace Base {
+		class TreeImpl;
+
+		struct TreeTop {
+			TreeImpl * head = nullptr;
+			TreeImpl * tail = nullptr;
+
+			inline void append(TreeImpl * node);
+		};
+
+		template<typename func_t>
+		void ForAll(TreeTop & range, std::size_t level, func_t func, bool destroy = false);
+
+		class TreeImpl {
+		public:
+			virtual void print(std::ostream &) = 0;
+
+			const char * const name;
+			TreeImpl(const char * const name) : name(name) {}
+
+		protected:
+			virtual ~TreeImpl() = default;
+
+			TreeImpl * next = nullptr;
+			TreeTop children;
+
+			friend struct TreeTop;
+
+			template<typename func_t>
+			friend void ForAll(TreeTop & range, std::size_t level, func_t func, bool destroy);
+		};
+
+		void TreeTop::append(TreeImpl * node) {
+			if(!head) { head = node; }
+			else      { tail->next = node;}
+			tail = node;
+		}
+
+		template<typename func_t>
+		inline void ForAll(TreeTop & range, std::size_t level, func_t func, bool destroy) {
+			auto it = range.head;
+			while(it) {
+				auto next = it->next;
+				func(it, level);
+				ForAll(it->children, level + 1, func);
+				if(destroy) delete it;
+				it = next;
+			}
+		}
+
+		template<TreeTop & top>
+		class Tree : public TreeImpl {
+		public:
+			Tree(const char * const name) : TreeImpl{name} {
+				top.append(this);
+			}
+
+			Tree(const char * const name, Tree * parent) : TreeImpl{name} {
+				parent->children.append(this);
+			}
+		protected:
+			virtual ~Tree() = default;
+		};
+	}
+}
Index: src/Common/Stats/Counter.cc
===================================================================
--- src/Common/Stats/Counter.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,58 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Counter.cc --
-//
-// Author           : Thierry Delisle
-// Created On       : Thu Feb 28 13::27:10 2019
-// Last Modified By :
-// Last Modified On :
-// Update Count     :
-//
-
-#include "Counter.h"
-
-#include <algorithm>
-#include <cstring>
-#include <functional>
-#include <iomanip>
-
-namespace Stats {
-	namespace Counters {
-		void print() {
-			if(!top.head) return;
-			size_t nc = 0;
-			Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
-				nc = std::max(nc, (4 * level) + std::strlen(node->name));
-			});
-
-			const char * const title = "Counter Statistic";
-			size_t nct = nc + 14;
-			std::cerr << std::string(nct, '=') << std::endl;
-			std::cerr << std::string((nct - std::strlen(title)) / 2, ' ');
-			std::cerr << title << std::endl;
-			std::cerr << std::string(nct, '-') << std::endl;
-
-
-			Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
-				std::cerr << std::string(level * 4, ' ');
-				std::cerr << node->name;
-				std::cerr << std::string(nc - ((level * 4) + std::strlen(node->name)), ' ');
-				std::cerr << " | ";
-				std::cerr << std::setw(9);
-				node->print(std::cerr);
-				std::cerr << " |";
-				std::cerr << '\n';
-			}, true);
-
-			std::cerr << std::string(nct, '-') << std::endl;
-		}
-
-		Base::TreeTop top;
-
-		extern bool enabled;
-	}
-}
Index: src/Common/Stats/Counter.cpp
===================================================================
--- src/Common/Stats/Counter.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/Counter.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,58 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Counter.cpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Thu Feb 28 13::27:10 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#include "Counter.hpp"
+
+#include <algorithm>
+#include <cstring>
+#include <functional>
+#include <iomanip>
+
+namespace Stats {
+	namespace Counters {
+		void print() {
+			if(!top.head) return;
+			size_t nc = 0;
+			Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
+				nc = std::max(nc, (4 * level) + std::strlen(node->name));
+			});
+
+			const char * const title = "Counter Statistic";
+			size_t nct = nc + 14;
+			std::cerr << std::string(nct, '=') << std::endl;
+			std::cerr << std::string((nct - std::strlen(title)) / 2, ' ');
+			std::cerr << title << std::endl;
+			std::cerr << std::string(nct, '-') << std::endl;
+
+
+			Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
+				std::cerr << std::string(level * 4, ' ');
+				std::cerr << node->name;
+				std::cerr << std::string(nc - ((level * 4) + std::strlen(node->name)), ' ');
+				std::cerr << " | ";
+				std::cerr << std::setw(9);
+				node->print(std::cerr);
+				std::cerr << " |";
+				std::cerr << '\n';
+			}, true);
+
+			std::cerr << std::string(nct, '-') << std::endl;
+		}
+
+		Base::TreeTop top;
+
+		extern bool enabled;
+	}
+}
Index: src/Common/Stats/Counter.h
===================================================================
--- src/Common/Stats/Counter.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,146 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Counter.h --
-//
-// Author           : Thierry Delisle
-// Created On       : Thu Feb 28 12::05:10 2019
-// Last Modified By :
-// Last Modified On :
-// Update Count     :
-//
-
-#pragma once
-
-#include <cstdint>
-#include <iostream>
-
-#include "Common/Stats/Base.h"
-
-#if defined( NO_STATISTICS )
-	#define NO_COUNTER_STATISTICS
-#endif
-
-namespace Stats {
-	namespace Counters {
-# 		if defined(NO_COUNTERS_STATISTICS)
-
-			static inline void print() {}
-
-			class CounterGroup {
-			public:
-			};
-
-			class SimpleCounter {
-			public:
-				inline void operator++() {}
-				inline void operator++(int) {}
-				inline void operator+=(size_t) {}
-			};
-
-			template<typename T>
-			class AverageCounter {
-			public:
-				inline void push(T value) {}
-			};
-
-			template<typename T>
-			class MaxCounter {
-			public:
-				inline void push(T value) {}
-			};
-
-			template<typename counter_t>
-			counter_t * build(const char * const name) {
-				return nullptr;
-			}
-
-			template<typename counter_t>
-			counter_t * build(const char * const name, Base::Tree<top> * parent) {
-					return nullptr;
-			}
-#		else
-			extern bool enabled;
-
-			extern Base::TreeTop top;
-
-			class CounterGroup : public Base::Tree<top> {
-			public:
-				CounterGroup(const char * const name ) : Base::Tree<top>(name) {}
-				CounterGroup(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent) {}
-
-				virtual void print(std::ostream & os) override { os << ""; }
-			protected:
-				virtual ~CounterGroup() = default;
-			};
-
-			class SimpleCounter : public Base::Tree<top> {
-			public:
-				SimpleCounter(const char * const name ) : Base::Tree<top>(name) {}
-				SimpleCounter(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent) {}
-
-				virtual void print(std::ostream & os) override { os << count; }
-
-				inline void operator++()             { if(!enabled) return; count++;        }
-				inline void operator++(int)          { if(!enabled) return; count++;        }
-				inline void operator+=(size_t value) { if(!enabled) return; count += value; }
-
-			protected:
-				virtual ~SimpleCounter() = default;
-
-			private:
-				size_t count = 0;
-			};
-
-			template<typename T>
-			class AverageCounter : public Base::Tree<top> {
-			public:
-				AverageCounter(const char * const name ) : Base::Tree<top>(name), sum{} {}
-				AverageCounter(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent), sum{} {}
-
-				virtual void print(std::ostream & os) { os << sum / count; }
-
-				inline void push(T value) { if(!enabled) return; sum += value; count++; }
-
-			protected:
-				virtual ~AverageCounter() = default;
-
-			private:
-				T sum;
-				size_t count = 1;
-			};
-
-			template<typename T>
-			class MaxCounter : public Base::Tree<top> {
-			public:
-				MaxCounter(const char * const name ) : Base::Tree<top>(name), max{} {}
-				MaxCounter(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent), max{} {}
-
-				virtual void print(std::ostream & os) { os << max; }
-
-				inline void push(T value) { if(!enabled) return; max = std::max(max, value); }
-
-			protected:
-				virtual ~MaxCounter() = default;
-
-			private:
-				T max;
-			};
-
-			template<typename counter_t>
-			counter_t * build(const char * const name) {
-				if(!enabled) return nullptr;
-				return new counter_t(name);
-			}
-
-			template<typename counter_t>
-			counter_t * build(const char * const name, Base::Tree<top> * parent) {
-				if(!enabled) return nullptr;
-				return new counter_t(name, parent);
-			}
-#		endif
-	}
-}
Index: src/Common/Stats/Counter.hpp
===================================================================
--- src/Common/Stats/Counter.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/Counter.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,146 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Counter.hpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Thu Feb 28 12::05:10 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#pragma once
+
+#include <cstdint>
+#include <iostream>
+
+#include "Common/Stats/Base.hpp"
+
+#if defined( NO_STATISTICS )
+	#define NO_COUNTER_STATISTICS
+#endif
+
+namespace Stats {
+	namespace Counters {
+# 		if defined(NO_COUNTERS_STATISTICS)
+
+			static inline void print() {}
+
+			class CounterGroup {
+			public:
+			};
+
+			class SimpleCounter {
+			public:
+				inline void operator++() {}
+				inline void operator++(int) {}
+				inline void operator+=(size_t) {}
+			};
+
+			template<typename T>
+			class AverageCounter {
+			public:
+				inline void push(T value) {}
+			};
+
+			template<typename T>
+			class MaxCounter {
+			public:
+				inline void push(T value) {}
+			};
+
+			template<typename counter_t>
+			counter_t * build(const char * const name) {
+				return nullptr;
+			}
+
+			template<typename counter_t>
+			counter_t * build(const char * const name, Base::Tree<top> * parent) {
+					return nullptr;
+			}
+#		else
+			extern bool enabled;
+
+			extern Base::TreeTop top;
+
+			class CounterGroup : public Base::Tree<top> {
+			public:
+				CounterGroup(const char * const name ) : Base::Tree<top>(name) {}
+				CounterGroup(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent) {}
+
+				virtual void print(std::ostream & os) override { os << ""; }
+			protected:
+				virtual ~CounterGroup() = default;
+			};
+
+			class SimpleCounter : public Base::Tree<top> {
+			public:
+				SimpleCounter(const char * const name ) : Base::Tree<top>(name) {}
+				SimpleCounter(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent) {}
+
+				virtual void print(std::ostream & os) override { os << count; }
+
+				inline void operator++()             { if(!enabled) return; count++;        }
+				inline void operator++(int)          { if(!enabled) return; count++;        }
+				inline void operator+=(size_t value) { if(!enabled) return; count += value; }
+
+			protected:
+				virtual ~SimpleCounter() = default;
+
+			private:
+				size_t count = 0;
+			};
+
+			template<typename T>
+			class AverageCounter : public Base::Tree<top> {
+			public:
+				AverageCounter(const char * const name ) : Base::Tree<top>(name), sum{} {}
+				AverageCounter(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent), sum{} {}
+
+				virtual void print(std::ostream & os) { os << sum / count; }
+
+				inline void push(T value) { if(!enabled) return; sum += value; count++; }
+
+			protected:
+				virtual ~AverageCounter() = default;
+
+			private:
+				T sum;
+				size_t count = 1;
+			};
+
+			template<typename T>
+			class MaxCounter : public Base::Tree<top> {
+			public:
+				MaxCounter(const char * const name ) : Base::Tree<top>(name), max{} {}
+				MaxCounter(const char * const name, Base::Tree<top> * parent) : Base::Tree<top>(name, parent), max{} {}
+
+				virtual void print(std::ostream & os) { os << max; }
+
+				inline void push(T value) { if(!enabled) return; max = std::max(max, value); }
+
+			protected:
+				virtual ~MaxCounter() = default;
+
+			private:
+				T max;
+			};
+
+			template<typename counter_t>
+			counter_t * build(const char * const name) {
+				if(!enabled) return nullptr;
+				return new counter_t(name);
+			}
+
+			template<typename counter_t>
+			counter_t * build(const char * const name, Base::Tree<top> * parent) {
+				if(!enabled) return nullptr;
+				return new counter_t(name, parent);
+			}
+#		endif
+	}
+}
Index: src/Common/Stats/Heap.cc
===================================================================
--- src/Common/Stats/Heap.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,272 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Heap.cc --
-//
-// Author           : Thierry Delisle
-// Created On       : Thu May  3 16:16:10 2018
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri May  4 17:27:31 2018
-// Update Count     : 28
-//
-
-#include <cassert>
-#include <cmath>
-#include <cstddef>
-#include <cstring>
-#include <iomanip>
-#include <iostream>
-
-#if defined(__has_feature)
-	#if __has_feature(address_sanitizer)
-		#define NO_HEAP_STATISTICS
-	# endif
-#endif
-
-#if defined( NO_STATISTICS ) || defined( TCMALLOC ) || defined(__SANITIZE_ADDRESS__)
-	#if !defined(NO_HEAP_STATISTICS)
-		#define NO_HEAP_STATISTICS
-	#endif
-#endif
-
-namespace Stats {
-	namespace Heap {
-#if defined( NO_HEAP_STATISTICS )
-		void newPass( const char * const ) {}
-
-		void print() {}
-#else
-		extern bool enabled;
-
-		struct StatBlock {
-			const char * name  = nullptr;	///< Name of this pass
-			size_t mallocs     = 0;			///< Allocations in this pass
-			size_t frees       = 0;			///< Frees in this pass
-			size_t n_allocs    = 0;			///< Current number of live allocations
-			size_t peak_allocs = 0;			///< Peak number of live allocations this pass
-		};
-
-		StatBlock    passes[100] = {{ "Pre-Parse", 0, 0, 0, 0 }};
-		const size_t passes_size = sizeof(passes) / sizeof(passes[0]);
-		size_t       passes_cnt = 1;
-
-		StatBlock    stacktrace_stats[100];
-		size_t       stacktrace_stats_count = 0;
-		bool         stacktrace_stats_enabled = true;
-
-		size_t       trace[1000];
-		const size_t stacktrace_max_depth = sizeof(trace) / sizeof(size_t);
-		size_t       stacktrace_depth;
-
-		size_t new_stacktrace_id(const char * const name) {
-			stacktrace_stats[stacktrace_stats_count].name = name;
-			return stacktrace_stats_count++;
-		}
-
-		void stacktrace_push(size_t id) {
-			++stacktrace_depth;
-			assertf(stacktrace_depth < stacktrace_max_depth, "Stack trace too deep: increase size of array in Heap.cc");
-			trace[stacktrace_depth] = id;
-		}
-
-		void stacktrace_pop() {
-			assertf(stacktrace_depth > 0, "Invalid stack tracing operation: trace is empty");
-			--stacktrace_depth;
-		}
-
-		void newPass( const char * const name ) {
-			passes[passes_cnt].name    = name;
-			passes[passes_cnt].mallocs = 0;
-			passes[passes_cnt].frees   = 0;
-			passes[passes_cnt].n_allocs
-				= passes[passes_cnt].peak_allocs
-				= passes[passes_cnt-1].n_allocs;
-			passes_cnt++;
-
-			assertf(passes_cnt < passes_size, "Too many passes for Stats::Heap, increase the size of the array in Heap.cc");
-		}
-
-		void print(size_t value, size_t total) {
-			std::cerr << std::setw(12) << value;
-			std::cerr << "(" << std::setw(3);
-			std::cerr << (value == 0 ? 0 : value * 100 / total);
-			std::cerr << "%) | ";
-		}
-
-		void print(const StatBlock& stat, size_t nc, size_t total_mallocs, size_t total_frees, size_t overall_peak) {
-			std::cerr << std::setw(nc) << stat.name;
-			std::cerr << " | ";
-
-			print(stat.mallocs,     total_mallocs);
-			print(stat.frees,       total_frees  );
-			print(stat.peak_allocs, overall_peak );
-			std::cerr << "\n";
-		}
-
-		void print(char c, size_t nc) {
-			for(size_t i = 0; i < nc; i++) {
-				std::cerr << c;
-			}
-			std::cerr << '\n';
-		}
-
-		void print() {
-			if(!enabled) return;
-
-			size_t nc = 0;
-			size_t total_mallocs = 0;
-			size_t total_frees   = 0;
-			size_t overall_peak  = 0;
-			for(size_t i = 0; i < passes_cnt; i++) {
-				nc = std::max(nc, std::strlen(passes[i].name));
-				total_mallocs += passes[i].mallocs;
-				total_frees   += passes[i].frees;
-				overall_peak = std::max(overall_peak, passes[i].peak_allocs);
-			}
-			size_t nct = nc + 65;
-
-			const char * const title = "Heap Usage Statistic";
-			print('=', nct);
-			for(size_t i = 0; i < (nct - std::strlen(title)) / 2; i++) std::cerr << ' ';
-			std::cerr << title << std::endl;
-			print('-', nct);
-			std::cerr << std::setw(nc) << "Pass";
-			std::cerr << " |       Malloc Count |         Free Count |        Peak Allocs |" << std::endl;
-
-			print('-', nct);
-			for(size_t i = 0; i < passes_cnt; i++) {
-				print(passes[i], nc, total_mallocs, total_frees, overall_peak);
-			}
-
-			print('-', nct);
-			std::cerr << std::setw(nc) << "Trace";
-			std::cerr << " |       Malloc Count |         Free Count |        Peak Allocs |" << std::endl;
-
-			print('-', nct);
-			for (size_t i = 0; i < stacktrace_stats_count; i++) {
-				print(stacktrace_stats[i], nc, total_mallocs, total_frees, overall_peak);
-			}
-			print('-', nct);
-			print({"Sum", total_mallocs, total_frees, 0, overall_peak},
-				nc, total_mallocs, total_frees, overall_peak);
-
-		}
-
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-		extern "C" {
-#include <dlfcn.h>
-#include <execinfo.h>
-		}
-
-	//=============================================================================================
-	// Interposing helpers
-	//=============================================================================================
-
-		typedef void (* generic_fptr_t)(void);
-		generic_fptr_t interpose_symbol( const char * symbol, const char * version ) {
-			const char * error;
-
-			static void * library;
-			if ( ! library ) {
-#				if defined( RTLD_NEXT )
-					library = RTLD_NEXT;
-#				else
-					// missing RTLD_NEXT => must hard-code library name, assuming libstdc++
-					library = dlopen( "libc.so.6", RTLD_LAZY );
-					error = dlerror();
-					if ( error ) {
-						std::cerr << "interpose_symbol : failed to open libc, " << error << std::endl;
-						abort();
-					}
-#				endif // RTLD_NEXT
-			} // if
-
-			generic_fptr_t fptr;
-
-#			if defined( _GNU_SOURCE )
-				if ( version ) {
-					fptr = (generic_fptr_t)dlvsym( library, symbol, version );
-				} else {
-					fptr = (generic_fptr_t)dlsym( library, symbol );
-				}
-#			else
-				fptr = (generic_fptr_t)dlsym( library, symbol );
-#			endif // _GNU_SOURCE
-
-			error = dlerror();
-			if ( error ) {
-				std::cerr << "interpose_symbol : internal error, " << error << std::endl;
-				abort();
-			}
-
-			return fptr;
-		}
-
-		extern "C" {
-			void * malloc( size_t size ) __attribute__((malloc));
-			void * malloc( size_t size ) {
-				static auto __malloc = reinterpret_cast<void * (*)(size_t)>(interpose_symbol( "malloc", nullptr ));
-				if( enabled && passes_cnt > 0 ) {
-					passes[passes_cnt - 1].mallocs++;
-					passes[passes_cnt - 1].n_allocs++;
-					passes[passes_cnt - 1].peak_allocs
-						= std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
-				}
-
-				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
-					stacktrace_stats[trace[stacktrace_depth]].mallocs++;
-				}
-				return __malloc( size );
-			}
-
-			void free( void * ptr ) {
-				static auto __free = reinterpret_cast<void   (*)(void *)>(interpose_symbol( "free", nullptr ));
-				if( enabled && passes_cnt > 0 ) {
-					passes[passes_cnt - 1].frees++;
-					passes[passes_cnt - 1].n_allocs--;
-				}
-				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
-					stacktrace_stats[trace[stacktrace_depth]].frees++;
-				}
-				return __free( ptr );
-			}
-
-			void * calloc( size_t nelem, size_t size ) {
-				static auto __calloc = reinterpret_cast<void * (*)(size_t, size_t)>(interpose_symbol( "calloc", nullptr ));
-				if( enabled && passes_cnt > 0 ) {
-					passes[passes_cnt - 1].mallocs++;
-					passes[passes_cnt - 1].n_allocs++;
-					passes[passes_cnt - 1].peak_allocs
-						= std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
-				}
-				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
-					stacktrace_stats[trace[stacktrace_depth]].mallocs++;
-				}
-				return __calloc( nelem, size );
-			}
-
-			void * realloc( void * ptr, size_t size ) {
-				static auto __realloc = reinterpret_cast<void * (*)(void *, size_t)>(interpose_symbol( "realloc", nullptr ));
-				void * s = __realloc( ptr, size );
-				if ( enabled && s != ptr && passes_cnt > 0 ) {			// did realloc get new storage ?
-					passes[passes_cnt - 1].mallocs++;
-					passes[passes_cnt - 1].frees++;
-				} // if
-				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
-					stacktrace_stats[trace[stacktrace_depth]].mallocs++;
-					stacktrace_stats[trace[stacktrace_depth]].frees++;
-				}
-				return s;
-			}
-		}
-#endif
-	}
-}
Index: src/Common/Stats/Heap.cpp
===================================================================
--- src/Common/Stats/Heap.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/Heap.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,272 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Heap.cc --
+//
+// Author           : Thierry Delisle
+// Created On       : Thu May  3 16:16:10 2018
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri May  4 17:27:31 2018
+// Update Count     : 28
+//
+
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstring>
+#include <iomanip>
+#include <iostream>
+
+#if defined(__has_feature)
+	#if __has_feature(address_sanitizer)
+		#define NO_HEAP_STATISTICS
+	# endif
+#endif
+
+#if defined( NO_STATISTICS ) || defined( TCMALLOC ) || defined(__SANITIZE_ADDRESS__)
+	#if !defined(NO_HEAP_STATISTICS)
+		#define NO_HEAP_STATISTICS
+	#endif
+#endif
+
+namespace Stats {
+	namespace Heap {
+#if defined( NO_HEAP_STATISTICS )
+		void newPass( const char * const ) {}
+
+		void print() {}
+#else
+		extern bool enabled;
+
+		struct StatBlock {
+			const char * name  = nullptr;	///< Name of this pass
+			size_t mallocs     = 0;			///< Allocations in this pass
+			size_t frees       = 0;			///< Frees in this pass
+			size_t n_allocs    = 0;			///< Current number of live allocations
+			size_t peak_allocs = 0;			///< Peak number of live allocations this pass
+		};
+
+		StatBlock    passes[100] = {{ "Pre-Parse", 0, 0, 0, 0 }};
+		const size_t passes_size = sizeof(passes) / sizeof(passes[0]);
+		size_t       passes_cnt = 1;
+
+		StatBlock    stacktrace_stats[100];
+		size_t       stacktrace_stats_count = 0;
+		bool         stacktrace_stats_enabled = true;
+
+		size_t       trace[1000];
+		const size_t stacktrace_max_depth = sizeof(trace) / sizeof(size_t);
+		size_t       stacktrace_depth;
+
+		size_t new_stacktrace_id(const char * const name) {
+			stacktrace_stats[stacktrace_stats_count].name = name;
+			return stacktrace_stats_count++;
+		}
+
+		void stacktrace_push(size_t id) {
+			++stacktrace_depth;
+			assertf(stacktrace_depth < stacktrace_max_depth, "Stack trace too deep: increase size of array in Heap.cc");
+			trace[stacktrace_depth] = id;
+		}
+
+		void stacktrace_pop() {
+			assertf(stacktrace_depth > 0, "Invalid stack tracing operation: trace is empty");
+			--stacktrace_depth;
+		}
+
+		void newPass( const char * const name ) {
+			passes[passes_cnt].name    = name;
+			passes[passes_cnt].mallocs = 0;
+			passes[passes_cnt].frees   = 0;
+			passes[passes_cnt].n_allocs
+				= passes[passes_cnt].peak_allocs
+				= passes[passes_cnt-1].n_allocs;
+			passes_cnt++;
+
+			assertf(passes_cnt < passes_size, "Too many passes for Stats::Heap, increase the size of the array in Heap.cc");
+		}
+
+		void print(size_t value, size_t total) {
+			std::cerr << std::setw(12) << value;
+			std::cerr << "(" << std::setw(3);
+			std::cerr << (value == 0 ? 0 : value * 100 / total);
+			std::cerr << "%) | ";
+		}
+
+		void print(const StatBlock& stat, size_t nc, size_t total_mallocs, size_t total_frees, size_t overall_peak) {
+			std::cerr << std::setw(nc) << stat.name;
+			std::cerr << " | ";
+
+			print(stat.mallocs,     total_mallocs);
+			print(stat.frees,       total_frees  );
+			print(stat.peak_allocs, overall_peak );
+			std::cerr << "\n";
+		}
+
+		void print(char c, size_t nc) {
+			for(size_t i = 0; i < nc; i++) {
+				std::cerr << c;
+			}
+			std::cerr << '\n';
+		}
+
+		void print() {
+			if(!enabled) return;
+
+			size_t nc = 0;
+			size_t total_mallocs = 0;
+			size_t total_frees   = 0;
+			size_t overall_peak  = 0;
+			for(size_t i = 0; i < passes_cnt; i++) {
+				nc = std::max(nc, std::strlen(passes[i].name));
+				total_mallocs += passes[i].mallocs;
+				total_frees   += passes[i].frees;
+				overall_peak = std::max(overall_peak, passes[i].peak_allocs);
+			}
+			size_t nct = nc + 65;
+
+			const char * const title = "Heap Usage Statistic";
+			print('=', nct);
+			for(size_t i = 0; i < (nct - std::strlen(title)) / 2; i++) std::cerr << ' ';
+			std::cerr << title << std::endl;
+			print('-', nct);
+			std::cerr << std::setw(nc) << "Pass";
+			std::cerr << " |       Malloc Count |         Free Count |        Peak Allocs |" << std::endl;
+
+			print('-', nct);
+			for(size_t i = 0; i < passes_cnt; i++) {
+				print(passes[i], nc, total_mallocs, total_frees, overall_peak);
+			}
+
+			print('-', nct);
+			std::cerr << std::setw(nc) << "Trace";
+			std::cerr << " |       Malloc Count |         Free Count |        Peak Allocs |" << std::endl;
+
+			print('-', nct);
+			for (size_t i = 0; i < stacktrace_stats_count; i++) {
+				print(stacktrace_stats[i], nc, total_mallocs, total_frees, overall_peak);
+			}
+			print('-', nct);
+			print({"Sum", total_mallocs, total_frees, 0, overall_peak},
+				nc, total_mallocs, total_frees, overall_peak);
+
+		}
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+		extern "C" {
+#include <dlfcn.h>
+#include <execinfo.h>
+		}
+
+	//=============================================================================================
+	// Interposing helpers
+	//=============================================================================================
+
+		typedef void (* generic_fptr_t)(void);
+		generic_fptr_t interpose_symbol( const char * symbol, const char * version ) {
+			const char * error;
+
+			static void * library;
+			if ( ! library ) {
+#				if defined( RTLD_NEXT )
+					library = RTLD_NEXT;
+#				else
+					// missing RTLD_NEXT => must hard-code library name, assuming libstdc++
+					library = dlopen( "libc.so.6", RTLD_LAZY );
+					error = dlerror();
+					if ( error ) {
+						std::cerr << "interpose_symbol : failed to open libc, " << error << std::endl;
+						abort();
+					}
+#				endif // RTLD_NEXT
+			} // if
+
+			generic_fptr_t fptr;
+
+#			if defined( _GNU_SOURCE )
+				if ( version ) {
+					fptr = (generic_fptr_t)dlvsym( library, symbol, version );
+				} else {
+					fptr = (generic_fptr_t)dlsym( library, symbol );
+				}
+#			else
+				fptr = (generic_fptr_t)dlsym( library, symbol );
+#			endif // _GNU_SOURCE
+
+			error = dlerror();
+			if ( error ) {
+				std::cerr << "interpose_symbol : internal error, " << error << std::endl;
+				abort();
+			}
+
+			return fptr;
+		}
+
+		extern "C" {
+			void * malloc( size_t size ) __attribute__((malloc));
+			void * malloc( size_t size ) {
+				static auto __malloc = reinterpret_cast<void * (*)(size_t)>(interpose_symbol( "malloc", nullptr ));
+				if( enabled && passes_cnt > 0 ) {
+					passes[passes_cnt - 1].mallocs++;
+					passes[passes_cnt - 1].n_allocs++;
+					passes[passes_cnt - 1].peak_allocs
+						= std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
+				}
+
+				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
+					stacktrace_stats[trace[stacktrace_depth]].mallocs++;
+				}
+				return __malloc( size );
+			}
+
+			void free( void * ptr ) {
+				static auto __free = reinterpret_cast<void   (*)(void *)>(interpose_symbol( "free", nullptr ));
+				if( enabled && passes_cnt > 0 ) {
+					passes[passes_cnt - 1].frees++;
+					passes[passes_cnt - 1].n_allocs--;
+				}
+				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
+					stacktrace_stats[trace[stacktrace_depth]].frees++;
+				}
+				return __free( ptr );
+			}
+
+			void * calloc( size_t nelem, size_t size ) {
+				static auto __calloc = reinterpret_cast<void * (*)(size_t, size_t)>(interpose_symbol( "calloc", nullptr ));
+				if( enabled && passes_cnt > 0 ) {
+					passes[passes_cnt - 1].mallocs++;
+					passes[passes_cnt - 1].n_allocs++;
+					passes[passes_cnt - 1].peak_allocs
+						= std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
+				}
+				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
+					stacktrace_stats[trace[stacktrace_depth]].mallocs++;
+				}
+				return __calloc( nelem, size );
+			}
+
+			void * realloc( void * ptr, size_t size ) {
+				static auto __realloc = reinterpret_cast<void * (*)(void *, size_t)>(interpose_symbol( "realloc", nullptr ));
+				void * s = __realloc( ptr, size );
+				if ( enabled && s != ptr && passes_cnt > 0 ) {			// did realloc get new storage ?
+					passes[passes_cnt - 1].mallocs++;
+					passes[passes_cnt - 1].frees++;
+				} // if
+				if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
+					stacktrace_stats[trace[stacktrace_depth]].mallocs++;
+					stacktrace_stats[trace[stacktrace_depth]].frees++;
+				}
+				return s;
+			}
+		}
+#endif
+	}
+}
Index: src/Common/Stats/Heap.h
===================================================================
--- src/Common/Stats/Heap.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,27 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Heap.h --
-//
-// Author           : Thierry Delisle
-// Created On       : Thu May  3 16:16:10 2018
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri May  4 14:34:08 2018
-// Update Count     : 3
-//
-
-#pragma once
-
-namespace Stats {
-	namespace Heap {
-		void newPass( const char * const name );
-		void print();
-
-		size_t new_stacktrace_id(const char * const name);
-		void stacktrace_push(size_t id);
-		void stacktrace_pop();
-	}
-}
Index: src/Common/Stats/Heap.hpp
===================================================================
--- src/Common/Stats/Heap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/Heap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,27 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Heap.hpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Thu May  3 16:16:10 2018
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri May  4 14:34:08 2018
+// Update Count     : 3
+//
+
+#pragma once
+
+namespace Stats {
+	namespace Heap {
+		void newPass( const char * const name );
+		void print();
+
+		size_t new_stacktrace_id(const char * const name);
+		void stacktrace_push(size_t id);
+		void stacktrace_pop();
+	}
+}
Index: src/Common/Stats/ResolveTime.cc
===================================================================
--- src/Common/Stats/ResolveTime.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,67 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ResolveTime.cc --
-//
-// Author           : Thierry Delisle
-// Created On       : Wed Sep 16 15:45:51 2020
-// Last Modified By :
-// Last Modified On :
-// Update Count     :
-//
-
-#include "ResolveTime.h"
-
-#include <fstream>
-#include <iomanip>
-
-#include "AST/Fwd.hpp"
-#include "AST/Expr.hpp"
-#include "AST/Print.hpp"
-#include "AST/Type.hpp"
-
-namespace Stats {
-	namespace ResolveTime {
-		static inline long long rdtscl(void) {
-			#if defined( __i386 ) || defined( __x86_64 )
-				unsigned int lo, hi;
-				__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
-				return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
-			#elif defined( __aarch64__ )
-				int64_t value;
-				asm volatile("mrs %0, cntvct_el0" : "=r"(value));
-				return value;
-			#else
-				#error unknown hardware architecture
-			#endif
-		}
-
-		extern bool enabled;
-		bool started = false;
-		long long before;
-		std::ostream & out = std::cout;
-
-		void start( const ast::Expr * expr ) {
-			if(enabled) {
-				assert(!started);
-				started = true;
-
-				out << expr->location << " : ";
-
-				before = rdtscl();
-			}
-		}
-		void stop() {
-			if(enabled) {
-				assert(started);
-				auto after = rdtscl();
-				out << (after - before) << std::endl;
-
-				started = false;
-			}
-		}
-	};
-};
Index: src/Common/Stats/ResolveTime.cpp
===================================================================
--- src/Common/Stats/ResolveTime.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/ResolveTime.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,67 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ResolveTime.cpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Wed Sep 16 15:45:51 2020
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#include "ResolveTime.hpp"
+
+#include <fstream>
+#include <iomanip>
+
+#include "AST/Fwd.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Print.hpp"
+#include "AST/Type.hpp"
+
+namespace Stats {
+	namespace ResolveTime {
+		static inline long long rdtscl(void) {
+			#if defined( __i386 ) || defined( __x86_64 )
+				unsigned int lo, hi;
+				__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
+				return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
+			#elif defined( __aarch64__ )
+				int64_t value;
+				asm volatile("mrs %0, cntvct_el0" : "=r"(value));
+				return value;
+			#else
+				#error unknown hardware architecture
+			#endif
+		}
+
+		extern bool enabled;
+		bool started = false;
+		long long before;
+		std::ostream & out = std::cout;
+
+		void start( const ast::Expr * expr ) {
+			if(enabled) {
+				assert(!started);
+				started = true;
+
+				out << expr->location << " : ";
+
+				before = rdtscl();
+			}
+		}
+		void stop() {
+			if(enabled) {
+				assert(started);
+				auto after = rdtscl();
+				out << (after - before) << std::endl;
+
+				started = false;
+			}
+		}
+	};
+};
Index: src/Common/Stats/ResolveTime.h
===================================================================
--- src/Common/Stats/ResolveTime.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,38 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ResolveTime.h --
-//
-// Author           : Thierry Delisle
-// Created On       : Wed Sep 16 15:45:51 2020
-// Last Modified By :
-// Last Modified On :
-// Update Count     :
-//
-
-#pragma once
-
-#include "Common/Stats/Base.h"
-
-#if defined( NO_STATISTICS )
-	#define NO_RESOLVE_TIME_STATISTICS
-#endif
-
-namespace ast {
-	class Expr;
-}
-
-namespace Stats {
-	namespace ResolveTime {
-		#if defined(NO_RESOLVE_TIME_STATISTICS)
-			void start( const ast::Expr * ) {}
-			void stop() {}
-		#else
-			void start( const ast::Expr * );
-			void stop();
-		#endif
-	};
-};
Index: src/Common/Stats/ResolveTime.hpp
===================================================================
--- src/Common/Stats/ResolveTime.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/ResolveTime.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,38 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ResolveTime.hpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Wed Sep 16 15:45:51 2020
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#pragma once
+
+#include "Common/Stats/Base.hpp"
+
+#if defined( NO_STATISTICS )
+	#define NO_RESOLVE_TIME_STATISTICS
+#endif
+
+namespace ast {
+	class Expr;
+}
+
+namespace Stats {
+	namespace ResolveTime {
+		#if defined(NO_RESOLVE_TIME_STATISTICS)
+			void start( const ast::Expr * ) {}
+			void stop() {}
+		#else
+			void start( const ast::Expr * );
+			void stop();
+		#endif
+	};
+};
Index: src/Common/Stats/Stats.cc
===================================================================
--- src/Common/Stats/Stats.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,91 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Stats.cc --
-//
-// Author           : Thierry Delisle
-// Created On       : Fri Mar 01 15:45:08 2019
-// Last Modified By :
-// Last Modified On :
-// Update Count     :
-//
-
-#include <iostream>
-#include <sstream>
-#include <string>
-
-
-namespace Stats {
-	namespace Counters {
-		bool enabled = false;
-		void print();
-	}
-
-	namespace Heap {
-		bool enabled = false;
-		void print();
-	}
-
-	namespace Time {
-		bool enabled = false;
-		void print();
-	}
-
-	namespace ResolveTime {
-		bool enabled = false;
-	}
-
-	struct {
-		const char * const opt;
-		bool & enabled;
-	}
-	statistics[] = {
-		{ "counters", Counters::enabled },
-		{ "heap"    , Heap::enabled },
-		{ "time"    , Time::enabled },
-		{ "resolve" , ResolveTime::enabled },
-	};
-
-	void set_param(std::string & param) {
-		if(param == "all") {
-			for(auto & stat : statistics) {
-				stat.enabled = true;
-			}
-			return;
-		}
-
-		if(param == "none") {
-			for(auto & stat : statistics) {
-				stat.enabled = false;
-			}
-			return;
-		}
-
-		for(auto & stat : statistics) {
-			if(stat.opt == param) {
-				stat.enabled = true;
-				return;
-			}
-		}
-
-		std::cerr << "Ignoring unknown statistic " << param << std::endl;
-	}
-
-	void parse_params(const char * const params) {
-		std::stringstream ss(params);
-		while(ss.good()) {
-			std::string substr;
-			getline( ss, substr, ',' );
-			set_param(substr);
-		}
-	}
-
-	void print() {
-		Counters::print();
-		Heap::print();
-		Time::print();
-	}
-}
Index: src/Common/Stats/Stats.cpp
===================================================================
--- src/Common/Stats/Stats.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/Stats.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,91 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Stats.cc --
+//
+// Author           : Thierry Delisle
+// Created On       : Fri Mar 01 15:45:08 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+
+namespace Stats {
+	namespace Counters {
+		bool enabled = false;
+		void print();
+	}
+
+	namespace Heap {
+		bool enabled = false;
+		void print();
+	}
+
+	namespace Time {
+		bool enabled = false;
+		void print();
+	}
+
+	namespace ResolveTime {
+		bool enabled = false;
+	}
+
+	struct {
+		const char * const opt;
+		bool & enabled;
+	}
+	statistics[] = {
+		{ "counters", Counters::enabled },
+		{ "heap"    , Heap::enabled },
+		{ "time"    , Time::enabled },
+		{ "resolve" , ResolveTime::enabled },
+	};
+
+	void set_param(std::string & param) {
+		if(param == "all") {
+			for(auto & stat : statistics) {
+				stat.enabled = true;
+			}
+			return;
+		}
+
+		if(param == "none") {
+			for(auto & stat : statistics) {
+				stat.enabled = false;
+			}
+			return;
+		}
+
+		for(auto & stat : statistics) {
+			if(stat.opt == param) {
+				stat.enabled = true;
+				return;
+			}
+		}
+
+		std::cerr << "Ignoring unknown statistic " << param << std::endl;
+	}
+
+	void parse_params(const char * const params) {
+		std::stringstream ss(params);
+		while(ss.good()) {
+			std::string substr;
+			getline( ss, substr, ',' );
+			set_param(substr);
+		}
+	}
+
+	void print() {
+		Counters::print();
+		Heap::print();
+		Time::print();
+	}
+}
Index: src/Common/Stats/Time.cc
===================================================================
--- src/Common/Stats/Time.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,199 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Time.cc --
-//
-// Author           : Thierry Delisle
-// Created On       : Mon Mar 04 15:16:07 2019
-// Last Modified By :
-// Last Modified On :
-// Update Count     :
-//
-
-#include "Time.h"
-
-#include <cassert>
-#include <chrono>
-#include <cstdint>
-#include <cstring>
-#include <iostream>
-#include <iomanip>
-#include <stack>
-
-namespace Stats {
-	namespace Time {
-#		if !defined(NO_TIME_STATISTICS)
-			extern bool enabled;
-
-			Base::TreeTop top;
-
-			typedef  std::chrono::time_point<std::chrono::high_resolution_clock> point_t;
-			std::chrono::duration<double> total;
-
-			point_t global_begin;
-
-			int prevl = 0;
-			int currl = 0;
-
-			template<typename T>
-			static inline std::ostream & operator<<(std::ostream & os, const std::chrono::duration<T> & dd) {
-				auto d = std::chrono::duration_cast<std::chrono::milliseconds>(dd);
-				auto minutes = std::chrono::duration_cast<std::chrono::minutes>(d);
-				auto seconds = std::chrono::duration_cast<std::chrono::seconds>(d % std::chrono::minutes(1));
-				auto millis  = std::chrono::duration_cast<std::chrono::milliseconds>(d % std::chrono::seconds(1));
-
-				bool zmin = minutes == minutes.zero();
-				bool zsec = seconds == seconds.zero();
-				bool zmil = millis  == millis .zero();
-
-				if(!zmin) {
-					os << std::setw(4) << minutes.count() << "m";
-				} else {
-					os << std::string(5, ' ');
-				}
-
-				if(!zmin || !zsec) {
-					if(!zmin) os << std::setfill('0');
-					os << std::setw(2) << seconds.count() << "s";
-				} else {
-					os << std::string(3, ' ');
-				}
-				os << std::setfill(' ');
-
-				if(!zmin || !zsec || !zmil) {
-					if(!zmin || !zsec) os << std::setfill('0');
-					os << std::setw(3) << millis .count();
-				} else {
-					os << std::string(4, ' ');
-				}
-				os << std::setfill(' ');
-
-				return os;
-			}
-
-			class TimerNode : public Base::Tree<top> {
-			public:
-				TimerNode(const char * const name )
-					: Base::Tree<top>(name)
-				{}
-
-				TimerNode(const char * const name, Base::Tree<top> * parent)
-					: Base::Tree<top>(name, parent)
-
-				{}
-
-				virtual void print(std::ostream & os) override {
-					if(currl > prevl) {
-						parents.push(last);
-					}
-					for(auto lvl = prevl - currl; lvl > 0; lvl--) {
-						parents.pop();
-					}
-					last = end - begin;
-
-					assert(finished);
-					std::chrono::duration<double> diff = end - begin;
-					os << diff << " | ";
-					if(parents.empty()) {
-						os << "     N/A | ";
-					} else {
-						os << std::setw(7) << std::setprecision(0);
-						os << size_t(100.0 * diff.count() / parents.top().count()) << "% | ";
-					}
-					os << std::setw(5) << std::setprecision(0);
-					os << size_t(100.0 * diff.count() / total.count()) << "% ";
-				}
-
-				void start() {
-					begin = std::chrono::high_resolution_clock::now();
-				}
-
-				void finish() {
-					end = std::chrono::high_resolution_clock::now();
-					finished = true;
-				}
-
-			protected:
-				virtual ~TimerNode() = default;
-
-			private:
-				bool finished = false;
-
-				point_t begin;
-				point_t end;
-
-				static std::chrono::duration<double> last;
-				static std::stack<std::chrono::duration<double>> parents;
-			};
-
-			std::stack<TimerNode *> nodes;
-
-			std::chrono::duration<double> TimerNode::last;
-			std::stack<std::chrono::duration<double>> TimerNode::parents;
-
-			void StartGlobal() {
-				global_begin = std::chrono::high_resolution_clock::now();
-			}
-
-			void StartBlock(const char * const name) {
-				if(!enabled) return;
-				auto node = nodes.empty()
-					? new TimerNode(name)
-					: new TimerNode(name, nodes.top());
-
-				nodes.push(node);
-				node->start();
-			}
-
-			void StopBlock() {
-				if(!enabled) return;
-				nodes.top()->finish();
-				nodes.pop();
-			}
-
-			void print() {
-				if(!top.head) return;
-				auto global_end = std::chrono::high_resolution_clock::now();
-				total = global_end - global_begin;
-
-				size_t nc = 0;
-				Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
-					nc = std::max(nc, (4 * level) + std::strlen(node->name));
-				});
-
-				size_t nct = nc + 37;
-				std::cerr << std::string(nct, '=') << std::endl;
-				const char * const title = "Timing Results";
-				std::cerr << std::string((nct - std::strlen(title)) / 2, ' ');
-				std::cerr << title << std::endl;
-				std::cerr << std::string(nct, '-') << std::endl;
-				std::cerr << "Location";
-				std::cerr << std::string(nc - (std::strlen("Location")), ' ');
-				std::cerr << " | ";
-				std::cerr << "       Time | ";
-				std::cerr << "% parent | ";
-				std::cerr << "% total |" << std::endl;
-				std::cerr << std::string(nct, '-') << std::endl;
-
-				Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
-					currl = level;
-					std::cerr << std::string(level * 4, ' ');
-					std::cerr << node->name;
-					std::cerr << std::string(nc - ((level * 4) + std::strlen(node->name)), ' ');
-					std::cerr << " | ";
-					node->print(std::cerr);
-					std::cerr << " |";
-					std::cerr << '\n';
-					prevl = level;
-				}, true);
-
-				std::cerr << std::string(nct, '-') << std::endl;
-				std::cerr << "Total " << total << std::endl;
-				std::cerr << std::string(nct, '-') << std::endl;
-			}
-#		endif
-	}
-}
Index: src/Common/Stats/Time.cpp
===================================================================
--- src/Common/Stats/Time.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/Time.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,199 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Time.cpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Mon Mar 04 15:16:07 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#include "Time.hpp"
+
+#include <cassert>
+#include <chrono>
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <iomanip>
+#include <stack>
+
+namespace Stats {
+	namespace Time {
+#		if !defined(NO_TIME_STATISTICS)
+			extern bool enabled;
+
+			Base::TreeTop top;
+
+			typedef  std::chrono::time_point<std::chrono::high_resolution_clock> point_t;
+			std::chrono::duration<double> total;
+
+			point_t global_begin;
+
+			int prevl = 0;
+			int currl = 0;
+
+			template<typename T>
+			static inline std::ostream & operator<<(std::ostream & os, const std::chrono::duration<T> & dd) {
+				auto d = std::chrono::duration_cast<std::chrono::milliseconds>(dd);
+				auto minutes = std::chrono::duration_cast<std::chrono::minutes>(d);
+				auto seconds = std::chrono::duration_cast<std::chrono::seconds>(d % std::chrono::minutes(1));
+				auto millis  = std::chrono::duration_cast<std::chrono::milliseconds>(d % std::chrono::seconds(1));
+
+				bool zmin = minutes == minutes.zero();
+				bool zsec = seconds == seconds.zero();
+				bool zmil = millis  == millis .zero();
+
+				if(!zmin) {
+					os << std::setw(4) << minutes.count() << "m";
+				} else {
+					os << std::string(5, ' ');
+				}
+
+				if(!zmin || !zsec) {
+					if(!zmin) os << std::setfill('0');
+					os << std::setw(2) << seconds.count() << "s";
+				} else {
+					os << std::string(3, ' ');
+				}
+				os << std::setfill(' ');
+
+				if(!zmin || !zsec || !zmil) {
+					if(!zmin || !zsec) os << std::setfill('0');
+					os << std::setw(3) << millis .count();
+				} else {
+					os << std::string(4, ' ');
+				}
+				os << std::setfill(' ');
+
+				return os;
+			}
+
+			class TimerNode : public Base::Tree<top> {
+			public:
+				TimerNode(const char * const name )
+					: Base::Tree<top>(name)
+				{}
+
+				TimerNode(const char * const name, Base::Tree<top> * parent)
+					: Base::Tree<top>(name, parent)
+
+				{}
+
+				virtual void print(std::ostream & os) override {
+					if(currl > prevl) {
+						parents.push(last);
+					}
+					for(auto lvl = prevl - currl; lvl > 0; lvl--) {
+						parents.pop();
+					}
+					last = end - begin;
+
+					assert(finished);
+					std::chrono::duration<double> diff = end - begin;
+					os << diff << " | ";
+					if(parents.empty()) {
+						os << "     N/A | ";
+					} else {
+						os << std::setw(7) << std::setprecision(0);
+						os << size_t(100.0 * diff.count() / parents.top().count()) << "% | ";
+					}
+					os << std::setw(5) << std::setprecision(0);
+					os << size_t(100.0 * diff.count() / total.count()) << "% ";
+				}
+
+				void start() {
+					begin = std::chrono::high_resolution_clock::now();
+				}
+
+				void finish() {
+					end = std::chrono::high_resolution_clock::now();
+					finished = true;
+				}
+
+			protected:
+				virtual ~TimerNode() = default;
+
+			private:
+				bool finished = false;
+
+				point_t begin;
+				point_t end;
+
+				static std::chrono::duration<double> last;
+				static std::stack<std::chrono::duration<double>> parents;
+			};
+
+			std::stack<TimerNode *> nodes;
+
+			std::chrono::duration<double> TimerNode::last;
+			std::stack<std::chrono::duration<double>> TimerNode::parents;
+
+			void StartGlobal() {
+				global_begin = std::chrono::high_resolution_clock::now();
+			}
+
+			void StartBlock(const char * const name) {
+				if(!enabled) return;
+				auto node = nodes.empty()
+					? new TimerNode(name)
+					: new TimerNode(name, nodes.top());
+
+				nodes.push(node);
+				node->start();
+			}
+
+			void StopBlock() {
+				if(!enabled) return;
+				nodes.top()->finish();
+				nodes.pop();
+			}
+
+			void print() {
+				if(!top.head) return;
+				auto global_end = std::chrono::high_resolution_clock::now();
+				total = global_end - global_begin;
+
+				size_t nc = 0;
+				Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
+					nc = std::max(nc, (4 * level) + std::strlen(node->name));
+				});
+
+				size_t nct = nc + 37;
+				std::cerr << std::string(nct, '=') << std::endl;
+				const char * const title = "Timing Results";
+				std::cerr << std::string((nct - std::strlen(title)) / 2, ' ');
+				std::cerr << title << std::endl;
+				std::cerr << std::string(nct, '-') << std::endl;
+				std::cerr << "Location";
+				std::cerr << std::string(nc - (std::strlen("Location")), ' ');
+				std::cerr << " | ";
+				std::cerr << "       Time | ";
+				std::cerr << "% parent | ";
+				std::cerr << "% total |" << std::endl;
+				std::cerr << std::string(nct, '-') << std::endl;
+
+				Base::ForAll(top, 0, [&](Base::TreeImpl * node, size_t level) {
+					currl = level;
+					std::cerr << std::string(level * 4, ' ');
+					std::cerr << node->name;
+					std::cerr << std::string(nc - ((level * 4) + std::strlen(node->name)), ' ');
+					std::cerr << " | ";
+					node->print(std::cerr);
+					std::cerr << " |";
+					std::cerr << '\n';
+					prevl = level;
+				}, true);
+
+				std::cerr << std::string(nct, '-') << std::endl;
+				std::cerr << "Total " << total << std::endl;
+				std::cerr << std::string(nct, '-') << std::endl;
+			}
+#		endif
+	}
+}
Index: src/Common/Stats/Time.h
===================================================================
--- src/Common/Stats/Time.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,76 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Time.h --
-//
-// Author           : Thierry Delisle
-// Created On       : Fri Mar 01 15:14:11 2019
-// Last Modified By : Andrew Beach
-// Last Modified On :
-// Update Count     :
-//
-
-#pragma once
-
-#include "Common/Stats/Base.h"
-
-#if defined( NO_STATISTICS )
-	#define NO_TIME_STATISTICS
-#endif
-
-namespace Stats {
-	namespace Time {
-#		if defined(NO_TIME_STATISTICS)
-			inline void StartGlobal() {}
-
-			inline void StartBlock(const char * const) {}
-			inline void StopBlock() {}
-
-			inline void print() {}
-
-			struct BlockGuard {
-				BlockGuard(const char * const) {}
-				~BlockGuard() {}
-			};
-
-			template<typename func_t>
-			inline void TimeBlock(const char *, func_t f) {
-				f();
-			}
-
-			template<typename ret_t = void, typename func_t, typename... arg_t>
-			inline ret_t TimeCall(
-					const char *, func_t func, arg_t&&... arg) {
-				return func(std::forward<arg_t>(arg)...);
-			}
-#		else
-			void StartGlobal();
-
-			void StartBlock(const char * const name);
-			void StopBlock();
-
-			void print();
-
-			struct BlockGuard {
-				BlockGuard(const char * const name ) { StartBlock(name); }
-				~BlockGuard() { StopBlock(); }
-			};
-
-			template<typename func_t>
-			inline void TimeBlock(const char * name, func_t func) {
-				BlockGuard guard(name);
-				func();
-			}
-
-			template<typename ret_t = void, typename func_t, typename... arg_t>
-			inline ret_t TimeCall(
-					const char * name, func_t func, arg_t&&... arg) {
-				BlockGuard guard(name);
-				return func(std::forward<arg_t>(arg)...);
-			}
-#		endif
-	}
-}
Index: src/Common/Stats/Time.hpp
===================================================================
--- src/Common/Stats/Time.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Stats/Time.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,76 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Time.hpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Fri Mar 01 15:14:11 2019
+// Last Modified By : Andrew Beach
+// Last Modified On :
+// Update Count     :
+//
+
+#pragma once
+
+#include "Common/Stats/Base.hpp"
+
+#if defined( NO_STATISTICS )
+	#define NO_TIME_STATISTICS
+#endif
+
+namespace Stats {
+	namespace Time {
+#		if defined(NO_TIME_STATISTICS)
+			inline void StartGlobal() {}
+
+			inline void StartBlock(const char * const) {}
+			inline void StopBlock() {}
+
+			inline void print() {}
+
+			struct BlockGuard {
+				BlockGuard(const char * const) {}
+				~BlockGuard() {}
+			};
+
+			template<typename func_t>
+			inline void TimeBlock(const char *, func_t f) {
+				f();
+			}
+
+			template<typename ret_t = void, typename func_t, typename... arg_t>
+			inline ret_t TimeCall(
+					const char *, func_t func, arg_t&&... arg) {
+				return func(std::forward<arg_t>(arg)...);
+			}
+#		else
+			void StartGlobal();
+
+			void StartBlock(const char * const name);
+			void StopBlock();
+
+			void print();
+
+			struct BlockGuard {
+				BlockGuard(const char * const name ) { StartBlock(name); }
+				~BlockGuard() { StopBlock(); }
+			};
+
+			template<typename func_t>
+			inline void TimeBlock(const char * name, func_t func) {
+				BlockGuard guard(name);
+				func();
+			}
+
+			template<typename ret_t = void, typename func_t, typename... arg_t>
+			inline ret_t TimeCall(
+					const char * name, func_t func, arg_t&&... arg) {
+				BlockGuard guard(name);
+				return func(std::forward<arg_t>(arg)...);
+			}
+#		endif
+	}
+}
Index: src/Common/UniqueName.cc
===================================================================
--- src/Common/UniqueName.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,31 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// UniqueName.cc -- Create a unique variants of a base name with a counter.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Nov  7 15:04:00 2023
-// Update Count     : 4
-//
-
-#include "UniqueName.h"
-
-#include "Common/ToString.hpp"
-
-UniqueName::UniqueName( const std::string &base ) : base( base ), count( 0 ) {
-}
-
-std::string UniqueName::newName( const std::string &additional ) {
-	return toString( base, additional, count++ );
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/UniqueName.cpp
===================================================================
--- src/Common/UniqueName.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/UniqueName.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+// UniqueName.cpp -- Create a unique variants of a base name with a counter.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Nov  7 15:04:00 2023
+// Update Count     : 4
+//
+
+#include "UniqueName.hpp"
+
+#include "Common/ToString.hpp"
+
+UniqueName::UniqueName( const std::string &base ) : base( base ), count( 0 ) {
+}
+
+std::string UniqueName::newName( const std::string &additional ) {
+	return toString( base, additional, count++ );
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/UniqueName.h
===================================================================
--- src/Common/UniqueName.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,33 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// UniqueName.h -- Create a unique variants of a base name with a counter.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Nov  7 15:00:00 2023
-// Update Count     : 3
-//
-
-#pragma once
-
-#include <string>
-
-class UniqueName {
-public:
-	UniqueName( const std::string &base );
-	std::string newName( const std::string &additional = "" );
-private:
-	std::string base;
-	int count;
-};
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/UniqueName.hpp
===================================================================
--- src/Common/UniqueName.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/UniqueName.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -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.
+//
+// UniqueName.hpp -- Create a unique variants of a base name with a counter.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Nov  7 15:00:00 2023
+// Update Count     : 3
+//
+
+#pragma once
+
+#include <string>
+
+class UniqueName {
+public:
+	UniqueName( const std::string &base );
+	std::string newName( const std::string &additional = "" );
+private:
+	std::string base;
+	int count;
+};
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/Utility.hpp
===================================================================
--- src/Common/Utility.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/Utility.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,117 @@
+//
+// 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.
+//
+// Utility.hpp -- General utilities used across the compiler.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Jan 17 14:40:00 2024
+// Update Count     : 54
+//
+
+#pragma once
+
+#include <cassert>
+#include <algorithm>
+#include <list>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+/// partner to move that copies any copyable type
+template<typename T>
+T copy( const T & x ) { return x; }
+
+/// Splice src onto the end of dst, clearing src
+template< typename T >
+void splice( std::vector< T > & dst, std::vector< T > & src ) {
+	dst.reserve( dst.size() + src.size() );
+	for ( T & x : src ) { dst.emplace_back( std::move( x ) ); }
+	src.clear();
+}
+
+/// Splice src onto the begining of dst, clearing src
+template< typename T >
+void spliceBegin( std::vector< T > & dst, std::vector< T > & src ) {
+	splice( src, dst );
+	dst.swap( src );
+}
+
+/// Remove elements that match pred from the container.
+template<typename Container, typename Pred>
+void erase_if( Container & cont, Pred && pred ) {
+	auto keep_end = std::remove_if( cont.begin(), cont.end(), pred );
+	cont.erase( keep_end, cont.end() );
+}
+
+// determines if pref is a prefix of str
+static inline bool isPrefix( const std::string & str, const std::string & pref, unsigned int start = 0 ) {
+	if ( pref.size() > str.size() ) return false;
+	return pref == str.substr(start, pref.size());
+}
+
+// -----------------------------------------------------------------------------
+// RAII object to regulate "save and restore" behaviour, e.g.
+// void Foo::bar() {
+//   ValueGuard<int> guard(var); // var is a member of type Foo
+//   var = ...;
+// } // var's original value is restored
+template< typename T >
+struct ValueGuard {
+	T old;
+	T& ref;
+
+	ValueGuard(T& inRef) : old(inRef), ref(inRef) {}
+	~ValueGuard() { ref = old; }
+};
+
+template< typename T >
+struct ValueGuardPtr {
+	T old;
+	T* ref;
+
+	ValueGuardPtr(T * inRef) : old( inRef ? *inRef : T() ), ref(inRef) {}
+	ValueGuardPtr(const ValueGuardPtr& other) = delete;
+	ValueGuardPtr(ValueGuardPtr&& other) : old(other.old), ref(other.ref) { other.ref = nullptr; }
+	~ValueGuardPtr() { if( ref ) *ref = old; }
+};
+
+template< typename aT >
+struct FuncGuard {
+	aT m_after;
+
+	template< typename bT >
+	FuncGuard( bT before, aT after ) : m_after( after ) {
+		before();
+	}
+
+	~FuncGuard() {
+		m_after();
+	}
+};
+
+template< typename bT, typename aT >
+FuncGuard<aT> makeFuncGuard( bT && before, aT && after ) {
+	return FuncGuard<aT>( std::forward<bT>(before), std::forward<aT>(after) );
+}
+
+template< typename T >
+struct ValueGuardPtr< std::list< T > > {
+	std::list< T > old;
+	std::list< T >* ref;
+
+	ValueGuardPtr( std::list< T > * inRef) : old(), ref(inRef) {
+		if( ref ) { swap( *ref, old ); }
+	}
+	~ValueGuardPtr() { if( ref ) { swap( *ref, old ); } }
+};
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/VectorMap.h
===================================================================
--- src/Common/VectorMap.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,248 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// VectorMap.h --
-//
-// Author           : Aaron B. Moss
-// Created On       : Wed Feb  1 16:55:00 2017
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Jul 21 22:19:29 2017
-// Update Count     : 2
-//
-
-#pragma once
-
-#include <iterator>
-#include <utility>
-#include <vector>
-
-/// Maps integers from a contiguous range to T's
-template<typename T>
-class VectorMap {
-	std::vector<T> data;
-
-public:
-	typedef typename std::vector<T>::size_type size_type;
-	typedef size_type key_type;
-	typedef T mapped_type;
-	typedef std::pair<size_type, T&> value_type;
-	typedef std::pair<size_type, const T&> const_value_type;
-	typedef typename std::vector<T>::difference_type difference_type;
-	typedef const value_type& reference;
-	typedef const const_value_type& const_reference;
-	typedef const value_type* pointer;
-	typedef const const_value_type* const_pointer;
-
-	class iterator : public std::iterator<
-			std::random_access_iterator_tag,
-			value_type, difference_type, pointer, reference > {
-		friend class VectorMap;
-		friend class const_iterator;
-
-		value_type data;
-
-		iterator(size_type i, std::vector<T>& v) : data(i, v[i]) {}
-	public:
-		iterator(const iterator& that) : data(that.data) {}
-		iterator& operator= (const iterator& that) {
-			new(&data) value_type{ that.data };
-			return *this;
-		}
-
-		reference operator* () { return data; }
-		pointer operator-> () { return &data; }
-
-		iterator& operator++ () {
-			// SHENANIGANS: relies on pair<unsigned, T&> having a trivial destructor and
-			// vector<T> having contiguous layout
-			new(&data) value_type{ (data.first + 1), *(&data.second + 1) };
-			return *this;
-		}
-		iterator operator++ (int) { iterator tmp = *this; ++(*this); return tmp; }
-
-		iterator& operator-- () {
-			// SHENANIGANS: same reasons as operator++
-			new(&data) value_type{ (data.first - 1), *(&data.second - 1) };
-			return *this;
-		}
-		iterator operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
-
-		iterator& operator+= (difference_type i) {
-			// SHENANIGANS: same reasons as operator++
-			new(&data) value_type{ (data.first + i), *(&data.second + i) };
-			return *this;
-		}
-
-		iterator operator+ (difference_type i) const { iterator tmp = *this; return tmp += i; }
-
-		iterator& operator-= (difference_type i) {
-			// SHENANIGANS: same reasons as operator++
-			new(&data) value_type{ (data.first - i), *(&data.second - i) };
-			return *this;
-		}
-
-		iterator operator- (difference_type i) const { iterator tmp = *this; return tmp -= i; }
-
-		difference_type operator- (const iterator& o) const { return data.first - o.data.first; }
-
-		value_type operator[] (difference_type i) const {
-			// SHENANIGANS: same reasons as operator++
-			return value_type{ (data.first + i), *(&data.second + i) };
-		}
-
-		bool operator== (const iterator& o) const {
-			return data.first == o.data.first && &data.second == &o.data.second;
-		}
-
-		bool operator!= (const iterator& that) const { return !(*this == that); }
-
-		bool operator< (const iterator& o) const { return data.first < o.data.first; }
-
-		bool operator> (const iterator& o) const { return data.first > o.data.first; }
-
-		bool operator<= (const iterator& o) const { return data.first <= o.data.first; }
-
-		bool operator>= (const iterator& o) const { return data.first >= o.data.first; }
-	};
-
-	class const_iterator : public std::iterator<
-			std::bidirectional_iterator_tag,
-			const_value_type, difference_type, const_pointer, const_reference > {
-		friend class VectorMap;
-		const_value_type data;
-
-		const_iterator(size_type i, const std::vector<T>& v) : data(i, v[i]) {}
-	public:
-		const_iterator(const iterator& that) : data(that.data) {}
-		const_iterator(const const_iterator& that) : data(that.data) {}
-		const_iterator& operator= (const iterator& that) {
-			new(&data) const_value_type{ that.data };
-			return *this;
-		}
-		const_iterator& operator= (const const_iterator& that) {
-			new(&data) const_value_type{ that.data };
-			return *this;
-		}
-
-		const_reference operator* () { return data; }
-		const_pointer operator-> () { return &data; }
-
-		const_iterator& operator++ () {
-			// SHENANIGANS: same reasons as iterator::operator++
-			new(&data) const_value_type{ (data.first + 1), *(&data.second + 1) };
-			return *this;
-		}
-		const_iterator operator++ (int) { const_iterator tmp = *this; ++(*this); return tmp; }
-
-		const_iterator& operator-- () {
-			// SHENANIGANS: same reasons as iterator::operator++
-			new(&data) const_value_type{ (data.first - 1), *(&data.second - 1) };
-			return *this;
-		}
-		const_iterator operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
-
-		const_iterator& operator+= (difference_type i) {
-			// SHENANIGANS: same reasons as iterator::operator++
-			new(&data) const_value_type{ (data.first + i), *(&data.second + i) };
-			return *this;
-		}
-
-		const_iterator operator+ (difference_type i) const {
-			const_iterator tmp = *this; return tmp += i;
-		}
-
-		const_iterator& operator-= (difference_type i) {
-			// SHENANIGANS: same reasons as iterator::operator++
-			new(&data) const_value_type{ (data.first - i), *(&data.second - i) };
-			return *this;
-		}
-
-		const_iterator operator- (difference_type i) const {
-			const_iterator tmp = *this; return tmp -= i;
-		}
-
-		difference_type operator- (const const_iterator& o) const {
-			return data.first - o.data.first;
-		}
-
-		const_value_type operator[] (difference_type i) const {
-			// SHENANIGANS: same reasons as iterator::operator++
-			return const_value_type{ (data.first + i), *(&data.second + i) };
-		}
-
-		bool operator== (const const_iterator& o) const {
-			return data.first == o.data.first && &data.second == &o.data.second;
-		}
-
-		bool operator!= (const const_iterator& that) const { return !(*this == that); }
-
-		bool operator< (const const_iterator& o) const { return data.first < o.data.first; }
-
-		bool operator> (const const_iterator& o) const { return data.first > o.data.first; }
-
-		bool operator<= (const const_iterator& o) const { return data.first <= o.data.first; }
-
-		bool operator>= (const const_iterator& o) const { return data.first >= o.data.first; }
-	};
-
-	/// Reserve space for n elements
-	void reserve(size_type n) {
-		if ( n > data.size() ) { data.insert( data.end(), n - data.size(), T{} ); }
-	}
-
-	/// Unsafe access; no bounds checking
-	T& operator[] (size_type i) { return data[i]; }
-	const T& operator[] (size_type i) const { return data[i]; }
-
-	/// Safe access; will insert new values if needed
-	T& at(size_type i) {
-		reserve(i+1);
-		return data[i];
-	}
-
-	/// Number of stored values
-	unsigned size() const { return data.size(); }
-
-	/// No stored values
-	bool empty() const { return data.empty(); }
-
-	/// Empties the map
-	void clear() { data.clear(); }
-
-	/// Returns 1 if element in map, 0 otherwise
-	size_type count( size_type i ) const { return i < size() ? 1 : 0; }
-
-	iterator begin() { return iterator{ 0, data }; }
-	const_iterator begin() const { return const_iterator{ 0, data }; }
-	const_iterator cbegin() const { return const_iterator{ 0, data }; }
-
-	iterator end() { return iterator{ data.size(), data }; }
-	const_iterator end() const { return const_iterator{ data.size(), data }; }
-	const_iterator cend() const { return const_iterator{ data.size(), data }; }
-
-	iterator find( size_type i ) { return i < size() ? iterator{ i, data } : end(); }
-	const_iterator find( size_type i ) const { return i < size() ? const_iterator{ i, data } : end(); }
-};
-
-template<typename T>
-typename VectorMap<T>::iterator operator+(
-		typename VectorMap<T>::difference_type i,
-		const typename VectorMap<T>::iterator& it) {
-	return it + i;
-}
-
-template<typename T>
-typename VectorMap<T>::const_iterator operator+(
-		typename VectorMap<T>::difference_type i,
-		const typename VectorMap<T>::const_iterator& it) {
-	return it + i;
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Common/VectorMap.hpp
===================================================================
--- src/Common/VectorMap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Common/VectorMap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,248 @@
+//
+// 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.
+//
+// VectorMap.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Wed Feb  1 16:55:00 2017
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri Jul 21 22:19:29 2017
+// Update Count     : 2
+//
+
+#pragma once
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+/// Maps integers from a contiguous range to T's
+template<typename T>
+class VectorMap {
+	std::vector<T> data;
+
+public:
+	typedef typename std::vector<T>::size_type size_type;
+	typedef size_type key_type;
+	typedef T mapped_type;
+	typedef std::pair<size_type, T&> value_type;
+	typedef std::pair<size_type, const T&> const_value_type;
+	typedef typename std::vector<T>::difference_type difference_type;
+	typedef const value_type& reference;
+	typedef const const_value_type& const_reference;
+	typedef const value_type* pointer;
+	typedef const const_value_type* const_pointer;
+
+	class iterator : public std::iterator<
+			std::random_access_iterator_tag,
+			value_type, difference_type, pointer, reference > {
+		friend class VectorMap;
+		friend class const_iterator;
+
+		value_type data;
+
+		iterator(size_type i, std::vector<T>& v) : data(i, v[i]) {}
+	public:
+		iterator(const iterator& that) : data(that.data) {}
+		iterator& operator= (const iterator& that) {
+			new(&data) value_type{ that.data };
+			return *this;
+		}
+
+		reference operator* () { return data; }
+		pointer operator-> () { return &data; }
+
+		iterator& operator++ () {
+			// SHENANIGANS: relies on pair<unsigned, T&> having a trivial destructor and
+			// vector<T> having contiguous layout
+			new(&data) value_type{ (data.first + 1), *(&data.second + 1) };
+			return *this;
+		}
+		iterator operator++ (int) { iterator tmp = *this; ++(*this); return tmp; }
+
+		iterator& operator-- () {
+			// SHENANIGANS: same reasons as operator++
+			new(&data) value_type{ (data.first - 1), *(&data.second - 1) };
+			return *this;
+		}
+		iterator operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
+
+		iterator& operator+= (difference_type i) {
+			// SHENANIGANS: same reasons as operator++
+			new(&data) value_type{ (data.first + i), *(&data.second + i) };
+			return *this;
+		}
+
+		iterator operator+ (difference_type i) const { iterator tmp = *this; return tmp += i; }
+
+		iterator& operator-= (difference_type i) {
+			// SHENANIGANS: same reasons as operator++
+			new(&data) value_type{ (data.first - i), *(&data.second - i) };
+			return *this;
+		}
+
+		iterator operator- (difference_type i) const { iterator tmp = *this; return tmp -= i; }
+
+		difference_type operator- (const iterator& o) const { return data.first - o.data.first; }
+
+		value_type operator[] (difference_type i) const {
+			// SHENANIGANS: same reasons as operator++
+			return value_type{ (data.first + i), *(&data.second + i) };
+		}
+
+		bool operator== (const iterator& o) const {
+			return data.first == o.data.first && &data.second == &o.data.second;
+		}
+
+		bool operator!= (const iterator& that) const { return !(*this == that); }
+
+		bool operator< (const iterator& o) const { return data.first < o.data.first; }
+
+		bool operator> (const iterator& o) const { return data.first > o.data.first; }
+
+		bool operator<= (const iterator& o) const { return data.first <= o.data.first; }
+
+		bool operator>= (const iterator& o) const { return data.first >= o.data.first; }
+	};
+
+	class const_iterator : public std::iterator<
+			std::bidirectional_iterator_tag,
+			const_value_type, difference_type, const_pointer, const_reference > {
+		friend class VectorMap;
+		const_value_type data;
+
+		const_iterator(size_type i, const std::vector<T>& v) : data(i, v[i]) {}
+	public:
+		const_iterator(const iterator& that) : data(that.data) {}
+		const_iterator(const const_iterator& that) : data(that.data) {}
+		const_iterator& operator= (const iterator& that) {
+			new(&data) const_value_type{ that.data };
+			return *this;
+		}
+		const_iterator& operator= (const const_iterator& that) {
+			new(&data) const_value_type{ that.data };
+			return *this;
+		}
+
+		const_reference operator* () { return data; }
+		const_pointer operator-> () { return &data; }
+
+		const_iterator& operator++ () {
+			// SHENANIGANS: same reasons as iterator::operator++
+			new(&data) const_value_type{ (data.first + 1), *(&data.second + 1) };
+			return *this;
+		}
+		const_iterator operator++ (int) { const_iterator tmp = *this; ++(*this); return tmp; }
+
+		const_iterator& operator-- () {
+			// SHENANIGANS: same reasons as iterator::operator++
+			new(&data) const_value_type{ (data.first - 1), *(&data.second - 1) };
+			return *this;
+		}
+		const_iterator operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
+
+		const_iterator& operator+= (difference_type i) {
+			// SHENANIGANS: same reasons as iterator::operator++
+			new(&data) const_value_type{ (data.first + i), *(&data.second + i) };
+			return *this;
+		}
+
+		const_iterator operator+ (difference_type i) const {
+			const_iterator tmp = *this; return tmp += i;
+		}
+
+		const_iterator& operator-= (difference_type i) {
+			// SHENANIGANS: same reasons as iterator::operator++
+			new(&data) const_value_type{ (data.first - i), *(&data.second - i) };
+			return *this;
+		}
+
+		const_iterator operator- (difference_type i) const {
+			const_iterator tmp = *this; return tmp -= i;
+		}
+
+		difference_type operator- (const const_iterator& o) const {
+			return data.first - o.data.first;
+		}
+
+		const_value_type operator[] (difference_type i) const {
+			// SHENANIGANS: same reasons as iterator::operator++
+			return const_value_type{ (data.first + i), *(&data.second + i) };
+		}
+
+		bool operator== (const const_iterator& o) const {
+			return data.first == o.data.first && &data.second == &o.data.second;
+		}
+
+		bool operator!= (const const_iterator& that) const { return !(*this == that); }
+
+		bool operator< (const const_iterator& o) const { return data.first < o.data.first; }
+
+		bool operator> (const const_iterator& o) const { return data.first > o.data.first; }
+
+		bool operator<= (const const_iterator& o) const { return data.first <= o.data.first; }
+
+		bool operator>= (const const_iterator& o) const { return data.first >= o.data.first; }
+	};
+
+	/// Reserve space for n elements
+	void reserve(size_type n) {
+		if ( n > data.size() ) { data.insert( data.end(), n - data.size(), T{} ); }
+	}
+
+	/// Unsafe access; no bounds checking
+	T& operator[] (size_type i) { return data[i]; }
+	const T& operator[] (size_type i) const { return data[i]; }
+
+	/// Safe access; will insert new values if needed
+	T& at(size_type i) {
+		reserve(i+1);
+		return data[i];
+	}
+
+	/// Number of stored values
+	unsigned size() const { return data.size(); }
+
+	/// No stored values
+	bool empty() const { return data.empty(); }
+
+	/// Empties the map
+	void clear() { data.clear(); }
+
+	/// Returns 1 if element in map, 0 otherwise
+	size_type count( size_type i ) const { return i < size() ? 1 : 0; }
+
+	iterator begin() { return iterator{ 0, data }; }
+	const_iterator begin() const { return const_iterator{ 0, data }; }
+	const_iterator cbegin() const { return const_iterator{ 0, data }; }
+
+	iterator end() { return iterator{ data.size(), data }; }
+	const_iterator end() const { return const_iterator{ data.size(), data }; }
+	const_iterator cend() const { return const_iterator{ data.size(), data }; }
+
+	iterator find( size_type i ) { return i < size() ? iterator{ i, data } : end(); }
+	const_iterator find( size_type i ) const { return i < size() ? const_iterator{ i, data } : end(); }
+};
+
+template<typename T>
+typename VectorMap<T>::iterator operator+(
+		typename VectorMap<T>::difference_type i,
+		const typename VectorMap<T>::iterator& it) {
+	return it + i;
+}
+
+template<typename T>
+typename VectorMap<T>::const_iterator operator+(
+		typename VectorMap<T>::difference_type i,
+		const typename VectorMap<T>::const_iterator& it) {
+	return it + i;
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Common/module.mk
===================================================================
--- src/Common/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Common/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,44 +16,44 @@
 
 SRC_COMMON = \
-	Common/Assert.cc \
-	Common/CodeLocation.h \
+	Common/Assert.cpp \
+	Common/CodeLocation.hpp \
 	Common/CodeLocationTools.hpp \
 	Common/CodeLocationTools.cpp \
 	Common/DeclStats.hpp \
 	Common/DeclStats.cpp \
-	Common/ErrorObjects.h \
-	Common/Eval.cc \
-	Common/Eval.h \
-	Common/Examine.cc \
-	Common/Examine.h \
-	Common/FilterCombos.h \
-	Common/Indenter.h \
-	Common/Indenter.cc \
+	Common/ErrorObjects.hpp \
+	Common/Eval.cpp \
+	Common/Eval.hpp \
+	Common/Examine.cpp \
+	Common/Examine.hpp \
+	Common/FilterCombos.hpp \
+	Common/Indenter.hpp \
+	Common/Indenter.cpp \
 	Common/Iterate.hpp \
-	Common/PersistentMap.h \
+	Common/PersistentMap.hpp \
 	Common/ResolvProtoDump.hpp \
 	Common/ResolvProtoDump.cpp \
-	Common/ScopedMap.h \
-	Common/SemanticError.cc \
-	Common/SemanticError.h \
-	Common/Stats.h \
-	Common/Stats/Base.h \
-	Common/Stats/Counter.cc \
-	Common/Stats/Counter.h \
-	Common/Stats/Heap.cc \
-	Common/Stats/Heap.h \
-	Common/Stats/ResolveTime.cc \
-	Common/Stats/ResolveTime.h \
-	Common/Stats/Stats.cc \
-	Common/Stats/Time.cc \
-	Common/Stats/Time.h \
+	Common/ScopedMap.hpp \
+	Common/SemanticError.cpp \
+	Common/SemanticError.hpp \
+	Common/Stats.hpp \
+	Common/Stats/Base.hpp \
+	Common/Stats/Counter.cpp \
+	Common/Stats/Counter.hpp \
+	Common/Stats/Heap.cpp \
+	Common/Stats/Heap.hpp \
+	Common/Stats/ResolveTime.cpp \
+	Common/Stats/ResolveTime.hpp \
+	Common/Stats/Stats.cpp \
+	Common/Stats/Time.cpp \
+	Common/Stats/Time.hpp \
 	Common/ToString.hpp \
-	Common/UniqueName.cc \
-	Common/UniqueName.h \
-	Common/utility.h \
-	Common/VectorMap.h
+	Common/UniqueName.cpp \
+	Common/UniqueName.hpp \
+	Common/utility.hpp \
+	Common/VectorMap.hpp
 
 SRC += $(SRC_COMMON) \
-	Common/DebugMalloc.cc
+	Common/DebugMalloc.cpp
 
 SRCDEMANGLE += $(SRC_COMMON)
Index: src/Common/utility.h
===================================================================
--- src/Common/utility.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,117 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// utility.h -- General utilities used across the compiler.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jan 17 14:40:00 2024
-// Update Count     : 54
-//
-
-#pragma once
-
-#include <cassert>
-#include <algorithm>
-#include <list>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-/// partner to move that copies any copyable type
-template<typename T>
-T copy( const T & x ) { return x; }
-
-/// Splice src onto the end of dst, clearing src
-template< typename T >
-void splice( std::vector< T > & dst, std::vector< T > & src ) {
-	dst.reserve( dst.size() + src.size() );
-	for ( T & x : src ) { dst.emplace_back( std::move( x ) ); }
-	src.clear();
-}
-
-/// Splice src onto the begining of dst, clearing src
-template< typename T >
-void spliceBegin( std::vector< T > & dst, std::vector< T > & src ) {
-	splice( src, dst );
-	dst.swap( src );
-}
-
-/// Remove elements that match pred from the container.
-template<typename Container, typename Pred>
-void erase_if( Container & cont, Pred && pred ) {
-	auto keep_end = std::remove_if( cont.begin(), cont.end(), pred );
-	cont.erase( keep_end, cont.end() );
-}
-
-// determines if pref is a prefix of str
-static inline bool isPrefix( const std::string & str, const std::string & pref, unsigned int start = 0 ) {
-	if ( pref.size() > str.size() ) return false;
-	return pref == str.substr(start, pref.size());
-}
-
-// -----------------------------------------------------------------------------
-// RAII object to regulate "save and restore" behaviour, e.g.
-// void Foo::bar() {
-//   ValueGuard<int> guard(var); // var is a member of type Foo
-//   var = ...;
-// } // var's original value is restored
-template< typename T >
-struct ValueGuard {
-	T old;
-	T& ref;
-
-	ValueGuard(T& inRef) : old(inRef), ref(inRef) {}
-	~ValueGuard() { ref = old; }
-};
-
-template< typename T >
-struct ValueGuardPtr {
-	T old;
-	T* ref;
-
-	ValueGuardPtr(T * inRef) : old( inRef ? *inRef : T() ), ref(inRef) {}
-	ValueGuardPtr(const ValueGuardPtr& other) = delete;
-	ValueGuardPtr(ValueGuardPtr&& other) : old(other.old), ref(other.ref) { other.ref = nullptr; }
-	~ValueGuardPtr() { if( ref ) *ref = old; }
-};
-
-template< typename aT >
-struct FuncGuard {
-	aT m_after;
-
-	template< typename bT >
-	FuncGuard( bT before, aT after ) : m_after( after ) {
-		before();
-	}
-
-	~FuncGuard() {
-		m_after();
-	}
-};
-
-template< typename bT, typename aT >
-FuncGuard<aT> makeFuncGuard( bT && before, aT && after ) {
-	return FuncGuard<aT>( std::forward<bT>(before), std::forward<aT>(after) );
-}
-
-template< typename T >
-struct ValueGuardPtr< std::list< T > > {
-	std::list< T > old;
-	std::list< T >* ref;
-
-	ValueGuardPtr( std::list< T > * inRef) : old(), ref(inRef) {
-		if( ref ) { swap( *ref, old ); }
-	}
-	~ValueGuardPtr() { if( ref ) { swap( *ref, old ); } }
-};
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Concurrency/Corun.cpp
===================================================================
--- src/Concurrency/Corun.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Concurrency/Corun.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -19,5 +19,5 @@
 #include "AST/Stmt.hpp"
 #include "AST/TranslationUnit.hpp"
-#include "Common/UniqueName.h"
+#include "Common/UniqueName.hpp"
 using namespace ast;
 using namespace std;
Index: src/Concurrency/Keywords.cpp
===================================================================
--- src/Concurrency/Keywords.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Concurrency/Keywords.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,5 +14,5 @@
 //
 
-#include "Concurrency/Keywords.h"
+#include "Concurrency/Keywords.hpp"
 
 #include <iostream>
@@ -26,11 +26,11 @@
 #include "AST/DeclReplacer.hpp"
 #include "AST/TranslationUnit.hpp"
-#include "CodeGen/OperatorTable.h"
-#include "Common/Examine.h"
-#include "Common/utility.h"
-#include "Common/UniqueName.h"
+#include "CodeGen/OperatorTable.hpp"
+#include "Common/Examine.hpp"
+#include "Common/Utility.hpp"
+#include "Common/UniqueName.hpp"
 #include "ControlStruct/LabelGenerator.hpp"
-#include "InitTweak/InitTweak.h"
-#include "Virtual/Tables.h"
+#include "InitTweak/InitTweak.hpp"
+#include "Virtual/Tables.hpp"
 
 namespace Concurrency {
Index: src/Concurrency/Keywords.h
===================================================================
--- src/Concurrency/Keywords.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,37 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Keywords.h -- Implement concurrency constructs from their keywords.
-//
-// Author           : Thierry Delisle
-// Created On       : Fri Mar 10 15:16:42 2017
-// Last Modified By :
-// Last Modified On :
-// Update Count     : 1
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace Concurrency {
-
-/// Implement the sue-like keywords and the suspend keyword. Pre-Autogen
-void implementKeywords( ast::TranslationUnit & translationUnit );
-/// Implement the mutex parameters and mutex statement. Post-Autogen
-void implementMutex( ast::TranslationUnit & translationUnit );
-/// Add the thread starter code to constructors. Post-Autogen
-void implementThreadStarter( ast::TranslationUnit & translationUnit );
-
-};
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Concurrency/Keywords.hpp
===================================================================
--- src/Concurrency/Keywords.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Concurrency/Keywords.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,37 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Keywords.hpp -- Implement concurrency constructs from their keywords.
+//
+// Author           : Thierry Delisle
+// Created On       : Fri Mar 10 15:16:42 2017
+// Last Modified By :
+// Last Modified On :
+// Update Count     : 1
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace Concurrency {
+
+/// Implement the sue-like keywords and the suspend keyword. Pre-Autogen
+void implementKeywords( ast::TranslationUnit & translationUnit );
+/// Implement the mutex parameters and mutex statement. Post-Autogen
+void implementMutex( ast::TranslationUnit & translationUnit );
+/// Add the thread starter code to constructors. Post-Autogen
+void implementThreadStarter( ast::TranslationUnit & translationUnit );
+
+};
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Concurrency/Waitfor.cpp
===================================================================
--- src/Concurrency/Waitfor.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Concurrency/Waitfor.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,12 +14,12 @@
 //
 
-#include "Waitfor.h"
+#include "Waitfor.hpp"
 
 #include <string>
 
 #include "AST/Pass.hpp"
-#include "Common/UniqueName.h"
-#include "InitTweak/InitTweak.h"
-#include "ResolvExpr/Resolver.h"
+#include "Common/UniqueName.hpp"
+#include "InitTweak/InitTweak.hpp"
+#include "ResolvExpr/Resolver.hpp"
 
 #include "AST/Print.hpp"
Index: src/Concurrency/Waitfor.h
===================================================================
--- src/Concurrency/Waitfor.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,32 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Waitfor.h --
-//
-// Author           : Thierry Delisle
-// Created On       : Mon Aug 28 11:03:31 2017
-// Last Modified By :
-// Last Modified On :
-// Update Count     : 1
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace Concurrency {
-
-void generateWaitFor( ast::TranslationUnit & translationUnit );
-
-};
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Concurrency/Waitfor.hpp
===================================================================
--- src/Concurrency/Waitfor.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Concurrency/Waitfor.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,32 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Waitfor.hpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Mon Aug 28 11:03:31 2017
+// Last Modified By :
+// Last Modified On :
+// Update Count     : 1
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace Concurrency {
+
+void generateWaitFor( ast::TranslationUnit & translationUnit );
+
+};
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Concurrency/Waituntil.cpp
===================================================================
--- src/Concurrency/Waituntil.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Concurrency/Waituntil.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -24,5 +24,5 @@
 #include "AST/Stmt.hpp"
 #include "AST/Type.hpp"
-#include "Common/UniqueName.h"
+#include "Common/UniqueName.hpp"
 
 using namespace ast;
Index: src/Concurrency/module.mk
===================================================================
--- src/Concurrency/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Concurrency/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -21,7 +21,7 @@
 	Concurrency/Corun.hpp \
 	Concurrency/Keywords.cpp \
-	Concurrency/Keywords.h \
+	Concurrency/Keywords.hpp \
 	Concurrency/Waitfor.cpp \
-	Concurrency/Waitfor.h \
+	Concurrency/Waitfor.hpp \
 	Concurrency/Waituntil.cpp \
 	Concurrency/Waituntil.hpp
Index: src/ControlStruct/ExceptDecl.cpp
===================================================================
--- src/ControlStruct/ExceptDecl.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ControlStruct/ExceptDecl.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,5 +14,5 @@
 //
 
-#include "ExceptDecl.h"
+#include "ExceptDecl.hpp"
 
 #include <sstream>
@@ -23,5 +23,5 @@
 #include "AST/Print.hpp"
 #include "AST/Type.hpp"
-#include "Virtual/Tables.h"
+#include "Virtual/Tables.hpp"
 
 namespace ControlStruct {
Index: src/ControlStruct/ExceptDecl.h
===================================================================
--- src/ControlStruct/ExceptDecl.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,28 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ExceptDecl.h -- Handles declarations of exception types.
-//
-// Author           : Henry Xue
-// Created On       : Tue Jul 20 04:10:50 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Jul 12 15:49:00 2022
-// Update Count     : 2
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace ControlStruct {
-
-/// Unfold exception declarations into raw structure declarations.
-/// Also builds vtable declarations and converts vtable types.
-void translateExcept( ast::TranslationUnit & translationUnit );
-
-}
Index: src/ControlStruct/ExceptDecl.hpp
===================================================================
--- src/ControlStruct/ExceptDecl.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ControlStruct/ExceptDecl.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,28 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ExceptDecl.hpp -- Handles declarations of exception types.
+//
+// Author           : Henry Xue
+// Created On       : Tue Jul 20 04:10:50 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Jul 12 15:49:00 2022
+// Update Count     : 2
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace ControlStruct {
+
+/// Unfold exception declarations into raw structure declarations.
+/// Also builds vtable declarations and converts vtable types.
+void translateExcept( ast::TranslationUnit & translationUnit );
+
+}
Index: src/ControlStruct/ExceptTranslate.cpp
===================================================================
--- src/ControlStruct/ExceptTranslate.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ControlStruct/ExceptTranslate.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,5 +14,5 @@
 //
 
-#include "ExceptTranslate.h"
+#include "ExceptTranslate.hpp"
 
 #include "AST/Expr.hpp"
Index: src/ControlStruct/ExceptTranslate.h
===================================================================
--- src/ControlStruct/ExceptTranslate.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,41 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// 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 : Mon Nov  8 11:43:00 2020
-// Update Count     : 6
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace ControlStruct {
-
-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.
- */
-
-void translateTries( ast::TranslationUnit & transUnit );
-/* Replaces all try blocks (and their many clauses) with function definitions and calls.
- * This uses the exception built-ins to produce typed output and should take place after
- * the resolver. It also produces virtual casts and should happen before they are expanded.
- */
-
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ControlStruct/ExceptTranslate.hpp
===================================================================
--- src/ControlStruct/ExceptTranslate.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ControlStruct/ExceptTranslate.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,41 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ExceptTranslate.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 : Mon Nov  8 11:43:00 2020
+// Update Count     : 6
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace ControlStruct {
+
+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.
+ */
+
+void translateTries( ast::TranslationUnit & transUnit );
+/* Replaces all try blocks (and their many clauses) with function definitions and calls.
+ * This uses the exception built-ins to produce typed output and should take place after
+ * the resolver. It also produces virtual casts and should happen before they are expanded.
+ */
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/Box.cpp
===================================================================
--- src/GenPoly/Box.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/GenPoly/Box.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,5 +14,5 @@
 //
 
-#include "Box.h"
+#include "Box.hpp"
 
 #include "AST/Decl.hpp"                // for Decl, FunctionDecl, ...
@@ -24,16 +24,16 @@
 #include "AST/Vector.hpp"              // for vector
 #include "AST/GenericSubstitution.hpp" // for genericSubstitution
-#include "CodeGen/OperatorTable.h"     // for isAssignment
+#include "CodeGen/OperatorTable.hpp"   // for isAssignment
 #include "Common/Iterate.hpp"          // for group_iterate
-#include "Common/ScopedMap.h"          // for ScopedMap
+#include "Common/ScopedMap.hpp"        // for ScopedMap
 #include "Common/ToString.hpp"         // for toCString
-#include "Common/UniqueName.h"         // for UniqueName
-#include "GenPoly/FindFunction.h"      // for findFunction
-#include "GenPoly/GenPoly.h"           // for getFunctionType, ...
-#include "GenPoly/Lvalue.h"            // for generalizedLvalue
-#include "GenPoly/ScopedSet.h"         // for ScopedSet
+#include "Common/UniqueName.hpp"       // for UniqueName
+#include "GenPoly/FindFunction.hpp"    // for findFunction
+#include "GenPoly/GenPoly.hpp"         // for getFunctionType, ...
+#include "GenPoly/Lvalue.hpp"          // for generalizedLvalue
+#include "GenPoly/ScopedSet.hpp"       // for ScopedSet
 #include "GenPoly/ScrubTypeVars.hpp"   // for scrubTypeVars, scrubAllTypeVars
-#include "ResolvExpr/Unify.h"          // for typesCompatible
-#include "SymTab/Mangler.h"            // for mangle, mangleType
+#include "ResolvExpr/Unify.hpp"        // for typesCompatible
+#include "SymTab/Mangler.hpp"          // for mangle, mangleType
 
 namespace GenPoly {
Index: src/GenPoly/Box.h
===================================================================
--- src/GenPoly/Box.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,32 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Box.h -- Implement polymorphic function calls and types.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Thr Oct  6 13:37:00 2022
-// Update Count     : 7
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace GenPoly {
-
-void box( ast::TranslationUnit & translationUnit );
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/Box.hpp
===================================================================
--- src/GenPoly/Box.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/Box.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,32 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Box.hpp -- Implement polymorphic function calls and types.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Thr Oct  6 13:37:00 2022
+// Update Count     : 7
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace GenPoly {
+
+void box( ast::TranslationUnit & translationUnit );
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/ErasableScopedMap.h
===================================================================
--- src/GenPoly/ErasableScopedMap.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,297 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ErasableScopedMap.h -- A map that supports scoping and erasing elements.
-//
-// Author           : Aaron B. Moss
-// Created On       : Wed Dec 2 11:37:00 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:23:24 2017
-// Update Count     : 2
-//
-
-#pragma once
-
-#include <cassert>
-#include <iterator>
-#include <map>
-#include <utility>
-#include <vector>
-
-namespace GenPoly {
-
-/// A map where the items are placed into nested scopes.
-/// Inserted items are placed into the innermost scope, lookup looks from the
-/// innermost scope outward. Erasing a key means that find() will no longer
-/// report any instance of the key in a scope further out, but the erasure
-/// itself is scoped. Key erasure works by inserting a sentinal value into
-/// the value field, and thus only works for Value types where a meaningful
-/// sentinal can be chosen.
-template<typename Key, typename Value>
-class ErasableScopedMap {
-	typedef std::map< Key, Value > Scope;
-	typedef std::vector< Scope > ScopeList;
-
-	/// Scoped list of maps.
-	ScopeList scopes;
-	/// Sentinal value for erased keys.
-	Value erased;
-public:
-	typedef typename Scope::key_type key_type;
-	typedef typename Scope::mapped_type mapped_type;
-	typedef typename Scope::value_type value_type;
-	typedef typename ScopeList::size_type size_type;
-	typedef typename ScopeList::difference_type difference_type;
-	typedef typename Scope::reference reference;
-	typedef typename Scope::const_reference const_reference;
-	typedef typename Scope::pointer pointer;
-	typedef typename Scope::const_pointer const_pointer;
-
-	// Both iterator types are complete bidirectional iterators, see below.
-	class iterator;
-	class const_iterator;
-
-	/// Starts a new scope
-	void beginScope() {
-		scopes.emplace_back();
-	}
-
-	/// Ends a scope; invalidates any iterators pointing to elements of that scope
-	void endScope() {
-		scopes.pop_back();
-		assert( ! scopes.empty() );
-	}
-
-	/// Default constructor initializes with one scope
-	ErasableScopedMap( const Value &erased_ ) : erased( erased_ ) { beginScope(); }
-
-	iterator begin() { return iterator(*this, scopes.back().begin(), scopes.size()-1).next_valid(); }
-	const_iterator begin() const { return const_iterator(*this, scopes.back().begin(), scopes.size()-1).next_valid(); }
-	const_iterator cbegin() const { return const_iterator(*this, scopes.back().begin(), scopes.size()-1).next_valid(); }
-	iterator end() { return iterator(*this, scopes[0].end(), 0); }
-	const_iterator end() const { return const_iterator(*this, scopes[0].end(), 0); }
-	const_iterator cend() const { return const_iterator(*this, scopes[0].end(), 0); }
-
-	/// Gets the index of the current scope (counted from 1)
-	size_type currentScope() const { return scopes.size(); }
-
-	/// Finds the given key in the outermost scope it occurs; returns end() for none such
-	iterator find( const Key &key ) {
-		for ( size_type i = scopes.size() - 1; ; --i ) {
-			typename Scope::iterator val = scopes[i].find( key );
-			if ( val != scopes[i].end() ) {
-				return val->second == erased ? end() : iterator( *this, val, i );
-			}
-			if ( i == 0 ) break;
-		}
-		return end();
-	}
-	const_iterator find( const Key &key ) const {
-		return const_iterator( const_cast< ErasableScopedMap< Key, Value >* >(this)->find( key ) );
-	}
-
-	/// Finds the given key in the outermost scope inside the given scope where it occurs
-	iterator findNext( const_iterator &it, const Key &key ) {
-		if ( it.i == 0 ) return end();
-		for ( size_type i = it.i - 1; ; --i ) {
-			typename Scope::iterator val = scopes[i].find( key );
-			if ( val != scopes[i].end() ) {
-				return val->second == erased ? end() : iterator( *this, val, i );
-			}
-			if ( i == 0 ) break;
-		}
-		return end();
-	}
-	const_iterator findNext( const_iterator &it, const Key &key ) const {
-		return const_iterator( const_cast< ErasableScopedMap< Key, Value >* >(this)->findNext( it, key ) );
-	}
-
-	/// Inserts the given key-value pair into the outermost scope
-	std::pair< iterator, bool > insert( const value_type &value ) {
-		std::pair< typename Scope::iterator, bool > res = scopes.back().insert( value );
-		return std::make_pair( iterator(*this, res.first, scopes.size()-1), res.second );
-	}
-	std::pair< iterator, bool > insert( const Key &key, const Value &value ) { return insert( std::make_pair( key, value ) ); }
-
-	Value& operator[] ( const Key &key ) {
-		iterator slot = find( key );
-		if ( slot != end() ) return slot->second;
-		return insert( key, Value() ).first->second;
-	}
-
-	/// Marks the given element as erased from this scope inward; returns 1 for erased an element, 0 otherwise
-	size_type erase( const Key &key ) {
-		typename Scope::iterator val = scopes.back().find( key );
-		if ( val != scopes.back().end() ) {
-			val->second = erased;
-			return 1;
-		} else {
-			scopes.back().insert( val, std::make_pair( key, erased ) );
-			return 0;
-		}
-	}
-
-	bool contains( const Key & key ) const {
-		return find( key ) != cend();
-	}
-};
-
-template<typename Key, typename Value>
-class ErasableScopedMap<Key, Value>::iterator :
-		public std::iterator< std::bidirectional_iterator_tag, value_type > {
-	friend class ErasableScopedMap;
-	typedef typename Scope::iterator wrapped_iterator;
-	typedef typename ScopeList::size_type size_type;
-
-	/// Checks if this iterator points to a valid item
-	bool is_valid() const {
-		return it != map->scopes[i].end() && it->second != map->erased;
-	}
-
-	/// Increments on invalid
-	iterator& next_valid() {
-		if ( ! is_valid() ) { ++(*this); }
-		return *this;
-	}
-
-	/// Decrements on invalid
-	iterator& prev_valid() {
-		if ( ! is_valid() ) { --(*this); }
-		return *this;
-	}
-
-	iterator(ErasableScopedMap< Key, Value > const &_map, const wrapped_iterator &_it, size_type _i)
-			: map(&_map), it(_it), i(_i) {}
-
-public:
-	iterator(const iterator &that) : map(that.map), it(that.it), i(that.i) {}
-	iterator& operator= (const iterator &that) {
-		map = that.map; i = that.i; it = that.it;
-		return *this;
-	}
-
-	reference operator* () { return *it; }
-	pointer operator-> () { return it.operator->(); }
-
-	iterator& operator++ () {
-		if ( it == map->scopes[i].end() ) {
-			if ( i == 0 ) return *this;
-			--i;
-			it = map->scopes[i].begin();
-		} else {
-			++it;
-		}
-		return next_valid();
-	}
-
-	iterator& operator++ (int) { iterator tmp = *this; ++(*this); return tmp; }
-
-	iterator& operator-- () {
-		// may fail if this is the begin iterator; allowed by STL spec
-		if ( it == map->scopes[i].begin() ) {
-			++i;
-			it = map->scopes[i].end();
-		}
-		--it;
-		return prev_valid();
-	}
-	iterator& operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
-
-	bool operator== (const iterator &that) {
-		return map == that.map && i == that.i && it == that.it;
-	}
-	bool operator!= (const iterator &that) { return !( *this == that ); }
-
-private:
-	ErasableScopedMap< Key, Value > const *map;
-	wrapped_iterator it;
-	size_type i;
-};
-
-template<typename Key, typename Value>
-class ErasableScopedMap<Key, Value>::const_iterator :
-		public std::iterator< std::bidirectional_iterator_tag, value_type > {
-	friend class ErasableScopedMap;
-	typedef typename std::map< Key, Value >::iterator wrapped_iterator;
-	typedef typename std::map< Key, Value >::const_iterator wrapped_const_iterator;
-	typedef typename std::vector< std::map< Key, Value > > scope_list;
-	typedef typename scope_list::size_type size_type;
-
-	/// Checks if this iterator points to a valid item
-	bool is_valid() const {
-		return it != map->scopes[i].end() && it->second != map->erased;
-	}
-
-	/// Increments on invalid
-	const_iterator& next_valid() {
-		if ( ! is_valid() ) { ++(*this); }
-		return *this;
-	}
-
-	/// Decrements on invalid
-	const_iterator& prev_valid() {
-		if ( ! is_valid() ) { --(*this); }
-		return *this;
-	}
-
-	const_iterator(ErasableScopedMap< Key, Value > const &_map, const wrapped_const_iterator &_it, size_type _i)
-			: map(&_map), it(_it), i(_i) {}
-public:
-	const_iterator(const iterator &that) : map(that.map), it(that.it), i(that.i) {}
-	const_iterator(const const_iterator &that) : map(that.map), it(that.it), i(that.i) {}
-	const_iterator& operator= (const iterator &that) {
-		map = that.map; i = that.i; it = that.it;
-		return *this;
-	}
-	const_iterator& operator= (const const_iterator &that) {
-		map = that.map; i = that.i; it = that.it;
-		return *this;
-	}
-
-	const_reference operator* () { return *it; }
-	const_pointer operator-> () { return it.operator->(); }
-
-	const_iterator& operator++ () {
-		if ( it == map->scopes[i].end() ) {
-			if ( i == 0 ) return *this;
-			--i;
-			it = map->scopes[i].begin();
-		} else {
-			++it;
-		}
-		return next_valid();
-	}
-	const_iterator& operator++ (int) { const_iterator tmp = *this; ++(*this); return tmp; }
-
-	const_iterator& operator-- () {
-		// may fail if this is the begin iterator; allowed by STL spec
-		if ( it == map->scopes[i].begin() ) {
-			++i;
-			it = map->scopes[i].end();
-		}
-		--it;
-		return prev_valid();
-	}
-	const_iterator& operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
-
-	bool operator== (const const_iterator &that) {
-		return map == that.map && i == that.i && it == that.it;
-	}
-	bool operator!= (const const_iterator &that) { return !( *this == that ); }
-
-private:
-	ErasableScopedMap< Key, Value > const *map;
-	wrapped_const_iterator it;
-	size_type i;
-};
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/ErasableScopedMap.hpp
===================================================================
--- src/GenPoly/ErasableScopedMap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/ErasableScopedMap.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,297 @@
+//
+// 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.
+//
+// ErasableScopedMap.h -- A map that supports scoping and erasing elements.
+//
+// Author           : Aaron B. Moss
+// Created On       : Wed Dec 2 11:37:00 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Jul 22 09:23:24 2017
+// Update Count     : 2
+//
+
+#pragma once
+
+#include <cassert>
+#include <iterator>
+#include <map>
+#include <utility>
+#include <vector>
+
+namespace GenPoly {
+
+/// A map where the items are placed into nested scopes.
+/// Inserted items are placed into the innermost scope, lookup looks from the
+/// innermost scope outward. Erasing a key means that find() will no longer
+/// report any instance of the key in a scope further out, but the erasure
+/// itself is scoped. Key erasure works by inserting a sentinal value into
+/// the value field, and thus only works for Value types where a meaningful
+/// sentinal can be chosen.
+template<typename Key, typename Value>
+class ErasableScopedMap {
+	typedef std::map< Key, Value > Scope;
+	typedef std::vector< Scope > ScopeList;
+
+	/// Scoped list of maps.
+	ScopeList scopes;
+	/// Sentinal value for erased keys.
+	Value erased;
+public:
+	typedef typename Scope::key_type key_type;
+	typedef typename Scope::mapped_type mapped_type;
+	typedef typename Scope::value_type value_type;
+	typedef typename ScopeList::size_type size_type;
+	typedef typename ScopeList::difference_type difference_type;
+	typedef typename Scope::reference reference;
+	typedef typename Scope::const_reference const_reference;
+	typedef typename Scope::pointer pointer;
+	typedef typename Scope::const_pointer const_pointer;
+
+	// Both iterator types are complete bidirectional iterators, see below.
+	class iterator;
+	class const_iterator;
+
+	/// Starts a new scope
+	void beginScope() {
+		scopes.emplace_back();
+	}
+
+	/// Ends a scope; invalidates any iterators pointing to elements of that scope
+	void endScope() {
+		scopes.pop_back();
+		assert( ! scopes.empty() );
+	}
+
+	/// Default constructor initializes with one scope
+	ErasableScopedMap( const Value &erased_ ) : erased( erased_ ) { beginScope(); }
+
+	iterator begin() { return iterator(*this, scopes.back().begin(), scopes.size()-1).next_valid(); }
+	const_iterator begin() const { return const_iterator(*this, scopes.back().begin(), scopes.size()-1).next_valid(); }
+	const_iterator cbegin() const { return const_iterator(*this, scopes.back().begin(), scopes.size()-1).next_valid(); }
+	iterator end() { return iterator(*this, scopes[0].end(), 0); }
+	const_iterator end() const { return const_iterator(*this, scopes[0].end(), 0); }
+	const_iterator cend() const { return const_iterator(*this, scopes[0].end(), 0); }
+
+	/// Gets the index of the current scope (counted from 1)
+	size_type currentScope() const { return scopes.size(); }
+
+	/// Finds the given key in the outermost scope it occurs; returns end() for none such
+	iterator find( const Key &key ) {
+		for ( size_type i = scopes.size() - 1; ; --i ) {
+			typename Scope::iterator val = scopes[i].find( key );
+			if ( val != scopes[i].end() ) {
+				return val->second == erased ? end() : iterator( *this, val, i );
+			}
+			if ( i == 0 ) break;
+		}
+		return end();
+	}
+	const_iterator find( const Key &key ) const {
+		return const_iterator( const_cast< ErasableScopedMap< Key, Value >* >(this)->find( key ) );
+	}
+
+	/// Finds the given key in the outermost scope inside the given scope where it occurs
+	iterator findNext( const_iterator &it, const Key &key ) {
+		if ( it.i == 0 ) return end();
+		for ( size_type i = it.i - 1; ; --i ) {
+			typename Scope::iterator val = scopes[i].find( key );
+			if ( val != scopes[i].end() ) {
+				return val->second == erased ? end() : iterator( *this, val, i );
+			}
+			if ( i == 0 ) break;
+		}
+		return end();
+	}
+	const_iterator findNext( const_iterator &it, const Key &key ) const {
+		return const_iterator( const_cast< ErasableScopedMap< Key, Value >* >(this)->findNext( it, key ) );
+	}
+
+	/// Inserts the given key-value pair into the outermost scope
+	std::pair< iterator, bool > insert( const value_type &value ) {
+		std::pair< typename Scope::iterator, bool > res = scopes.back().insert( value );
+		return std::make_pair( iterator(*this, res.first, scopes.size()-1), res.second );
+	}
+	std::pair< iterator, bool > insert( const Key &key, const Value &value ) { return insert( std::make_pair( key, value ) ); }
+
+	Value& operator[] ( const Key &key ) {
+		iterator slot = find( key );
+		if ( slot != end() ) return slot->second;
+		return insert( key, Value() ).first->second;
+	}
+
+	/// Marks the given element as erased from this scope inward; returns 1 for erased an element, 0 otherwise
+	size_type erase( const Key &key ) {
+		typename Scope::iterator val = scopes.back().find( key );
+		if ( val != scopes.back().end() ) {
+			val->second = erased;
+			return 1;
+		} else {
+			scopes.back().insert( val, std::make_pair( key, erased ) );
+			return 0;
+		}
+	}
+
+	bool contains( const Key & key ) const {
+		return find( key ) != cend();
+	}
+};
+
+template<typename Key, typename Value>
+class ErasableScopedMap<Key, Value>::iterator :
+		public std::iterator< std::bidirectional_iterator_tag, value_type > {
+	friend class ErasableScopedMap;
+	typedef typename Scope::iterator wrapped_iterator;
+	typedef typename ScopeList::size_type size_type;
+
+	/// Checks if this iterator points to a valid item
+	bool is_valid() const {
+		return it != map->scopes[i].end() && it->second != map->erased;
+	}
+
+	/// Increments on invalid
+	iterator& next_valid() {
+		if ( ! is_valid() ) { ++(*this); }
+		return *this;
+	}
+
+	/// Decrements on invalid
+	iterator& prev_valid() {
+		if ( ! is_valid() ) { --(*this); }
+		return *this;
+	}
+
+	iterator(ErasableScopedMap< Key, Value > const &_map, const wrapped_iterator &_it, size_type _i)
+			: map(&_map), it(_it), i(_i) {}
+
+public:
+	iterator(const iterator &that) : map(that.map), it(that.it), i(that.i) {}
+	iterator& operator= (const iterator &that) {
+		map = that.map; i = that.i; it = that.it;
+		return *this;
+	}
+
+	reference operator* () { return *it; }
+	pointer operator-> () { return it.operator->(); }
+
+	iterator& operator++ () {
+		if ( it == map->scopes[i].end() ) {
+			if ( i == 0 ) return *this;
+			--i;
+			it = map->scopes[i].begin();
+		} else {
+			++it;
+		}
+		return next_valid();
+	}
+
+	iterator& operator++ (int) { iterator tmp = *this; ++(*this); return tmp; }
+
+	iterator& operator-- () {
+		// may fail if this is the begin iterator; allowed by STL spec
+		if ( it == map->scopes[i].begin() ) {
+			++i;
+			it = map->scopes[i].end();
+		}
+		--it;
+		return prev_valid();
+	}
+	iterator& operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
+
+	bool operator== (const iterator &that) {
+		return map == that.map && i == that.i && it == that.it;
+	}
+	bool operator!= (const iterator &that) { return !( *this == that ); }
+
+private:
+	ErasableScopedMap< Key, Value > const *map;
+	wrapped_iterator it;
+	size_type i;
+};
+
+template<typename Key, typename Value>
+class ErasableScopedMap<Key, Value>::const_iterator :
+		public std::iterator< std::bidirectional_iterator_tag, value_type > {
+	friend class ErasableScopedMap;
+	typedef typename std::map< Key, Value >::iterator wrapped_iterator;
+	typedef typename std::map< Key, Value >::const_iterator wrapped_const_iterator;
+	typedef typename std::vector< std::map< Key, Value > > scope_list;
+	typedef typename scope_list::size_type size_type;
+
+	/// Checks if this iterator points to a valid item
+	bool is_valid() const {
+		return it != map->scopes[i].end() && it->second != map->erased;
+	}
+
+	/// Increments on invalid
+	const_iterator& next_valid() {
+		if ( ! is_valid() ) { ++(*this); }
+		return *this;
+	}
+
+	/// Decrements on invalid
+	const_iterator& prev_valid() {
+		if ( ! is_valid() ) { --(*this); }
+		return *this;
+	}
+
+	const_iterator(ErasableScopedMap< Key, Value > const &_map, const wrapped_const_iterator &_it, size_type _i)
+			: map(&_map), it(_it), i(_i) {}
+public:
+	const_iterator(const iterator &that) : map(that.map), it(that.it), i(that.i) {}
+	const_iterator(const const_iterator &that) : map(that.map), it(that.it), i(that.i) {}
+	const_iterator& operator= (const iterator &that) {
+		map = that.map; i = that.i; it = that.it;
+		return *this;
+	}
+	const_iterator& operator= (const const_iterator &that) {
+		map = that.map; i = that.i; it = that.it;
+		return *this;
+	}
+
+	const_reference operator* () { return *it; }
+	const_pointer operator-> () { return it.operator->(); }
+
+	const_iterator& operator++ () {
+		if ( it == map->scopes[i].end() ) {
+			if ( i == 0 ) return *this;
+			--i;
+			it = map->scopes[i].begin();
+		} else {
+			++it;
+		}
+		return next_valid();
+	}
+	const_iterator& operator++ (int) { const_iterator tmp = *this; ++(*this); return tmp; }
+
+	const_iterator& operator-- () {
+		// may fail if this is the begin iterator; allowed by STL spec
+		if ( it == map->scopes[i].begin() ) {
+			++i;
+			it = map->scopes[i].end();
+		}
+		--it;
+		return prev_valid();
+	}
+	const_iterator& operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
+
+	bool operator== (const const_iterator &that) {
+		return map == that.map && i == that.i && it == that.it;
+	}
+	bool operator!= (const const_iterator &that) { return !( *this == that ); }
+
+private:
+	ErasableScopedMap< Key, Value > const *map;
+	wrapped_const_iterator it;
+	size_type i;
+};
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/FindFunction.cc
===================================================================
--- src/GenPoly/FindFunction.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,112 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FindFunction.cc -- Find function types in a larger type.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Oct  7 17:05:20 2022
-// Update Count     : 7
-//
-
-#include "FindFunction.h"
-
-#include <utility>                      // for pair
-
-#include "AST/Pass.hpp"                 // for Pass
-#include "AST/Type.hpp"
-#include "GenPoly/ErasableScopedMap.h"  // for ErasableScopedMap<>::iterator
-#include "GenPoly/GenPoly.h"            // for TyVarMap
-#include "ScrubTypeVars.hpp"            // for scrubTypeVars
-
-namespace GenPoly {
-
-namespace {
-
-struct FindFunctionCore :
-		public ast::WithGuards,
-		public ast::WithShortCircuiting,
-		public ast::WithVisitorRef<FindFunctionCore> {
-	FindFunctionCore(
-		std::vector<ast::ptr<ast::FunctionType>> & functions,
-		const TypeVarMap & typeVars, FindFunctionPred predicate,
-		bool replaceMode );
-
-	void previsit( ast::FunctionType const * type );
-	ast::Type const * postvisit( ast::FunctionType const * type );
-	void previsit( ast::PointerType const * type );
-private:
-	void handleForall( const ast::FunctionType::ForallList & forall );
-
-	std::vector<ast::ptr<ast::FunctionType>> &functions;
-	TypeVarMap typeVars;
-	FindFunctionPred predicate;
-	bool replaceMode;
-};
-
-FindFunctionCore::FindFunctionCore(
-		std::vector<ast::ptr<ast::FunctionType>> & functions,
-		const TypeVarMap &typeVars, FindFunctionPred predicate,
-		bool replaceMode ) :
-	functions( functions ), typeVars( typeVars ),
-	predicate( predicate ), replaceMode( replaceMode ) {}
-
-void FindFunctionCore::handleForall( const ast::FunctionType::ForallList & forall ) {
-	for ( const ast::ptr<ast::TypeInstType> & td : forall ) {
-		TypeVarMap::iterator var = typeVars.find( *td );
-		if ( var != typeVars.end() ) {
-			typeVars.erase( var->first );
-		} // if
-	} // for
-}
-
-void FindFunctionCore::previsit( ast::FunctionType const * type ) {
-	visit_children = false;
-	GuardScope( typeVars );
-	handleForall( type->forall );
-	ast::accept_each( type->returns, *visitor );
-}
-
-ast::Type const * FindFunctionCore::postvisit( ast::FunctionType const * type ) {
-	ast::Type const * ret = type;
-	if ( predicate( type, typeVars ) ) {
-		functions.push_back( type );
-		if ( replaceMode ) {
-			// Replace type parameters in function type with void *.
-			ret = scrubTypeVars( ast::deepCopy( type ), typeVars );
-		} // if
-	} // if
-	return ret;
-}
-
-void FindFunctionCore::previsit( ast::PointerType const * /*type*/ ) {
-	GuardScope( typeVars );
-}
-
-} // namespace
-
-void findFunction( const ast::Type * type,
-		std::vector<ast::ptr<ast::FunctionType>> & functions,
-		const TypeVarMap & typeVars, FindFunctionPred predicate ) {
-	ast::Pass<FindFunctionCore> pass( functions, typeVars, predicate, false );
-	type->accept( pass );
-}
-
-const ast::Type * findAndReplaceFunction( const ast::Type * type,
-		std::vector<ast::ptr<ast::FunctionType>> & functions,
-		const TypeVarMap & typeVars, FindFunctionPred predicate ) {
-	ast::Pass<FindFunctionCore> pass( functions, typeVars, predicate, true );
-	return type->accept( pass );
-}
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/FindFunction.cpp
===================================================================
--- src/GenPoly/FindFunction.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/FindFunction.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,112 @@
+//
+// 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.
+//
+// FindFunction.cpp -- Find function types in a larger type.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Oct  7 17:05:20 2022
+// Update Count     : 7
+//
+
+#include "FindFunction.hpp"
+
+#include <utility>                        // for pair
+
+#include "AST/Pass.hpp"                   // for Pass
+#include "AST/Type.hpp"
+#include "GenPoly/ErasableScopedMap.hpp"  // for ErasableScopedMap<>::iterator
+#include "GenPoly/GenPoly.hpp"            // for TyVarMap
+#include "ScrubTypeVars.hpp"              // for scrubTypeVars
+
+namespace GenPoly {
+
+namespace {
+
+struct FindFunctionCore :
+		public ast::WithGuards,
+		public ast::WithShortCircuiting,
+		public ast::WithVisitorRef<FindFunctionCore> {
+	FindFunctionCore(
+		std::vector<ast::ptr<ast::FunctionType>> & functions,
+		const TypeVarMap & typeVars, FindFunctionPred predicate,
+		bool replaceMode );
+
+	void previsit( ast::FunctionType const * type );
+	ast::Type const * postvisit( ast::FunctionType const * type );
+	void previsit( ast::PointerType const * type );
+private:
+	void handleForall( const ast::FunctionType::ForallList & forall );
+
+	std::vector<ast::ptr<ast::FunctionType>> &functions;
+	TypeVarMap typeVars;
+	FindFunctionPred predicate;
+	bool replaceMode;
+};
+
+FindFunctionCore::FindFunctionCore(
+		std::vector<ast::ptr<ast::FunctionType>> & functions,
+		const TypeVarMap &typeVars, FindFunctionPred predicate,
+		bool replaceMode ) :
+	functions( functions ), typeVars( typeVars ),
+	predicate( predicate ), replaceMode( replaceMode ) {}
+
+void FindFunctionCore::handleForall( const ast::FunctionType::ForallList & forall ) {
+	for ( const ast::ptr<ast::TypeInstType> & td : forall ) {
+		TypeVarMap::iterator var = typeVars.find( *td );
+		if ( var != typeVars.end() ) {
+			typeVars.erase( var->first );
+		} // if
+	} // for
+}
+
+void FindFunctionCore::previsit( ast::FunctionType const * type ) {
+	visit_children = false;
+	GuardScope( typeVars );
+	handleForall( type->forall );
+	ast::accept_each( type->returns, *visitor );
+}
+
+ast::Type const * FindFunctionCore::postvisit( ast::FunctionType const * type ) {
+	ast::Type const * ret = type;
+	if ( predicate( type, typeVars ) ) {
+		functions.push_back( type );
+		if ( replaceMode ) {
+			// Replace type parameters in function type with void *.
+			ret = scrubTypeVars( ast::deepCopy( type ), typeVars );
+		} // if
+	} // if
+	return ret;
+}
+
+void FindFunctionCore::previsit( ast::PointerType const * /*type*/ ) {
+	GuardScope( typeVars );
+}
+
+} // namespace
+
+void findFunction( const ast::Type * type,
+		std::vector<ast::ptr<ast::FunctionType>> & functions,
+		const TypeVarMap & typeVars, FindFunctionPred predicate ) {
+	ast::Pass<FindFunctionCore> pass( functions, typeVars, predicate, false );
+	type->accept( pass );
+}
+
+const ast::Type * findAndReplaceFunction( const ast::Type * type,
+		std::vector<ast::ptr<ast::FunctionType>> & functions,
+		const TypeVarMap & typeVars, FindFunctionPred predicate ) {
+	ast::Pass<FindFunctionCore> pass( functions, typeVars, predicate, true );
+	return type->accept( pass );
+}
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/FindFunction.h
===================================================================
--- src/GenPoly/FindFunction.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,40 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FindFunction.h -- Find function types in a larger type.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Oct  7 10:30:00 2022
-// Update Count     : 3
-//
-
-#pragma once
-
-#include "GenPoly.h"            // for TypeVarMap
-
-namespace GenPoly {
-
-typedef bool (*FindFunctionPred)( const ast::FunctionType *, const TypeVarMap & );
-
-/// Recursively walks `type`, placing all functions that match `predicate`
-/// under `typeVars` into `functions`.
-void findFunction( const ast::Type * type,
-		std::vector<ast::ptr<ast::FunctionType>> & functions,
-		const TypeVarMap & typeVars, FindFunctionPred predicate );
-/// Like findFunction, but also replaces the function type with `void ()(void)`.
-const ast::Type * findAndReplaceFunction( const ast::Type * type,
-		std::vector<ast::ptr<ast::FunctionType>> & functions,
-		const TypeVarMap & typeVars, FindFunctionPred predicate );
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/FindFunction.hpp
===================================================================
--- src/GenPoly/FindFunction.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/FindFunction.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -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.
+//
+// FindFunction.hpp -- Find function types in a larger type.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Oct  7 10:30:00 2022
+// Update Count     : 3
+//
+
+#pragma once
+
+#include "GenPoly.hpp"            // for TypeVarMap
+
+namespace GenPoly {
+
+typedef bool (*FindFunctionPred)( const ast::FunctionType *, const TypeVarMap & );
+
+/// Recursively walks `type`, placing all functions that match `predicate`
+/// under `typeVars` into `functions`.
+void findFunction( const ast::Type * type,
+		std::vector<ast::ptr<ast::FunctionType>> & functions,
+		const TypeVarMap & typeVars, FindFunctionPred predicate );
+/// Like findFunction, but also replaces the function type with `void ()(void)`.
+const ast::Type * findAndReplaceFunction( const ast::Type * type,
+		std::vector<ast::ptr<ast::FunctionType>> & functions,
+		const TypeVarMap & typeVars, FindFunctionPred predicate );
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,414 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// GenPoly.cc -- General GenPoly utilities.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Oct 24 15:19:00 2022
-// Update Count     : 17
-//
-
-#include "GenPoly.h"
-
-#include <cassert>                      // for assertf, assert
-#include <iostream>                     // for operator<<, ostream, basic_os...
-#include <iterator>                     // for back_insert_iterator, back_in...
-#include <list>                         // for list, _List_iterator, list<>:...
-#include <typeindex>                    // for type_index
-#include <utility>                      // for pair
-#include <vector>                       // for vector
-
-#include "AST/Expr.hpp"
-#include "AST/Type.hpp"
-#include "AST/TypeSubstitution.hpp"
-#include "GenPoly/ErasableScopedMap.h"  // for ErasableScopedMap<>::const_it...
-#include "ResolvExpr/typeops.h"         // for flatten
-
-using namespace std;
-
-namespace GenPoly {
-
-namespace {
-	/// Checks a parameter list for polymorphic parameters; will substitute according to env if present.
-	bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const ast::TypeSubstitution * env ) {
-		for ( auto & param : params ) {
-			auto paramType = param.as<ast::TypeExpr>();
-			assertf( paramType, "Aggregate parameters should be type expressions" );
-			if ( isPolyType( paramType->type, env ) ) return true;
-		}
-		return false;
-	}
-
-	/// Checks a parameter list for polymorphic parameters from typeVars; will substitute according to env if present.
-	bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const TypeVarMap & typeVars, const ast::TypeSubstitution * env ) {
-		for ( auto & param : params ) {
-			auto paramType = param.as<ast::TypeExpr>();
-			assertf( paramType, "Aggregate parameters should be type expressions" );
-			if ( isPolyType( paramType->type, typeVars, env ) ) return true;
-		}
-		return false;
-	}
-
-	/// Checks a parameter list for dynamic-layout parameters from tyVars; will substitute according to env if present.
-	bool hasDynParams(
-			const std::vector<ast::ptr<ast::Expr>> & params,
-			const TypeVarMap & typeVars,
-			const ast::TypeSubstitution * subst ) {
-		for ( ast::ptr<ast::Expr> const & paramExpr : params ) {
-			auto param = paramExpr.as<ast::TypeExpr>();
-			assertf( param, "Aggregate parameters should be type expressions." );
-			if ( isDynType( param->type.get(), typeVars, subst ) ) {
-				return true;
-			}
-		}
-		return false;
-	}
-} // namespace
-
-const ast::Type * replaceTypeInst( const ast::Type * type, const ast::TypeSubstitution * env ) {
-	if ( !env ) return type;
-	if ( auto typeInst = dynamic_cast<const ast::TypeInstType*>( type ) ) {
-		if ( auto newType = env->lookup( typeInst ) ) return newType;
-	}
-	return type;
-}
-
-const ast::Type * isPolyType( const ast::Type * type, const ast::TypeSubstitution * subst ) {
-	type = replaceTypeInst( type, subst );
-
-	if ( dynamic_cast< const ast::TypeInstType * >( type ) ) {
-		// This case is where the two variants of isPolyType differ.
-		return type;
-	} else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
-		return isPolyType( arrayType->base, subst );
-	} else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {
-		if ( hasPolyParams( structType->params, subst ) ) return type;
-	} else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {
-		if ( hasPolyParams( unionType->params, subst ) ) return type;
-	}
-	return nullptr;
-}
-
-const ast::Type * isPolyType( const ast::Type * type,
-		const TypeVarMap & typeVars, const ast::TypeSubstitution * subst ) {
-	type = replaceTypeInst( type, subst );
-
-	if ( auto inst = dynamic_cast< const ast::TypeInstType * >( type ) ) {
-		if ( typeVars.contains( *inst ) ) return type;
-	} else if ( auto array = dynamic_cast< const ast::ArrayType * >( type ) ) {
-		return isPolyType( array->base, typeVars, subst );
-	} else if ( auto sue = dynamic_cast< const ast::StructInstType * >( type ) ) {
-		if ( hasPolyParams( sue->params, typeVars, subst ) ) return type;
-	} else if ( auto sue = dynamic_cast< const ast::UnionInstType * >( type ) ) {
-		if ( hasPolyParams( sue->params, typeVars, subst ) ) return type;
-	}
-	return nullptr;
-}
-
-const ast::BaseInstType * isDynType(
-		const ast::Type * type, const TypeVarMap & typeVars,
-		const ast::TypeSubstitution * subst ) {
-	type = replaceTypeInst( type, subst );
-
-	if ( auto inst = dynamic_cast<ast::TypeInstType const *>( type ) ) {
-		auto var = typeVars.find( *inst );
-		if ( var != typeVars.end() && var->second.isComplete ) {
-			return inst;
-		}
-	} else if ( auto inst = dynamic_cast<ast::StructInstType const *>( type ) ) {
-		if ( hasDynParams( inst->params, typeVars, subst ) ) return inst;
-	} else if ( auto inst = dynamic_cast<ast::UnionInstType const *>( type ) ) {
-		if ( hasDynParams( inst->params, typeVars, subst ) ) return inst;
-	}
-	return nullptr;
-}
-
-const ast::BaseInstType *isDynRet(
-		const ast::FunctionType * type, const TypeVarMap & typeVars ) {
-	if ( type->returns.empty() ) return nullptr;
-
-	return isDynType( type->returns.front(), typeVars );
-}
-
-const ast::BaseInstType *isDynRet( const ast::FunctionType * func ) {
-	if ( func->returns.empty() ) return nullptr;
-
-	TypeVarMap forallTypes;
-	makeTypeVarMap( func, forallTypes );
-	return isDynType( func->returns.front(), forallTypes );
-}
-
-bool needsAdapter(
-		ast::FunctionType const * adaptee, const TypeVarMap & typeVars ) {
-	if ( isDynRet( adaptee, typeVars ) ) return true;
-
-	for ( auto param : adaptee->params ) {
-		if ( isDynType( param, typeVars ) ) {
-			return true;
-		}
-	}
-	return false;
-}
-
-const ast::Type * isPolyPtr(
-		const ast::Type * type, const TypeVarMap & typeVars,
-		const ast::TypeSubstitution * typeSubs ) {
-	type = replaceTypeInst( type, typeSubs );
-
-	if ( auto * ptr = dynamic_cast<ast::PointerType const *>( type ) ) {
-		return isPolyType( ptr->base, typeVars, typeSubs );
-	}
-	return nullptr;
-}
-
-ast::Type const * hasPolyBase(
-		ast::Type const * type, const TypeVarMap & typeVars,
-		int * levels, const ast::TypeSubstitution * subst ) {
-	int level_count = 0;
-
-	while ( true ) {
-		type = replaceTypeInst( type, subst );
-
-		if ( auto ptr = dynamic_cast<ast::PointerType const *>( type ) ) {
-			type = ptr->base;
-			++level_count;
-		} else {
-			break;
-		}
-	}
-
-	if ( nullptr != levels ) { *levels = level_count; }
-	return isPolyType( type, typeVars, subst );
-}
-
-const ast::FunctionType * getFunctionType( const ast::Type * ty ) {
-	if ( auto pty = dynamic_cast< const ast::PointerType * >( ty ) ) {
-		return pty->base.as< ast::FunctionType >();
-	} else {
-		return dynamic_cast< const ast::FunctionType * >( ty );
-	}
-}
-
-namespace {
-	/// Checks if is a pointer to D
-	template<typename D, typename B>
-	bool is( const B* p ) { return type_index{typeid(D)} == type_index{typeid(*p)}; }
-
-	/// Converts to a pointer to D without checking for safety
-	template<typename D, typename B>
-	inline D* as( B* p ) { return reinterpret_cast<D*>(p); }
-
-	template<typename D, typename B>
-	inline D const * as( B const * p ) {
-		return reinterpret_cast<D const *>( p );
-	}
-
-	/// Flattens a list of types.
-	void flattenList( vector<ast::ptr<ast::Type>> const & src,
-			vector<ast::ptr<ast::Type>> & out ) {
-		for ( auto const & type : src ) {
-			ResolvExpr::flatten( type, out );
-		}
-	}
-
-	bool paramListsPolyCompatible(
-			std::vector<ast::ptr<ast::Expr>> const & lparams,
-			std::vector<ast::ptr<ast::Expr>> const & rparams ) {
-		if ( lparams.size() != rparams.size() ) {
-			return false;
-		}
-
-		for ( auto lparam = lparams.begin(), rparam = rparams.begin() ;
-				lparam != lparams.end() ; ++lparam, ++rparam ) {
-			ast::TypeExpr const * lexpr = lparam->as<ast::TypeExpr>();
-			assertf( lexpr, "Aggregate parameters should be type expressions" );
-			ast::TypeExpr const * rexpr = rparam->as<ast::TypeExpr>();
-			assertf( rexpr, "Aggregate parameters should be type expressions" );
-
-			// xxx - might need to let VoidType be a wildcard here too; could have some voids
-			// stuffed in for dtype-statics.
-			// if ( is<VoidType>( lexpr->type() ) || is<VoidType>( bparam->get_type() ) ) continue;
-			if ( !typesPolyCompatible( lexpr->type, rexpr->type ) ) {
-				return false;
-			}
-		}
-
-		return true;
-	}
-} // namespace
-
-bool typesPolyCompatible( ast::Type const * lhs, ast::Type const * rhs ) {
-	type_index const lid = typeid(*lhs);
-
-	// Polymorphic types always match:
-	if ( type_index(typeid(ast::TypeInstType)) == lid ) return true;
-
-	type_index const rid = typeid(*rhs);
-	if ( type_index(typeid(ast::TypeInstType)) == rid ) return true;
-
-	// All other types only match if they are the same type:
-	if ( lid != rid ) return false;
-
-	// So remaining types can be examined case by case.
-	// Recurse through type structure (conditions borrowed from Unify.cc).
-
-	if ( type_index(typeid(ast::BasicType)) == lid ) {
-		return as<ast::BasicType>(lhs)->kind == as<ast::BasicType>(rhs)->kind;
-	} else if ( type_index(typeid(ast::PointerType)) == lid ) {
-		ast::PointerType const * l = as<ast::PointerType>(lhs);
-		ast::PointerType const * r = as<ast::PointerType>(rhs);
-
-		// void pointers should match any other pointer type.
-		return is<ast::VoidType>( l->base.get() )
-			|| is<ast::VoidType>( r->base.get() )
-			|| typesPolyCompatible( l->base.get(), r->base.get() );
-	} else if ( type_index(typeid(ast::ReferenceType)) == lid ) {
-		ast::ReferenceType const * l = as<ast::ReferenceType>(lhs);
-		ast::ReferenceType const * r = as<ast::ReferenceType>(rhs);
-
-		// void references should match any other reference type.
-		return is<ast::VoidType>( l->base.get() )
-			|| is<ast::VoidType>( r->base.get() )
-			|| typesPolyCompatible( l->base.get(), r->base.get() );
-	} else if ( type_index(typeid(ast::ArrayType)) == lid ) {
-		ast::ArrayType const * l = as<ast::ArrayType>(lhs);
-		ast::ArrayType const * r = as<ast::ArrayType>(rhs);
-
-		if ( l->isVarLen ) {
-			if ( !r->isVarLen ) return false;
-		} else {
-			if ( r->isVarLen ) return false;
-
-			auto lc = l->dimension.as<ast::ConstantExpr>();
-			auto rc = r->dimension.as<ast::ConstantExpr>();
-			if ( lc && rc && lc->intValue() != rc->intValue() ) {
-				return false;
-			}
-		}
-
-		return typesPolyCompatible( l->base.get(), r->base.get() );
-	} else if ( type_index(typeid(ast::FunctionType)) == lid ) {
-		ast::FunctionType const * l = as<ast::FunctionType>(lhs);
-		ast::FunctionType const * r = as<ast::FunctionType>(rhs);
-
-		std::vector<ast::ptr<ast::Type>> lparams, rparams;
-		flattenList( l->params, lparams );
-		flattenList( r->params, rparams );
-		if ( lparams.size() != rparams.size() ) return false;
-		for ( unsigned i = 0; i < lparams.size(); ++i ) {
-			if ( !typesPolyCompatible( lparams[i], rparams[i] ) ) return false;
-		}
-
-		std::vector<ast::ptr<ast::Type>> lrets, rrets;
-		flattenList( l->returns, lrets );
-		flattenList( r->returns, rrets );
-		if ( lrets.size() != rrets.size() ) return false;
-		for ( unsigned i = 0; i < lrets.size(); ++i ) {
-			if ( !typesPolyCompatible( lrets[i], rrets[i] ) ) return false;
-		}
-		return true;
-	} else if ( type_index(typeid(ast::StructInstType)) == lid ) {
-		ast::StructInstType const * l = as<ast::StructInstType>(lhs);
-		ast::StructInstType const * r = as<ast::StructInstType>(rhs);
-
-		if ( l->name != r->name ) return false;
-		return paramListsPolyCompatible( l->params, r->params );
-	} else if ( type_index(typeid(ast::UnionInstType)) == lid ) {
-		ast::UnionInstType const * l = as<ast::UnionInstType>(lhs);
-		ast::UnionInstType const * r = as<ast::UnionInstType>(rhs);
-
-		if ( l->name != r->name ) return false;
-		return paramListsPolyCompatible( l->params, r->params );
-	} else if ( type_index(typeid(ast::EnumInstType)) == lid ) {
-		ast::EnumInstType const * l = as<ast::EnumInstType>(lhs);
-		ast::EnumInstType const * r = as<ast::EnumInstType>(rhs);
-
-		return l->name == r->name;
-	} else if ( type_index(typeid(ast::TraitInstType)) == lid ) {
-		ast::TraitInstType const * l = as<ast::TraitInstType>(lhs);
-		ast::TraitInstType const * r = as<ast::TraitInstType>(rhs);
-
-		return l->name == r->name;
-	} else if ( type_index(typeid(ast::TupleType)) == lid ) {
-		ast::TupleType const * l = as<ast::TupleType>(lhs);
-		ast::TupleType const * r = as<ast::TupleType>(rhs);
-
-		std::vector<ast::ptr<ast::Type>> ltypes, rtypes;
-		flattenList( l->types, ( ltypes ) );
-		flattenList( r->types, ( rtypes ) );
-		if ( ltypes.size() != rtypes.size() ) return false;
-
-		for ( unsigned i = 0 ; i < ltypes.size() ; ++i ) {
-			if ( !typesPolyCompatible( ltypes[i], rtypes[i] ) ) return false;
-		}
-		return true;
-	// The remaining types (VoidType, VarArgsType, ZeroType & OneType)
-	// have no variation so will always be equal.
-	} else {
-		return true;
-	}
-}
-
-bool needsBoxing( const ast::Type * param, const ast::Type * arg,
-		const TypeVarMap & typeVars, const ast::TypeSubstitution * subst ) {
-	// Don't need to box if the parameter is not polymorphic.
-	if ( !isPolyType( param, typeVars ) ) return false;
-
-	ast::ptr<ast::Type> newType = arg;
-	if ( subst ) {
-		int count = subst->apply( newType );
-		(void)count;
-	}
-	// Only need to box if the argument is not also polymorphic.
-	return !isPolyType( newType );
-}
-
-bool needsBoxing(
-		const ast::Type * param, const ast::Type * arg,
-		const ast::ApplicationExpr * expr,
-		const ast::TypeSubstitution * subst ) {
-	const ast::FunctionType * function = getFunctionType( expr->func->result );
-	assertf( function, "ApplicationExpr has non-function type: %s", toCString( expr->func->result ) );
-	TypeVarMap exprTyVars;
-	makeTypeVarMap( function, exprTyVars );
-	return needsBoxing( param, arg, exprTyVars, subst );
-}
-
-void addToTypeVarMap( const ast::TypeDecl * decl, TypeVarMap & typeVars ) {
-	typeVars.insert( ast::TypeEnvKey( decl, 0, 0 ), ast::TypeData( decl ) );
-}
-
-void addToTypeVarMap( const ast::TypeInstType * type, TypeVarMap & typeVars ) {
-	typeVars.insert( ast::TypeEnvKey( *type ), ast::TypeData( type->base ) );
-}
-
-void makeTypeVarMap( const ast::Type * type, TypeVarMap & typeVars ) {
-	if ( auto func = dynamic_cast<ast::FunctionType const *>( type ) ) {
-		for ( auto & typeVar : func->forall ) {
-			assert( typeVar );
-			addToTypeVarMap( typeVar, typeVars );
-		}
-	}
-	if ( auto pointer = dynamic_cast<ast::PointerType const *>( type ) ) {
-		makeTypeVarMap( pointer->base, typeVars );
-	}
-}
-
-void makeTypeVarMap( const ast::FunctionDecl * decl, TypeVarMap & typeVars ) {
-	for ( auto & typeDecl : decl->type_params ) {
-		addToTypeVarMap( typeDecl, typeVars );
-	}
-}
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/GenPoly.cpp
===================================================================
--- src/GenPoly/GenPoly.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/GenPoly.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,414 @@
+//
+// 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.
+//
+// GenPoly.cpp -- General GenPoly utilities.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Oct 24 15:19:00 2022
+// Update Count     : 17
+//
+
+#include "GenPoly.hpp"
+
+#include <cassert>                        // for assertf, assert
+#include <iostream>                       // for operator<<, ostream, basic_...
+#include <iterator>                       // for back_insert_iterator, back_...
+#include <list>                           // for list, _List_iterator, list<...
+#include <typeindex>                      // for type_index
+#include <utility>                        // for pair
+#include <vector>                         // for vector
+
+#include "AST/Expr.hpp"
+#include "AST/Type.hpp"
+#include "AST/TypeSubstitution.hpp"
+#include "GenPoly/ErasableScopedMap.hpp"  // for ErasableScopedMap<>::const_...
+#include "ResolvExpr/Typeops.hpp"         // for flatten
+
+using namespace std;
+
+namespace GenPoly {
+
+namespace {
+	/// Checks a parameter list for polymorphic parameters; will substitute according to env if present.
+	bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const ast::TypeSubstitution * env ) {
+		for ( auto & param : params ) {
+			auto paramType = param.as<ast::TypeExpr>();
+			assertf( paramType, "Aggregate parameters should be type expressions" );
+			if ( isPolyType( paramType->type, env ) ) return true;
+		}
+		return false;
+	}
+
+	/// Checks a parameter list for polymorphic parameters from typeVars; will substitute according to env if present.
+	bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const TypeVarMap & typeVars, const ast::TypeSubstitution * env ) {
+		for ( auto & param : params ) {
+			auto paramType = param.as<ast::TypeExpr>();
+			assertf( paramType, "Aggregate parameters should be type expressions" );
+			if ( isPolyType( paramType->type, typeVars, env ) ) return true;
+		}
+		return false;
+	}
+
+	/// Checks a parameter list for dynamic-layout parameters from tyVars; will substitute according to env if present.
+	bool hasDynParams(
+			const std::vector<ast::ptr<ast::Expr>> & params,
+			const TypeVarMap & typeVars,
+			const ast::TypeSubstitution * subst ) {
+		for ( ast::ptr<ast::Expr> const & paramExpr : params ) {
+			auto param = paramExpr.as<ast::TypeExpr>();
+			assertf( param, "Aggregate parameters should be type expressions." );
+			if ( isDynType( param->type.get(), typeVars, subst ) ) {
+				return true;
+			}
+		}
+		return false;
+	}
+} // namespace
+
+const ast::Type * replaceTypeInst( const ast::Type * type, const ast::TypeSubstitution * env ) {
+	if ( !env ) return type;
+	if ( auto typeInst = dynamic_cast<const ast::TypeInstType*>( type ) ) {
+		if ( auto newType = env->lookup( typeInst ) ) return newType;
+	}
+	return type;
+}
+
+const ast::Type * isPolyType( const ast::Type * type, const ast::TypeSubstitution * subst ) {
+	type = replaceTypeInst( type, subst );
+
+	if ( dynamic_cast< const ast::TypeInstType * >( type ) ) {
+		// This case is where the two variants of isPolyType differ.
+		return type;
+	} else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
+		return isPolyType( arrayType->base, subst );
+	} else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {
+		if ( hasPolyParams( structType->params, subst ) ) return type;
+	} else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {
+		if ( hasPolyParams( unionType->params, subst ) ) return type;
+	}
+	return nullptr;
+}
+
+const ast::Type * isPolyType( const ast::Type * type,
+		const TypeVarMap & typeVars, const ast::TypeSubstitution * subst ) {
+	type = replaceTypeInst( type, subst );
+
+	if ( auto inst = dynamic_cast< const ast::TypeInstType * >( type ) ) {
+		if ( typeVars.contains( *inst ) ) return type;
+	} else if ( auto array = dynamic_cast< const ast::ArrayType * >( type ) ) {
+		return isPolyType( array->base, typeVars, subst );
+	} else if ( auto sue = dynamic_cast< const ast::StructInstType * >( type ) ) {
+		if ( hasPolyParams( sue->params, typeVars, subst ) ) return type;
+	} else if ( auto sue = dynamic_cast< const ast::UnionInstType * >( type ) ) {
+		if ( hasPolyParams( sue->params, typeVars, subst ) ) return type;
+	}
+	return nullptr;
+}
+
+const ast::BaseInstType * isDynType(
+		const ast::Type * type, const TypeVarMap & typeVars,
+		const ast::TypeSubstitution * subst ) {
+	type = replaceTypeInst( type, subst );
+
+	if ( auto inst = dynamic_cast<ast::TypeInstType const *>( type ) ) {
+		auto var = typeVars.find( *inst );
+		if ( var != typeVars.end() && var->second.isComplete ) {
+			return inst;
+		}
+	} else if ( auto inst = dynamic_cast<ast::StructInstType const *>( type ) ) {
+		if ( hasDynParams( inst->params, typeVars, subst ) ) return inst;
+	} else if ( auto inst = dynamic_cast<ast::UnionInstType const *>( type ) ) {
+		if ( hasDynParams( inst->params, typeVars, subst ) ) return inst;
+	}
+	return nullptr;
+}
+
+const ast::BaseInstType *isDynRet(
+		const ast::FunctionType * type, const TypeVarMap & typeVars ) {
+	if ( type->returns.empty() ) return nullptr;
+
+	return isDynType( type->returns.front(), typeVars );
+}
+
+const ast::BaseInstType *isDynRet( const ast::FunctionType * func ) {
+	if ( func->returns.empty() ) return nullptr;
+
+	TypeVarMap forallTypes;
+	makeTypeVarMap( func, forallTypes );
+	return isDynType( func->returns.front(), forallTypes );
+}
+
+bool needsAdapter(
+		ast::FunctionType const * adaptee, const TypeVarMap & typeVars ) {
+	if ( isDynRet( adaptee, typeVars ) ) return true;
+
+	for ( auto param : adaptee->params ) {
+		if ( isDynType( param, typeVars ) ) {
+			return true;
+		}
+	}
+	return false;
+}
+
+const ast::Type * isPolyPtr(
+		const ast::Type * type, const TypeVarMap & typeVars,
+		const ast::TypeSubstitution * typeSubs ) {
+	type = replaceTypeInst( type, typeSubs );
+
+	if ( auto * ptr = dynamic_cast<ast::PointerType const *>( type ) ) {
+		return isPolyType( ptr->base, typeVars, typeSubs );
+	}
+	return nullptr;
+}
+
+ast::Type const * hasPolyBase(
+		ast::Type const * type, const TypeVarMap & typeVars,
+		int * levels, const ast::TypeSubstitution * subst ) {
+	int level_count = 0;
+
+	while ( true ) {
+		type = replaceTypeInst( type, subst );
+
+		if ( auto ptr = dynamic_cast<ast::PointerType const *>( type ) ) {
+			type = ptr->base;
+			++level_count;
+		} else {
+			break;
+		}
+	}
+
+	if ( nullptr != levels ) { *levels = level_count; }
+	return isPolyType( type, typeVars, subst );
+}
+
+const ast::FunctionType * getFunctionType( const ast::Type * ty ) {
+	if ( auto pty = dynamic_cast< const ast::PointerType * >( ty ) ) {
+		return pty->base.as< ast::FunctionType >();
+	} else {
+		return dynamic_cast< const ast::FunctionType * >( ty );
+	}
+}
+
+namespace {
+	/// Checks if is a pointer to D
+	template<typename D, typename B>
+	bool is( const B* p ) { return type_index{typeid(D)} == type_index{typeid(*p)}; }
+
+	/// Converts to a pointer to D without checking for safety
+	template<typename D, typename B>
+	inline D* as( B* p ) { return reinterpret_cast<D*>(p); }
+
+	template<typename D, typename B>
+	inline D const * as( B const * p ) {
+		return reinterpret_cast<D const *>( p );
+	}
+
+	/// Flattens a list of types.
+	void flattenList( vector<ast::ptr<ast::Type>> const & src,
+			vector<ast::ptr<ast::Type>> & out ) {
+		for ( auto const & type : src ) {
+			ResolvExpr::flatten( type, out );
+		}
+	}
+
+	bool paramListsPolyCompatible(
+			std::vector<ast::ptr<ast::Expr>> const & lparams,
+			std::vector<ast::ptr<ast::Expr>> const & rparams ) {
+		if ( lparams.size() != rparams.size() ) {
+			return false;
+		}
+
+		for ( auto lparam = lparams.begin(), rparam = rparams.begin() ;
+				lparam != lparams.end() ; ++lparam, ++rparam ) {
+			ast::TypeExpr const * lexpr = lparam->as<ast::TypeExpr>();
+			assertf( lexpr, "Aggregate parameters should be type expressions" );
+			ast::TypeExpr const * rexpr = rparam->as<ast::TypeExpr>();
+			assertf( rexpr, "Aggregate parameters should be type expressions" );
+
+			// xxx - might need to let VoidType be a wildcard here too; could have some voids
+			// stuffed in for dtype-statics.
+			// if ( is<VoidType>( lexpr->type() ) || is<VoidType>( bparam->get_type() ) ) continue;
+			if ( !typesPolyCompatible( lexpr->type, rexpr->type ) ) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+} // namespace
+
+bool typesPolyCompatible( ast::Type const * lhs, ast::Type const * rhs ) {
+	type_index const lid = typeid(*lhs);
+
+	// Polymorphic types always match:
+	if ( type_index(typeid(ast::TypeInstType)) == lid ) return true;
+
+	type_index const rid = typeid(*rhs);
+	if ( type_index(typeid(ast::TypeInstType)) == rid ) return true;
+
+	// All other types only match if they are the same type:
+	if ( lid != rid ) return false;
+
+	// So remaining types can be examined case by case.
+	// Recurse through type structure (conditions borrowed from Unify.cc).
+
+	if ( type_index(typeid(ast::BasicType)) == lid ) {
+		return as<ast::BasicType>(lhs)->kind == as<ast::BasicType>(rhs)->kind;
+	} else if ( type_index(typeid(ast::PointerType)) == lid ) {
+		ast::PointerType const * l = as<ast::PointerType>(lhs);
+		ast::PointerType const * r = as<ast::PointerType>(rhs);
+
+		// void pointers should match any other pointer type.
+		return is<ast::VoidType>( l->base.get() )
+			|| is<ast::VoidType>( r->base.get() )
+			|| typesPolyCompatible( l->base.get(), r->base.get() );
+	} else if ( type_index(typeid(ast::ReferenceType)) == lid ) {
+		ast::ReferenceType const * l = as<ast::ReferenceType>(lhs);
+		ast::ReferenceType const * r = as<ast::ReferenceType>(rhs);
+
+		// void references should match any other reference type.
+		return is<ast::VoidType>( l->base.get() )
+			|| is<ast::VoidType>( r->base.get() )
+			|| typesPolyCompatible( l->base.get(), r->base.get() );
+	} else if ( type_index(typeid(ast::ArrayType)) == lid ) {
+		ast::ArrayType const * l = as<ast::ArrayType>(lhs);
+		ast::ArrayType const * r = as<ast::ArrayType>(rhs);
+
+		if ( l->isVarLen ) {
+			if ( !r->isVarLen ) return false;
+		} else {
+			if ( r->isVarLen ) return false;
+
+			auto lc = l->dimension.as<ast::ConstantExpr>();
+			auto rc = r->dimension.as<ast::ConstantExpr>();
+			if ( lc && rc && lc->intValue() != rc->intValue() ) {
+				return false;
+			}
+		}
+
+		return typesPolyCompatible( l->base.get(), r->base.get() );
+	} else if ( type_index(typeid(ast::FunctionType)) == lid ) {
+		ast::FunctionType const * l = as<ast::FunctionType>(lhs);
+		ast::FunctionType const * r = as<ast::FunctionType>(rhs);
+
+		std::vector<ast::ptr<ast::Type>> lparams, rparams;
+		flattenList( l->params, lparams );
+		flattenList( r->params, rparams );
+		if ( lparams.size() != rparams.size() ) return false;
+		for ( unsigned i = 0; i < lparams.size(); ++i ) {
+			if ( !typesPolyCompatible( lparams[i], rparams[i] ) ) return false;
+		}
+
+		std::vector<ast::ptr<ast::Type>> lrets, rrets;
+		flattenList( l->returns, lrets );
+		flattenList( r->returns, rrets );
+		if ( lrets.size() != rrets.size() ) return false;
+		for ( unsigned i = 0; i < lrets.size(); ++i ) {
+			if ( !typesPolyCompatible( lrets[i], rrets[i] ) ) return false;
+		}
+		return true;
+	} else if ( type_index(typeid(ast::StructInstType)) == lid ) {
+		ast::StructInstType const * l = as<ast::StructInstType>(lhs);
+		ast::StructInstType const * r = as<ast::StructInstType>(rhs);
+
+		if ( l->name != r->name ) return false;
+		return paramListsPolyCompatible( l->params, r->params );
+	} else if ( type_index(typeid(ast::UnionInstType)) == lid ) {
+		ast::UnionInstType const * l = as<ast::UnionInstType>(lhs);
+		ast::UnionInstType const * r = as<ast::UnionInstType>(rhs);
+
+		if ( l->name != r->name ) return false;
+		return paramListsPolyCompatible( l->params, r->params );
+	} else if ( type_index(typeid(ast::EnumInstType)) == lid ) {
+		ast::EnumInstType const * l = as<ast::EnumInstType>(lhs);
+		ast::EnumInstType const * r = as<ast::EnumInstType>(rhs);
+
+		return l->name == r->name;
+	} else if ( type_index(typeid(ast::TraitInstType)) == lid ) {
+		ast::TraitInstType const * l = as<ast::TraitInstType>(lhs);
+		ast::TraitInstType const * r = as<ast::TraitInstType>(rhs);
+
+		return l->name == r->name;
+	} else if ( type_index(typeid(ast::TupleType)) == lid ) {
+		ast::TupleType const * l = as<ast::TupleType>(lhs);
+		ast::TupleType const * r = as<ast::TupleType>(rhs);
+
+		std::vector<ast::ptr<ast::Type>> ltypes, rtypes;
+		flattenList( l->types, ( ltypes ) );
+		flattenList( r->types, ( rtypes ) );
+		if ( ltypes.size() != rtypes.size() ) return false;
+
+		for ( unsigned i = 0 ; i < ltypes.size() ; ++i ) {
+			if ( !typesPolyCompatible( ltypes[i], rtypes[i] ) ) return false;
+		}
+		return true;
+	// The remaining types (VoidType, VarArgsType, ZeroType & OneType)
+	// have no variation so will always be equal.
+	} else {
+		return true;
+	}
+}
+
+bool needsBoxing( const ast::Type * param, const ast::Type * arg,
+		const TypeVarMap & typeVars, const ast::TypeSubstitution * subst ) {
+	// Don't need to box if the parameter is not polymorphic.
+	if ( !isPolyType( param, typeVars ) ) return false;
+
+	ast::ptr<ast::Type> newType = arg;
+	if ( subst ) {
+		int count = subst->apply( newType );
+		(void)count;
+	}
+	// Only need to box if the argument is not also polymorphic.
+	return !isPolyType( newType );
+}
+
+bool needsBoxing(
+		const ast::Type * param, const ast::Type * arg,
+		const ast::ApplicationExpr * expr,
+		const ast::TypeSubstitution * subst ) {
+	const ast::FunctionType * function = getFunctionType( expr->func->result );
+	assertf( function, "ApplicationExpr has non-function type: %s", toCString( expr->func->result ) );
+	TypeVarMap exprTyVars;
+	makeTypeVarMap( function, exprTyVars );
+	return needsBoxing( param, arg, exprTyVars, subst );
+}
+
+void addToTypeVarMap( const ast::TypeDecl * decl, TypeVarMap & typeVars ) {
+	typeVars.insert( ast::TypeEnvKey( decl, 0, 0 ), ast::TypeData( decl ) );
+}
+
+void addToTypeVarMap( const ast::TypeInstType * type, TypeVarMap & typeVars ) {
+	typeVars.insert( ast::TypeEnvKey( *type ), ast::TypeData( type->base ) );
+}
+
+void makeTypeVarMap( const ast::Type * type, TypeVarMap & typeVars ) {
+	if ( auto func = dynamic_cast<ast::FunctionType const *>( type ) ) {
+		for ( auto & typeVar : func->forall ) {
+			assert( typeVar );
+			addToTypeVarMap( typeVar, typeVars );
+		}
+	}
+	if ( auto pointer = dynamic_cast<ast::PointerType const *>( type ) ) {
+		makeTypeVarMap( pointer->base, typeVars );
+	}
+}
+
+void makeTypeVarMap( const ast::FunctionDecl * decl, TypeVarMap & typeVars ) {
+	for ( auto & typeDecl : decl->type_params ) {
+		addToTypeVarMap( typeDecl, typeVars );
+	}
+}
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/GenPoly.h
===================================================================
--- src/GenPoly/GenPoly.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,104 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// GenPoly.h -- General GenPoly utilities.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Oct 24 15:18:00 2022
-// Update Count     : 11
-//
-
-#pragma once
-
-#include <iostream>               // for ostream
-#include <string>                 // for string, allocator, operator+, basic...
-
-#include "ErasableScopedMap.h"    // for ErasableScopedMap
-#include "AST/Decl.hpp"           // for AggregateDecl
-#include "AST/Fwd.hpp"            // for ApplicationExpr, BaseInstType, Func...
-#include "SymTab/Mangler.h"       // for Mangler
-
-namespace ast {
-	struct TypeEnvKey;
-}
-
-namespace GenPoly {
-
-struct TypeVarMap : public ErasableScopedMap<ast::TypeEnvKey, ast::TypeData> {
-	TypeVarMap() : ErasableScopedMap( ast::TypeData() ) {}
-};
-
-/// Replaces a TypeInstType by its referrent in the environment, if applicable.
-const ast::Type * replaceTypeInst( const ast::Type *, const ast::TypeSubstitution * );
-
-/// Returns polymorphic type if is polymorphic type, NULL otherwise; will look up substitution in env if provided.
-const ast::Type * isPolyType( const ast::Type * type, const ast::TypeSubstitution * subst = nullptr );
-
-/// Returns polymorphic type if is polymorphic type in tyVars, NULL otherwise; will look up substitution in env if provided.
-const ast::Type * isPolyType( const ast::Type * type, const TypeVarMap & typeVars, const ast::TypeSubstitution * subst = nullptr );
-
-/// Returns dynamic-layout type if is dynamic-layout type in tyVars, NULL otherwise; will look up substitution in env if provided.
-const ast::BaseInstType *isDynType( const ast::Type * type, const TypeVarMap & typeVars, const ast::TypeSubstitution * subst = 0 );
-
-/// Returns true iff function has dynamic-layout return type under the given type variable map.
-const ast::BaseInstType *isDynRet( const ast::FunctionType * type, const TypeVarMap & typeVars );
-
-/// Returns true iff function has dynamic-layout return type under the type variable map generated from its forall-parameters.
-const ast::BaseInstType *isDynRet( const ast::FunctionType * func );
-
-/// A function needs an adapter if it returns a dynamic-layout value or if any of its parameters have dynamic-layout type.
-bool needsAdapter( ast::FunctionType const * adaptee, const TypeVarMap & typeVars );
-
-/// Returns polymorphic type if is pointer to polymorphic type in tyVars, NULL otherwise; will look up substitution in env if provided.
-const ast::Type * isPolyPtr( const ast::Type * type, const TypeVarMap & typeVars, const ast::TypeSubstitution * env = 0 );
-
-/// If the base type (after dereferencing N >= 0 pointers) is a polymorphic type in tyVars, returns the base type, NULL otherwise;
-/// N will be stored in levels, if provided, will look up substitution in env if provided.
-const ast::Type * hasPolyBase( const ast::Type * type, const TypeVarMap & typeVars, int * levels = 0, const ast::TypeSubstitution * env = 0 );
-
-/// Returns a pointer to the base FunctionType if ty is the type of a function (or pointer to one), NULL otherwise.
-const ast::FunctionType * getFunctionType( const ast::Type * ty );
-
-/// Returns true iff types are structurally identical, where TypeInstType's match any type.
-bool typesPolyCompatible( ast::Type const * lhs, ast::Type const * rhs );
-
-/// Returns true if arg requires boxing given typeVars.
-bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TypeVarMap & typeVars, const ast::TypeSubstitution * subst );
-
-/// Returns true if arg requires boxing in the call to appExpr.
-bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * expr, const ast::TypeSubstitution * subst );
-
-/// Adds the type variable `type` to `typeVars`.
-void addToTypeVarMap( const ast::TypeDecl * type, TypeVarMap & typeVars );
-void addToTypeVarMap( const ast::TypeInstType * type, TypeVarMap & typeVars );
-
-/// Adds the declarations in the forall list of type (and its pointed-to type if it's a pointer type) to `typeVars`.
-void makeTypeVarMap( const ast::Type * type, TypeVarMap & typeVars );
-void makeTypeVarMap( const ast::FunctionDecl * decl, TypeVarMap & typeVars );
-
-/// Gets the name of the sizeof parameter for the type, given its mangled name.
-inline std::string sizeofName( const std::string &name ) { return std::string( "_sizeof_" ) + name; }
-
-/// Gets the name of the alignof parameter for the type, given its mangled name.
-inline std::string alignofName( const std::string &name ) { return std::string( "_alignof_" ) + name; }
-
-/// Gets the name of the offsetof parameter for the type, given its mangled name.
-inline std::string offsetofName( const std::string &name ) { return std::string( "_offsetof_" ) + name; }
-
-/// Gets the name of the layout function for a given aggregate type, given its declaration.
-inline std::string layoutofName( ast::AggregateDecl const * decl ) {
-	return std::string( "_layoutof_" ) + decl->name;
-}
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/GenPoly.hpp
===================================================================
--- src/GenPoly/GenPoly.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/GenPoly.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,104 @@
+//
+// 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.
+//
+// GenPoly.hpp -- General GenPoly utilities.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Oct 24 15:18:00 2022
+// Update Count     : 11
+//
+
+#pragma once
+
+#include <iostream>               // for ostream
+#include <string>                 // for string, allocator, operator+, basic...
+
+#include "ErasableScopedMap.hpp"  // for ErasableScopedMap
+#include "AST/Decl.hpp"           // for AggregateDecl
+#include "AST/Fwd.hpp"            // for ApplicationExpr, BaseInstType, Func...
+#include "SymTab/Mangler.hpp"     // for Mangler
+
+namespace ast {
+	struct TypeEnvKey;
+}
+
+namespace GenPoly {
+
+struct TypeVarMap : public ErasableScopedMap<ast::TypeEnvKey, ast::TypeData> {
+	TypeVarMap() : ErasableScopedMap( ast::TypeData() ) {}
+};
+
+/// Replaces a TypeInstType by its referrent in the environment, if applicable.
+const ast::Type * replaceTypeInst( const ast::Type *, const ast::TypeSubstitution * );
+
+/// Returns polymorphic type if is polymorphic type, NULL otherwise; will look up substitution in env if provided.
+const ast::Type * isPolyType( const ast::Type * type, const ast::TypeSubstitution * subst = nullptr );
+
+/// Returns polymorphic type if is polymorphic type in tyVars, NULL otherwise; will look up substitution in env if provided.
+const ast::Type * isPolyType( const ast::Type * type, const TypeVarMap & typeVars, const ast::TypeSubstitution * subst = nullptr );
+
+/// Returns dynamic-layout type if is dynamic-layout type in tyVars, NULL otherwise; will look up substitution in env if provided.
+const ast::BaseInstType *isDynType( const ast::Type * type, const TypeVarMap & typeVars, const ast::TypeSubstitution * subst = 0 );
+
+/// Returns true iff function has dynamic-layout return type under the given type variable map.
+const ast::BaseInstType *isDynRet( const ast::FunctionType * type, const TypeVarMap & typeVars );
+
+/// Returns true iff function has dynamic-layout return type under the type variable map generated from its forall-parameters.
+const ast::BaseInstType *isDynRet( const ast::FunctionType * func );
+
+/// A function needs an adapter if it returns a dynamic-layout value or if any of its parameters have dynamic-layout type.
+bool needsAdapter( ast::FunctionType const * adaptee, const TypeVarMap & typeVars );
+
+/// Returns polymorphic type if is pointer to polymorphic type in tyVars, NULL otherwise; will look up substitution in env if provided.
+const ast::Type * isPolyPtr( const ast::Type * type, const TypeVarMap & typeVars, const ast::TypeSubstitution * env = 0 );
+
+/// If the base type (after dereferencing N >= 0 pointers) is a polymorphic type in tyVars, returns the base type, NULL otherwise;
+/// N will be stored in levels, if provided, will look up substitution in env if provided.
+const ast::Type * hasPolyBase( const ast::Type * type, const TypeVarMap & typeVars, int * levels = 0, const ast::TypeSubstitution * env = 0 );
+
+/// Returns a pointer to the base FunctionType if ty is the type of a function (or pointer to one), NULL otherwise.
+const ast::FunctionType * getFunctionType( const ast::Type * ty );
+
+/// Returns true iff types are structurally identical, where TypeInstType's match any type.
+bool typesPolyCompatible( ast::Type const * lhs, ast::Type const * rhs );
+
+/// Returns true if arg requires boxing given typeVars.
+bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TypeVarMap & typeVars, const ast::TypeSubstitution * subst );
+
+/// Returns true if arg requires boxing in the call to appExpr.
+bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * expr, const ast::TypeSubstitution * subst );
+
+/// Adds the type variable `type` to `typeVars`.
+void addToTypeVarMap( const ast::TypeDecl * type, TypeVarMap & typeVars );
+void addToTypeVarMap( const ast::TypeInstType * type, TypeVarMap & typeVars );
+
+/// Adds the declarations in the forall list of type (and its pointed-to type if it's a pointer type) to `typeVars`.
+void makeTypeVarMap( const ast::Type * type, TypeVarMap & typeVars );
+void makeTypeVarMap( const ast::FunctionDecl * decl, TypeVarMap & typeVars );
+
+/// Gets the name of the sizeof parameter for the type, given its mangled name.
+inline std::string sizeofName( const std::string &name ) { return std::string( "_sizeof_" ) + name; }
+
+/// Gets the name of the alignof parameter for the type, given its mangled name.
+inline std::string alignofName( const std::string &name ) { return std::string( "_alignof_" ) + name; }
+
+/// Gets the name of the offsetof parameter for the type, given its mangled name.
+inline std::string offsetofName( const std::string &name ) { return std::string( "_offsetof_" ) + name; }
+
+/// Gets the name of the layout function for a given aggregate type, given its declaration.
+inline std::string layoutofName( ast::AggregateDecl const * decl ) {
+	return std::string( "_layoutof_" ) + decl->name;
+}
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/InstantiateGeneric.cpp
===================================================================
--- src/GenPoly/InstantiateGeneric.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/GenPoly/InstantiateGeneric.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,5 +14,5 @@
 //
 
-#include "InstantiateGeneric.h"
+#include "InstantiateGeneric.hpp"
 
 #include <cassert>                     // for assertf, assert
@@ -27,11 +27,11 @@
 #include "AST/TranslationUnit.hpp"     // for TranslationUnit
 #include "AST/Vector.hpp"              // for vector
-#include "CodeGen/OperatorTable.h"     // for isAssignment
-#include "Common/ScopedMap.h"          // for ScopedMap
-#include "Common/UniqueName.h"         // for UniqueName
-#include "GenPoly/GenPoly.h"           // for isPolyType, typesPolyCompatible
+#include "CodeGen/OperatorTable.hpp"   // for isAssignment
+#include "Common/ScopedMap.hpp"        // for ScopedMap
+#include "Common/UniqueName.hpp"       // for UniqueName
+#include "GenPoly/GenPoly.hpp"         // for isPolyType, typesPolyCompatible
 #include "GenPoly/ScrubTypeVars.hpp"   // for scrubAllTypeVars
 #include "ResolvExpr/AdjustExprType.hpp"  // for adjustExprType
-#include "ResolvExpr/Unify.h"          // for typesCompatible
+#include "ResolvExpr/Unify.hpp"        // for typesCompatible
 
 namespace GenPoly {
Index: src/GenPoly/InstantiateGeneric.h
===================================================================
--- src/GenPoly/InstantiateGeneric.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,36 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// InstantiateGeneric.h -- Create concrete instances of generic types.
-//
-// Author           : Aaron B. Moss
-// Created On       : Thu Aug 04 18:33:00 2016
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:22:42 2017
-// Update Count     : 2
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace GenPoly {
-
-void instantiateGeneric( ast::TranslationUnit & translationUnit );
-/// Replaces all generic types that have static layout with concrete
-/// instantiations. Sized types are replaced with the concrete argument types
-/// while unsized types are erased to a void type.
-/// This pass can cause designators to ignore the pretty print option.
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/InstantiateGeneric.hpp
===================================================================
--- src/GenPoly/InstantiateGeneric.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/InstantiateGeneric.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,36 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// InstantiateGeneric.hpp -- Create concrete instances of generic types.
+//
+// Author           : Aaron B. Moss
+// Created On       : Thu Aug 04 18:33:00 2016
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Jul 22 09:22:42 2017
+// Update Count     : 2
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace GenPoly {
+
+void instantiateGeneric( ast::TranslationUnit & translationUnit );
+/// Replaces all generic types that have static layout with concrete
+/// instantiations. Sized types are replaced with the concrete argument types
+/// while unsized types are erased to a void type.
+/// This pass can cause designators to ignore the pretty print option.
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/Lvalue.cpp
===================================================================
--- src/GenPoly/Lvalue.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/GenPoly/Lvalue.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,5 +14,5 @@
 //
 
-#include "Lvalue.h"
+#include "Lvalue.hpp"
 
 #include <set>
@@ -24,10 +24,10 @@
 #include "AST/LinkageSpec.hpp"         // for Linkage
 #include "AST/Pass.hpp"
-#include "Common/SemanticError.h"      // for SemanticWarning
+#include "Common/SemanticError.hpp"    // for SemanticWarning
 #include "Common/ToString.hpp"         // for toCString
-#include "Common/UniqueName.h"         // for UniqueName
-#include "GenPoly/GenPoly.h"           // for genFunctionType
-#include "ResolvExpr/typeops.h"        // for typesCompatible
-#include "ResolvExpr/Unify.h"          // for unify
+#include "Common/UniqueName.hpp"       // for UniqueName
+#include "GenPoly/GenPoly.hpp"         // for genFunctionType
+#include "ResolvExpr/Typeops.hpp"      // for typesCompatible
+#include "ResolvExpr/Unify.hpp"        // for unify
 
 #if 0
Index: src/GenPoly/Lvalue.h
===================================================================
--- src/GenPoly/Lvalue.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,40 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Lvalue.h -- Clean up lvalues and remove references.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Thu Sep 15 14:13:00 2022
-// Update Count     : 3
-//
-
-#pragma once
-
-namespace ast {
-	class Expr;
-	class TranslationUnit;
-}
-
-namespace GenPoly {
-
-/// Replaces return type of `T&` with `T*`, along with appropriate address-of and dereference operators.
-void convertLvalue( ast::TranslationUnit & translationUnit );
-
-/// Returns true until reference types have been eliminated from the source code. After this point, reference types should not be added to the AST.
-bool referencesPermissable();
-
-/// Applies transformations that allow GCC to accept more complicated lvalue expressions, e.g. &(a, b).
-ast::Expr const * generalizedLvalue( ast::Expr const * expr );
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/Lvalue.hpp
===================================================================
--- src/GenPoly/Lvalue.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/Lvalue.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -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.
+//
+// Lvalue.h -- Clean up lvalues and remove references.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Thu Sep 15 14:13:00 2022
+// Update Count     : 3
+//
+
+#pragma once
+
+namespace ast {
+	class Expr;
+	class TranslationUnit;
+}
+
+namespace GenPoly {
+
+/// Replaces return type of `T&` with `T*`, along with appropriate address-of and dereference operators.
+void convertLvalue( ast::TranslationUnit & translationUnit );
+
+/// Returns true until reference types have been eliminated from the source code. After this point, reference types should not be added to the AST.
+bool referencesPermissable();
+
+/// Applies transformations that allow GCC to accept more complicated lvalue expressions, e.g. &(a, b).
+ast::Expr const * generalizedLvalue( ast::Expr const * expr );
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/Lvalue2.cc
===================================================================
--- src/GenPoly/Lvalue2.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,25 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Lvalue2.cc -- Seperate Lvalue module for linking.
-//
-// Author           : Andrew Beach
-// Created On       : Mon May 16 14:05:00 2022
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon May 16 14:05:00 2022
-// Update Count     : 0
-//
-
-namespace GenPoly {
-
-bool referencesEliminated = false;
-
-/// Are reference types still allowed in the AST?
-bool referencesPermissable() {
-	return !referencesEliminated;
-}
-
-}
Index: src/GenPoly/Lvalue2.cpp
===================================================================
--- src/GenPoly/Lvalue2.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/Lvalue2.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,25 @@
+//
+// 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.
+//
+// Lvalue2.cc -- Seperate Lvalue module for linking.
+//
+// Author           : Andrew Beach
+// Created On       : Mon May 16 14:05:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon May 16 14:05:00 2022
+// Update Count     : 0
+//
+
+namespace GenPoly {
+
+bool referencesEliminated = false;
+
+/// Are reference types still allowed in the AST?
+bool referencesPermissable() {
+	return !referencesEliminated;
+}
+
+}
Index: src/GenPoly/ScopedSet.h
===================================================================
--- src/GenPoly/ScopedSet.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,265 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ScopedSet.h -- A set that supports save/restore scoping.
-//
-// Author           : Aaron B. Moss
-// Created On       : Thu Dec 3 11:51:00 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:22:17 2017
-// Update Count     : 2
-//
-
-#pragma once
-
-#include <iterator>
-#include <set>
-#include <vector>
-
-namespace GenPoly {
-
-/// A set where the items are placed into nested scopes;
-/// inserted items are placed into the innermost scope, lookup looks from the innermost scope outward
-template<typename Value>
-class ScopedSet {
-	typedef std::set< Value > Scope;
-	typedef std::vector< Scope > ScopeList;
-
-	/// Scoped list of sets.
-	ScopeList scopes;
-public:
-	typedef typename Scope::key_type key_type;
-	typedef typename Scope::value_type value_type;
-	typedef typename ScopeList::size_type size_type;
-	typedef typename ScopeList::difference_type difference_type;
-	typedef typename Scope::reference reference;
-	typedef typename Scope::const_reference const_reference;
-	typedef typename Scope::pointer pointer;
-	typedef typename Scope::const_pointer const_pointer;
-
-	// Both iterator types are complete bidirectional iterators, see below.
-	class iterator;
-	class const_iterator;
-
-	/// Starts a new scope
-	void beginScope() {
-		scopes.emplace_back();
-	}
-
-	/// Ends a scope; invalidates any iterators pointing to elements of that scope
-	void endScope() {
-		scopes.pop_back();
-	}
-
-	/// Default constructor initializes with one scope
-	ScopedSet() { beginScope(); }
-
-	iterator begin() { return iterator(scopes, scopes.back().begin(), scopes.size()-1).next_valid(); }
-	const_iterator begin() const { return const_iterator(scopes, scopes.back().begin(), scopes.size()-1).next_valid(); }
-	const_iterator cbegin() const { return const_iterator(scopes, scopes.back().begin(), scopes.size()-1).next_valid(); }
-	iterator end() { return iterator(scopes, scopes[0].end(), 0); }
-	const_iterator end() const { return const_iterator(scopes, scopes[0].end(), 0); }
-	const_iterator cend() const { return const_iterator(scopes, scopes[0].end(), 0); }
-
-	/// Gets the index of the current scope (counted from 1)
-	size_type currentScope() const { return scopes.size(); }
-
-	/// Finds the given key in the outermost scope it occurs; returns end() for none such
-	iterator find( const Value &key ) {
-		for ( size_type i = scopes.size() - 1; ; --i ) {
-			typename Scope::iterator val = scopes[i].find( key );
-			if ( val != scopes[i].end() ) return iterator( scopes, val, i );
-			if ( i == 0 ) break;
-		}
-		return end();
-	}
-	const_iterator find( const Value &key ) const {
-		return const_iterator( const_cast< ScopedSet< Value >* >(this)->find( key ) );
-	}
-
-	/// Finds the given key in the outermost scope inside the given scope where it occurs
-	iterator findNext( const_iterator &it, const Value &key ) {
-		if ( it.i == 0 ) return end();
-		for ( size_type i = it.i - 1; ; --i ) {
-			typename Scope::iterator val = scopes[i].find( key );
-			if ( val != scopes[i].end() ) return iterator( scopes, val, i );
-			if ( i == 0 ) break;
-		}
-		return end();
-	}
-	const_iterator findNext( const_iterator &it, const Value &key ) const {
-		return const_iterator( const_cast< ScopedSet< Value >* >(this)->findNext( it, key ) );
-	}
-
-	/// Inserts the given value into the outermost scope
-	std::pair< iterator, bool > insert( const value_type &value ) {
-		std::pair< typename Scope::iterator, bool > res = scopes.back().insert( value );
-		return std::make_pair( iterator(scopes, res.first, scopes.size()-1), res.second );
-	}
-
-	bool contains( const Value & key ) const {
-		return find( key ) != cend();
-	}
-};
-
-template<typename Value>
-class ScopedSet<Value>::iterator :
-		public std::iterator< std::bidirectional_iterator_tag, value_type > {
-	friend class ScopedSet;
-	friend class const_iterator;
-	typedef typename Scope::iterator wrapped_iterator;
-	typedef typename ScopeList::size_type size_type;
-
-	/// Checks if this iterator points to a valid item
-	bool is_valid() const {
-		return it != (*scopes)[i].end();
-	}
-
-	/// Increments on invalid
-	iterator& next_valid() {
-		if ( ! is_valid() ) { ++(*this); }
-		return *this;
-	}
-
-	/// Decrements on invalid
-	iterator& prev_valid() {
-		if ( ! is_valid() ) { --(*this); }
-		return *this;
-	}
-
-	iterator(ScopeList const &_scopes, const wrapped_iterator &_it, size_type _i)
-		: scopes(&_scopes), it(_it), i(_i) {}
-public:
-	iterator(const iterator &that) : scopes(that.scopes), it(that.it), i(that.i) {}
-	iterator& operator= (const iterator &that) {
-		scopes = that.scopes; i = that.i; it = that.it;
-		return *this;
-	}
-
-	reference operator* () { return *it; }
-	pointer operator-> () { return it.operator->(); }
-
-	iterator& operator++ () {
-		if ( it == (*scopes)[i].end() ) {
-			if ( i == 0 ) return *this;
-			--i;
-			it = (*scopes)[i].begin();
-		} else {
-			++it;
-		}
-		return next_valid();
-	}
-	iterator operator++ (int) { iterator tmp = *this; ++(*this); return tmp; }
-
-	iterator& operator-- () {
-		// may fail if this is the begin iterator; allowed by STL spec
-		if ( it == (*scopes)[i].begin() ) {
-			++i;
-			it = (*scopes)[i].end();
-		}
-		--it;
-		return prev_valid();
-	}
-	iterator operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
-
-	bool operator== (const iterator &that) {
-		return scopes == that.scopes && i == that.i && it == that.it;
-	}
-	bool operator!= (const iterator &that) { return !( *this == that ); }
-
-	size_type get_level() const { return i; }
-
-private:
-	ScopeList const *scopes;
-	wrapped_iterator it;
-	size_type i;
-};
-
-template<typename Value>
-class ScopedSet<Value>::const_iterator :
-		public std::iterator< std::bidirectional_iterator_tag, value_type > {
-	friend class ScopedSet;
-	typedef typename Scope::iterator wrapped_iterator;
-	typedef typename Scope::const_iterator wrapped_const_iterator;
-	typedef typename ScopeList::size_type size_type;
-
-	/// Checks if this iterator points to a valid item
-	bool is_valid() const {
-		return it != (*scopes)[i].end();
-	}
-
-	/// Increments on invalid
-	const_iterator& next_valid() {
-		if ( ! is_valid() ) { ++(*this); }
-		return *this;
-	}
-
-	/// Decrements on invalid
-	const_iterator& prev_valid() {
-		if ( ! is_valid() ) { --(*this); }
-		return *this;
-	}
-
-	const_iterator(ScopeList const &_scopes, const wrapped_const_iterator &_it, size_type _i)
-		: scopes(&_scopes), it(_it), i(_i) {}
-public:
-	const_iterator(const iterator &that) : scopes(that.scopes), it(that.it), i(that.i) {}
-	const_iterator(const const_iterator &that) : scopes(that.scopes), it(that.it), i(that.i) {}
-	const_iterator& operator= (const iterator &that) {
-		scopes = that.scopes; i = that.i; it = that.it;
-		return *this;
-	}
-	const_iterator& operator= (const const_iterator &that) {
-		scopes = that.scopes; i = that.i; it = that.it;
-		return *this;
-	}
-
-	const_reference operator* () { return *it; }
-	const_pointer operator-> () { return it.operator->(); }
-
-	const_iterator& operator++ () {
-		if ( it == (*scopes)[i].end() ) {
-			if ( i == 0 ) return *this;
-			--i;
-			it = (*scopes)[i].begin();
-		} else {
-			++it;
-		}
-		return next_valid();
-	}
-	const_iterator operator++ (int) { const_iterator tmp = *this; ++(*this); return tmp; }
-
-	const_iterator& operator-- () {
-		// may fail if this is the begin iterator; allowed by STL spec
-		if ( it == (*scopes)[i].begin() ) {
-			++i;
-			it = (*scopes)[i].end();
-		}
-		--it;
-		return prev_valid();
-	}
-	const_iterator operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
-
-	bool operator== (const const_iterator &that) {
-		return scopes == that.scopes && i == that.i && it == that.it;
-	}
-	bool operator!= (const const_iterator &that) { return !( *this == that ); }
-
-	size_type get_level() const { return i; }
-
-private:
-	ScopeList const *scopes;
-	wrapped_const_iterator it;
-	size_type i;
-};
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/ScopedSet.hpp
===================================================================
--- src/GenPoly/ScopedSet.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/ScopedSet.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,265 @@
+//
+// 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.
+//
+// ScopedSet.h -- A set that supports save/restore scoping.
+//
+// Author           : Aaron B. Moss
+// Created On       : Thu Dec 3 11:51:00 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Jul 22 09:22:17 2017
+// Update Count     : 2
+//
+
+#pragma once
+
+#include <iterator>
+#include <set>
+#include <vector>
+
+namespace GenPoly {
+
+/// A set where the items are placed into nested scopes;
+/// inserted items are placed into the innermost scope, lookup looks from the innermost scope outward
+template<typename Value>
+class ScopedSet {
+	typedef std::set< Value > Scope;
+	typedef std::vector< Scope > ScopeList;
+
+	/// Scoped list of sets.
+	ScopeList scopes;
+public:
+	typedef typename Scope::key_type key_type;
+	typedef typename Scope::value_type value_type;
+	typedef typename ScopeList::size_type size_type;
+	typedef typename ScopeList::difference_type difference_type;
+	typedef typename Scope::reference reference;
+	typedef typename Scope::const_reference const_reference;
+	typedef typename Scope::pointer pointer;
+	typedef typename Scope::const_pointer const_pointer;
+
+	// Both iterator types are complete bidirectional iterators, see below.
+	class iterator;
+	class const_iterator;
+
+	/// Starts a new scope
+	void beginScope() {
+		scopes.emplace_back();
+	}
+
+	/// Ends a scope; invalidates any iterators pointing to elements of that scope
+	void endScope() {
+		scopes.pop_back();
+	}
+
+	/// Default constructor initializes with one scope
+	ScopedSet() { beginScope(); }
+
+	iterator begin() { return iterator(scopes, scopes.back().begin(), scopes.size()-1).next_valid(); }
+	const_iterator begin() const { return const_iterator(scopes, scopes.back().begin(), scopes.size()-1).next_valid(); }
+	const_iterator cbegin() const { return const_iterator(scopes, scopes.back().begin(), scopes.size()-1).next_valid(); }
+	iterator end() { return iterator(scopes, scopes[0].end(), 0); }
+	const_iterator end() const { return const_iterator(scopes, scopes[0].end(), 0); }
+	const_iterator cend() const { return const_iterator(scopes, scopes[0].end(), 0); }
+
+	/// Gets the index of the current scope (counted from 1)
+	size_type currentScope() const { return scopes.size(); }
+
+	/// Finds the given key in the outermost scope it occurs; returns end() for none such
+	iterator find( const Value &key ) {
+		for ( size_type i = scopes.size() - 1; ; --i ) {
+			typename Scope::iterator val = scopes[i].find( key );
+			if ( val != scopes[i].end() ) return iterator( scopes, val, i );
+			if ( i == 0 ) break;
+		}
+		return end();
+	}
+	const_iterator find( const Value &key ) const {
+		return const_iterator( const_cast< ScopedSet< Value >* >(this)->find( key ) );
+	}
+
+	/// Finds the given key in the outermost scope inside the given scope where it occurs
+	iterator findNext( const_iterator &it, const Value &key ) {
+		if ( it.i == 0 ) return end();
+		for ( size_type i = it.i - 1; ; --i ) {
+			typename Scope::iterator val = scopes[i].find( key );
+			if ( val != scopes[i].end() ) return iterator( scopes, val, i );
+			if ( i == 0 ) break;
+		}
+		return end();
+	}
+	const_iterator findNext( const_iterator &it, const Value &key ) const {
+		return const_iterator( const_cast< ScopedSet< Value >* >(this)->findNext( it, key ) );
+	}
+
+	/// Inserts the given value into the outermost scope
+	std::pair< iterator, bool > insert( const value_type &value ) {
+		std::pair< typename Scope::iterator, bool > res = scopes.back().insert( value );
+		return std::make_pair( iterator(scopes, res.first, scopes.size()-1), res.second );
+	}
+
+	bool contains( const Value & key ) const {
+		return find( key ) != cend();
+	}
+};
+
+template<typename Value>
+class ScopedSet<Value>::iterator :
+		public std::iterator< std::bidirectional_iterator_tag, value_type > {
+	friend class ScopedSet;
+	friend class const_iterator;
+	typedef typename Scope::iterator wrapped_iterator;
+	typedef typename ScopeList::size_type size_type;
+
+	/// Checks if this iterator points to a valid item
+	bool is_valid() const {
+		return it != (*scopes)[i].end();
+	}
+
+	/// Increments on invalid
+	iterator& next_valid() {
+		if ( ! is_valid() ) { ++(*this); }
+		return *this;
+	}
+
+	/// Decrements on invalid
+	iterator& prev_valid() {
+		if ( ! is_valid() ) { --(*this); }
+		return *this;
+	}
+
+	iterator(ScopeList const &_scopes, const wrapped_iterator &_it, size_type _i)
+		: scopes(&_scopes), it(_it), i(_i) {}
+public:
+	iterator(const iterator &that) : scopes(that.scopes), it(that.it), i(that.i) {}
+	iterator& operator= (const iterator &that) {
+		scopes = that.scopes; i = that.i; it = that.it;
+		return *this;
+	}
+
+	reference operator* () { return *it; }
+	pointer operator-> () { return it.operator->(); }
+
+	iterator& operator++ () {
+		if ( it == (*scopes)[i].end() ) {
+			if ( i == 0 ) return *this;
+			--i;
+			it = (*scopes)[i].begin();
+		} else {
+			++it;
+		}
+		return next_valid();
+	}
+	iterator operator++ (int) { iterator tmp = *this; ++(*this); return tmp; }
+
+	iterator& operator-- () {
+		// may fail if this is the begin iterator; allowed by STL spec
+		if ( it == (*scopes)[i].begin() ) {
+			++i;
+			it = (*scopes)[i].end();
+		}
+		--it;
+		return prev_valid();
+	}
+	iterator operator-- (int) { iterator tmp = *this; --(*this); return tmp; }
+
+	bool operator== (const iterator &that) {
+		return scopes == that.scopes && i == that.i && it == that.it;
+	}
+	bool operator!= (const iterator &that) { return !( *this == that ); }
+
+	size_type get_level() const { return i; }
+
+private:
+	ScopeList const *scopes;
+	wrapped_iterator it;
+	size_type i;
+};
+
+template<typename Value>
+class ScopedSet<Value>::const_iterator :
+		public std::iterator< std::bidirectional_iterator_tag, value_type > {
+	friend class ScopedSet;
+	typedef typename Scope::iterator wrapped_iterator;
+	typedef typename Scope::const_iterator wrapped_const_iterator;
+	typedef typename ScopeList::size_type size_type;
+
+	/// Checks if this iterator points to a valid item
+	bool is_valid() const {
+		return it != (*scopes)[i].end();
+	}
+
+	/// Increments on invalid
+	const_iterator& next_valid() {
+		if ( ! is_valid() ) { ++(*this); }
+		return *this;
+	}
+
+	/// Decrements on invalid
+	const_iterator& prev_valid() {
+		if ( ! is_valid() ) { --(*this); }
+		return *this;
+	}
+
+	const_iterator(ScopeList const &_scopes, const wrapped_const_iterator &_it, size_type _i)
+		: scopes(&_scopes), it(_it), i(_i) {}
+public:
+	const_iterator(const iterator &that) : scopes(that.scopes), it(that.it), i(that.i) {}
+	const_iterator(const const_iterator &that) : scopes(that.scopes), it(that.it), i(that.i) {}
+	const_iterator& operator= (const iterator &that) {
+		scopes = that.scopes; i = that.i; it = that.it;
+		return *this;
+	}
+	const_iterator& operator= (const const_iterator &that) {
+		scopes = that.scopes; i = that.i; it = that.it;
+		return *this;
+	}
+
+	const_reference operator* () { return *it; }
+	const_pointer operator-> () { return it.operator->(); }
+
+	const_iterator& operator++ () {
+		if ( it == (*scopes)[i].end() ) {
+			if ( i == 0 ) return *this;
+			--i;
+			it = (*scopes)[i].begin();
+		} else {
+			++it;
+		}
+		return next_valid();
+	}
+	const_iterator operator++ (int) { const_iterator tmp = *this; ++(*this); return tmp; }
+
+	const_iterator& operator-- () {
+		// may fail if this is the begin iterator; allowed by STL spec
+		if ( it == (*scopes)[i].begin() ) {
+			++i;
+			it = (*scopes)[i].end();
+		}
+		--it;
+		return prev_valid();
+	}
+	const_iterator operator-- (int) { const_iterator tmp = *this; --(*this); return tmp; }
+
+	bool operator== (const const_iterator &that) {
+		return scopes == that.scopes && i == that.i && it == that.it;
+	}
+	bool operator!= (const const_iterator &that) { return !( *this == that ); }
+
+	size_type get_level() const { return i; }
+
+private:
+	ScopeList const *scopes;
+	wrapped_const_iterator it;
+	size_type i;
+};
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/ScrubTypeVars.cpp
===================================================================
--- src/GenPoly/ScrubTypeVars.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/GenPoly/ScrubTypeVars.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,10 +16,10 @@
 #include "ScrubTypeVars.hpp"
 
-#include <utility>                      // for pair
+#include <utility>                        // for pair
 
 #include "AST/Pass.hpp"
-#include "GenPoly.h"                    // for mangleType, TyVarMap, alignof...
-#include "GenPoly/ErasableScopedMap.h"  // for ErasableScopedMap<>::const_it...
-#include "SymTab/Mangler.h"             // for mangleType
+#include "GenPoly.hpp"                    // for mangleType, TyVarMap, align...
+#include "GenPoly/ErasableScopedMap.hpp"  // for ErasableScopedMap<>::const_...
+#include "SymTab/Mangler.hpp"             // for mangleType
 
 namespace GenPoly {
Index: src/GenPoly/ScrubTypeVars.hpp
===================================================================
--- src/GenPoly/ScrubTypeVars.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/GenPoly/ScrubTypeVars.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -19,5 +19,5 @@
 
 #include "AST/Fwd.hpp"        // for Node
-#include "GenPoly.h"          // for TypeVarMap
+#include "GenPoly.hpp"        // for TypeVarMap
 
 namespace GenPoly {
Index: src/GenPoly/Specialize.cpp
===================================================================
--- src/GenPoly/Specialize.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/GenPoly/Specialize.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,5 +14,5 @@
 //
 
-#include "Specialize.h"
+#include "Specialize.hpp"
 
 #include "AST/Copy.hpp"                  // for deepCopy
@@ -20,7 +20,7 @@
 #include "AST/Pass.hpp"                  // for Pass
 #include "AST/TypeEnvironment.hpp"       // for OpenVarSet, AssertionSet
-#include "Common/UniqueName.h"           // for UniqueName
-#include "GenPoly/GenPoly.h"             // for getFunctionType
-#include "ResolvExpr/FindOpenVars.h"     // for findOpenVars
+#include "Common/UniqueName.hpp"         // for UniqueName
+#include "GenPoly/GenPoly.hpp"           // for getFunctionType
+#include "ResolvExpr/FindOpenVars.hpp"   // for findOpenVars
 
 namespace GenPoly {
Index: src/GenPoly/Specialize.h
===================================================================
--- src/GenPoly/Specialize.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,32 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Specialize.h -- Generate thunks to specialize polymorphic functions.
-//
-// Author           : Richard C. Bilson
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:22:31 2017
-// Update Count     : 2
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace GenPoly {
-
-void convertSpecializations( ast::TranslationUnit & translationUnit );
-
-} // namespace GenPoly
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/GenPoly/Specialize.hpp
===================================================================
--- src/GenPoly/Specialize.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/GenPoly/Specialize.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,32 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Specialize.h -- Generate thunks to specialize polymorphic functions.
+//
+// Author           : Richard C. Bilson
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Jul 22 09:22:31 2017
+// Update Count     : 2
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace GenPoly {
+
+void convertSpecializations( ast::TranslationUnit & translationUnit );
+
+} // namespace GenPoly
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/GenPoly/module.mk
===================================================================
--- src/GenPoly/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/GenPoly/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,23 +16,23 @@
 
 SRC_GENPOLY = \
-	GenPoly/GenPoly.cc \
-	GenPoly/GenPoly.h \
-	GenPoly/Lvalue2.cc \
-	GenPoly/Lvalue.h
+	GenPoly/GenPoly.cpp \
+	GenPoly/GenPoly.hpp \
+	GenPoly/Lvalue2.cpp \
+	GenPoly/Lvalue.hpp
 
 SRC += $(SRC_GENPOLY) \
 	GenPoly/Box.cpp \
-	GenPoly/Box.h \
-	GenPoly/ErasableScopedMap.h \
-	GenPoly/FindFunction.cc \
-	GenPoly/FindFunction.h \
+	GenPoly/Box.hpp \
+	GenPoly/ErasableScopedMap.hpp \
+	GenPoly/FindFunction.cpp \
+	GenPoly/FindFunction.hpp \
 	GenPoly/InstantiateGeneric.cpp \
-	GenPoly/InstantiateGeneric.h \
+	GenPoly/InstantiateGeneric.hpp \
 	GenPoly/Lvalue.cpp \
-	GenPoly/ScopedSet.h \
+	GenPoly/ScopedSet.hpp \
 	GenPoly/ScrubTypeVars.cpp \
 	GenPoly/ScrubTypeVars.hpp \
 	GenPoly/Specialize.cpp \
-	GenPoly/Specialize.h
+	GenPoly/Specialize.hpp
 
 SRCDEMANGLE += $(SRC_GENPOLY)
Index: src/InitTweak/FixGlobalInit.cc
===================================================================
--- src/InitTweak/FixGlobalInit.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,113 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FixGlobalInit.cc --
-//
-// Author           : Rob Schluntz
-// Created On       : Mon May 04 15:14:56 2016
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Dec 13 23:41:10 2019
-// Update Count     : 19
-//
-
-#include "FixGlobalInit.h"
-
-#include <cassert>                 // for assert
-#include <stddef.h>                // for NULL
-#include <algorithm>               // for replace_if
-
-#include "AST/Expr.hpp"
-#include "AST/Node.hpp"
-#include "AST/Pass.hpp"
-#include "Common/UniqueName.h"     // for UniqueName
-#include "InitTweak.h"             // for isIntrinsicSingleArgCallStmt
-
-namespace InitTweak {
-
-namespace {
-
-class GlobalFixer : public ast::WithShortCircuiting {
-public:
-	void previsit(const ast::ObjectDecl *);
-	void previsit(const ast::FunctionDecl *) { visit_children = false; }
-	void previsit(const ast::StructDecl *) { visit_children = false; }
-	void previsit(const ast::UnionDecl *) { visit_children = false; }
-	void previsit(const ast::EnumDecl *) { visit_children = false; }
-	void previsit(const ast::TraitDecl *) { visit_children = false; }
-	void previsit(const ast::TypeDecl *) { visit_children = false; }
-
-	std::list< ast::ptr<ast::Stmt> > initStmts;
-	std::list< ast::ptr<ast::Stmt> > destroyStmts;
-};
-
-void GlobalFixer::previsit(const ast::ObjectDecl * objDecl) {
-	auto mutDecl = mutate(objDecl);
-	assertf(mutDecl == objDecl, "Global object decl must be unique");
-	auto ctorInit = objDecl->init.as<ast::ConstructorInit>();
-	if ( nullptr == ctorInit ) return;
-
-	// a decision should have been made by the resolver, so ctor and init are not both non-NULL
-	assert( !ctorInit->ctor || !ctorInit->init );
-
-	const ast::Stmt * dtor = ctorInit->dtor;
-	if ( dtor && !isIntrinsicSingleArgCallStmt( dtor ) ) {
-		// don't need to call intrinsic dtor, because it does nothing, but
-		// non-intrinsic dtors must be called
-		destroyStmts.push_front( dtor );
-	} // if
-	if ( const ast::Stmt * ctor = ctorInit->ctor ) {
-		addDataSectionAttribute(mutDecl);
-		initStmts.push_back( ctor );
-		mutDecl->init = nullptr;
-	} else if ( const ast::Init * init = ctorInit->init ) {
-		mutDecl->init = init;
-	} else {
-		// no constructor and no initializer, which is okay
-		mutDecl->init = nullptr;
-	}
-}
-
-} // namespace
-
-void fixGlobalInit(ast::TranslationUnit & translationUnit, bool inLibrary) {
-	ast::Pass<GlobalFixer> fixer;
-	accept_all(translationUnit, fixer);
-
-	// Say these magic declarations come at the end of the file.
-	CodeLocation const & location = translationUnit.decls.back()->location;
-
-	if ( !fixer.core.initStmts.empty() ) {
-		std::vector<ast::ptr<ast::Expr>> ctorParams;
-		if (inLibrary) ctorParams.emplace_back(ast::ConstantExpr::from_int(location, 200));
-		auto initFunction = new ast::FunctionDecl(location,
-			"__global_init__", {}, {}, {}, {},
-			new ast::CompoundStmt(location, std::move(fixer.core.initStmts)),
-			ast::Storage::Static, ast::Linkage::C,
-			{new ast::Attribute("constructor", std::move(ctorParams))});
-
-		translationUnit.decls.emplace_back( initFunction );
-	} // if
-
-	if ( !fixer.core.destroyStmts.empty() ) {
-		std::vector<ast::ptr<ast::Expr>> dtorParams;
-		if (inLibrary) dtorParams.emplace_back(ast::ConstantExpr::from_int(location, 200));
-		auto destroyFunction = new ast::FunctionDecl( location,
-			"__global_destroy__", {}, {}, {}, {},
-			new ast::CompoundStmt(location, std::move(fixer.core.destroyStmts)),
-			ast::Storage::Static, ast::Linkage::C,
-			{new ast::Attribute("destructor", std::move(dtorParams))});
-
-		translationUnit.decls.emplace_back(destroyFunction);
-	} // if
-}
-
-} // namespace InitTweak
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/InitTweak/FixGlobalInit.cpp
===================================================================
--- src/InitTweak/FixGlobalInit.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/InitTweak/FixGlobalInit.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,113 @@
+//
+// 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.
+//
+// FixGlobalInit.cpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Mon May 04 15:14:56 2016
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri Dec 13 23:41:10 2019
+// Update Count     : 19
+//
+
+#include "FixGlobalInit.hpp"
+
+#include <cassert>                 // for assert
+#include <stddef.h>                // for NULL
+#include <algorithm>               // for replace_if
+
+#include "AST/Expr.hpp"
+#include "AST/Node.hpp"
+#include "AST/Pass.hpp"
+#include "Common/UniqueName.hpp"   // for UniqueName
+#include "InitTweak.hpp"           // for isIntrinsicSingleArgCallStmt
+
+namespace InitTweak {
+
+namespace {
+
+class GlobalFixer : public ast::WithShortCircuiting {
+public:
+	void previsit(const ast::ObjectDecl *);
+	void previsit(const ast::FunctionDecl *) { visit_children = false; }
+	void previsit(const ast::StructDecl *) { visit_children = false; }
+	void previsit(const ast::UnionDecl *) { visit_children = false; }
+	void previsit(const ast::EnumDecl *) { visit_children = false; }
+	void previsit(const ast::TraitDecl *) { visit_children = false; }
+	void previsit(const ast::TypeDecl *) { visit_children = false; }
+
+	std::list< ast::ptr<ast::Stmt> > initStmts;
+	std::list< ast::ptr<ast::Stmt> > destroyStmts;
+};
+
+void GlobalFixer::previsit(const ast::ObjectDecl * objDecl) {
+	auto mutDecl = mutate(objDecl);
+	assertf(mutDecl == objDecl, "Global object decl must be unique");
+	auto ctorInit = objDecl->init.as<ast::ConstructorInit>();
+	if ( nullptr == ctorInit ) return;
+
+	// a decision should have been made by the resolver, so ctor and init are not both non-NULL
+	assert( !ctorInit->ctor || !ctorInit->init );
+
+	const ast::Stmt * dtor = ctorInit->dtor;
+	if ( dtor && !isIntrinsicSingleArgCallStmt( dtor ) ) {
+		// don't need to call intrinsic dtor, because it does nothing, but
+		// non-intrinsic dtors must be called
+		destroyStmts.push_front( dtor );
+	} // if
+	if ( const ast::Stmt * ctor = ctorInit->ctor ) {
+		addDataSectionAttribute(mutDecl);
+		initStmts.push_back( ctor );
+		mutDecl->init = nullptr;
+	} else if ( const ast::Init * init = ctorInit->init ) {
+		mutDecl->init = init;
+	} else {
+		// no constructor and no initializer, which is okay
+		mutDecl->init = nullptr;
+	}
+}
+
+} // namespace
+
+void fixGlobalInit(ast::TranslationUnit & translationUnit, bool inLibrary) {
+	ast::Pass<GlobalFixer> fixer;
+	accept_all(translationUnit, fixer);
+
+	// Say these magic declarations come at the end of the file.
+	CodeLocation const & location = translationUnit.decls.back()->location;
+
+	if ( !fixer.core.initStmts.empty() ) {
+		std::vector<ast::ptr<ast::Expr>> ctorParams;
+		if (inLibrary) ctorParams.emplace_back(ast::ConstantExpr::from_int(location, 200));
+		auto initFunction = new ast::FunctionDecl(location,
+			"__global_init__", {}, {}, {}, {},
+			new ast::CompoundStmt(location, std::move(fixer.core.initStmts)),
+			ast::Storage::Static, ast::Linkage::C,
+			{new ast::Attribute("constructor", std::move(ctorParams))});
+
+		translationUnit.decls.emplace_back( initFunction );
+	} // if
+
+	if ( !fixer.core.destroyStmts.empty() ) {
+		std::vector<ast::ptr<ast::Expr>> dtorParams;
+		if (inLibrary) dtorParams.emplace_back(ast::ConstantExpr::from_int(location, 200));
+		auto destroyFunction = new ast::FunctionDecl( location,
+			"__global_destroy__", {}, {}, {}, {},
+			new ast::CompoundStmt(location, std::move(fixer.core.destroyStmts)),
+			ast::Storage::Static, ast::Linkage::C,
+			{new ast::Attribute("destructor", std::move(dtorParams))});
+
+		translationUnit.decls.emplace_back(destroyFunction);
+	} // if
+}
+
+} // namespace InitTweak
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/InitTweak/FixGlobalInit.h
===================================================================
--- src/InitTweak/FixGlobalInit.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,35 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FixGlobalInit.h --
-//
-// Author           : Rob Schluntz
-// Created On       : Mon May 04 15:14:56 2016
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:30:54 2017
-// Update Count     : 3
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace InitTweak {
-
-/// Moves global initialization into an _init function that is unique to the translation unit.
-/// Sets the priority of the initialization function depending on whether the initialization
-/// function is for library code.
-void fixGlobalInit( ast::TranslationUnit & translationUnit, bool inLibrary );
-
-} // namespace
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/InitTweak/FixGlobalInit.hpp
===================================================================
--- src/InitTweak/FixGlobalInit.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/InitTweak/FixGlobalInit.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -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.
+//
+// FixGlobalInit.hpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Mon May 04 15:14:56 2016
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Jul 22 09:30:54 2017
+// Update Count     : 3
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace InitTweak {
+
+/// Moves global initialization into an _init function that is unique to the translation unit.
+/// Sets the priority of the initialization function depending on whether the initialization
+/// function is for library code.
+void fixGlobalInit( ast::TranslationUnit & translationUnit, bool inLibrary );
+
+} // namespace
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/InitTweak/FixInit.cpp
===================================================================
--- src/InitTweak/FixInit.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/InitTweak/FixInit.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -1,3 +1,3 @@
-#include "FixInit.h"
+#include "FixInit.hpp"
 
 #include <stddef.h>                    // for NULL
@@ -22,13 +22,13 @@
 #include "AST/SymbolTable.hpp"
 #include "AST/Type.hpp"
-#include "CodeGen/OperatorTable.h"     // for isConstructor, isCtorDtor, isD...
-#include "Common/SemanticError.h"      // for SemanticError
+#include "CodeGen/OperatorTable.hpp"   // for isConstructor, isCtorDtor, isD...
+#include "Common/SemanticError.hpp"    // for SemanticError
 #include "Common/ToString.hpp"         // for toCString
-#include "Common/UniqueName.h"         // for UniqueName
-#include "FixGlobalInit.h"             // for fixGlobalInit
-#include "GenInit.h"                   // for genCtorDtor
-#include "GenPoly/GenPoly.h"           // for getFunctionType
-#include "ResolvExpr/Resolver.h"       // for findVoidExpression
-#include "ResolvExpr/Unify.h"          // for typesCompatible
+#include "Common/UniqueName.hpp"       // for UniqueName
+#include "FixGlobalInit.hpp"           // for fixGlobalInit
+#include "GenInit.hpp"                 // for genCtorDtor
+#include "GenPoly/GenPoly.hpp"         // for getFunctionType
+#include "ResolvExpr/Resolver.hpp"     // for findVoidExpression
+#include "ResolvExpr/Unify.hpp"        // for typesCompatible
 #include "SymTab/GenImplicitCall.hpp"  // for genImplicitCall
 
Index: src/InitTweak/FixInit.h
===================================================================
--- src/InitTweak/FixInit.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,33 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FixInit.h --
-//
-// Author           : Rob Schluntz
-// Created On       : Wed Jan 13 16:29:30 2016
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sun Feb 16 07:54:50 2020
-// Update Count     : 8
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace InitTweak {
-
-/// Replace constructor initializers with expression statements and unwrap basic C-style initializers.
-void fix( ast::TranslationUnit & translationUnit, bool inLibrary);
-
-} // namespace
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/InitTweak/FixInit.hpp
===================================================================
--- src/InitTweak/FixInit.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/InitTweak/FixInit.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -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.
+//
+// FixInit.hpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Wed Jan 13 16:29:30 2016
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sun Feb 16 07:54:50 2020
+// Update Count     : 8
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace InitTweak {
+
+/// Replace constructor initializers with expression statements and unwrap basic C-style initializers.
+void fix( ast::TranslationUnit & translationUnit, bool inLibrary);
+
+} // namespace
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/InitTweak/GenInit.cc
===================================================================
--- src/InitTweak/GenInit.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,368 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// GenInit.cc -- Generate initializers, and other stuff.
-//
-// Author           : Rob Schluntz
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Oct 25 13:53:00 2021
-// Update Count     : 186
-//
-#include "GenInit.h"
-
-#include <stddef.h>                    // for NULL
-#include <algorithm>                   // for any_of
-#include <cassert>                     // for assert, strict_dynamic_cast, assertf
-#include <deque>
-#include <iterator>                    // for back_inserter, inserter, back_inse...
-#include <list>                        // for _List_iterator, list
-
-#include "AST/Decl.hpp"
-#include "AST/Init.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Node.hpp"
-#include "AST/Stmt.hpp"
-#include "CompilationState.hpp"
-#include "CodeGen/OperatorTable.h"
-#include "Common/SemanticError.h"      // for SemanticError
-#include "Common/ToString.hpp"         // for toCString
-#include "Common/UniqueName.h"         // for UniqueName
-#include "Common/utility.h"            // for ValueGuard, maybeClone
-#include "GenPoly/GenPoly.h"           // for getFunctionType, isPolyType
-#include "GenPoly/ScopedSet.h"         // for ScopedSet, ScopedSet<>::const_iter...
-#include "InitTweak.h"                 // for isConstExpr, InitExpander, checkIn...
-#include "ResolvExpr/Resolver.h"
-#include "SymTab/GenImplicitCall.hpp"  // for genImplicitCall
-#include "SymTab/Mangler.h"            // for Mangler
-#include "Tuples/Tuples.h"             // for maybeImpure
-#include "Validate/FindSpecialDecls.h" // for SizeType
-
-namespace InitTweak {
-
-namespace {
-
-	// Outer pass finds declarations, for their type could wrap a type that needs hoisting
-	struct HoistArrayDimension_NoResolve final :
-			public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting,
-			public ast::WithGuards, public ast::WithConstTranslationUnit,
-			public ast::WithVisitorRef<HoistArrayDimension_NoResolve>,
-			public ast::WithSymbolTableX<ast::SymbolTable::ErrorDetection::IgnoreErrors> {
-
-		// Inner pass looks within a type, for a part that depends on an expression
-		struct HoistDimsFromTypes final :
-				public ast::WithShortCircuiting, public ast::WithGuards {
-
-			HoistArrayDimension_NoResolve * outer;
-			HoistDimsFromTypes( HoistArrayDimension_NoResolve * outer ) : outer(outer) {}
-
-			// Only intended for visiting through types.
-			// Tolerate, and short-circuit at, the dimension expression of an array type.
-			//    (We'll operate on the dimension expression of an array type directly
-			//    from the parent type, not by visiting through it)
-			// Look inside type exprs.
-			void previsit( const ast::Node * ) {
-				assert( false && "unsupported node type" );
-			};
-			const ast::Expr * allowedExpr = nullptr;
-			void previsit( const ast::Type * ) {
-				GuardValue( allowedExpr ) = nullptr;
-			}
-			void previsit( const ast::ArrayType * t ) {
-				GuardValue( allowedExpr ) = t->dimension.get();
-			}
-			void previsit( const ast::PointerType * t ) {
-				GuardValue( allowedExpr ) = t->dimension.get();
-			}
-			void previsit( const ast::TypeofType * t ) {
-				GuardValue( allowedExpr ) = t->expr.get();
-			}
-			void previsit( const ast::Expr * e ) {
-				assert( e == allowedExpr &&
-				    "only expecting to visit exprs that are dimension exprs or typeof(-) inner exprs" );
-
-				// Skip the tolerated expressions
-				visit_children = false;
-			}
-			void previsit( const ast::TypeExpr * ) {}
-
-			const ast::Type * postvisit(
-					const ast::ArrayType * arrayType ) {
-				static UniqueName dimensionName( "_array_dim" );
-
-				if ( nullptr == arrayType->dimension ) {  // if no dimension is given, don't presume to invent one
-					return arrayType;
-				}
-
-				// find size_t; use it as the type for a dim expr
-				ast::ptr<ast::Type> dimType = outer->transUnit().global.sizeType;
-				assert( dimType );
-				add_qualifiers( dimType, ast::CV::Qualifiers( ast::CV::Const ) );
-
-				// Special-case handling: leave the user's dimension expression alone
-				// - requires the user to have followed a careful convention
-				// - may apply to extremely simple applications, but only as windfall
-				// - users of advanced applications will be following the convention on purpose
-				// - CFA maintainers must protect the criteria against leaving too much alone
-
-				// Actual leave-alone cases following are conservative approximations of "cannot vary"
-
-				// Leave alone: literals and enum constants
-				if ( dynamic_cast< const ast::ConstantExpr * >( arrayType->dimension.get() ) ) {
-					return arrayType;
-				}
-
-				// Leave alone: direct use of an object declared to be const
-				const ast::NameExpr * dimn = dynamic_cast< const ast::NameExpr * >( arrayType->dimension.get() );
-				if ( dimn ) {
-					std::vector<ast::SymbolTable::IdData> dimnDefs = outer->symtab.lookupId( dimn->name );
-					if ( dimnDefs.size() == 1 ) {
-						const ast::DeclWithType * dimnDef = dimnDefs[0].id.get();
-						assert( dimnDef && "symbol table binds a name to nothing" );
-						const ast::ObjectDecl * dimOb = dynamic_cast< const ast::ObjectDecl * >( dimnDef );
-						if( dimOb ) {
-							const ast::Type * dimTy = dimOb->type.get();
-							assert( dimTy && "object declaration bearing no type" );
-							// must not hoist some: size_t
-							// must hoist all: pointers and references
-							// the analysis is conservative; BasicType is a simple approximation
-							if ( dynamic_cast< const ast::BasicType * >( dimTy ) ||
-							     dynamic_cast< const ast::SueInstType<ast::EnumDecl> * >( dimTy ) ) {
-								if ( dimTy->is_const() ) {
-									// The dimension is certainly re-evaluable, giving the same answer each time.
-									// Our user might be hoping to write the array type in multiple places, having them unify.
-									// Leave the type alone.
-
-									// We believe the new criterion leaves less alone than the old criterion.
-									// Thus, the old criterion should have left the current case alone.
-									// Catch cases that weren't thought through.
-									assert( !Tuples::maybeImpure( arrayType->dimension ) );
-
-									return arrayType;
-								}
-							};
-						}
-					}
-				}
-
-				// Leave alone: any sizeof expression (answer cannot vary during current lexical scope)
-				const ast::SizeofExpr * sz = dynamic_cast< const ast::SizeofExpr * >( arrayType->dimension.get() );
-				if ( sz ) {
-					return arrayType;
-				}
-
-				// General-case handling: change the array-type's dim expr (hoist the user-given content out of the type)
-				// - always safe
-				// - user-unnoticeable in common applications (benign noise in -CFA output)
-				// - may annoy a responsible user of advanced applications (but they can work around)
-				// - protects against misusing advanced features
-				//
-				// The hoist, by example, is:
-				// FROM USER:  float a[ rand() ];
-				// TO GCC:     const size_t __len_of_a = rand(); float a[ __len_of_a ];
-
-				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 );
-				outer->declsToAddBefore.push_back( arrayDimension );
-
-				return mutType;
-			}  // postvisit( const ast::ArrayType * )
-		}; // struct HoistDimsFromTypes
-
-		ast::Storage::Classes storageClasses;
-		void previsit(
-				const ast::ObjectDecl * decl ) {
-			GuardValue( storageClasses ) = decl->storage;
-		}
-
-		const ast::DeclWithType * postvisit(
-				const ast::ObjectDecl * objectDecl ) {
-
-			if ( !isInFunction() || storageClasses.is_static ) {
-				return objectDecl;
-			}
-
-			const ast::Type * mid = objectDecl->type;
-
-			ast::Pass<HoistDimsFromTypes> hoist{this};
-			const ast::Type * result = mid->accept( hoist );
-
-			return mutate_field( objectDecl, &ast::ObjectDecl::type, result );
-		}
-	};
-
-	struct ReturnFixer final :
-			public ast::WithStmtsToAdd<>, ast::WithGuards, ast::WithShortCircuiting {
-		void previsit( const ast::FunctionDecl * decl );
-		const ast::ReturnStmt * previsit( const ast::ReturnStmt * stmt );
-	private:
-		const ast::FunctionDecl * funcDecl = nullptr;
-	};
-
-	void ReturnFixer::previsit( const ast::FunctionDecl * decl ) {
-		if (decl->linkage == ast::Linkage::Intrinsic) visit_children = false;
-		GuardValue( funcDecl ) = decl;
-	}
-
-	const ast::ReturnStmt * ReturnFixer::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;
-				}
-				const ast::Stmt * ctorStmt = genCtorDtor(
-					retVal->location, "?{}", retVal, stmt->expr );
-				assertf( ctorStmt,
-					"ReturnFixer: genCtorDtor returned nullptr: %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>::run( transUnit );
-	ast::Pass<ReturnFixer>::run( transUnit );
-}
-
-void fixReturnStatements( ast::TranslationUnit & transUnit ) {
-	ast::Pass<ReturnFixer>::run( transUnit );
-}
-
-bool ManagedTypes::isManaged( const ast::Type * type ) const {
-	// references are never constructed
-	if ( dynamic_cast< const ast::ReferenceType * >( type ) ) return false;
-	if ( auto tupleType = dynamic_cast< const ast::TupleType * > ( type ) ) {
-		// tuple is also managed if any of its components are managed
-		for (auto & component : tupleType->types) {
-			if (isManaged(component)) return true;
-		}
-	}
-	// need to clear and reset qualifiers when determining if a type is managed
-	// ValueGuard< Type::Qualifiers > qualifiers( type->get_qualifiers() );
-	auto tmp = shallowCopy(type);
-	tmp->qualifiers = {};
-	// delete tmp at return
-	ast::ptr<ast::Type> guard = tmp;
-	// a type is managed if it appears in the map of known managed types, or if it contains any polymorphism (is a type variable or generic type containing a type variable)
-	return managedTypes.find( Mangle::mangle( tmp, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) ) != managedTypes.end() || GenPoly::isPolyType( tmp );
-}
-
-bool ManagedTypes::isManaged( const ast::ObjectDecl * objDecl ) const {
-	const ast::Type * type = objDecl->type;
-	while ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) {
-		// must always construct VLAs with an initializer, since this is an error in C
-		if ( at->isVarLen && objDecl->init ) return true;
-		type = at->base;
-	}
-	return isManaged( type );
-}
-
-void ManagedTypes::handleDWT( const ast::DeclWithType * dwt ) {
-	// if this function is a user-defined constructor or destructor, mark down the type as "managed"
-	if ( ! dwt->linkage.is_overrideable && CodeGen::isCtorDtor( dwt->name ) ) {
-		auto & params = GenPoly::getFunctionType( dwt->get_type())->params;
-		assert( ! params.empty() );
-		// Type * type = InitTweak::getPointerBase( params.front() );
-		// assert( type );
-		managedTypes.insert( Mangle::mangle( params.front(), {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );
-	}
-}
-
-void ManagedTypes::handleStruct( const ast::StructDecl * aggregateDecl ) {
-	// don't construct members, but need to take note if there is a managed member,
-	// because that means that this type is also managed
-	for ( auto & member : aggregateDecl->members ) {
-		if ( auto field = member.as<ast::ObjectDecl>() ) {
-			if ( isManaged( field ) ) {
-				// generic parameters should not play a role in determining whether a generic type is constructed - construct all generic types, so that
-				// polymorphic constructors make generic types managed types
-				ast::StructInstType inst( aggregateDecl );
-				managedTypes.insert( Mangle::mangle( &inst, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );
-				break;
-			}
-		}
-	}
-}
-
-void ManagedTypes::beginScope() { managedTypes.beginScope(); }
-void ManagedTypes::endScope() { managedTypes.endScope(); }
-
-const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg ) {
-	assertf(objDecl, "genCtorDtor passed null objDecl");
-	InitExpander srcParam(arg);
-	return SymTab::genImplicitCall(srcParam, new ast::VariableExpr(loc, objDecl), loc, fname, objDecl);
-}
-
-ast::ConstructorInit * genCtorInit( const CodeLocation & loc, const ast::ObjectDecl * objDecl ) {
-	// call into genImplicitCall from Autogen.h to generate calls to ctor/dtor for each
-	// constructable object
-	InitExpander srcParam{ objDecl->init }, nullParam{ (const ast::Init *)nullptr };
-	ast::ptr< ast::Expr > dstParam = new ast::VariableExpr(loc, objDecl);
-
-	ast::ptr< ast::Stmt > ctor = SymTab::genImplicitCall(
-		srcParam, dstParam, loc, "?{}", objDecl );
-	ast::ptr< ast::Stmt > dtor = SymTab::genImplicitCall(
-		nullParam, dstParam, loc, "^?{}", objDecl,
-		SymTab::LoopBackward );
-
-	// check that either both ctor and dtor are present, or neither
-	assert( (bool)ctor == (bool)dtor );
-
-	if ( ctor ) {
-		// need to remember init expression, in case no ctors exist. If ctor does exist, want to
-		// use ctor expression instead of init.
-		ctor.strict_as< ast::ImplicitCtorDtorStmt >();
-		dtor.strict_as< ast::ImplicitCtorDtorStmt >();
-
-		return new ast::ConstructorInit{ loc, ctor, dtor, objDecl->init };
-	}
-
-	return nullptr;
-}
-
-} // namespace InitTweak
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/InitTweak/GenInit.cpp
===================================================================
--- src/InitTweak/GenInit.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/InitTweak/GenInit.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,367 @@
+//
+// 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.
+//
+// GenInit.cpp -- Generate initializers, and other stuff.
+//
+// Author           : Rob Schluntz
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Oct 25 13:53:00 2021
+// Update Count     : 186
+//
+#include "GenInit.hpp"
+
+#include <stddef.h>                    // for NULL
+#include <algorithm>                   // for any_of
+#include <cassert>                     // for assert, strict_dynamic_cast, assertf
+#include <deque>
+#include <iterator>                    // for back_inserter, inserter, back_inse...
+#include <list>                        // for _List_iterator, list
+
+#include "AST/Decl.hpp"
+#include "AST/Init.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Node.hpp"
+#include "AST/Stmt.hpp"
+#include "CompilationState.hpp"
+#include "CodeGen/OperatorTable.hpp"
+#include "Common/SemanticError.hpp"    // for SemanticError
+#include "Common/ToString.hpp"         // for toCString
+#include "Common/UniqueName.hpp"       // for UniqueName
+#include "Common/Utility.hpp"          // for ValueGuard, maybeClone
+#include "GenPoly/GenPoly.hpp"         // for getFunctionType, isPolyType
+#include "GenPoly/ScopedSet.hpp"       // for ScopedSet, ScopedSet<>::const_iter...
+#include "InitTweak.hpp"               // for isConstExpr, InitExpander, checkIn...
+#include "ResolvExpr/Resolver.hpp"
+#include "SymTab/GenImplicitCall.hpp"  // for genImplicitCall
+#include "SymTab/Mangler.hpp"          // for Mangler
+#include "Tuples/Tuples.hpp"           // for maybeImpure
+
+namespace InitTweak {
+
+namespace {
+
+	// Outer pass finds declarations, for their type could wrap a type that needs hoisting
+	struct HoistArrayDimension_NoResolve final :
+			public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting,
+			public ast::WithGuards, public ast::WithConstTranslationUnit,
+			public ast::WithVisitorRef<HoistArrayDimension_NoResolve>,
+			public ast::WithSymbolTableX<ast::SymbolTable::ErrorDetection::IgnoreErrors> {
+
+		// Inner pass looks within a type, for a part that depends on an expression
+		struct HoistDimsFromTypes final :
+				public ast::WithShortCircuiting, public ast::WithGuards {
+
+			HoistArrayDimension_NoResolve * outer;
+			HoistDimsFromTypes( HoistArrayDimension_NoResolve * outer ) : outer(outer) {}
+
+			// Only intended for visiting through types.
+			// Tolerate, and short-circuit at, the dimension expression of an array type.
+			//    (We'll operate on the dimension expression of an array type directly
+			//    from the parent type, not by visiting through it)
+			// Look inside type exprs.
+			void previsit( const ast::Node * ) {
+				assert( false && "unsupported node type" );
+			};
+			const ast::Expr * allowedExpr = nullptr;
+			void previsit( const ast::Type * ) {
+				GuardValue( allowedExpr ) = nullptr;
+			}
+			void previsit( const ast::ArrayType * t ) {
+				GuardValue( allowedExpr ) = t->dimension.get();
+			}
+			void previsit( const ast::PointerType * t ) {
+				GuardValue( allowedExpr ) = t->dimension.get();
+			}
+			void previsit( const ast::TypeofType * t ) {
+				GuardValue( allowedExpr ) = t->expr.get();
+			}
+			void previsit( const ast::Expr * e ) {
+				assert( e == allowedExpr &&
+				    "only expecting to visit exprs that are dimension exprs or typeof(-) inner exprs" );
+
+				// Skip the tolerated expressions
+				visit_children = false;
+			}
+			void previsit( const ast::TypeExpr * ) {}
+
+			const ast::Type * postvisit(
+					const ast::ArrayType * arrayType ) {
+				static UniqueName dimensionName( "_array_dim" );
+
+				if ( nullptr == arrayType->dimension ) {  // if no dimension is given, don't presume to invent one
+					return arrayType;
+				}
+
+				// find size_t; use it as the type for a dim expr
+				ast::ptr<ast::Type> dimType = outer->transUnit().global.sizeType;
+				assert( dimType );
+				add_qualifiers( dimType, ast::CV::Qualifiers( ast::CV::Const ) );
+
+				// Special-case handling: leave the user's dimension expression alone
+				// - requires the user to have followed a careful convention
+				// - may apply to extremely simple applications, but only as windfall
+				// - users of advanced applications will be following the convention on purpose
+				// - CFA maintainers must protect the criteria against leaving too much alone
+
+				// Actual leave-alone cases following are conservative approximations of "cannot vary"
+
+				// Leave alone: literals and enum constants
+				if ( dynamic_cast< const ast::ConstantExpr * >( arrayType->dimension.get() ) ) {
+					return arrayType;
+				}
+
+				// Leave alone: direct use of an object declared to be const
+				const ast::NameExpr * dimn = dynamic_cast< const ast::NameExpr * >( arrayType->dimension.get() );
+				if ( dimn ) {
+					std::vector<ast::SymbolTable::IdData> dimnDefs = outer->symtab.lookupId( dimn->name );
+					if ( dimnDefs.size() == 1 ) {
+						const ast::DeclWithType * dimnDef = dimnDefs[0].id.get();
+						assert( dimnDef && "symbol table binds a name to nothing" );
+						const ast::ObjectDecl * dimOb = dynamic_cast< const ast::ObjectDecl * >( dimnDef );
+						if( dimOb ) {
+							const ast::Type * dimTy = dimOb->type.get();
+							assert( dimTy && "object declaration bearing no type" );
+							// must not hoist some: size_t
+							// must hoist all: pointers and references
+							// the analysis is conservative; BasicType is a simple approximation
+							if ( dynamic_cast< const ast::BasicType * >( dimTy ) ||
+							     dynamic_cast< const ast::SueInstType<ast::EnumDecl> * >( dimTy ) ) {
+								if ( dimTy->is_const() ) {
+									// The dimension is certainly re-evaluable, giving the same answer each time.
+									// Our user might be hoping to write the array type in multiple places, having them unify.
+									// Leave the type alone.
+
+									// We believe the new criterion leaves less alone than the old criterion.
+									// Thus, the old criterion should have left the current case alone.
+									// Catch cases that weren't thought through.
+									assert( !Tuples::maybeImpure( arrayType->dimension ) );
+
+									return arrayType;
+								}
+							};
+						}
+					}
+				}
+
+				// Leave alone: any sizeof expression (answer cannot vary during current lexical scope)
+				const ast::SizeofExpr * sz = dynamic_cast< const ast::SizeofExpr * >( arrayType->dimension.get() );
+				if ( sz ) {
+					return arrayType;
+				}
+
+				// General-case handling: change the array-type's dim expr (hoist the user-given content out of the type)
+				// - always safe
+				// - user-unnoticeable in common applications (benign noise in -CFA output)
+				// - may annoy a responsible user of advanced applications (but they can work around)
+				// - protects against misusing advanced features
+				//
+				// The hoist, by example, is:
+				// FROM USER:  float a[ rand() ];
+				// TO GCC:     const size_t __len_of_a = rand(); float a[ __len_of_a ];
+
+				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 );
+				outer->declsToAddBefore.push_back( arrayDimension );
+
+				return mutType;
+			}  // postvisit( const ast::ArrayType * )
+		}; // struct HoistDimsFromTypes
+
+		ast::Storage::Classes storageClasses;
+		void previsit(
+				const ast::ObjectDecl * decl ) {
+			GuardValue( storageClasses ) = decl->storage;
+		}
+
+		const ast::DeclWithType * postvisit(
+				const ast::ObjectDecl * objectDecl ) {
+
+			if ( !isInFunction() || storageClasses.is_static ) {
+				return objectDecl;
+			}
+
+			const ast::Type * mid = objectDecl->type;
+
+			ast::Pass<HoistDimsFromTypes> hoist{this};
+			const ast::Type * result = mid->accept( hoist );
+
+			return mutate_field( objectDecl, &ast::ObjectDecl::type, result );
+		}
+	};
+
+	struct ReturnFixer final :
+			public ast::WithStmtsToAdd<>, ast::WithGuards, ast::WithShortCircuiting {
+		void previsit( const ast::FunctionDecl * decl );
+		const ast::ReturnStmt * previsit( const ast::ReturnStmt * stmt );
+	private:
+		const ast::FunctionDecl * funcDecl = nullptr;
+	};
+
+	void ReturnFixer::previsit( const ast::FunctionDecl * decl ) {
+		if (decl->linkage == ast::Linkage::Intrinsic) visit_children = false;
+		GuardValue( funcDecl ) = decl;
+	}
+
+	const ast::ReturnStmt * ReturnFixer::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;
+				}
+				const ast::Stmt * ctorStmt = genCtorDtor(
+					retVal->location, "?{}", retVal, stmt->expr );
+				assertf( ctorStmt,
+					"ReturnFixer: genCtorDtor returned nullptr: %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>::run( transUnit );
+	ast::Pass<ReturnFixer>::run( transUnit );
+}
+
+void fixReturnStatements( ast::TranslationUnit & transUnit ) {
+	ast::Pass<ReturnFixer>::run( transUnit );
+}
+
+bool ManagedTypes::isManaged( const ast::Type * type ) const {
+	// references are never constructed
+	if ( dynamic_cast< const ast::ReferenceType * >( type ) ) return false;
+	if ( auto tupleType = dynamic_cast< const ast::TupleType * > ( type ) ) {
+		// tuple is also managed if any of its components are managed
+		for (auto & component : tupleType->types) {
+			if (isManaged(component)) return true;
+		}
+	}
+	// need to clear and reset qualifiers when determining if a type is managed
+	// ValueGuard< Type::Qualifiers > qualifiers( type->get_qualifiers() );
+	auto tmp = shallowCopy(type);
+	tmp->qualifiers = {};
+	// delete tmp at return
+	ast::ptr<ast::Type> guard = tmp;
+	// a type is managed if it appears in the map of known managed types, or if it contains any polymorphism (is a type variable or generic type containing a type variable)
+	return managedTypes.find( Mangle::mangle( tmp, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) ) != managedTypes.end() || GenPoly::isPolyType( tmp );
+}
+
+bool ManagedTypes::isManaged( const ast::ObjectDecl * objDecl ) const {
+	const ast::Type * type = objDecl->type;
+	while ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) {
+		// must always construct VLAs with an initializer, since this is an error in C
+		if ( at->isVarLen && objDecl->init ) return true;
+		type = at->base;
+	}
+	return isManaged( type );
+}
+
+void ManagedTypes::handleDWT( const ast::DeclWithType * dwt ) {
+	// if this function is a user-defined constructor or destructor, mark down the type as "managed"
+	if ( ! dwt->linkage.is_overrideable && CodeGen::isCtorDtor( dwt->name ) ) {
+		auto & params = GenPoly::getFunctionType( dwt->get_type())->params;
+		assert( ! params.empty() );
+		// Type * type = InitTweak::getPointerBase( params.front() );
+		// assert( type );
+		managedTypes.insert( Mangle::mangle( params.front(), {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );
+	}
+}
+
+void ManagedTypes::handleStruct( const ast::StructDecl * aggregateDecl ) {
+	// don't construct members, but need to take note if there is a managed member,
+	// because that means that this type is also managed
+	for ( auto & member : aggregateDecl->members ) {
+		if ( auto field = member.as<ast::ObjectDecl>() ) {
+			if ( isManaged( field ) ) {
+				// generic parameters should not play a role in determining whether a generic type is constructed - construct all generic types, so that
+				// polymorphic constructors make generic types managed types
+				ast::StructInstType inst( aggregateDecl );
+				managedTypes.insert( Mangle::mangle( &inst, {Mangle::NoOverrideable | Mangle::NoGenericParams | Mangle::Type} ) );
+				break;
+			}
+		}
+	}
+}
+
+void ManagedTypes::beginScope() { managedTypes.beginScope(); }
+void ManagedTypes::endScope() { managedTypes.endScope(); }
+
+const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg ) {
+	assertf(objDecl, "genCtorDtor passed null objDecl");
+	InitExpander srcParam(arg);
+	return SymTab::genImplicitCall(srcParam, new ast::VariableExpr(loc, objDecl), loc, fname, objDecl);
+}
+
+ast::ConstructorInit * genCtorInit( const CodeLocation & loc, const ast::ObjectDecl * objDecl ) {
+	// call into genImplicitCall from Autogen.h to generate calls to ctor/dtor for each
+	// constructable object
+	InitExpander srcParam{ objDecl->init }, nullParam{ (const ast::Init *)nullptr };
+	ast::ptr< ast::Expr > dstParam = new ast::VariableExpr(loc, objDecl);
+
+	ast::ptr< ast::Stmt > ctor = SymTab::genImplicitCall(
+		srcParam, dstParam, loc, "?{}", objDecl );
+	ast::ptr< ast::Stmt > dtor = SymTab::genImplicitCall(
+		nullParam, dstParam, loc, "^?{}", objDecl,
+		SymTab::LoopBackward );
+
+	// check that either both ctor and dtor are present, or neither
+	assert( (bool)ctor == (bool)dtor );
+
+	if ( ctor ) {
+		// need to remember init expression, in case no ctors exist. If ctor does exist, want to
+		// use ctor expression instead of init.
+		ctor.strict_as< ast::ImplicitCtorDtorStmt >();
+		dtor.strict_as< ast::ImplicitCtorDtorStmt >();
+
+		return new ast::ConstructorInit{ loc, ctor, dtor, objDecl->init };
+	}
+
+	return nullptr;
+}
+
+} // namespace InitTweak
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/InitTweak/GenInit.h
===================================================================
--- src/InitTweak/GenInit.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,60 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// GenInit.h -- Generate initializers, and other stuff.
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Mar 18 14:22:00 2022
-// Update Count     : 7
-//
-
-#pragma once
-
-#include <list>                // for list
-#include <string>              // for string
-
-#include "AST/Fwd.hpp"
-#include "Common/CodeLocation.h"
-#include "GenPoly/ScopedSet.h" // for ScopedSet
-
-namespace InitTweak {
-
-/// Adds return value temporaries and wraps Initializers in ConstructorInit nodes
-void genInit( ast::TranslationUnit & translationUnit );
-
-/// Converts return statements into copy constructor calls on the hidden return variable.
-/// This pass must happen before auto-gen.
-void fixReturnStatements( ast::TranslationUnit & translationUnit );
-
-/// generates a single ctor/dtor statement using objDecl as the 'this' parameter and arg as the optional argument
-const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg = nullptr );
-
-/// creates an appropriate ConstructorInit node which contains a constructor, destructor, and C-initializer
-ast::ConstructorInit * genCtorInit( const CodeLocation & loc, const ast::ObjectDecl * objDecl );
-
-class ManagedTypes final {
-public:
-	bool isManaged( const ast::ObjectDecl * objDecl ) const ; // determine if object is managed
-	bool isManaged( const ast::Type * type ) const; // determine if type is managed
-
-	void handleDWT( const ast::DeclWithType * dwt ); // add type to managed if ctor/dtor
-	void handleStruct( const ast::StructDecl * aggregateDecl ); // add type to managed if child is managed
-
-	void beginScope();
-	void endScope();
-private:
-	GenPoly::ScopedSet< std::string > managedTypes;
-};
-
-} // namespace
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/InitTweak/GenInit.hpp
===================================================================
--- src/InitTweak/GenInit.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/InitTweak/GenInit.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,60 @@
+//
+// 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.
+//
+// GenInit.hpp -- Generate initializers, and other stuff.
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Mar 18 14:22:00 2022
+// Update Count     : 7
+//
+
+#pragma once
+
+#include <list>                // for list
+#include <string>              // for string
+
+#include "AST/Fwd.hpp"
+#include "Common/CodeLocation.hpp"
+#include "GenPoly/ScopedSet.hpp"   // for ScopedSet
+
+namespace InitTweak {
+
+/// Adds return value temporaries and wraps Initializers in ConstructorInit nodes
+void genInit( ast::TranslationUnit & translationUnit );
+
+/// Converts return statements into copy constructor calls on the hidden return variable.
+/// This pass must happen before auto-gen.
+void fixReturnStatements( ast::TranslationUnit & translationUnit );
+
+/// generates a single ctor/dtor statement using objDecl as the 'this' parameter and arg as the optional argument
+const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg = nullptr );
+
+/// creates an appropriate ConstructorInit node which contains a constructor, destructor, and C-initializer
+ast::ConstructorInit * genCtorInit( const CodeLocation & loc, const ast::ObjectDecl * objDecl );
+
+class ManagedTypes final {
+public:
+	bool isManaged( const ast::ObjectDecl * objDecl ) const ; // determine if object is managed
+	bool isManaged( const ast::Type * type ) const; // determine if type is managed
+
+	void handleDWT( const ast::DeclWithType * dwt ); // add type to managed if ctor/dtor
+	void handleStruct( const ast::StructDecl * aggregateDecl ); // add type to managed if child is managed
+
+	void beginScope();
+	void endScope();
+private:
+	GenPoly::ScopedSet< std::string > managedTypes;
+};
+
+} // namespace
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/InitTweak/InitTweak.cc
===================================================================
--- src/InitTweak/InitTweak.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,475 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// InitTweak.cc --
-//
-// Author           : Rob Schluntz
-// Created On       : Fri May 13 11:26:36 2016
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Sep 22  9:50:00 2022
-// Update Count     : 21
-//
-
-#include "InitTweak.h"
-
-#include <algorithm>               // for find, all_of
-#include <cassert>                 // for assertf, assert, strict_dynamic_cast
-#include <iostream>                // for ostream, cerr, endl
-#include <iterator>                // for back_insert_iterator, back_inserter
-#include <memory>                  // for __shared_ptr
-#include <vector>
-
-#include "AST/Expr.hpp"
-#include "AST/Init.hpp"
-#include "AST/Inspect.hpp"
-#include "AST/Node.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Stmt.hpp"
-#include "AST/Type.hpp"
-#include "CodeGen/OperatorTable.h" // for isConstructor, isDestructor, isCto...
-#include "Common/SemanticError.h"  // for SemanticError
-#include "Common/ToString.hpp"     // for toCString
-#include "Common/UniqueName.h"     // for UniqueName
-#include "GenPoly/GenPoly.h"       // for getFunctionType
-#include "ResolvExpr/Unify.h"      // for typesCompatibleIgnoreQualifiers
-#include "Tuples/Tuples.h"         // for Tuples::isTtype
-
-namespace InitTweak {
-
-// Forward declared, because it is used as a parent type.
-class InitExpander::ExpanderImpl {
-public:
-	virtual ~ExpanderImpl() = default;
-	virtual std::vector< ast::ptr< ast::Expr > > next( IndexList & indices ) = 0;
-	virtual ast::ptr< ast::Stmt > buildListInit(
-		ast::UntypedExpr * callExpr, IndexList & indices ) = 0;
-};
-
-namespace {
-	struct HasDesignations : public ast::WithShortCircuiting {
-		bool result = false;
-
-		void previsit( const ast::Node * ) {
-			// Short circuit if we already know there are designations.
-			if ( result ) visit_children = false;
-		}
-
-		void previsit( const ast::Designation * des ) {
-			// Short circuit if we already know there are designations.
-			if ( result ) visit_children = false;
-			else if ( !des->designators.empty() ) {
-				result = true;
-				visit_children = false;
-			}
-		}
-	};
-
-	struct InitDepthChecker {
-		bool result = true;
-		const ast::Type * type;
-		int curDepth = 0, maxDepth = 0;
-		InitDepthChecker( const ast::Type * type ) : type( type ) {
-			const ast::Type * t = type;
-			while ( auto at = dynamic_cast< const ast::ArrayType * >( t ) ) {
-				maxDepth++;
-				t = at->base;
-			}
-			maxDepth++;
-		}
-		void previsit( ast::ListInit const * ) {
-			curDepth++;
-			if ( curDepth > maxDepth ) result = false;
-		}
-		void postvisit( ast::ListInit const * ) {
-			curDepth--;
-		}
-	};
-
-	struct InitFlattener : public ast::WithShortCircuiting {
-		std::vector< ast::ptr< ast::Expr > > argList;
-
-		void previsit( const ast::SingleInit * singleInit ) {
-			visit_children = false;
-			argList.emplace_back( singleInit->value );
-		}
-	};
-
-	template< typename Out >
-	void buildCallExpr(
-		ast::UntypedExpr * callExpr, const ast::Expr * index, const ast::Expr * dimension,
-		const ast::Init * init, Out & out
-	) {
-		const CodeLocation & loc = init->location;
-
-		auto cond = new ast::UntypedExpr{
-			loc, new ast::NameExpr{ loc, "?<?" }, { index, dimension } };
-
-		std::vector< ast::ptr< ast::Expr > > args = makeInitList( init );
-		splice( callExpr->args, args );
-
-		out.emplace_back( new ast::IfStmt{ loc, cond, new ast::ExprStmt{ loc, callExpr } } );
-
-		out.emplace_back( new ast::ExprStmt{
-			loc, new ast::UntypedExpr{ loc, new ast::NameExpr{ loc, "++?" }, { index } } } );
-	}
-
-	template< typename Out >
-	void build(
-		ast::UntypedExpr * callExpr, const InitExpander::IndexList & indices,
-		const ast::Init * init, Out & out
-	) {
-		if ( indices.empty() ) return;
-
-		unsigned idx = 0;
-
-		const ast::Expr * index = indices[idx++];
-		assert( idx != indices.size() );
-		const ast::Expr * dimension = indices[idx++];
-
-		if ( idx == indices.size() ) {
-			if ( auto listInit = dynamic_cast< const ast::ListInit * >( init ) ) {
-				for ( const ast::Init * init : *listInit ) {
-					buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
-				}
-			} else {
-				buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
-			}
-		} else {
-			const CodeLocation & loc = init->location;
-
-			unsigned long cond = 0;
-			auto listInit = dynamic_cast< const ast::ListInit * >( init );
-			if ( ! listInit ) { SemanticError( loc, "unbalanced list initializers" ); }
-
-			static UniqueName targetLabel( "L__autogen__" );
-			ast::Label switchLabel{
-				loc, targetLabel.newName(), { new ast::Attribute{ "unused" } } };
-
-			std::vector< ast::ptr< ast::CaseClause > > branches;
-			for ( const ast::Init * init : *listInit ) {
-				auto condition = ast::ConstantExpr::from_ulong( loc, cond );
-				++cond;
-
-				std::vector< ast::ptr< ast::Stmt > > stmts;
-				build( callExpr, indices, init, stmts );
-				stmts.emplace_back(
-					new ast::BranchStmt{ loc, ast::BranchStmt::Break, switchLabel } );
-				branches.emplace_back( new ast::CaseClause{ loc, condition, std::move( stmts ) } );
-			}
-			out.emplace_back( new ast::SwitchStmt{ loc, index, std::move( branches ) } );
-			out.emplace_back( new ast::NullStmt{ loc, { switchLabel } } );
-		}
-	}
-
-	class InitImpl final : public InitExpander::ExpanderImpl {
-		ast::ptr< ast::Init > init;
-	public:
-		InitImpl( const ast::Init * i ) : init( i ) {}
-
-		std::vector< ast::ptr< ast::Expr > > next( InitExpander::IndexList & ) override {
-			return makeInitList( init );
-		}
-
-		ast::ptr< ast::Stmt > buildListInit(
-			ast::UntypedExpr * callExpr, InitExpander::IndexList & indices
-		) override {
-			// If array came with an initializer list, initialize each element. We may have more
-			// initializers than elements of the array; need to check at each index that we have
-			// not exceeded size. We may have fewer initializers than elements in the array; need
-			// to default-construct remaining elements. To accomplish this, generate switch
-			// statement consuming all of expander's elements
-
-			if ( ! init ) return {};
-
-			std::list< ast::ptr< ast::Stmt > > stmts;
-			build( callExpr, indices, init, stmts );
-			if ( stmts.empty() ) {
-				return {};
-			} else if ( 1 == stmts.size() ) {
-				return std::move( stmts.front() );
-			} else {
-				auto block = new ast::CompoundStmt{ init->location, std::move( stmts ) };
-				init = nullptr;  // consumed in creating the list init
-				return block;
-			}
-		}
-	};
-
-	class ExprImpl final : public InitExpander::ExpanderImpl {
-		ast::ptr< ast::Expr > arg;
-	public:
-		ExprImpl( const ast::Expr * a ) : arg( a ) {}
-
-		std::vector< ast::ptr< ast::Expr > > next(
-			InitExpander::IndexList & indices
-		) override {
-			if ( !arg ) return {};
-
-			const CodeLocation & loc = arg->location;
-			const ast::Expr * expr = arg;
-			for ( auto it = indices.rbegin(); it != indices.rend(); ++it ) {
-				// Go through indices and layer on subscript exprs ?[?].
-				++it;
-				expr = new ast::UntypedExpr{
-					loc, new ast::NameExpr{ loc, "?[?]" }, { expr, *it } };
-			}
-			return { expr };
-		}
-
-		ast::ptr< ast::Stmt > buildListInit(
-			ast::UntypedExpr *, InitExpander::IndexList &
-		) override {
-			return {};
-		}
-	};
-
-	struct CallFinder final {
-		std::vector< const ast::Expr * > matches;
-		const std::vector< std::string > names;
-
-		CallFinder( std::vector< std::string > && ns ) : matches(), names( std::move(ns) ) {}
-
-		void handleCallExpr( const ast::Expr * expr ) {
-			std::string fname = getFunctionName( expr );
-			if ( std::find( names.begin(), names.end(), fname ) != names.end() ) {
-				matches.emplace_back( expr );
-			}
-		}
-
-		void postvisit( const ast::ApplicationExpr * expr ) { handleCallExpr( expr ); }
-		void postvisit( const ast::UntypedExpr *     expr ) { handleCallExpr( expr ); }
-	};
-
-	template <typename Predicate>
-	bool allofCtorDtor( const ast::Stmt * stmt, const Predicate & pred ) {
-		std::vector< const ast::Expr * > callExprs = collectCtorDtorCalls( stmt );
-		return std::all_of( callExprs.begin(), callExprs.end(), pred );
-	}
-
-	struct ConstExprChecker : public ast::WithShortCircuiting {
-		// Most expressions are not const-expr.
-		void previsit( const ast::Expr * ) { result = false; visit_children = false; }
-
-		void previsit( const ast::AddressExpr *addressExpr ) {
-			visit_children = false;
-			const ast::Expr * arg = addressExpr->arg;
-
-			// Address of a variable or member expression is const-expr.
-			if ( !dynamic_cast< const ast::NameExpr * >( arg )
-				&& !dynamic_cast< const ast::VariableExpr * >( arg )
-				&& !dynamic_cast< const ast::MemberExpr * >( arg )
-				&& !dynamic_cast< const ast::UntypedMemberExpr * >( arg ) ) result = false;
-		}
-
-		// These expressions may be const expr, depending on their children.
-		void previsit( const ast::SizeofExpr * ) {}
-		void previsit( const ast::AlignofExpr * ) {}
-		void previsit( const ast::UntypedOffsetofExpr * ) {}
-		void previsit( const ast::OffsetofExpr * ) {}
-		void previsit( const ast::OffsetPackExpr * ) {}
-		void previsit( const ast::CommaExpr * ) {}
-		void previsit( const ast::LogicalExpr * ) {}
-		void previsit( const ast::ConditionalExpr * ) {}
-		void previsit( const ast::CastExpr * ) {}
-		void previsit( const ast::ConstantExpr * ) {}
-
-		void previsit( const ast::VariableExpr * varExpr ) {
-			visit_children = false;
-
-			if ( auto inst = varExpr->result.as<ast::EnumInstType>() ) {
-				long long int value;
-				if ( inst->base->valueOf( varExpr->var, value ) ) {
-					// enumerators are const expr
-					return;
-				}
-			}
-			result = false;
-		}
-
-		bool result = true;
-	};
-} // namespace
-
-bool isAssignment( const ast::FunctionDecl * decl ) {
-	return CodeGen::isAssignment( decl->name ) && isCopyFunction( decl );
-}
-
-bool isDestructor( const ast::FunctionDecl * decl ) {
-	return CodeGen::isDestructor( decl->name );
-}
-
-bool isDefaultConstructor( const ast::FunctionDecl * decl ) {
-	return CodeGen::isConstructor( decl->name ) && 1 == decl->params.size();
-}
-
-bool isCopyConstructor( const ast::FunctionDecl * decl ) {
-	return CodeGen::isConstructor( decl->name ) && 2 == decl->params.size();
-}
-
-bool isCopyFunction( const ast::FunctionDecl * decl ) {
-	const ast::FunctionType * ftype = decl->type;
-	if ( ftype->params.size() != 2 ) return false;
-
-	const ast::Type * t1 = ast::getPointerBase( ftype->params.front() );
-	if ( ! t1 ) return false;
-	const ast::Type * t2 = ftype->params.back();
-
-	return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2 );
-}
-
-const ast::Type * getTypeofThis( const ast::FunctionType * ftype ) {
-	assertf( ftype, "getTypeofThis: nullptr ftype" );
-	const std::vector<ast::ptr<ast::Type>> & params = ftype->params;
-	assertf( !params.empty(), "getTypeofThis: ftype with 0 parameters: %s",
-			toCString( ftype ) );
-	const ast::ReferenceType * refType =
-		params.front().strict_as<ast::ReferenceType>();
-	return refType->base;
-}
-
-const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func) {
-	assertf( func, "getParamThis: nullptr ftype" );
-	auto & params = func->params;
-	assertf( !params.empty(), "getParamThis: ftype with 0 parameters: %s", toCString( func ));
-	return params.front().strict_as<ast::ObjectDecl>();
-}
-
-// looks like some other such codegen uses UntypedExpr and does not create fake function. should revisit afterwards
-// following passes may accidentally resolve this expression if returned as untyped...
-ast::Expr * createBitwiseAssignment(const ast::Expr * dst, const ast::Expr * src) {
-	static ast::ptr<ast::FunctionDecl> assign = nullptr;
-	if (!assign) {
-		auto td = new ast::TypeDecl(CodeLocation(), "T", {}, nullptr, ast::TypeDecl::Dtype, true);
-		assign = new ast::FunctionDecl(CodeLocation(), "?=?", {td}, {},
-		{ new ast::ObjectDecl(CodeLocation(), "_dst", new ast::ReferenceType(new ast::TypeInstType("T", td))),
-		  new ast::ObjectDecl(CodeLocation(), "_src", new ast::TypeInstType("T", td))},
-		{ new ast::ObjectDecl(CodeLocation(), "_ret", new ast::TypeInstType("T", td))}, nullptr, {}, ast::Linkage::Intrinsic);
-	}
-	if (dst->result.as<ast::ReferenceType>()) {
-		for (int depth = dst->result->referenceDepth(); depth > 0; depth--) {
-			dst = new ast::AddressExpr(dst);
-		}
-	} else {
-		dst = new ast::CastExpr(dst, new ast::ReferenceType(dst->result, {}));
-	}
-	if (src->result.as<ast::ReferenceType>()) {
-		for (int depth = src->result->referenceDepth(); depth > 0; depth--) {
-			src = new ast::AddressExpr(src);
-		}
-	}
-	auto var = ast::VariableExpr::functionPointer(dst->location, assign);
-	auto app = new ast::ApplicationExpr(dst->location, var, {dst, src});
-	// Skip the resolver, just set the result to the correct type.
-	app->result = ast::deepCopy( src->result );
-	return app;
-}
-
-std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init ) {
-	ast::Pass< InitFlattener > flattener;
-	maybe_accept( init, flattener );
-	return std::move( flattener.core.argList );
-}
-
-bool tryConstruct( const ast::DeclWithType * dwt ) {
-	auto objDecl = dynamic_cast< const ast::ObjectDecl * >( dwt );
-	if ( !objDecl ) return false;
-	return (objDecl->init == nullptr ||
-			( objDecl->init != nullptr && objDecl->init->maybeConstructed ))
-		&& !objDecl->storage.is_extern
-		&& isConstructable( objDecl->type );
-}
-
-bool isConstructable( const ast::Type * type ) {
-	return !dynamic_cast< const ast::VarArgsType * >( type ) && !dynamic_cast< const ast::ReferenceType * >( type )
-		&& !dynamic_cast< const ast::FunctionType * >( type ) && !Tuples::isTtype( type );
-}
-
-bool isDesignated( const ast::Init * init ) {
-	return ( init ) ? ast::Pass<HasDesignations>::read( init ) : false;
-}
-
-bool checkInitDepth( const ast::ObjectDecl * objDecl ) {
-	return ( objDecl->init ) ? ast::Pass<InitDepthChecker>::read(
-		objDecl->init.get(), objDecl->type.get() ) : true;
-}
-
-bool isIntrinsicSingleArgCallStmt( const ast::Stmt * stmt ) {
-	return allofCtorDtor( stmt, []( const ast::Expr * callExpr ){
-		if ( const ast::ApplicationExpr * appExpr = isIntrinsicCallExpr( callExpr ) ) {
-			const ast::FunctionType * funcType =
-				GenPoly::getFunctionType( appExpr->func->result );
-			assert( funcType );
-			return funcType->params.size() == 1;
-		}
-		return false;
-	});
-}
-
-std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt ) {
-	ast::Pass< CallFinder > finder{ std::vector< std::string >{ "?{}", "^?{}" } };
-	maybe_accept( stmt, finder );
-	return std::move( finder.core.matches );
-}
-
-bool isConstExpr( const ast::Expr * expr ) {
-	return ( expr ) ? ast::Pass<ConstExprChecker>::read( expr ) : true;
-}
-
-bool isConstExpr( const ast::Init * init ) {
-	// for all intents and purposes, no initializer means const expr
-	return ( init ) ? ast::Pass<ConstExprChecker>::read( init ) : true;
-}
-
-#if defined( __x86_64 ) || defined( __i386 ) // assembler comment to prevent assembler warning message
-	#define ASM_COMMENT "#"
-#else // defined( __ARM_ARCH )
-	#define ASM_COMMENT "//"
-#endif
-static const char * const data_section =  ".data" ASM_COMMENT;
-static const char * const tlsd_section = ".tdata" ASM_COMMENT;
-
-void addDataSectionAttribute( ast::ObjectDecl * objDecl ) {
-	const bool is_tls = objDecl->storage.is_threadlocal_any();
-	const char * section = is_tls ? tlsd_section : data_section;
-	objDecl->attributes.push_back(new ast::Attribute("section", {
-		ast::ConstantExpr::from_string(objDecl->location, section)
-	}));
-}
-
-InitExpander::InitExpander( const ast::Init * init )
-: expander( new InitImpl{ init } ), crnt(), indices() {}
-
-InitExpander::InitExpander( const ast::Expr * expr )
-: expander( new ExprImpl{ expr } ), crnt(), indices() {}
-
-std::vector< ast::ptr< ast::Expr > > InitExpander::operator* () { return crnt; }
-
-InitExpander & InitExpander::operator++ () {
-	crnt = expander->next( indices );
-	return *this;
-}
-
-/// builds statement which has the same semantics as a C-style list initializer (for array
-/// initializers) using callExpr as the base expression to perform initialization
-ast::ptr< ast::Stmt > InitExpander::buildListInit( ast::UntypedExpr * callExpr ) {
-	return expander->buildListInit( callExpr, indices );
-}
-
-void InitExpander::addArrayIndex( const ast::Expr * index, const ast::Expr * dimension ) {
-	indices.emplace_back( index );
-	indices.emplace_back( dimension );
-}
-
-void InitExpander::clearArrayIndices() { indices.clear(); }
-
-bool InitExpander::addReference() {
-	for ( ast::ptr< ast::Expr > & expr : crnt ) {
-		expr = new ast::AddressExpr{ expr };
-	}
-	return !crnt.empty();
-}
-
-} // namespace InitTweak
Index: src/InitTweak/InitTweak.cpp
===================================================================
--- src/InitTweak/InitTweak.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/InitTweak/InitTweak.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,475 @@
+//
+// 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.
+//
+// InitTweak.cpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Fri May 13 11:26:36 2016
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Sep 22  9:50:00 2022
+// Update Count     : 21
+//
+
+#include "InitTweak.hpp"
+
+#include <algorithm>                  // for find, all_of
+#include <cassert>                    // for assertf, assert, strict_dynamic_...
+#include <iostream>                   // for ostream, cerr, endl
+#include <iterator>                   // for back_insert_iterator, back_inser...
+#include <memory>                     // for __shared_ptr
+#include <vector>
+
+#include "AST/Expr.hpp"
+#include "AST/Init.hpp"
+#include "AST/Inspect.hpp"
+#include "AST/Node.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/Type.hpp"
+#include "CodeGen/OperatorTable.hpp"  // for isConstructor, isDestructor, isC...
+#include "Common/SemanticError.hpp"   // for SemanticError
+#include "Common/ToString.hpp"        // for toCString
+#include "Common/UniqueName.hpp"      // for UniqueName
+#include "GenPoly/GenPoly.hpp"        // for getFunctionType
+#include "ResolvExpr/Unify.hpp"       // for typesCompatibleIgnoreQualifiers
+#include "Tuples/Tuples.hpp"          // for Tuples::isTtype
+
+namespace InitTweak {
+
+// Forward declared, because it is used as a parent type.
+class InitExpander::ExpanderImpl {
+public:
+	virtual ~ExpanderImpl() = default;
+	virtual std::vector< ast::ptr< ast::Expr > > next( IndexList & indices ) = 0;
+	virtual ast::ptr< ast::Stmt > buildListInit(
+		ast::UntypedExpr * callExpr, IndexList & indices ) = 0;
+};
+
+namespace {
+	struct HasDesignations : public ast::WithShortCircuiting {
+		bool result = false;
+
+		void previsit( const ast::Node * ) {
+			// Short circuit if we already know there are designations.
+			if ( result ) visit_children = false;
+		}
+
+		void previsit( const ast::Designation * des ) {
+			// Short circuit if we already know there are designations.
+			if ( result ) visit_children = false;
+			else if ( !des->designators.empty() ) {
+				result = true;
+				visit_children = false;
+			}
+		}
+	};
+
+	struct InitDepthChecker {
+		bool result = true;
+		const ast::Type * type;
+		int curDepth = 0, maxDepth = 0;
+		InitDepthChecker( const ast::Type * type ) : type( type ) {
+			const ast::Type * t = type;
+			while ( auto at = dynamic_cast< const ast::ArrayType * >( t ) ) {
+				maxDepth++;
+				t = at->base;
+			}
+			maxDepth++;
+		}
+		void previsit( ast::ListInit const * ) {
+			curDepth++;
+			if ( curDepth > maxDepth ) result = false;
+		}
+		void postvisit( ast::ListInit const * ) {
+			curDepth--;
+		}
+	};
+
+	struct InitFlattener : public ast::WithShortCircuiting {
+		std::vector< ast::ptr< ast::Expr > > argList;
+
+		void previsit( const ast::SingleInit * singleInit ) {
+			visit_children = false;
+			argList.emplace_back( singleInit->value );
+		}
+	};
+
+	template< typename Out >
+	void buildCallExpr(
+		ast::UntypedExpr * callExpr, const ast::Expr * index, const ast::Expr * dimension,
+		const ast::Init * init, Out & out
+	) {
+		const CodeLocation & loc = init->location;
+
+		auto cond = new ast::UntypedExpr{
+			loc, new ast::NameExpr{ loc, "?<?" }, { index, dimension } };
+
+		std::vector< ast::ptr< ast::Expr > > args = makeInitList( init );
+		splice( callExpr->args, args );
+
+		out.emplace_back( new ast::IfStmt{ loc, cond, new ast::ExprStmt{ loc, callExpr } } );
+
+		out.emplace_back( new ast::ExprStmt{
+			loc, new ast::UntypedExpr{ loc, new ast::NameExpr{ loc, "++?" }, { index } } } );
+	}
+
+	template< typename Out >
+	void build(
+		ast::UntypedExpr * callExpr, const InitExpander::IndexList & indices,
+		const ast::Init * init, Out & out
+	) {
+		if ( indices.empty() ) return;
+
+		unsigned idx = 0;
+
+		const ast::Expr * index = indices[idx++];
+		assert( idx != indices.size() );
+		const ast::Expr * dimension = indices[idx++];
+
+		if ( idx == indices.size() ) {
+			if ( auto listInit = dynamic_cast< const ast::ListInit * >( init ) ) {
+				for ( const ast::Init * init : *listInit ) {
+					buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
+				}
+			} else {
+				buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
+			}
+		} else {
+			const CodeLocation & loc = init->location;
+
+			unsigned long cond = 0;
+			auto listInit = dynamic_cast< const ast::ListInit * >( init );
+			if ( ! listInit ) { SemanticError( loc, "unbalanced list initializers" ); }
+
+			static UniqueName targetLabel( "L__autogen__" );
+			ast::Label switchLabel{
+				loc, targetLabel.newName(), { new ast::Attribute{ "unused" } } };
+
+			std::vector< ast::ptr< ast::CaseClause > > branches;
+			for ( const ast::Init * init : *listInit ) {
+				auto condition = ast::ConstantExpr::from_ulong( loc, cond );
+				++cond;
+
+				std::vector< ast::ptr< ast::Stmt > > stmts;
+				build( callExpr, indices, init, stmts );
+				stmts.emplace_back(
+					new ast::BranchStmt{ loc, ast::BranchStmt::Break, switchLabel } );
+				branches.emplace_back( new ast::CaseClause{ loc, condition, std::move( stmts ) } );
+			}
+			out.emplace_back( new ast::SwitchStmt{ loc, index, std::move( branches ) } );
+			out.emplace_back( new ast::NullStmt{ loc, { switchLabel } } );
+		}
+	}
+
+	class InitImpl final : public InitExpander::ExpanderImpl {
+		ast::ptr< ast::Init > init;
+	public:
+		InitImpl( const ast::Init * i ) : init( i ) {}
+
+		std::vector< ast::ptr< ast::Expr > > next( InitExpander::IndexList & ) override {
+			return makeInitList( init );
+		}
+
+		ast::ptr< ast::Stmt > buildListInit(
+			ast::UntypedExpr * callExpr, InitExpander::IndexList & indices
+		) override {
+			// If array came with an initializer list, initialize each element. We may have more
+			// initializers than elements of the array; need to check at each index that we have
+			// not exceeded size. We may have fewer initializers than elements in the array; need
+			// to default-construct remaining elements. To accomplish this, generate switch
+			// statement consuming all of expander's elements
+
+			if ( ! init ) return {};
+
+			std::list< ast::ptr< ast::Stmt > > stmts;
+			build( callExpr, indices, init, stmts );
+			if ( stmts.empty() ) {
+				return {};
+			} else if ( 1 == stmts.size() ) {
+				return std::move( stmts.front() );
+			} else {
+				auto block = new ast::CompoundStmt{ init->location, std::move( stmts ) };
+				init = nullptr;  // consumed in creating the list init
+				return block;
+			}
+		}
+	};
+
+	class ExprImpl final : public InitExpander::ExpanderImpl {
+		ast::ptr< ast::Expr > arg;
+	public:
+		ExprImpl( const ast::Expr * a ) : arg( a ) {}
+
+		std::vector< ast::ptr< ast::Expr > > next(
+			InitExpander::IndexList & indices
+		) override {
+			if ( !arg ) return {};
+
+			const CodeLocation & loc = arg->location;
+			const ast::Expr * expr = arg;
+			for ( auto it = indices.rbegin(); it != indices.rend(); ++it ) {
+				// Go through indices and layer on subscript exprs ?[?].
+				++it;
+				expr = new ast::UntypedExpr{
+					loc, new ast::NameExpr{ loc, "?[?]" }, { expr, *it } };
+			}
+			return { expr };
+		}
+
+		ast::ptr< ast::Stmt > buildListInit(
+			ast::UntypedExpr *, InitExpander::IndexList &
+		) override {
+			return {};
+		}
+	};
+
+	struct CallFinder final {
+		std::vector< const ast::Expr * > matches;
+		const std::vector< std::string > names;
+
+		CallFinder( std::vector< std::string > && ns ) : matches(), names( std::move(ns) ) {}
+
+		void handleCallExpr( const ast::Expr * expr ) {
+			std::string fname = getFunctionName( expr );
+			if ( std::find( names.begin(), names.end(), fname ) != names.end() ) {
+				matches.emplace_back( expr );
+			}
+		}
+
+		void postvisit( const ast::ApplicationExpr * expr ) { handleCallExpr( expr ); }
+		void postvisit( const ast::UntypedExpr *     expr ) { handleCallExpr( expr ); }
+	};
+
+	template <typename Predicate>
+	bool allofCtorDtor( const ast::Stmt * stmt, const Predicate & pred ) {
+		std::vector< const ast::Expr * > callExprs = collectCtorDtorCalls( stmt );
+		return std::all_of( callExprs.begin(), callExprs.end(), pred );
+	}
+
+	struct ConstExprChecker : public ast::WithShortCircuiting {
+		// Most expressions are not const-expr.
+		void previsit( const ast::Expr * ) { result = false; visit_children = false; }
+
+		void previsit( const ast::AddressExpr *addressExpr ) {
+			visit_children = false;
+			const ast::Expr * arg = addressExpr->arg;
+
+			// Address of a variable or member expression is const-expr.
+			if ( !dynamic_cast< const ast::NameExpr * >( arg )
+				&& !dynamic_cast< const ast::VariableExpr * >( arg )
+				&& !dynamic_cast< const ast::MemberExpr * >( arg )
+				&& !dynamic_cast< const ast::UntypedMemberExpr * >( arg ) ) result = false;
+		}
+
+		// These expressions may be const expr, depending on their children.
+		void previsit( const ast::SizeofExpr * ) {}
+		void previsit( const ast::AlignofExpr * ) {}
+		void previsit( const ast::UntypedOffsetofExpr * ) {}
+		void previsit( const ast::OffsetofExpr * ) {}
+		void previsit( const ast::OffsetPackExpr * ) {}
+		void previsit( const ast::CommaExpr * ) {}
+		void previsit( const ast::LogicalExpr * ) {}
+		void previsit( const ast::ConditionalExpr * ) {}
+		void previsit( const ast::CastExpr * ) {}
+		void previsit( const ast::ConstantExpr * ) {}
+
+		void previsit( const ast::VariableExpr * varExpr ) {
+			visit_children = false;
+
+			if ( auto inst = varExpr->result.as<ast::EnumInstType>() ) {
+				long long int value;
+				if ( inst->base->valueOf( varExpr->var, value ) ) {
+					// enumerators are const expr
+					return;
+				}
+			}
+			result = false;
+		}
+
+		bool result = true;
+	};
+} // namespace
+
+bool isAssignment( const ast::FunctionDecl * decl ) {
+	return CodeGen::isAssignment( decl->name ) && isCopyFunction( decl );
+}
+
+bool isDestructor( const ast::FunctionDecl * decl ) {
+	return CodeGen::isDestructor( decl->name );
+}
+
+bool isDefaultConstructor( const ast::FunctionDecl * decl ) {
+	return CodeGen::isConstructor( decl->name ) && 1 == decl->params.size();
+}
+
+bool isCopyConstructor( const ast::FunctionDecl * decl ) {
+	return CodeGen::isConstructor( decl->name ) && 2 == decl->params.size();
+}
+
+bool isCopyFunction( const ast::FunctionDecl * decl ) {
+	const ast::FunctionType * ftype = decl->type;
+	if ( ftype->params.size() != 2 ) return false;
+
+	const ast::Type * t1 = ast::getPointerBase( ftype->params.front() );
+	if ( ! t1 ) return false;
+	const ast::Type * t2 = ftype->params.back();
+
+	return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2 );
+}
+
+const ast::Type * getTypeofThis( const ast::FunctionType * ftype ) {
+	assertf( ftype, "getTypeofThis: nullptr ftype" );
+	const std::vector<ast::ptr<ast::Type>> & params = ftype->params;
+	assertf( !params.empty(), "getTypeofThis: ftype with 0 parameters: %s",
+			toCString( ftype ) );
+	const ast::ReferenceType * refType =
+		params.front().strict_as<ast::ReferenceType>();
+	return refType->base;
+}
+
+const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func) {
+	assertf( func, "getParamThis: nullptr ftype" );
+	auto & params = func->params;
+	assertf( !params.empty(), "getParamThis: ftype with 0 parameters: %s", toCString( func ));
+	return params.front().strict_as<ast::ObjectDecl>();
+}
+
+// looks like some other such codegen uses UntypedExpr and does not create fake function. should revisit afterwards
+// following passes may accidentally resolve this expression if returned as untyped...
+ast::Expr * createBitwiseAssignment(const ast::Expr * dst, const ast::Expr * src) {
+	static ast::ptr<ast::FunctionDecl> assign = nullptr;
+	if (!assign) {
+		auto td = new ast::TypeDecl(CodeLocation(), "T", {}, nullptr, ast::TypeDecl::Dtype, true);
+		assign = new ast::FunctionDecl(CodeLocation(), "?=?", {td}, {},
+		{ new ast::ObjectDecl(CodeLocation(), "_dst", new ast::ReferenceType(new ast::TypeInstType("T", td))),
+		  new ast::ObjectDecl(CodeLocation(), "_src", new ast::TypeInstType("T", td))},
+		{ new ast::ObjectDecl(CodeLocation(), "_ret", new ast::TypeInstType("T", td))}, nullptr, {}, ast::Linkage::Intrinsic);
+	}
+	if (dst->result.as<ast::ReferenceType>()) {
+		for (int depth = dst->result->referenceDepth(); depth > 0; depth--) {
+			dst = new ast::AddressExpr(dst);
+		}
+	} else {
+		dst = new ast::CastExpr(dst, new ast::ReferenceType(dst->result, {}));
+	}
+	if (src->result.as<ast::ReferenceType>()) {
+		for (int depth = src->result->referenceDepth(); depth > 0; depth--) {
+			src = new ast::AddressExpr(src);
+		}
+	}
+	auto var = ast::VariableExpr::functionPointer(dst->location, assign);
+	auto app = new ast::ApplicationExpr(dst->location, var, {dst, src});
+	// Skip the resolver, just set the result to the correct type.
+	app->result = ast::deepCopy( src->result );
+	return app;
+}
+
+std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init ) {
+	ast::Pass< InitFlattener > flattener;
+	maybe_accept( init, flattener );
+	return std::move( flattener.core.argList );
+}
+
+bool tryConstruct( const ast::DeclWithType * dwt ) {
+	auto objDecl = dynamic_cast< const ast::ObjectDecl * >( dwt );
+	if ( !objDecl ) return false;
+	return (objDecl->init == nullptr ||
+			( objDecl->init != nullptr && objDecl->init->maybeConstructed ))
+		&& !objDecl->storage.is_extern
+		&& isConstructable( objDecl->type );
+}
+
+bool isConstructable( const ast::Type * type ) {
+	return !dynamic_cast< const ast::VarArgsType * >( type ) && !dynamic_cast< const ast::ReferenceType * >( type )
+		&& !dynamic_cast< const ast::FunctionType * >( type ) && !Tuples::isTtype( type );
+}
+
+bool isDesignated( const ast::Init * init ) {
+	return ( init ) ? ast::Pass<HasDesignations>::read( init ) : false;
+}
+
+bool checkInitDepth( const ast::ObjectDecl * objDecl ) {
+	return ( objDecl->init ) ? ast::Pass<InitDepthChecker>::read(
+		objDecl->init.get(), objDecl->type.get() ) : true;
+}
+
+bool isIntrinsicSingleArgCallStmt( const ast::Stmt * stmt ) {
+	return allofCtorDtor( stmt, []( const ast::Expr * callExpr ){
+		if ( const ast::ApplicationExpr * appExpr = isIntrinsicCallExpr( callExpr ) ) {
+			const ast::FunctionType * funcType =
+				GenPoly::getFunctionType( appExpr->func->result );
+			assert( funcType );
+			return funcType->params.size() == 1;
+		}
+		return false;
+	});
+}
+
+std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt ) {
+	ast::Pass< CallFinder > finder{ std::vector< std::string >{ "?{}", "^?{}" } };
+	maybe_accept( stmt, finder );
+	return std::move( finder.core.matches );
+}
+
+bool isConstExpr( const ast::Expr * expr ) {
+	return ( expr ) ? ast::Pass<ConstExprChecker>::read( expr ) : true;
+}
+
+bool isConstExpr( const ast::Init * init ) {
+	// for all intents and purposes, no initializer means const expr
+	return ( init ) ? ast::Pass<ConstExprChecker>::read( init ) : true;
+}
+
+#if defined( __x86_64 ) || defined( __i386 ) // assembler comment to prevent assembler warning message
+	#define ASM_COMMENT "#"
+#else // defined( __ARM_ARCH )
+	#define ASM_COMMENT "//"
+#endif
+static const char * const data_section =  ".data" ASM_COMMENT;
+static const char * const tlsd_section = ".tdata" ASM_COMMENT;
+
+void addDataSectionAttribute( ast::ObjectDecl * objDecl ) {
+	const bool is_tls = objDecl->storage.is_threadlocal_any();
+	const char * section = is_tls ? tlsd_section : data_section;
+	objDecl->attributes.push_back(new ast::Attribute("section", {
+		ast::ConstantExpr::from_string(objDecl->location, section)
+	}));
+}
+
+InitExpander::InitExpander( const ast::Init * init )
+: expander( new InitImpl{ init } ), crnt(), indices() {}
+
+InitExpander::InitExpander( const ast::Expr * expr )
+: expander( new ExprImpl{ expr } ), crnt(), indices() {}
+
+std::vector< ast::ptr< ast::Expr > > InitExpander::operator* () { return crnt; }
+
+InitExpander & InitExpander::operator++ () {
+	crnt = expander->next( indices );
+	return *this;
+}
+
+/// builds statement which has the same semantics as a C-style list initializer (for array
+/// initializers) using callExpr as the base expression to perform initialization
+ast::ptr< ast::Stmt > InitExpander::buildListInit( ast::UntypedExpr * callExpr ) {
+	return expander->buildListInit( callExpr, indices );
+}
+
+void InitExpander::addArrayIndex( const ast::Expr * index, const ast::Expr * dimension ) {
+	indices.emplace_back( index );
+	indices.emplace_back( dimension );
+}
+
+void InitExpander::clearArrayIndices() { indices.clear(); }
+
+bool InitExpander::addReference() {
+	for ( ast::ptr< ast::Expr > & expr : crnt ) {
+		expr = new ast::AddressExpr{ expr };
+	}
+	return !crnt.empty();
+}
+
+} // namespace InitTweak
Index: src/InitTweak/InitTweak.h
===================================================================
--- src/InitTweak/InitTweak.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,121 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// InitTweak.h --
-//
-// Author           : Rob Schluntz
-// Created On       : Fri May 13 11:26:36 2016
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Sep 22  9:21:00 2022
-// Update Count     : 9
-//
-
-#pragma once
-
-#include <list>               // for list
-#include <memory>             // for shared_ptr
-#include <string>             // for string, allocator
-#include <vector>
-
-#include "AST/Fwd.hpp"        // for AST nodes
-
-// helper functions for initialization
-namespace InitTweak {
-
-bool isAssignment( const ast::FunctionDecl * decl );
-bool isDestructor( const ast::FunctionDecl * decl );
-bool isDefaultConstructor( const ast::FunctionDecl * decl );
-bool isCopyConstructor( const ast::FunctionDecl * decl );
-bool isCopyFunction( const ast::FunctionDecl * decl );
-
-/// returns the base type of the first parameter to a constructor/destructor/assignment function
-const ast::Type * getTypeofThis( const ast::FunctionType * ftype );
-
-/// returns the first parameter of a constructor/destructor/assignment function
-const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func);
-
-/// generate a bitwise assignment operation.
-ast::Expr * createBitwiseAssignment( const ast::Expr * dst, const ast::Expr * src);
-
-/// transform Initializer into an argument list that can be passed to a call expression
-std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init );
-
-/// True if the resolver should try to construct dwt
-bool tryConstruct( const ast::DeclWithType * dwt );
-
-/// True if the type can have a user-defined constructor
-bool isConstructable( const ast::Type * t );
-
-/// True if the Initializer contains designations
-bool isDesignated( const ast::Init * init );
-
-/// True if the ObjectDecl's Initializer nesting level is not deeper than the depth of its
-/// type, where the depth of its type is the number of nested ArrayTypes + 1
-bool checkInitDepth( const ast::ObjectDecl * objDecl );
-
-/// True if stmt is a call statement where the function called is intrinsic and takes one parameter.
-/// Intended to be used for default ctor/dtor calls, but might have use elsewhere.
-/// Currently has assertions that make it less than fully general.
-bool isIntrinsicSingleArgCallStmt( const ast::Stmt * stmt );
-
-/// get all Ctor/Dtor call expressions from a Statement
-std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt );
-
-/// returns true if expr is trivially a compile-time constant
-bool isConstExpr( const ast::Expr * expr );
-bool isConstExpr( const ast::Init * init );
-
-/// Modifies objDecl to have:
-///    __attribute__((section (".data#")))
-/// which makes gcc put the declared variable in the data section,
-/// which is helpful for global constants on newer gcc versions,
-/// so that CFA's generated initialization won't segfault when writing it via a const cast.
-/// The trailing # is an injected assembly comment, to suppress the "a" in
-///    .section .data,"a"
-///    .section .data#,"a"
-/// to avoid assembler warning "ignoring changed section attributes for .data"
-void addDataSectionAttribute( ast::ObjectDecl * objDecl );
-
-class InitExpander final {
-public:
-	using IndexList = std::vector< ast::ptr< ast::Expr > >;
-	class ExpanderImpl;
-
-private:
-	std::shared_ptr< ExpanderImpl > expander;
-	std::vector< ast::ptr< ast::Expr > > crnt;
-	// invariant: list of size 2N (elements come in pairs [index, dimension])
-	IndexList indices;
-
-public:
-	/// Expand by stepping through init to get each list of arguments
-	InitExpander( const ast::Init * init );
-
-	/// Always expand to expression
-	InitExpander( const ast::Expr * expr );
-
-	std::vector< ast::ptr< ast::Expr > > operator* ();
-	InitExpander & operator++ ();
-
-	/// builds statement which has the same semantics as a C-style list initializer (for array
-	/// initializers) using callExpr as the base expression to perform initialization.
-	/// Mutates callExpr
-	ast::ptr< ast::Stmt > buildListInit( ast::UntypedExpr * callExpr );
-
-	void addArrayIndex( const ast::Expr * index, const ast::Expr * dimension );
-
-	void clearArrayIndices();
-
-	bool addReference();
-};
-
-} // namespace InitTweak
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/InitTweak/InitTweak.hpp
===================================================================
--- src/InitTweak/InitTweak.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/InitTweak/InitTweak.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,121 @@
+//
+// 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.
+//
+// InitTweak.h --
+//
+// Author           : Rob Schluntz
+// Created On       : Fri May 13 11:26:36 2016
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Sep 22  9:21:00 2022
+// Update Count     : 9
+//
+
+#pragma once
+
+#include <list>               // for list
+#include <memory>             // for shared_ptr
+#include <string>             // for string, allocator
+#include <vector>
+
+#include "AST/Fwd.hpp"        // for AST nodes
+
+// helper functions for initialization
+namespace InitTweak {
+
+bool isAssignment( const ast::FunctionDecl * decl );
+bool isDestructor( const ast::FunctionDecl * decl );
+bool isDefaultConstructor( const ast::FunctionDecl * decl );
+bool isCopyConstructor( const ast::FunctionDecl * decl );
+bool isCopyFunction( const ast::FunctionDecl * decl );
+
+/// returns the base type of the first parameter to a constructor/destructor/assignment function
+const ast::Type * getTypeofThis( const ast::FunctionType * ftype );
+
+/// returns the first parameter of a constructor/destructor/assignment function
+const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func);
+
+/// generate a bitwise assignment operation.
+ast::Expr * createBitwiseAssignment( const ast::Expr * dst, const ast::Expr * src);
+
+/// transform Initializer into an argument list that can be passed to a call expression
+std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init );
+
+/// True if the resolver should try to construct dwt
+bool tryConstruct( const ast::DeclWithType * dwt );
+
+/// True if the type can have a user-defined constructor
+bool isConstructable( const ast::Type * t );
+
+/// True if the Initializer contains designations
+bool isDesignated( const ast::Init * init );
+
+/// True if the ObjectDecl's Initializer nesting level is not deeper than the depth of its
+/// type, where the depth of its type is the number of nested ArrayTypes + 1
+bool checkInitDepth( const ast::ObjectDecl * objDecl );
+
+/// True if stmt is a call statement where the function called is intrinsic and takes one parameter.
+/// Intended to be used for default ctor/dtor calls, but might have use elsewhere.
+/// Currently has assertions that make it less than fully general.
+bool isIntrinsicSingleArgCallStmt( const ast::Stmt * stmt );
+
+/// get all Ctor/Dtor call expressions from a Statement
+std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt );
+
+/// returns true if expr is trivially a compile-time constant
+bool isConstExpr( const ast::Expr * expr );
+bool isConstExpr( const ast::Init * init );
+
+/// Modifies objDecl to have:
+///    __attribute__((section (".data#")))
+/// which makes gcc put the declared variable in the data section,
+/// which is helpful for global constants on newer gcc versions,
+/// so that CFA's generated initialization won't segfault when writing it via a const cast.
+/// The trailing # is an injected assembly comment, to suppress the "a" in
+///    .section .data,"a"
+///    .section .data#,"a"
+/// to avoid assembler warning "ignoring changed section attributes for .data"
+void addDataSectionAttribute( ast::ObjectDecl * objDecl );
+
+class InitExpander final {
+public:
+	using IndexList = std::vector< ast::ptr< ast::Expr > >;
+	class ExpanderImpl;
+
+private:
+	std::shared_ptr< ExpanderImpl > expander;
+	std::vector< ast::ptr< ast::Expr > > crnt;
+	// invariant: list of size 2N (elements come in pairs [index, dimension])
+	IndexList indices;
+
+public:
+	/// Expand by stepping through init to get each list of arguments
+	InitExpander( const ast::Init * init );
+
+	/// Always expand to expression
+	InitExpander( const ast::Expr * expr );
+
+	std::vector< ast::ptr< ast::Expr > > operator* ();
+	InitExpander & operator++ ();
+
+	/// builds statement which has the same semantics as a C-style list initializer (for array
+	/// initializers) using callExpr as the base expression to perform initialization.
+	/// Mutates callExpr
+	ast::ptr< ast::Stmt > buildListInit( ast::UntypedExpr * callExpr );
+
+	void addArrayIndex( const ast::Expr * index, const ast::Expr * dimension );
+
+	void clearArrayIndices();
+
+	bool addReference();
+};
+
+} // namespace InitTweak
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/InitTweak/module.mk
===================================================================
--- src/InitTweak/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/InitTweak/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,14 +16,14 @@
 
 SRC_INITTWEAK = \
-	InitTweak/GenInit.cc \
-	InitTweak/GenInit.h \
-	InitTweak/InitTweak.cc \
-	InitTweak/InitTweak.h
+	InitTweak/GenInit.cpp \
+	InitTweak/GenInit.hpp \
+	InitTweak/InitTweak.cpp \
+	InitTweak/InitTweak.hpp
 
 SRC += $(SRC_INITTWEAK) \
-	InitTweak/FixGlobalInit.cc \
-	InitTweak/FixGlobalInit.h \
+	InitTweak/FixGlobalInit.cpp \
+	InitTweak/FixGlobalInit.hpp \
 	InitTweak/FixInit.cpp \
-	InitTweak/FixInit.h
+	InitTweak/FixInit.hpp
 
 SRCDEMANGLE += $(SRC_INITTWEAK)
Index: src/MakeLibCfa.cpp
===================================================================
--- src/MakeLibCfa.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/MakeLibCfa.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -19,6 +19,6 @@
 #include "AST/Fwd.hpp"
 #include "AST/Pass.hpp"
-#include "CodeGen/OperatorTable.h"
-#include "Common/UniqueName.h"
+#include "CodeGen/OperatorTable.hpp"
+#include "Common/UniqueName.hpp"
 
 namespace LibCfa {
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Makefile.am	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -53,5 +53,5 @@
 include Virtual/module.mk
 
-$(addprefix $(srcdir)/, ResolvExpr/ConversionCost.cc ResolvExpr/CommonType.cc SymTab/ManglerCommon.cc) : $(srcdir)/AST/BasicKind.hpp
+$(addprefix $(srcdir)/, ResolvExpr/ConversionCost.cpp ResolvExpr/CommonType.cpp SymTab/ManglerCommon.cpp) : $(srcdir)/AST/BasicKind.hpp
 
 $(srcdir)/AST/BasicKind.hpp : BasicTypes-gen.cpp
Index: src/Parser/DeclarationNode.cc
===================================================================
--- src/Parser/DeclarationNode.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,1030 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// DeclarationNode.cc --
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Sat May 16 12:34:05 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Feb 23 18:25:57 2024
-// Update Count     : 1533
-//
-
-#include "DeclarationNode.h"
-
-#include <cassert>                 // for assert, assertf, strict_dynamic_cast
-#include <iterator>                // for back_insert_iterator
-#include <list>                    // for list
-#include <memory>                  // for unique_ptr
-#include <ostream>                 // for operator<<, ostream, basic_ostream
-#include <string>                  // for string, operator+, allocator, char...
-
-#include "AST/Attribute.hpp"       // for Attribute
-#include "AST/Copy.hpp"            // for shallowCopy
-#include "AST/Decl.hpp"            // for Decl
-#include "AST/Expr.hpp"            // for Expr
-#include "AST/Print.hpp"           // for print
-#include "AST/Stmt.hpp"            // for AsmStmt, DirectiveStmt
-#include "AST/StorageClasses.hpp"  // for Storage::Class
-#include "AST/Type.hpp"            // for Type
-#include "Common/CodeLocation.h"   // for CodeLocation
-#include "Common/Iterate.hpp"      // for reverseIterate
-#include "Common/SemanticError.h"  // for SemanticError
-#include "Common/UniqueName.h"     // for UniqueName
-#include "Common/utility.h"        // for copy, spliceBegin
-#include "Parser/ExpressionNode.h" // for ExpressionNode
-#include "Parser/InitializerNode.h"// for InitializerNode
-#include "Parser/StatementNode.h"  // for StatementNode
-#include "TypeData.h"              // for TypeData, TypeData::Aggregate_t
-#include "TypedefTable.h"          // for TypedefTable
-
-extern TypedefTable typedefTable;
-
-using namespace std;
-
-UniqueName DeclarationNode::anonymous( "__anonymous" );
-
-extern ast::Linkage::Spec linkage;						// defined in parser.yy
-
-DeclarationNode::DeclarationNode() :
-	linkage( ::linkage ) {
-
-	variable.tyClass = ast::TypeDecl::NUMBER_OF_KINDS;
-	variable.assertions = nullptr;
-	variable.initializer = nullptr;
-
-	assert.condition = nullptr;
-	assert.message = nullptr;
-}
-
-DeclarationNode::~DeclarationNode() {
-	delete name;
-
-	delete variable.assertions;
-	delete variable.initializer;
-
-	delete type;
-	delete bitfieldWidth;
-
-	delete asmStmt;
-	// asmName, no delete, passed to next stage
-	delete initializer;
-
-	delete assert.condition;
-	delete assert.message;
-}
-
-DeclarationNode * DeclarationNode::clone() const {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->next = maybeCopy( next );
-	newnode->name = name ? new string( *name ) : nullptr;
-
-	newnode->type = maybeCopy( type );
-	newnode->inLine = inLine;
-	newnode->storageClasses = storageClasses;
-	newnode->funcSpecs = funcSpecs;
-	newnode->bitfieldWidth = maybeCopy( bitfieldWidth );
-	newnode->enumeratorValue.reset( maybeCopy( enumeratorValue.get() ) );
-	newnode->hasEllipsis = hasEllipsis;
-	newnode->linkage = linkage;
-	newnode->asmName = maybeCopy( asmName );
-	newnode->attributes = attributes;
-	newnode->initializer = maybeCopy( initializer );
-	newnode->extension = extension;
-	newnode->asmStmt = maybeCopy( asmStmt );
-	newnode->error = error;
-
-	newnode->variable.tyClass = variable.tyClass;
-	newnode->variable.assertions = maybeCopy( variable.assertions );
-	newnode->variable.initializer = maybeCopy( variable.initializer );
-
-	newnode->assert.condition = maybeCopy( assert.condition );
-	newnode->assert.message = maybeCopy( assert.message );
-	return newnode;
-} // DeclarationNode::clone
-
-void DeclarationNode::print( std::ostream & os, int indent ) const {
-	os << string( indent, ' ' );
-	if ( name ) {
-		os << *name << ": ";
-	} // if
-
-	if ( linkage != ast::Linkage::Cforall ) {
-		os << ast::Linkage::name( linkage ) << " ";
-	} // if
-
-	ast::print( os, storageClasses );
-	ast::print( os, funcSpecs );
-
-	if ( type ) {
-		type->print( os, indent );
-	} else {
-		os << "untyped entity ";
-	} // if
-
-	if ( bitfieldWidth ) {
-		os << endl << string( indent + 2, ' ' ) << "with bitfield width ";
-		bitfieldWidth->printOneLine( os );
-	} // if
-
-	if ( initializer ) {
-		os << endl << string( indent + 2, ' ' ) << "with initializer ";
-		initializer->printOneLine( os );
-		os << " maybe constructed? " << initializer->get_maybeConstructed();
-	} // if
-
-	if ( ! attributes.empty() ) {
-		os << string( indent + 2, ' ' ) << "with attributes" << endl;
-		for ( ast::ptr<ast::Attribute> const & attr : reverseIterate( attributes ) ) {
-			os << string( indent + 4, ' ' );
-			ast::print( os, attr, indent + 2 );
-		} // for
-	} // if
-
-	os << endl;
-}
-
-void DeclarationNode::printList( std::ostream & os, int indent ) const {
-	ParseList::printList( os, indent );
-	if ( hasEllipsis ) {
-		os << string( indent, ' ' )  << "and a variable number of other arguments" << endl;
-	} // if
-}
-
-DeclarationNode * DeclarationNode::newFromTypeData( TypeData * type ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = type;
-	return newnode;
-} // DeclarationNode::newFromTypeData
-
-DeclarationNode * DeclarationNode::newStorageClass( ast::Storage::Classes sc ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->storageClasses = sc;
-	return newnode;
-} // DeclarationNode::newStorageClass
-
-DeclarationNode * DeclarationNode::newFuncSpecifier( ast::Function::Specs fs ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->funcSpecs = fs;
-	return newnode;
-} // DeclarationNode::newFuncSpecifier
-
-DeclarationNode * DeclarationNode::newAggregate( ast::AggregateDecl::Aggregate kind, const string * name, ExpressionNode * actuals, DeclarationNode * fields, bool body ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = new TypeData( TypeData::Aggregate );
-	newnode->type->aggregate.kind = kind;
-	newnode->type->aggregate.anon = name == nullptr;
-	newnode->type->aggregate.name = newnode->type->aggregate.anon ? new string( DeclarationNode::anonymous.newName() ) : name;
-	newnode->type->aggregate.actuals = actuals;
-	newnode->type->aggregate.fields = fields;
-	newnode->type->aggregate.body = body;
-	return newnode;
-} // DeclarationNode::newAggregate
-
-DeclarationNode * DeclarationNode::newEnum( const string * name, DeclarationNode * constants, bool body, bool typed, DeclarationNode * base, EnumHiding hiding ) {
-	DeclarationNode * newnode = newAggregate( ast::AggregateDecl::Enum, name, nullptr, constants, body );
-	newnode->type->aggregate.typed = typed;
-	newnode->type->aggregate.hiding = hiding;
-	if ( base ) {
-		assert( typed );
-		assert( base->type );
-		newnode->type->base = base->type;
-		base->type = nullptr;
-		delete base;
-	} // if
-
-	return newnode;
-} // DeclarationNode::newEnum
-
-DeclarationNode * DeclarationNode::newName( const string * name ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	assert( ! newnode->name );
-	newnode->name = name;
-	return newnode;
-} // DeclarationNode::newName
-
-DeclarationNode * DeclarationNode::newEnumConstant( const string * name, ExpressionNode * constant ) {
-	DeclarationNode * newnode = newName( name );
-	newnode->enumeratorValue.reset( constant );
-	return newnode;
-} // DeclarationNode::newEnumConstant
-
-DeclarationNode * DeclarationNode::newEnumValueGeneric( const string * name, InitializerNode * init ) {
-	if ( nullptr == init ) {
-		return newName( name );
-	} else if ( init->get_expression() ) {
-		return newEnumConstant( name, init->get_expression() );
-	} else {
-		DeclarationNode * newnode = newName( name );
-		newnode->initializer = init;
-		return newnode;
-	} // if
-} // DeclarationNode::newEnumValueGeneric
-
-DeclarationNode * DeclarationNode::newEnumInLine( const std::string * name ) {
-	DeclarationNode * newnode = newName( name );
-	newnode->enumInLine = true;
-	return newnode;
-}
-
-DeclarationNode * DeclarationNode::newTypeParam( ast::TypeDecl::Kind tc, const string * name ) {
-	DeclarationNode * newnode = newName( name );
-	newnode->type = nullptr;
-	newnode->variable.tyClass = tc;
-	newnode->variable.assertions = nullptr;
-	return newnode;
-} // DeclarationNode::newTypeParam
-
-DeclarationNode * DeclarationNode::newTrait( const string * name, DeclarationNode * params, DeclarationNode * asserts ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = new TypeData( TypeData::Aggregate );
-	newnode->type->aggregate.name = name;
-	newnode->type->aggregate.kind = ast::AggregateDecl::Trait;
-	newnode->type->aggregate.params = params;
-	newnode->type->aggregate.fields = asserts;
-	return newnode;
-} // DeclarationNode::newTrait
-
-DeclarationNode * DeclarationNode::newTraitUse( const string * name, ExpressionNode * params ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = new TypeData( TypeData::AggregateInst );
-	newnode->type->aggInst.aggregate = new TypeData( TypeData::Aggregate );
-	newnode->type->aggInst.aggregate->aggregate.kind = ast::AggregateDecl::Trait;
-	newnode->type->aggInst.aggregate->aggregate.name = name;
-	newnode->type->aggInst.params = params;
-	return newnode;
-} // DeclarationNode::newTraitUse
-
-DeclarationNode * DeclarationNode::newTypeDecl( const string * name, DeclarationNode * typeParams ) {
-	DeclarationNode * newnode = newName( name );
-	newnode->type = new TypeData( TypeData::Symbolic );
-	newnode->type->symbolic.isTypedef = false;
-	newnode->type->symbolic.params = typeParams;
-	return newnode;
-} // DeclarationNode::newTypeDecl
-
-DeclarationNode * DeclarationNode::newPointer( DeclarationNode * qualifiers, OperKinds kind ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = new TypeData( kind == OperKinds::PointTo ? TypeData::Pointer : TypeData::Reference );
-	if ( kind == OperKinds::And ) {
-		// T && is parsed as 'And' operator rather than two references => add a second reference type
-		TypeData * td = new TypeData( TypeData::Reference );
-		td->base = newnode->type;
-		newnode->type = td;
-	}
-	if ( qualifiers ) {
-		return newnode->addQualifiers( qualifiers );
-	} else {
-		return newnode;
-	} // if
-} // DeclarationNode::newPointer
-
-DeclarationNode * DeclarationNode::newArray( ExpressionNode * size, DeclarationNode * qualifiers, bool isStatic ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = new TypeData( TypeData::Array );
-	newnode->type->array.dimension = size;
-	newnode->type->array.isStatic = isStatic;
-	newnode->type->array.isVarLen = size && !size->isExpressionType<ast::ConstantExpr *>();
-	return newnode->addQualifiers( qualifiers );
-} // DeclarationNode::newArray
-
-DeclarationNode * DeclarationNode::newVarArray( DeclarationNode * qualifiers ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = new TypeData( TypeData::Array );
-	newnode->type->array.dimension = nullptr;
-	newnode->type->array.isStatic = false;
-	newnode->type->array.isVarLen = true;
-	return newnode->addQualifiers( qualifiers );
-}
-
-DeclarationNode * DeclarationNode::newBitfield( ExpressionNode * size ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->bitfieldWidth = size;
-	return newnode;
-}
-
-DeclarationNode * DeclarationNode::newTuple( DeclarationNode * members ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = new TypeData( TypeData::Tuple );
-	newnode->type->tuple = members;
-	return newnode;
-}
-
-DeclarationNode * DeclarationNode::newTypeof( ExpressionNode * expr, bool basetypeof ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = new TypeData( basetypeof ? TypeData::Basetypeof : TypeData::Typeof );
-	newnode->type->typeexpr = expr;
-	return newnode;
-}
-
-DeclarationNode * DeclarationNode::newFunction( const string * name, DeclarationNode * ret, DeclarationNode * param, StatementNode * body ) {
-	DeclarationNode * newnode = newName( name );
-	newnode->type = new TypeData( TypeData::Function );
-	newnode->type->function.params = param;
-	newnode->type->function.body = body;
-
-	if ( ret ) {
-		newnode->type->base = ret->type;
-		ret->type = nullptr;
-		delete ret;
-	} // if
-
-	return newnode;
-} // DeclarationNode::newFunction
-
-DeclarationNode * DeclarationNode::newAttribute( const string * name, ExpressionNode * expr ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->type = nullptr;
-	std::vector<ast::ptr<ast::Expr>> exprs;
-	buildList( expr, exprs );
-	newnode->attributes.push_back( new ast::Attribute( *name, std::move( exprs ) ) );
-	delete name;
-	return newnode;
-}
-
-DeclarationNode * DeclarationNode::newDirectiveStmt( StatementNode * stmt ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->directiveStmt = stmt;
-	return newnode;
-}
-
-DeclarationNode * DeclarationNode::newAsmStmt( StatementNode * stmt ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->asmStmt = stmt;
-	return newnode;
-}
-
-DeclarationNode * DeclarationNode::newStaticAssert( ExpressionNode * condition, ast::Expr * message ) {
-	DeclarationNode * newnode = new DeclarationNode;
-	newnode->assert.condition = condition;
-	newnode->assert.message = message;
-	return newnode;
-}
-
-static void appendError( string & dst, const string & src ) {
-	if ( src.empty() ) return;
-	if ( dst.empty() ) { dst = src; return; }
-	dst += ", " + src;
-} // appendError
-
-void DeclarationNode::checkQualifiers( const TypeData * src, const TypeData * dst ) {
-	const ast::CV::Qualifiers qsrc = src->qualifiers, qdst = dst->qualifiers; // optimization
-	const ast::CV::Qualifiers duplicates = qsrc & qdst;
-
-	if ( duplicates.any() ) {
-		std::stringstream str;
-		str << "duplicate ";
-		ast::print( str, duplicates );
-		str << "qualifier(s)";
-		appendError( error, str.str() );
-	} // for
-} // DeclarationNode::checkQualifiers
-
-void DeclarationNode::checkSpecifiers( DeclarationNode * src ) {
-	ast::Function::Specs fsDups = funcSpecs & src->funcSpecs;
-	if ( fsDups.any() ) {
-		std::stringstream str;
-		str << "duplicate ";
-		ast::print( str, fsDups );
-		str << "function specifier(s)";
-		appendError( error, str.str() );
-	} // if
-
-	// Skip if everything is unset.
-	if ( storageClasses.any() && src->storageClasses.any() ) {
-		ast::Storage::Classes dups = storageClasses & src->storageClasses;
-		// Check for duplicates.
-		if ( dups.any() ) {
-			std::stringstream str;
-			str << "duplicate ";
-			ast::print( str, dups );
-			str << "storage class(es)";
-			appendError( error, str.str() );
-		// Check for conflicts.
-		} else if ( !src->storageClasses.is_threadlocal_any() ) {
-			std::stringstream str;
-			str << "conflicting ";
-			ast::print( str, ast::Storage::Classes( 1 << storageClasses.ffs() ) );
-			str << "& ";
-			ast::print( str, ast::Storage::Classes( 1 << src->storageClasses.ffs() ) );
-			str << "storage classes";
-			appendError( error, str.str() );
-			// FIX to preserve invariant of one basic storage specifier
-			src->storageClasses.reset();
-		}
-	} // if
-
-	appendError( error, src->error );
-} // DeclarationNode::checkSpecifiers
-
-DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q, bool copyattr ) {
-	funcSpecs |= q->funcSpecs;
-	storageClasses |= q->storageClasses;
-
-	if ( copyattr ) {
-		std::vector<ast::ptr<ast::Attribute>> tmp;
-		tmp.reserve( q->attributes.size() );
-		for ( auto const & attr : q->attributes ) {
-			tmp.emplace_back( ast::shallowCopy( attr.get() ) );
-		} // for
-		spliceBegin( attributes, tmp );
-	} // if
-
-	return this;
-} // DeclarationNode::copySpecifiers
-
-DeclarationNode * DeclarationNode::addQualifiers( DeclarationNode * q ) {
-	if ( ! q ) { return this; }							// empty qualifier
-
-	checkSpecifiers( q );
-	copySpecifiers( q );
-
-	if ( ! q->type ) { delete q; return this; }
-
-	if ( ! type ) {
-		type = q->type;									// reuse structure
-		q->type = nullptr;
-		delete q;
-		return this;
-	} // if
-
-	checkQualifiers( type, q->type );
-	TypeData::BuiltinType const builtin = type->builtintype;
-	if ( (builtin == TypeData::Zero || builtin == TypeData::One) && q->type->qualifiers.any() && error.length() == 0 ) {
-		SemanticWarning( yylloc, Warning::BadQualifiersZeroOne, TypeData::builtinTypeNames[builtin] );
-	} // if
-	type = ::addQualifiers( type, q->type );
-	q->type = nullptr;
-
-	delete q;
-	return this;
-} // addQualifiers
-
-DeclarationNode * DeclarationNode::addType( DeclarationNode * o, bool copyattr ) {
-	if ( !o ) return this;
-
-	checkSpecifiers( o );
-	copySpecifiers( o, copyattr );
-	if ( o->type ) {
-		type = ::addType( type, o->type, o->attributes );
-		o->type = nullptr;
-	} // if
-	if ( o->bitfieldWidth ) {
-		bitfieldWidth = o->bitfieldWidth;
-	} // if
-
-	// there may be typedefs chained onto the type
-	if ( o->next ) {
-		set_last( o->next->clone() );
-	} // if
-
-	delete o;
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addEnumBase( DeclarationNode * o ) {
-	if ( o && o->type ) {
-		type->base = o->type;
-	} // if
-	delete o;
-	return this;
-}
-
-// This code handles a special issue with the attribute transparent_union.
-//
-//    typedef union U { int i; } typedef_name __attribute__(( aligned(16) )) __attribute__(( transparent_union ))
-//
-// Here the attribute aligned goes with the typedef_name, so variables declared of this type are
-// aligned.  However, the attribute transparent_union must be moved from the typedef_name to
-// alias union U.  Currently, this is the only know attribute that must be moved from typedef to
-// alias.
-static void moveUnionAttribute( DeclarationNode * decl, DeclarationNode * unionDecl ) {
-	assert( decl->type->kind == TypeData::Symbolic );
-	assert( decl->type->symbolic.isTypedef );
-	assert( unionDecl->type->kind == TypeData::Aggregate );
-
-	if ( unionDecl->type->aggregate.kind != ast::AggregateDecl::Union ) return;
-
-	// Ignore the Aggregate_t::attributes. Why did we add that before the rework?
-	for ( auto attr = decl->attributes.begin() ; attr != decl->attributes.end() ; ) {
-		if ( (*attr)->name == "transparent_union" || (*attr)->name == "__transparent_union__" ) {
-			unionDecl->attributes.emplace_back( attr->release() );
-			attr = decl->attributes.erase( attr );
-		} else {
-			++attr;
-		}
-	}
-}
-
-// Helper for addTypedef, handles the case where the typedef wraps an
-// aggregate declaration (not a type), returns a chain of nodes.
-static DeclarationNode * addTypedefAggr(
-		DeclarationNode * olddecl, TypeData * newtype ) {
-	TypeData *& oldaggr = olddecl->type->aggInst.aggregate;
-
-	// Handle anonymous aggregates: typedef struct { int i; } foo
-	// Give the typedefed type a consistent name across translation units.
-	if ( oldaggr->aggregate.anon ) {
-		delete oldaggr->aggregate.name;
-		oldaggr->aggregate.name = new string( "__anonymous_" + *olddecl->name );
-		oldaggr->aggregate.anon = false;
-		oldaggr->qualifiers.reset();
-	}
-
-	// Replace the wrapped TypeData with a forward declaration.
-	TypeData * newaggr = new TypeData( TypeData::Aggregate );
-	newaggr->aggregate.kind = oldaggr->aggregate.kind;
-	newaggr->aggregate.name = oldaggr->aggregate.name ? new string( *oldaggr->aggregate.name ) : nullptr;
-	newaggr->aggregate.body = false;
-	newaggr->aggregate.anon = oldaggr->aggregate.anon;
-	newaggr->aggregate.typed = oldaggr->aggregate.typed;
-	newaggr->aggregate.hiding = oldaggr->aggregate.hiding;
-	swap( newaggr, oldaggr );
-
-	newtype->base = olddecl->type;
-	olddecl->type = newtype;
-	DeclarationNode * newdecl = new DeclarationNode;
-	newdecl->type = newaggr;
-	newdecl->next = olddecl;
-
-	moveUnionAttribute( olddecl, newdecl );
-
-	return newdecl;
-}
-
-// Wrap the declaration in a typedef. It actually does that by modifying the
-// existing declaration, and may split it into two declarations.
-// This only happens if the wrapped type is actually a declaration of a SUE
-// type. If it does, the DeclarationNode for the SUE declaration is the node
-// returned, make sure later transformations are applied to the right node.
-DeclarationNode * DeclarationNode::addTypedef() {
-	TypeData * newtype = new TypeData( TypeData::Symbolic );
-	newtype->symbolic.params = nullptr;
-	newtype->symbolic.isTypedef = true;
-	newtype->symbolic.name = name ? new string( *name ) : nullptr;
-	// If this typedef is wrapping an aggregate, separate them out.
-	if ( TypeData::AggregateInst == type->kind
-			&& TypeData::Aggregate == type->aggInst.aggregate->kind
-			&& type->aggInst.aggregate->aggregate.body ) {
-		return addTypedefAggr( this, newtype );
-	// There is no internal declaration, just a type.
-	} else {
-		newtype->base = type;
-		type = newtype;
-		return this;
-	}
-}
-
-DeclarationNode * DeclarationNode::addAssertions( DeclarationNode * assertions ) {
-	if ( variable.tyClass != ast::TypeDecl::NUMBER_OF_KINDS ) {
-		extend( variable.assertions, assertions );
-		return this;
-	} // if
-
-	assert( type );
-	assert( TypeData::Symbolic == type->kind );
-	extend( type->symbolic.assertions, assertions );
-
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addName( string * newname ) {
-	assert( ! name );
-	name = newname;
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addAsmName( DeclarationNode * newname ) {
-	assert( ! asmName );
-	asmName = newname ? newname->asmName : nullptr;
-	return this->addQualifiers( newname );
-}
-
-DeclarationNode * DeclarationNode::addBitfield( ExpressionNode * size ) {
-	bitfieldWidth = size;
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addVarArgs() {
-	assert( type );
-	hasEllipsis = true;
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addFunctionBody( StatementNode * body, ExpressionNode * withExprs ) {
-	assert( type );
-	assert( type->kind == TypeData::Function );
-	assert( ! type->function.body );
-	type->function.body = body;
-	type->function.withExprs = withExprs;
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addOldDeclList( DeclarationNode * list ) {
-	assert( type );
-	assert( type->kind == TypeData::Function );
-	assert( ! type->function.oldDeclList );
-	type->function.oldDeclList = list;
-	return this;
-}
-
-DeclarationNode * DeclarationNode::setBase( TypeData * newType ) {
-	if ( type ) {
-		type->setLastBase( newType );
-	} else {
-		type = newType;
-	} // if
-	return this;
-}
-
-DeclarationNode * DeclarationNode::copyAttribute( DeclarationNode * a ) {
-	if ( a ) {
-		spliceBegin( attributes, a->attributes );
-		a->attributes.clear();
-	} // if
-	return this;
-} // copyAttribute
-
-DeclarationNode * DeclarationNode::addPointer( DeclarationNode * p ) {
-	if ( p ) {
-		assert( p->type->kind == TypeData::Pointer || p->type->kind == TypeData::Reference );
-		setBase( p->type );
-		p->type = nullptr;
-		copyAttribute( p );
-		delete p;
-	} // if
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addArray( DeclarationNode * a ) {
-	if ( a ) {
-		assert( a->type->kind == TypeData::Array );
-		setBase( a->type );
-		a->type = nullptr;
-		copyAttribute( a );
-		delete a;
-	} // if
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addNewPointer( DeclarationNode * p ) {
-	if ( !p ) return this;
-	assert( p->type->kind == TypeData::Pointer || p->type->kind == TypeData::Reference );
-	if ( type ) {
-		p->type->base = makeNewBase( type );
-		type = nullptr;
-	} // if
-	delete this;
-	return p;
-}
-
-DeclarationNode * DeclarationNode::addNewArray( DeclarationNode * a ) {
-	if ( !a ) return this;
-	assert( a->type->kind == TypeData::Array );
-	if ( type ) {
-		a->type->setLastBase( makeNewBase( type ) );
-		type = nullptr;
-	} // if
-	delete this;
-	return a;
-}
-
-DeclarationNode * DeclarationNode::addParamList( DeclarationNode * params ) {
-	TypeData * ftype = new TypeData( TypeData::Function );
-	ftype->function.params = params;
-	setBase( ftype );
-	return this;
-}
-
-static TypeData * addIdListToType( TypeData * type, DeclarationNode * ids ) {
-	if ( type ) {
-		if ( type->kind != TypeData::Function ) {
-			type->base = addIdListToType( type->base, ids );
-		} else {
-			type->function.idList = ids;
-		} // if
-		return type;
-	} else {
-		TypeData * newtype = new TypeData( TypeData::Function );
-		newtype->function.idList = ids;
-		return newtype;
-	} // if
-} // addIdListToType
-
-DeclarationNode * DeclarationNode::addIdList( DeclarationNode * ids ) {
-	type = addIdListToType( type, ids );
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addInitializer( InitializerNode * init ) {
-	initializer = init;
-	return this;
-}
-
-DeclarationNode * DeclarationNode::addTypeInitializer( DeclarationNode * init ) {
-	assertf( variable.tyClass != ast::TypeDecl::NUMBER_OF_KINDS, "Called addTypeInitializer on something that isn't a type variable." );
-	variable.initializer = init;
-	return this;
-}
-
-DeclarationNode * DeclarationNode::cloneType( string * name ) {
-	DeclarationNode * newnode = newName( name );
-	newnode->type = maybeCopy( type );
-	newnode->copySpecifiers( this );
-	return newnode;
-}
-
-DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o, bool copyattr ) {
-	if ( ! o ) return nullptr;
-	o->copySpecifiers( this, copyattr );
-	if ( type ) {
-		o->type = ::cloneBaseType( type, o->type );
-	} // if
-	return o;
-}
-
-DeclarationNode * DeclarationNode::extractAggregate() const {
-	if ( type ) {
-		TypeData * ret = typeextractAggregate( type );
-		if ( ret ) {
-			DeclarationNode * newnode = new DeclarationNode;
-			newnode->type = ret;
-			if ( ret->kind == TypeData::Aggregate ) {
-				newnode->attributes.swap( ret->aggregate.attributes );
-			} // if
-			return newnode;
-		} // if
-	} // if
-	return nullptr;
-}
-
-// Get the non-anonymous name of the instance type of the declaration,
-// if one exists.
-static const std::string * getInstTypeOfName( ast::Decl * decl ) {
-	if ( auto dwt = dynamic_cast<ast::DeclWithType *>( decl ) ) {
-		if ( auto aggr = dynamic_cast<ast::BaseInstType const *>( dwt->get_type() ) ) {
-			if ( aggr->name.find("anonymous") == std::string::npos ) {
-				return &aggr->name;
-			}
-		}
-	}
-	return nullptr;
-}
-
-void buildList( DeclarationNode * firstNode, std::vector<ast::ptr<ast::Decl>> & outputList ) {
-	SemanticErrorException errors;
-	std::back_insert_iterator<std::vector<ast::ptr<ast::Decl>>> out( outputList );
-
-	for ( const DeclarationNode * cur = firstNode ; cur ; cur = cur->next ) {
-		try {
-			bool extracted_named = false;
-
-			if ( DeclarationNode * extr = cur->extractAggregate() ) {
-				assert( cur->type );
-
-				if ( ast::Decl * decl = extr->build() ) {
-					*out++ = decl;
-
-					// need to remember the cases where a declaration contains an anonymous aggregate definition
-					assert( extr->type );
-					if ( extr->type->kind == TypeData::Aggregate ) {
-						// typedef struct { int A } B is the only case?
-						extracted_named = ! extr->type->aggregate.anon;
-					} else {
-						extracted_named = true;
-					}
-				} // if
-				delete extr;
-			} // if
-
-			if ( ast::Decl * decl = cur->build() ) {
-				if ( "" == decl->name && !cur->get_inLine() ) {
-					// Don't include anonymous declaration for named aggregates,
-					// but do include them for anonymous aggregates, e.g.:
-					// struct S {
-					//   struct T { int x; }; // no anonymous member
-					//   struct { int y; };   // anonymous member
-					//   struct T;            // anonymous member
-					// };
-					if ( extracted_named ) {
-						continue;
-					}
-
-					if ( auto name = getInstTypeOfName( decl ) ) {
-						// Temporary: warn about anonymous member declarations of named types, since
-						// this conflicts with the syntax for the forward declaration of an anonymous type.
-						SemanticWarning( cur->location, Warning::AggrForwardDecl, name->c_str() );
-					}
-				} // if
-				*out++ = decl;
-			} // if
-		} catch ( SemanticErrorException & e ) {
-			errors.append( e );
-		} // try
-	} // for
-
-	if ( ! errors.isEmpty() ) {
-		throw errors;
-	} // if
-} // buildList
-
-// currently only builds assertions, function parameters, and return values
-void buildList( DeclarationNode * firstNode, std::vector<ast::ptr<ast::DeclWithType>> & outputList ) {
-	SemanticErrorException errors;
-	std::back_insert_iterator<std::vector<ast::ptr<ast::DeclWithType>>> out( outputList );
-
-	for ( const DeclarationNode * cur = firstNode; cur; cur = cur->next ) {
-		try {
-			ast::Decl * decl = cur->build();
-			assertf( decl, "buildList: build for ast::DeclWithType." );
-			if ( ast::DeclWithType * dwt = dynamic_cast<ast::DeclWithType *>( decl ) ) {
-				dwt->location = cur->location;
-				*out++ = dwt;
-			} else if ( ast::StructDecl * agg = dynamic_cast<ast::StructDecl *>( decl ) ) {
-				// e.g., int foo(struct S) {}
-				auto inst = new ast::StructInstType( agg->name );
-				auto obj = new ast::ObjectDecl( cur->location, "", inst );
-				obj->linkage = linkage;
-				*out++ = obj;
-				delete agg;
-			} else if ( ast::UnionDecl * agg = dynamic_cast<ast::UnionDecl *>( decl ) ) {
-				// e.g., int foo(union U) {}
-				auto inst = new ast::UnionInstType( agg->name );
-				auto obj = new ast::ObjectDecl( cur->location,
-					"", inst, nullptr, ast::Storage::Classes(),
-					linkage );
-				*out++ = obj;
-			} else if ( ast::EnumDecl * agg = dynamic_cast<ast::EnumDecl *>( decl ) ) {
-				// e.g., int foo(enum E) {}
-				auto inst = new ast::EnumInstType( agg->name );
-				auto obj = new ast::ObjectDecl( cur->location,
-					"",
-					inst,
-					nullptr,
-					ast::Storage::Classes(),
-					linkage
-				);
-				*out++ = obj;
-			} else {
-				assertf( false, "buildList: Could not convert to ast::DeclWithType." );
-			} // if
-		} catch ( SemanticErrorException & e ) {
-			errors.append( e );
-		} // try
-	} // for
-
-	if ( ! errors.isEmpty() ) {
-		throw errors;
-	} // if
-} // buildList
-
-void buildTypeList( const DeclarationNode * firstNode,
-		std::vector<ast::ptr<ast::Type>> & outputList ) {
-	SemanticErrorException errors;
-	std::back_insert_iterator<std::vector<ast::ptr<ast::Type>>> out( outputList );
-
-	for ( const DeclarationNode * cur = firstNode ; cur ; cur = cur->next ) {
-		try {
-			* out++ = cur->buildType();
-		} catch ( SemanticErrorException & e ) {
-			errors.append( e );
-		} // try
-	} // for
-
-	if ( ! errors.isEmpty() ) {
-		throw errors;
-	} // if
-} // buildTypeList
-
-ast::Decl * DeclarationNode::build() const {
-	if ( ! error.empty() ) SemanticError( this, error + " in declaration of " );
-
-	if ( asmStmt ) {
-		auto stmt = strict_dynamic_cast<ast::AsmStmt *>( asmStmt->build() );
-		return new ast::AsmDecl( stmt->location, stmt );
-	} // if
-	if ( directiveStmt ) {
-		auto stmt = strict_dynamic_cast<ast::DirectiveStmt *>( directiveStmt->build() );
-		return new ast::DirectiveDecl( stmt->location, stmt );
-	} // if
-
-	if ( variable.tyClass != ast::TypeDecl::NUMBER_OF_KINDS ) {
-		// otype is internally converted to dtype + otype parameters
-		static const ast::TypeDecl::Kind kindMap[] = { ast::TypeDecl::Dtype, ast::TypeDecl::Dtype, ast::TypeDecl::Dtype, ast::TypeDecl::Ftype, ast::TypeDecl::Ttype, ast::TypeDecl::Dimension };
-		static_assert( sizeof(kindMap) / sizeof(kindMap[0]) == ast::TypeDecl::NUMBER_OF_KINDS, "DeclarationNode::build: kindMap is out of sync." );
-		assertf( variable.tyClass < sizeof(kindMap)/sizeof(kindMap[0]), "Variable's tyClass is out of bounds." );
-		ast::TypeDecl * ret = new ast::TypeDecl( location,
-			*name,
-			ast::Storage::Classes(),
-			(ast::Type *)nullptr,
-			kindMap[ variable.tyClass ],
-			variable.tyClass == ast::TypeDecl::Otype || variable.tyClass == ast::TypeDecl::DStype,
-			variable.initializer ? variable.initializer->buildType() : nullptr
-		);
-		buildList( variable.assertions, ret->assertions );
-		return ret;
-	} // if
-
-	if ( type ) {
-		// Function specifiers can only appear on a function definition/declaration.
-		//
-		//    inline _Noreturn int f();			// allowed
-		//    inline _Noreturn int g( int i );	// allowed
-		//    inline _Noreturn int i;			// disallowed
-		if ( type->kind != TypeData::Function && funcSpecs.any() ) {
-			SemanticError( this, "invalid function specifier for " );
-		} // if
-		// Forall qualifier can only appear on a function/aggregate definition/declaration.
-		//
-		//    forall int f();					// allowed
-		//    forall int g( int i );			// allowed
-		//    forall int i;						// disallowed
-		if ( type->kind != TypeData::Function && type->forall ) {
-			SemanticError( this, "invalid type qualifier for " );
-		} // if
-		bool isDelete = initializer && initializer->get_isDelete();
-		ast::Decl * decl = buildDecl(
-			type,
-			name ? *name : string( "" ),
-			storageClasses,
-			maybeBuild( bitfieldWidth ),
-			funcSpecs,
-			linkage,
-			asmName,
-			isDelete ? nullptr : maybeBuild( initializer ),
-			copy( attributes )
-		)->set_extension( extension );
-		if ( isDelete ) {
-			auto dwt = strict_dynamic_cast<ast::DeclWithType *>( decl );
-			dwt->isDeleted = true;
-		}
-		return decl;
-	} // if
-
-	if ( assert.condition ) {
-		auto cond = maybeBuild( assert.condition );
-		auto msg = strict_dynamic_cast<ast::ConstantExpr *>( maybeCopy( assert.message ) );
-		return new ast::StaticAssertDecl( location, cond, msg );
-	}
-
-	// SUE's cannot have function specifiers, either
-	//
-	//    inline _Noreturn struct S { ... };		// disallowed
-	//    inline _Noreturn enum   E { ... };		// disallowed
-	if ( funcSpecs.any() ) {
-		SemanticError( this, "invalid function specifier for " );
-	} // if
-	if ( enumInLine ) {
-		return new ast::InlineMemberDecl( location,
-			*name, (ast::Type*)nullptr, storageClasses, linkage );
-	} // if
-	assertf( name, "ObjectDecl must a have name\n" );
-	auto ret = new ast::ObjectDecl( location,
-		*name,
-		(ast::Type*)nullptr,
-		maybeBuild( initializer ),
-		storageClasses,
-		linkage,
-		maybeBuild( bitfieldWidth )
-	);
-	ret->asmName = asmName;
-	ret->extension = extension;
-	return ret;
-}
-
-ast::Type * DeclarationNode::buildType() const {
-	assert( type );
-
-	switch ( type->kind ) {
-	case TypeData::Aggregate: {
-		ast::BaseInstType * ret =
-			buildComAggInst( type, copy( attributes ), linkage );
-		buildList( type->aggregate.actuals, ret->params );
-		return ret;
-	}
-	case TypeData::Symbolic: {
-		ast::TypeInstType * ret = new ast::TypeInstType(
-			*type->symbolic.name,
-			// This is just a default, the true value is not known yet.
-			ast::TypeDecl::Dtype,
-			buildQualifiers( type ),
-			copy( attributes ) );
-		buildList( type->symbolic.actuals, ret->params );
-		return ret;
-	}
-	default:
-		ast::Type * simpletypes = typebuild( type );
-		// copy because member is const
-		simpletypes->attributes = attributes;
-		return simpletypes;
-	} // switch
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/DeclarationNode.cpp
===================================================================
--- src/Parser/DeclarationNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/DeclarationNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,1030 @@
+//
+// 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.
+//
+// DeclarationNode.cpp --
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Sat May 16 12:34:05 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri Feb 23 18:25:57 2024
+// Update Count     : 1533
+//
+
+#include "DeclarationNode.hpp"
+
+#include <cassert>                     // for assert, assertf, strict_dynami...
+#include <iterator>                    // for back_insert_iterator
+#include <list>                        // for list
+#include <memory>                      // for unique_ptr
+#include <ostream>                     // for operator<<, ostream, basic_ost...
+#include <string>                      // for string, operator+, allocator, ...
+
+#include "AST/Attribute.hpp"           // for Attribute
+#include "AST/Copy.hpp"                // for shallowCopy
+#include "AST/Decl.hpp"                // for Decl
+#include "AST/Expr.hpp"                // for Expr
+#include "AST/Print.hpp"               // for print
+#include "AST/Stmt.hpp"                // for AsmStmt, DirectiveStmt
+#include "AST/StorageClasses.hpp"      // for Storage::Class
+#include "AST/Type.hpp"                // for Type
+#include "Common/CodeLocation.hpp"     // for CodeLocation
+#include "Common/Iterate.hpp"          // for reverseIterate
+#include "Common/SemanticError.hpp"    // for SemanticError
+#include "Common/UniqueName.hpp"       // for UniqueName
+#include "Common/Utility.hpp"          // for copy, spliceBegin
+#include "Parser/ExpressionNode.hpp"   // for ExpressionNode
+#include "Parser/InitializerNode.hpp"  // for InitializerNode
+#include "Parser/StatementNode.hpp"    // for StatementNode
+#include "TypeData.hpp"                // for TypeData, TypeData::Aggregate_t
+#include "TypedefTable.hpp"            // for TypedefTable
+
+extern TypedefTable typedefTable;
+
+using namespace std;
+
+UniqueName DeclarationNode::anonymous( "__anonymous" );
+
+extern ast::Linkage::Spec linkage;						// defined in parser.yy
+
+DeclarationNode::DeclarationNode() :
+	linkage( ::linkage ) {
+
+	variable.tyClass = ast::TypeDecl::NUMBER_OF_KINDS;
+	variable.assertions = nullptr;
+	variable.initializer = nullptr;
+
+	assert.condition = nullptr;
+	assert.message = nullptr;
+}
+
+DeclarationNode::~DeclarationNode() {
+	delete name;
+
+	delete variable.assertions;
+	delete variable.initializer;
+
+	delete type;
+	delete bitfieldWidth;
+
+	delete asmStmt;
+	// asmName, no delete, passed to next stage
+	delete initializer;
+
+	delete assert.condition;
+	delete assert.message;
+}
+
+DeclarationNode * DeclarationNode::clone() const {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->next = maybeCopy( next );
+	newnode->name = name ? new string( *name ) : nullptr;
+
+	newnode->type = maybeCopy( type );
+	newnode->inLine = inLine;
+	newnode->storageClasses = storageClasses;
+	newnode->funcSpecs = funcSpecs;
+	newnode->bitfieldWidth = maybeCopy( bitfieldWidth );
+	newnode->enumeratorValue.reset( maybeCopy( enumeratorValue.get() ) );
+	newnode->hasEllipsis = hasEllipsis;
+	newnode->linkage = linkage;
+	newnode->asmName = maybeCopy( asmName );
+	newnode->attributes = attributes;
+	newnode->initializer = maybeCopy( initializer );
+	newnode->extension = extension;
+	newnode->asmStmt = maybeCopy( asmStmt );
+	newnode->error = error;
+
+	newnode->variable.tyClass = variable.tyClass;
+	newnode->variable.assertions = maybeCopy( variable.assertions );
+	newnode->variable.initializer = maybeCopy( variable.initializer );
+
+	newnode->assert.condition = maybeCopy( assert.condition );
+	newnode->assert.message = maybeCopy( assert.message );
+	return newnode;
+} // DeclarationNode::clone
+
+void DeclarationNode::print( std::ostream & os, int indent ) const {
+	os << string( indent, ' ' );
+	if ( name ) {
+		os << *name << ": ";
+	} // if
+
+	if ( linkage != ast::Linkage::Cforall ) {
+		os << ast::Linkage::name( linkage ) << " ";
+	} // if
+
+	ast::print( os, storageClasses );
+	ast::print( os, funcSpecs );
+
+	if ( type ) {
+		type->print( os, indent );
+	} else {
+		os << "untyped entity ";
+	} // if
+
+	if ( bitfieldWidth ) {
+		os << endl << string( indent + 2, ' ' ) << "with bitfield width ";
+		bitfieldWidth->printOneLine( os );
+	} // if
+
+	if ( initializer ) {
+		os << endl << string( indent + 2, ' ' ) << "with initializer ";
+		initializer->printOneLine( os );
+		os << " maybe constructed? " << initializer->get_maybeConstructed();
+	} // if
+
+	if ( ! attributes.empty() ) {
+		os << string( indent + 2, ' ' ) << "with attributes" << endl;
+		for ( ast::ptr<ast::Attribute> const & attr : reverseIterate( attributes ) ) {
+			os << string( indent + 4, ' ' );
+			ast::print( os, attr, indent + 2 );
+		} // for
+	} // if
+
+	os << endl;
+}
+
+void DeclarationNode::printList( std::ostream & os, int indent ) const {
+	ParseList::printList( os, indent );
+	if ( hasEllipsis ) {
+		os << string( indent, ' ' )  << "and a variable number of other arguments" << endl;
+	} // if
+}
+
+DeclarationNode * DeclarationNode::newFromTypeData( TypeData * type ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = type;
+	return newnode;
+} // DeclarationNode::newFromTypeData
+
+DeclarationNode * DeclarationNode::newStorageClass( ast::Storage::Classes sc ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->storageClasses = sc;
+	return newnode;
+} // DeclarationNode::newStorageClass
+
+DeclarationNode * DeclarationNode::newFuncSpecifier( ast::Function::Specs fs ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->funcSpecs = fs;
+	return newnode;
+} // DeclarationNode::newFuncSpecifier
+
+DeclarationNode * DeclarationNode::newAggregate( ast::AggregateDecl::Aggregate kind, const string * name, ExpressionNode * actuals, DeclarationNode * fields, bool body ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = new TypeData( TypeData::Aggregate );
+	newnode->type->aggregate.kind = kind;
+	newnode->type->aggregate.anon = name == nullptr;
+	newnode->type->aggregate.name = newnode->type->aggregate.anon ? new string( DeclarationNode::anonymous.newName() ) : name;
+	newnode->type->aggregate.actuals = actuals;
+	newnode->type->aggregate.fields = fields;
+	newnode->type->aggregate.body = body;
+	return newnode;
+} // DeclarationNode::newAggregate
+
+DeclarationNode * DeclarationNode::newEnum( const string * name, DeclarationNode * constants, bool body, bool typed, DeclarationNode * base, EnumHiding hiding ) {
+	DeclarationNode * newnode = newAggregate( ast::AggregateDecl::Enum, name, nullptr, constants, body );
+	newnode->type->aggregate.typed = typed;
+	newnode->type->aggregate.hiding = hiding;
+	if ( base ) {
+		assert( typed );
+		assert( base->type );
+		newnode->type->base = base->type;
+		base->type = nullptr;
+		delete base;
+	} // if
+
+	return newnode;
+} // DeclarationNode::newEnum
+
+DeclarationNode * DeclarationNode::newName( const string * name ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	assert( ! newnode->name );
+	newnode->name = name;
+	return newnode;
+} // DeclarationNode::newName
+
+DeclarationNode * DeclarationNode::newEnumConstant( const string * name, ExpressionNode * constant ) {
+	DeclarationNode * newnode = newName( name );
+	newnode->enumeratorValue.reset( constant );
+	return newnode;
+} // DeclarationNode::newEnumConstant
+
+DeclarationNode * DeclarationNode::newEnumValueGeneric( const string * name, InitializerNode * init ) {
+	if ( nullptr == init ) {
+		return newName( name );
+	} else if ( init->get_expression() ) {
+		return newEnumConstant( name, init->get_expression() );
+	} else {
+		DeclarationNode * newnode = newName( name );
+		newnode->initializer = init;
+		return newnode;
+	} // if
+} // DeclarationNode::newEnumValueGeneric
+
+DeclarationNode * DeclarationNode::newEnumInLine( const std::string * name ) {
+	DeclarationNode * newnode = newName( name );
+	newnode->enumInLine = true;
+	return newnode;
+}
+
+DeclarationNode * DeclarationNode::newTypeParam( ast::TypeDecl::Kind tc, const string * name ) {
+	DeclarationNode * newnode = newName( name );
+	newnode->type = nullptr;
+	newnode->variable.tyClass = tc;
+	newnode->variable.assertions = nullptr;
+	return newnode;
+} // DeclarationNode::newTypeParam
+
+DeclarationNode * DeclarationNode::newTrait( const string * name, DeclarationNode * params, DeclarationNode * asserts ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = new TypeData( TypeData::Aggregate );
+	newnode->type->aggregate.name = name;
+	newnode->type->aggregate.kind = ast::AggregateDecl::Trait;
+	newnode->type->aggregate.params = params;
+	newnode->type->aggregate.fields = asserts;
+	return newnode;
+} // DeclarationNode::newTrait
+
+DeclarationNode * DeclarationNode::newTraitUse( const string * name, ExpressionNode * params ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = new TypeData( TypeData::AggregateInst );
+	newnode->type->aggInst.aggregate = new TypeData( TypeData::Aggregate );
+	newnode->type->aggInst.aggregate->aggregate.kind = ast::AggregateDecl::Trait;
+	newnode->type->aggInst.aggregate->aggregate.name = name;
+	newnode->type->aggInst.params = params;
+	return newnode;
+} // DeclarationNode::newTraitUse
+
+DeclarationNode * DeclarationNode::newTypeDecl( const string * name, DeclarationNode * typeParams ) {
+	DeclarationNode * newnode = newName( name );
+	newnode->type = new TypeData( TypeData::Symbolic );
+	newnode->type->symbolic.isTypedef = false;
+	newnode->type->symbolic.params = typeParams;
+	return newnode;
+} // DeclarationNode::newTypeDecl
+
+DeclarationNode * DeclarationNode::newPointer( DeclarationNode * qualifiers, OperKinds kind ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = new TypeData( kind == OperKinds::PointTo ? TypeData::Pointer : TypeData::Reference );
+	if ( kind == OperKinds::And ) {
+		// T && is parsed as 'And' operator rather than two references => add a second reference type
+		TypeData * td = new TypeData( TypeData::Reference );
+		td->base = newnode->type;
+		newnode->type = td;
+	}
+	if ( qualifiers ) {
+		return newnode->addQualifiers( qualifiers );
+	} else {
+		return newnode;
+	} // if
+} // DeclarationNode::newPointer
+
+DeclarationNode * DeclarationNode::newArray( ExpressionNode * size, DeclarationNode * qualifiers, bool isStatic ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = new TypeData( TypeData::Array );
+	newnode->type->array.dimension = size;
+	newnode->type->array.isStatic = isStatic;
+	newnode->type->array.isVarLen = size && !size->isExpressionType<ast::ConstantExpr *>();
+	return newnode->addQualifiers( qualifiers );
+} // DeclarationNode::newArray
+
+DeclarationNode * DeclarationNode::newVarArray( DeclarationNode * qualifiers ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = new TypeData( TypeData::Array );
+	newnode->type->array.dimension = nullptr;
+	newnode->type->array.isStatic = false;
+	newnode->type->array.isVarLen = true;
+	return newnode->addQualifiers( qualifiers );
+}
+
+DeclarationNode * DeclarationNode::newBitfield( ExpressionNode * size ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->bitfieldWidth = size;
+	return newnode;
+}
+
+DeclarationNode * DeclarationNode::newTuple( DeclarationNode * members ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = new TypeData( TypeData::Tuple );
+	newnode->type->tuple = members;
+	return newnode;
+}
+
+DeclarationNode * DeclarationNode::newTypeof( ExpressionNode * expr, bool basetypeof ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = new TypeData( basetypeof ? TypeData::Basetypeof : TypeData::Typeof );
+	newnode->type->typeexpr = expr;
+	return newnode;
+}
+
+DeclarationNode * DeclarationNode::newFunction( const string * name, DeclarationNode * ret, DeclarationNode * param, StatementNode * body ) {
+	DeclarationNode * newnode = newName( name );
+	newnode->type = new TypeData( TypeData::Function );
+	newnode->type->function.params = param;
+	newnode->type->function.body = body;
+
+	if ( ret ) {
+		newnode->type->base = ret->type;
+		ret->type = nullptr;
+		delete ret;
+	} // if
+
+	return newnode;
+} // DeclarationNode::newFunction
+
+DeclarationNode * DeclarationNode::newAttribute( const string * name, ExpressionNode * expr ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->type = nullptr;
+	std::vector<ast::ptr<ast::Expr>> exprs;
+	buildList( expr, exprs );
+	newnode->attributes.push_back( new ast::Attribute( *name, std::move( exprs ) ) );
+	delete name;
+	return newnode;
+}
+
+DeclarationNode * DeclarationNode::newDirectiveStmt( StatementNode * stmt ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->directiveStmt = stmt;
+	return newnode;
+}
+
+DeclarationNode * DeclarationNode::newAsmStmt( StatementNode * stmt ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->asmStmt = stmt;
+	return newnode;
+}
+
+DeclarationNode * DeclarationNode::newStaticAssert( ExpressionNode * condition, ast::Expr * message ) {
+	DeclarationNode * newnode = new DeclarationNode;
+	newnode->assert.condition = condition;
+	newnode->assert.message = message;
+	return newnode;
+}
+
+static void appendError( string & dst, const string & src ) {
+	if ( src.empty() ) return;
+	if ( dst.empty() ) { dst = src; return; }
+	dst += ", " + src;
+} // appendError
+
+void DeclarationNode::checkQualifiers( const TypeData * src, const TypeData * dst ) {
+	const ast::CV::Qualifiers qsrc = src->qualifiers, qdst = dst->qualifiers; // optimization
+	const ast::CV::Qualifiers duplicates = qsrc & qdst;
+
+	if ( duplicates.any() ) {
+		std::stringstream str;
+		str << "duplicate ";
+		ast::print( str, duplicates );
+		str << "qualifier(s)";
+		appendError( error, str.str() );
+	} // for
+} // DeclarationNode::checkQualifiers
+
+void DeclarationNode::checkSpecifiers( DeclarationNode * src ) {
+	ast::Function::Specs fsDups = funcSpecs & src->funcSpecs;
+	if ( fsDups.any() ) {
+		std::stringstream str;
+		str << "duplicate ";
+		ast::print( str, fsDups );
+		str << "function specifier(s)";
+		appendError( error, str.str() );
+	} // if
+
+	// Skip if everything is unset.
+	if ( storageClasses.any() && src->storageClasses.any() ) {
+		ast::Storage::Classes dups = storageClasses & src->storageClasses;
+		// Check for duplicates.
+		if ( dups.any() ) {
+			std::stringstream str;
+			str << "duplicate ";
+			ast::print( str, dups );
+			str << "storage class(es)";
+			appendError( error, str.str() );
+		// Check for conflicts.
+		} else if ( !src->storageClasses.is_threadlocal_any() ) {
+			std::stringstream str;
+			str << "conflicting ";
+			ast::print( str, ast::Storage::Classes( 1 << storageClasses.ffs() ) );
+			str << "& ";
+			ast::print( str, ast::Storage::Classes( 1 << src->storageClasses.ffs() ) );
+			str << "storage classes";
+			appendError( error, str.str() );
+			// FIX to preserve invariant of one basic storage specifier
+			src->storageClasses.reset();
+		}
+	} // if
+
+	appendError( error, src->error );
+} // DeclarationNode::checkSpecifiers
+
+DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q, bool copyattr ) {
+	funcSpecs |= q->funcSpecs;
+	storageClasses |= q->storageClasses;
+
+	if ( copyattr ) {
+		std::vector<ast::ptr<ast::Attribute>> tmp;
+		tmp.reserve( q->attributes.size() );
+		for ( auto const & attr : q->attributes ) {
+			tmp.emplace_back( ast::shallowCopy( attr.get() ) );
+		} // for
+		spliceBegin( attributes, tmp );
+	} // if
+
+	return this;
+} // DeclarationNode::copySpecifiers
+
+DeclarationNode * DeclarationNode::addQualifiers( DeclarationNode * q ) {
+	if ( ! q ) { return this; }							// empty qualifier
+
+	checkSpecifiers( q );
+	copySpecifiers( q );
+
+	if ( ! q->type ) { delete q; return this; }
+
+	if ( ! type ) {
+		type = q->type;									// reuse structure
+		q->type = nullptr;
+		delete q;
+		return this;
+	} // if
+
+	checkQualifiers( type, q->type );
+	TypeData::BuiltinType const builtin = type->builtintype;
+	if ( (builtin == TypeData::Zero || builtin == TypeData::One) && q->type->qualifiers.any() && error.length() == 0 ) {
+		SemanticWarning( yylloc, Warning::BadQualifiersZeroOne, TypeData::builtinTypeNames[builtin] );
+	} // if
+	type = ::addQualifiers( type, q->type );
+	q->type = nullptr;
+
+	delete q;
+	return this;
+} // addQualifiers
+
+DeclarationNode * DeclarationNode::addType( DeclarationNode * o, bool copyattr ) {
+	if ( !o ) return this;
+
+	checkSpecifiers( o );
+	copySpecifiers( o, copyattr );
+	if ( o->type ) {
+		type = ::addType( type, o->type, o->attributes );
+		o->type = nullptr;
+	} // if
+	if ( o->bitfieldWidth ) {
+		bitfieldWidth = o->bitfieldWidth;
+	} // if
+
+	// there may be typedefs chained onto the type
+	if ( o->next ) {
+		set_last( o->next->clone() );
+	} // if
+
+	delete o;
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addEnumBase( DeclarationNode * o ) {
+	if ( o && o->type ) {
+		type->base = o->type;
+	} // if
+	delete o;
+	return this;
+}
+
+// This code handles a special issue with the attribute transparent_union.
+//
+//    typedef union U { int i; } typedef_name __attribute__(( aligned(16) )) __attribute__(( transparent_union ))
+//
+// Here the attribute aligned goes with the typedef_name, so variables declared of this type are
+// aligned.  However, the attribute transparent_union must be moved from the typedef_name to
+// alias union U.  Currently, this is the only know attribute that must be moved from typedef to
+// alias.
+static void moveUnionAttribute( DeclarationNode * decl, DeclarationNode * unionDecl ) {
+	assert( decl->type->kind == TypeData::Symbolic );
+	assert( decl->type->symbolic.isTypedef );
+	assert( unionDecl->type->kind == TypeData::Aggregate );
+
+	if ( unionDecl->type->aggregate.kind != ast::AggregateDecl::Union ) return;
+
+	// Ignore the Aggregate_t::attributes. Why did we add that before the rework?
+	for ( auto attr = decl->attributes.begin() ; attr != decl->attributes.end() ; ) {
+		if ( (*attr)->name == "transparent_union" || (*attr)->name == "__transparent_union__" ) {
+			unionDecl->attributes.emplace_back( attr->release() );
+			attr = decl->attributes.erase( attr );
+		} else {
+			++attr;
+		}
+	}
+}
+
+// Helper for addTypedef, handles the case where the typedef wraps an
+// aggregate declaration (not a type), returns a chain of nodes.
+static DeclarationNode * addTypedefAggr(
+		DeclarationNode * olddecl, TypeData * newtype ) {
+	TypeData *& oldaggr = olddecl->type->aggInst.aggregate;
+
+	// Handle anonymous aggregates: typedef struct { int i; } foo
+	// Give the typedefed type a consistent name across translation units.
+	if ( oldaggr->aggregate.anon ) {
+		delete oldaggr->aggregate.name;
+		oldaggr->aggregate.name = new string( "__anonymous_" + *olddecl->name );
+		oldaggr->aggregate.anon = false;
+		oldaggr->qualifiers.reset();
+	}
+
+	// Replace the wrapped TypeData with a forward declaration.
+	TypeData * newaggr = new TypeData( TypeData::Aggregate );
+	newaggr->aggregate.kind = oldaggr->aggregate.kind;
+	newaggr->aggregate.name = oldaggr->aggregate.name ? new string( *oldaggr->aggregate.name ) : nullptr;
+	newaggr->aggregate.body = false;
+	newaggr->aggregate.anon = oldaggr->aggregate.anon;
+	newaggr->aggregate.typed = oldaggr->aggregate.typed;
+	newaggr->aggregate.hiding = oldaggr->aggregate.hiding;
+	swap( newaggr, oldaggr );
+
+	newtype->base = olddecl->type;
+	olddecl->type = newtype;
+	DeclarationNode * newdecl = new DeclarationNode;
+	newdecl->type = newaggr;
+	newdecl->next = olddecl;
+
+	moveUnionAttribute( olddecl, newdecl );
+
+	return newdecl;
+}
+
+// Wrap the declaration in a typedef. It actually does that by modifying the
+// existing declaration, and may split it into two declarations.
+// This only happens if the wrapped type is actually a declaration of a SUE
+// type. If it does, the DeclarationNode for the SUE declaration is the node
+// returned, make sure later transformations are applied to the right node.
+DeclarationNode * DeclarationNode::addTypedef() {
+	TypeData * newtype = new TypeData( TypeData::Symbolic );
+	newtype->symbolic.params = nullptr;
+	newtype->symbolic.isTypedef = true;
+	newtype->symbolic.name = name ? new string( *name ) : nullptr;
+	// If this typedef is wrapping an aggregate, separate them out.
+	if ( TypeData::AggregateInst == type->kind
+			&& TypeData::Aggregate == type->aggInst.aggregate->kind
+			&& type->aggInst.aggregate->aggregate.body ) {
+		return addTypedefAggr( this, newtype );
+	// There is no internal declaration, just a type.
+	} else {
+		newtype->base = type;
+		type = newtype;
+		return this;
+	}
+}
+
+DeclarationNode * DeclarationNode::addAssertions( DeclarationNode * assertions ) {
+	if ( variable.tyClass != ast::TypeDecl::NUMBER_OF_KINDS ) {
+		extend( variable.assertions, assertions );
+		return this;
+	} // if
+
+	assert( type );
+	assert( TypeData::Symbolic == type->kind );
+	extend( type->symbolic.assertions, assertions );
+
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addName( string * newname ) {
+	assert( ! name );
+	name = newname;
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addAsmName( DeclarationNode * newname ) {
+	assert( ! asmName );
+	asmName = newname ? newname->asmName : nullptr;
+	return this->addQualifiers( newname );
+}
+
+DeclarationNode * DeclarationNode::addBitfield( ExpressionNode * size ) {
+	bitfieldWidth = size;
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addVarArgs() {
+	assert( type );
+	hasEllipsis = true;
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addFunctionBody( StatementNode * body, ExpressionNode * withExprs ) {
+	assert( type );
+	assert( type->kind == TypeData::Function );
+	assert( ! type->function.body );
+	type->function.body = body;
+	type->function.withExprs = withExprs;
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addOldDeclList( DeclarationNode * list ) {
+	assert( type );
+	assert( type->kind == TypeData::Function );
+	assert( ! type->function.oldDeclList );
+	type->function.oldDeclList = list;
+	return this;
+}
+
+DeclarationNode * DeclarationNode::setBase( TypeData * newType ) {
+	if ( type ) {
+		type->setLastBase( newType );
+	} else {
+		type = newType;
+	} // if
+	return this;
+}
+
+DeclarationNode * DeclarationNode::copyAttribute( DeclarationNode * a ) {
+	if ( a ) {
+		spliceBegin( attributes, a->attributes );
+		a->attributes.clear();
+	} // if
+	return this;
+} // copyAttribute
+
+DeclarationNode * DeclarationNode::addPointer( DeclarationNode * p ) {
+	if ( p ) {
+		assert( p->type->kind == TypeData::Pointer || p->type->kind == TypeData::Reference );
+		setBase( p->type );
+		p->type = nullptr;
+		copyAttribute( p );
+		delete p;
+	} // if
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addArray( DeclarationNode * a ) {
+	if ( a ) {
+		assert( a->type->kind == TypeData::Array );
+		setBase( a->type );
+		a->type = nullptr;
+		copyAttribute( a );
+		delete a;
+	} // if
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addNewPointer( DeclarationNode * p ) {
+	if ( !p ) return this;
+	assert( p->type->kind == TypeData::Pointer || p->type->kind == TypeData::Reference );
+	if ( type ) {
+		p->type->base = makeNewBase( type );
+		type = nullptr;
+	} // if
+	delete this;
+	return p;
+}
+
+DeclarationNode * DeclarationNode::addNewArray( DeclarationNode * a ) {
+	if ( !a ) return this;
+	assert( a->type->kind == TypeData::Array );
+	if ( type ) {
+		a->type->setLastBase( makeNewBase( type ) );
+		type = nullptr;
+	} // if
+	delete this;
+	return a;
+}
+
+DeclarationNode * DeclarationNode::addParamList( DeclarationNode * params ) {
+	TypeData * ftype = new TypeData( TypeData::Function );
+	ftype->function.params = params;
+	setBase( ftype );
+	return this;
+}
+
+static TypeData * addIdListToType( TypeData * type, DeclarationNode * ids ) {
+	if ( type ) {
+		if ( type->kind != TypeData::Function ) {
+			type->base = addIdListToType( type->base, ids );
+		} else {
+			type->function.idList = ids;
+		} // if
+		return type;
+	} else {
+		TypeData * newtype = new TypeData( TypeData::Function );
+		newtype->function.idList = ids;
+		return newtype;
+	} // if
+} // addIdListToType
+
+DeclarationNode * DeclarationNode::addIdList( DeclarationNode * ids ) {
+	type = addIdListToType( type, ids );
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addInitializer( InitializerNode * init ) {
+	initializer = init;
+	return this;
+}
+
+DeclarationNode * DeclarationNode::addTypeInitializer( DeclarationNode * init ) {
+	assertf( variable.tyClass != ast::TypeDecl::NUMBER_OF_KINDS, "Called addTypeInitializer on something that isn't a type variable." );
+	variable.initializer = init;
+	return this;
+}
+
+DeclarationNode * DeclarationNode::cloneType( string * name ) {
+	DeclarationNode * newnode = newName( name );
+	newnode->type = maybeCopy( type );
+	newnode->copySpecifiers( this );
+	return newnode;
+}
+
+DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o, bool copyattr ) {
+	if ( ! o ) return nullptr;
+	o->copySpecifiers( this, copyattr );
+	if ( type ) {
+		o->type = ::cloneBaseType( type, o->type );
+	} // if
+	return o;
+}
+
+DeclarationNode * DeclarationNode::extractAggregate() const {
+	if ( type ) {
+		TypeData * ret = typeextractAggregate( type );
+		if ( ret ) {
+			DeclarationNode * newnode = new DeclarationNode;
+			newnode->type = ret;
+			if ( ret->kind == TypeData::Aggregate ) {
+				newnode->attributes.swap( ret->aggregate.attributes );
+			} // if
+			return newnode;
+		} // if
+	} // if
+	return nullptr;
+}
+
+// Get the non-anonymous name of the instance type of the declaration,
+// if one exists.
+static const std::string * getInstTypeOfName( ast::Decl * decl ) {
+	if ( auto dwt = dynamic_cast<ast::DeclWithType *>( decl ) ) {
+		if ( auto aggr = dynamic_cast<ast::BaseInstType const *>( dwt->get_type() ) ) {
+			if ( aggr->name.find("anonymous") == std::string::npos ) {
+				return &aggr->name;
+			}
+		}
+	}
+	return nullptr;
+}
+
+void buildList( DeclarationNode * firstNode, std::vector<ast::ptr<ast::Decl>> & outputList ) {
+	SemanticErrorException errors;
+	std::back_insert_iterator<std::vector<ast::ptr<ast::Decl>>> out( outputList );
+
+	for ( const DeclarationNode * cur = firstNode ; cur ; cur = cur->next ) {
+		try {
+			bool extracted_named = false;
+
+			if ( DeclarationNode * extr = cur->extractAggregate() ) {
+				assert( cur->type );
+
+				if ( ast::Decl * decl = extr->build() ) {
+					*out++ = decl;
+
+					// need to remember the cases where a declaration contains an anonymous aggregate definition
+					assert( extr->type );
+					if ( extr->type->kind == TypeData::Aggregate ) {
+						// typedef struct { int A } B is the only case?
+						extracted_named = ! extr->type->aggregate.anon;
+					} else {
+						extracted_named = true;
+					}
+				} // if
+				delete extr;
+			} // if
+
+			if ( ast::Decl * decl = cur->build() ) {
+				if ( "" == decl->name && !cur->get_inLine() ) {
+					// Don't include anonymous declaration for named aggregates,
+					// but do include them for anonymous aggregates, e.g.:
+					// struct S {
+					//   struct T { int x; }; // no anonymous member
+					//   struct { int y; };   // anonymous member
+					//   struct T;            // anonymous member
+					// };
+					if ( extracted_named ) {
+						continue;
+					}
+
+					if ( auto name = getInstTypeOfName( decl ) ) {
+						// Temporary: warn about anonymous member declarations of named types, since
+						// this conflicts with the syntax for the forward declaration of an anonymous type.
+						SemanticWarning( cur->location, Warning::AggrForwardDecl, name->c_str() );
+					}
+				} // if
+				*out++ = decl;
+			} // if
+		} catch ( SemanticErrorException & e ) {
+			errors.append( e );
+		} // try
+	} // for
+
+	if ( ! errors.isEmpty() ) {
+		throw errors;
+	} // if
+} // buildList
+
+// currently only builds assertions, function parameters, and return values
+void buildList( DeclarationNode * firstNode, std::vector<ast::ptr<ast::DeclWithType>> & outputList ) {
+	SemanticErrorException errors;
+	std::back_insert_iterator<std::vector<ast::ptr<ast::DeclWithType>>> out( outputList );
+
+	for ( const DeclarationNode * cur = firstNode; cur; cur = cur->next ) {
+		try {
+			ast::Decl * decl = cur->build();
+			assertf( decl, "buildList: build for ast::DeclWithType." );
+			if ( ast::DeclWithType * dwt = dynamic_cast<ast::DeclWithType *>( decl ) ) {
+				dwt->location = cur->location;
+				*out++ = dwt;
+			} else if ( ast::StructDecl * agg = dynamic_cast<ast::StructDecl *>( decl ) ) {
+				// e.g., int foo(struct S) {}
+				auto inst = new ast::StructInstType( agg->name );
+				auto obj = new ast::ObjectDecl( cur->location, "", inst );
+				obj->linkage = linkage;
+				*out++ = obj;
+				delete agg;
+			} else if ( ast::UnionDecl * agg = dynamic_cast<ast::UnionDecl *>( decl ) ) {
+				// e.g., int foo(union U) {}
+				auto inst = new ast::UnionInstType( agg->name );
+				auto obj = new ast::ObjectDecl( cur->location,
+					"", inst, nullptr, ast::Storage::Classes(),
+					linkage );
+				*out++ = obj;
+			} else if ( ast::EnumDecl * agg = dynamic_cast<ast::EnumDecl *>( decl ) ) {
+				// e.g., int foo(enum E) {}
+				auto inst = new ast::EnumInstType( agg->name );
+				auto obj = new ast::ObjectDecl( cur->location,
+					"",
+					inst,
+					nullptr,
+					ast::Storage::Classes(),
+					linkage
+				);
+				*out++ = obj;
+			} else {
+				assertf( false, "buildList: Could not convert to ast::DeclWithType." );
+			} // if
+		} catch ( SemanticErrorException & e ) {
+			errors.append( e );
+		} // try
+	} // for
+
+	if ( ! errors.isEmpty() ) {
+		throw errors;
+	} // if
+} // buildList
+
+void buildTypeList( const DeclarationNode * firstNode,
+		std::vector<ast::ptr<ast::Type>> & outputList ) {
+	SemanticErrorException errors;
+	std::back_insert_iterator<std::vector<ast::ptr<ast::Type>>> out( outputList );
+
+	for ( const DeclarationNode * cur = firstNode ; cur ; cur = cur->next ) {
+		try {
+			* out++ = cur->buildType();
+		} catch ( SemanticErrorException & e ) {
+			errors.append( e );
+		} // try
+	} // for
+
+	if ( ! errors.isEmpty() ) {
+		throw errors;
+	} // if
+} // buildTypeList
+
+ast::Decl * DeclarationNode::build() const {
+	if ( ! error.empty() ) SemanticError( this, error + " in declaration of " );
+
+	if ( asmStmt ) {
+		auto stmt = strict_dynamic_cast<ast::AsmStmt *>( asmStmt->build() );
+		return new ast::AsmDecl( stmt->location, stmt );
+	} // if
+	if ( directiveStmt ) {
+		auto stmt = strict_dynamic_cast<ast::DirectiveStmt *>( directiveStmt->build() );
+		return new ast::DirectiveDecl( stmt->location, stmt );
+	} // if
+
+	if ( variable.tyClass != ast::TypeDecl::NUMBER_OF_KINDS ) {
+		// otype is internally converted to dtype + otype parameters
+		static const ast::TypeDecl::Kind kindMap[] = { ast::TypeDecl::Dtype, ast::TypeDecl::Dtype, ast::TypeDecl::Dtype, ast::TypeDecl::Ftype, ast::TypeDecl::Ttype, ast::TypeDecl::Dimension };
+		static_assert( sizeof(kindMap) / sizeof(kindMap[0]) == ast::TypeDecl::NUMBER_OF_KINDS, "DeclarationNode::build: kindMap is out of sync." );
+		assertf( variable.tyClass < sizeof(kindMap)/sizeof(kindMap[0]), "Variable's tyClass is out of bounds." );
+		ast::TypeDecl * ret = new ast::TypeDecl( location,
+			*name,
+			ast::Storage::Classes(),
+			(ast::Type *)nullptr,
+			kindMap[ variable.tyClass ],
+			variable.tyClass == ast::TypeDecl::Otype || variable.tyClass == ast::TypeDecl::DStype,
+			variable.initializer ? variable.initializer->buildType() : nullptr
+		);
+		buildList( variable.assertions, ret->assertions );
+		return ret;
+	} // if
+
+	if ( type ) {
+		// Function specifiers can only appear on a function definition/declaration.
+		//
+		//    inline _Noreturn int f();			// allowed
+		//    inline _Noreturn int g( int i );	// allowed
+		//    inline _Noreturn int i;			// disallowed
+		if ( type->kind != TypeData::Function && funcSpecs.any() ) {
+			SemanticError( this, "invalid function specifier for " );
+		} // if
+		// Forall qualifier can only appear on a function/aggregate definition/declaration.
+		//
+		//    forall int f();					// allowed
+		//    forall int g( int i );			// allowed
+		//    forall int i;						// disallowed
+		if ( type->kind != TypeData::Function && type->forall ) {
+			SemanticError( this, "invalid type qualifier for " );
+		} // if
+		bool isDelete = initializer && initializer->get_isDelete();
+		ast::Decl * decl = buildDecl(
+			type,
+			name ? *name : string( "" ),
+			storageClasses,
+			maybeBuild( bitfieldWidth ),
+			funcSpecs,
+			linkage,
+			asmName,
+			isDelete ? nullptr : maybeBuild( initializer ),
+			copy( attributes )
+		)->set_extension( extension );
+		if ( isDelete ) {
+			auto dwt = strict_dynamic_cast<ast::DeclWithType *>( decl );
+			dwt->isDeleted = true;
+		}
+		return decl;
+	} // if
+
+	if ( assert.condition ) {
+		auto cond = maybeBuild( assert.condition );
+		auto msg = strict_dynamic_cast<ast::ConstantExpr *>( maybeCopy( assert.message ) );
+		return new ast::StaticAssertDecl( location, cond, msg );
+	}
+
+	// SUE's cannot have function specifiers, either
+	//
+	//    inline _Noreturn struct S { ... };		// disallowed
+	//    inline _Noreturn enum   E { ... };		// disallowed
+	if ( funcSpecs.any() ) {
+		SemanticError( this, "invalid function specifier for " );
+	} // if
+	if ( enumInLine ) {
+		return new ast::InlineMemberDecl( location,
+			*name, (ast::Type*)nullptr, storageClasses, linkage );
+	} // if
+	assertf( name, "ObjectDecl must a have name\n" );
+	auto ret = new ast::ObjectDecl( location,
+		*name,
+		(ast::Type*)nullptr,
+		maybeBuild( initializer ),
+		storageClasses,
+		linkage,
+		maybeBuild( bitfieldWidth )
+	);
+	ret->asmName = asmName;
+	ret->extension = extension;
+	return ret;
+}
+
+ast::Type * DeclarationNode::buildType() const {
+	assert( type );
+
+	switch ( type->kind ) {
+	case TypeData::Aggregate: {
+		ast::BaseInstType * ret =
+			buildComAggInst( type, copy( attributes ), linkage );
+		buildList( type->aggregate.actuals, ret->params );
+		return ret;
+	}
+	case TypeData::Symbolic: {
+		ast::TypeInstType * ret = new ast::TypeInstType(
+			*type->symbolic.name,
+			// This is just a default, the true value is not known yet.
+			ast::TypeDecl::Dtype,
+			buildQualifiers( type ),
+			copy( attributes ) );
+		buildList( type->symbolic.actuals, ret->params );
+		return ret;
+	}
+	default:
+		ast::Type * simpletypes = typebuild( type );
+		// copy because member is const
+		simpletypes->attributes = attributes;
+		return simpletypes;
+	} // switch
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/DeclarationNode.h
===================================================================
--- src/Parser/DeclarationNode.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,173 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// DeclarationNode.h --
-//
-// Author           : Andrew Beach
-// Created On       : Wed Apr  5 11:38:00 2023
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Feb 17 09:24:12 2024
-// Update Count     : 4
-//
-
-#pragma once
-
-#include "ParseNode.h"
-
-struct TypeData;
-struct InitializerNode;
-
-struct DeclarationNode final : public ParseList<DeclarationNode> {
-	static DeclarationNode * newFromTypeData( TypeData * );
-	static DeclarationNode * newStorageClass( ast::Storage::Classes );
-	static DeclarationNode * newFuncSpecifier( ast::Function::Specs );
-	static DeclarationNode * newFunction( const std::string * name, DeclarationNode * ret, DeclarationNode * param, StatementNode * body );
-	static DeclarationNode * newAggregate( ast::AggregateDecl::Aggregate kind, const std::string * name, ExpressionNode * actuals, DeclarationNode * fields, bool body );
-	static DeclarationNode * newEnum( const std::string * name, DeclarationNode * constants, bool body, bool typed, DeclarationNode * base = nullptr, EnumHiding hiding = EnumHiding::Visible );
-	static DeclarationNode * newEnumConstant( const std::string * name, ExpressionNode * constant );
-	static DeclarationNode * newEnumValueGeneric( const std::string * name, InitializerNode * init );
-	static DeclarationNode * newEnumInLine( const std::string * name );
-	static DeclarationNode * newName( const std::string * );
-	static DeclarationNode * newTypeParam( ast::TypeDecl::Kind, const std::string * );
-	static DeclarationNode * newTrait( const std::string * name, DeclarationNode * params, DeclarationNode * asserts );
-	static DeclarationNode * newTraitUse( const std::string * name, ExpressionNode * params );
-	static DeclarationNode * newTypeDecl( const std::string * name, DeclarationNode * typeParams );
-	static DeclarationNode * newPointer( DeclarationNode * qualifiers, OperKinds kind );
-	static DeclarationNode * newArray( ExpressionNode * size, DeclarationNode * qualifiers, bool isStatic );
-	static DeclarationNode * newVarArray( DeclarationNode * qualifiers );
-	static DeclarationNode * newBitfield( ExpressionNode * size );
-	static DeclarationNode * newTuple( DeclarationNode * members );
-	static DeclarationNode * newTypeof( ExpressionNode * expr, bool basetypeof = false );
-	static DeclarationNode * newAttribute( const std::string *, ExpressionNode * expr = nullptr ); // gcc attributes
-	static DeclarationNode * newDirectiveStmt( StatementNode * stmt ); // gcc external directive statement
-	static DeclarationNode * newAsmStmt( StatementNode * stmt ); // gcc external asm statement
-	static DeclarationNode * newStaticAssert( ExpressionNode * condition, ast::Expr * message );
-
-	DeclarationNode();
-	~DeclarationNode();
-	DeclarationNode * clone() const override;
-
-	DeclarationNode * addQualifiers( DeclarationNode * );
-	void checkQualifiers( const TypeData *, const TypeData * );
-	void checkSpecifiers( DeclarationNode * );
-	DeclarationNode * copySpecifiers( DeclarationNode *, bool = true );
-	DeclarationNode * addType( DeclarationNode *, bool = true );
-	DeclarationNode * addTypedef();
-	DeclarationNode * addEnumBase( DeclarationNode * );
-	DeclarationNode * addAssertions( DeclarationNode * );
-	DeclarationNode * addName( std::string * );
-	DeclarationNode * addAsmName( DeclarationNode * );
-	DeclarationNode * addBitfield( ExpressionNode * size );
-	DeclarationNode * addVarArgs();
-	DeclarationNode * addFunctionBody( StatementNode * body, ExpressionNode * with = nullptr );
-	DeclarationNode * addOldDeclList( DeclarationNode * list );
-	DeclarationNode * setBase( TypeData * newType );
-	DeclarationNode * copyAttribute( DeclarationNode * attr );
-	DeclarationNode * addPointer( DeclarationNode * qualifiers );
-	DeclarationNode * addArray( DeclarationNode * array );
-	DeclarationNode * addNewPointer( DeclarationNode * pointer );
-	DeclarationNode * addNewArray( DeclarationNode * array );
-	DeclarationNode * addParamList( DeclarationNode * list );
-	DeclarationNode * addIdList( DeclarationNode * list ); // old-style functions
-	DeclarationNode * addInitializer( InitializerNode * init );
-	DeclarationNode * addTypeInitializer( DeclarationNode * init );
-
-	DeclarationNode * cloneType( std::string * newName );
-	DeclarationNode * cloneBaseType( DeclarationNode * newdecl, bool = true );
-
-	virtual void print( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const override;
-	virtual void printList( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const override;
-
-	ast::Decl * build() const;
-	ast::Type * buildType() const;
-
-	ast::Linkage::Spec get_linkage() const { return linkage; }
-	DeclarationNode * extractAggregate() const;
-	bool has_enumeratorValue() const { return (bool)enumeratorValue; }
-	ExpressionNode * consume_enumeratorValue() const { return const_cast<DeclarationNode *>(this)->enumeratorValue.release(); }
-
-	bool get_extension() const { return extension; }
-	DeclarationNode * set_extension( bool exten ) { extension = exten; return this; }
-
-	bool get_inLine() const { return inLine; }
-	DeclarationNode * set_inLine( bool inL ) { inLine = inL; return this; }
-
-	const std::string * name = nullptr;
-
-	struct Variable_t {
-		ast::TypeDecl::Kind tyClass;
-		DeclarationNode * assertions;
-		DeclarationNode * initializer;
-	};
-	Variable_t variable;
-
-	struct StaticAssert_t {
-		ExpressionNode * condition;
-		ast::Expr * message;
-	};
-	StaticAssert_t assert;
-
-	TypeData * type = nullptr;
-
-	bool inLine = false;
-	bool enumInLine = false;
-	ast::Function::Specs funcSpecs;
-	ast::Storage::Classes storageClasses;
-
-	ExpressionNode * bitfieldWidth = nullptr;
-	std::unique_ptr<ExpressionNode> enumeratorValue;
-	bool hasEllipsis = false;
-	ast::Linkage::Spec linkage;
-	ast::Expr * asmName = nullptr;
-	std::vector<ast::ptr<ast::Attribute>> attributes;
-	InitializerNode * initializer = nullptr;
-	bool extension = false;
-	std::string error;
-	StatementNode * asmStmt = nullptr;
-	StatementNode * directiveStmt = nullptr;
-
-	static UniqueName anonymous;
-}; // DeclarationNode
-
-static inline ast::Type * maybeMoveBuildType( const DeclarationNode * orig ) {
-	ast::Type * ret = orig ? orig->buildType() : nullptr;
-	delete orig;
-	return ret;
-}
-
-// This generic buildList is here along side its overloads.
-template<typename AstType, typename NodeType,
-		template<typename, typename...> class Container, typename... Args>
-void buildList( NodeType * firstNode,
-		Container<ast::ptr<AstType>, Args...> & output ) {
-	SemanticErrorException errors;
-	std::back_insert_iterator<Container<ast::ptr<AstType>, Args...>> out( output );
-
-	for ( NodeType * cur = firstNode ; cur ; cur = cur->next ) {
-		try {
-			AstType * node = dynamic_cast<AstType *>( maybeBuild( cur ) );
-			assertf( node, "buildList: Did not build node of correct type." );
-			*out++ = node;
-		} catch ( SemanticErrorException & e ) {
-			errors.append( e );
-		} // try
-	} // for
-	if ( ! errors.isEmpty() ) {
-		throw errors;
-	} // if
-}
-
-void buildList( DeclarationNode * firstNode, std::vector<ast::ptr<ast::Decl>> & outputList );
-void buildList( DeclarationNode * firstNode, std::vector<ast::ptr<ast::DeclWithType>> & outputList );
-void buildTypeList( const DeclarationNode * firstNode, std::vector<ast::ptr<ast::Type>> & outputList );
-
-template<typename AstType, typename NodeType,
-		template<typename, typename...> class Container, typename... Args>
-void buildMoveList( NodeType * firstNode,
-		Container<ast::ptr<AstType>, Args...> & output ) {
-	buildList<AstType, NodeType, Container, Args...>( firstNode, output );
-	delete firstNode;
-}
Index: src/Parser/DeclarationNode.hpp
===================================================================
--- src/Parser/DeclarationNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/DeclarationNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,173 @@
+//
+// 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.
+//
+// DeclarationNode.hpp --
+//
+// Author           : Andrew Beach
+// Created On       : Wed Apr  5 11:38:00 2023
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Feb 17 09:24:12 2024
+// Update Count     : 4
+//
+
+#pragma once
+
+#include "ParseNode.hpp"
+
+struct TypeData;
+struct InitializerNode;
+
+struct DeclarationNode final : public ParseList<DeclarationNode> {
+	static DeclarationNode * newFromTypeData( TypeData * );
+	static DeclarationNode * newStorageClass( ast::Storage::Classes );
+	static DeclarationNode * newFuncSpecifier( ast::Function::Specs );
+	static DeclarationNode * newFunction( const std::string * name, DeclarationNode * ret, DeclarationNode * param, StatementNode * body );
+	static DeclarationNode * newAggregate( ast::AggregateDecl::Aggregate kind, const std::string * name, ExpressionNode * actuals, DeclarationNode * fields, bool body );
+	static DeclarationNode * newEnum( const std::string * name, DeclarationNode * constants, bool body, bool typed, DeclarationNode * base = nullptr, EnumHiding hiding = EnumHiding::Visible );
+	static DeclarationNode * newEnumConstant( const std::string * name, ExpressionNode * constant );
+	static DeclarationNode * newEnumValueGeneric( const std::string * name, InitializerNode * init );
+	static DeclarationNode * newEnumInLine( const std::string * name );
+	static DeclarationNode * newName( const std::string * );
+	static DeclarationNode * newTypeParam( ast::TypeDecl::Kind, const std::string * );
+	static DeclarationNode * newTrait( const std::string * name, DeclarationNode * params, DeclarationNode * asserts );
+	static DeclarationNode * newTraitUse( const std::string * name, ExpressionNode * params );
+	static DeclarationNode * newTypeDecl( const std::string * name, DeclarationNode * typeParams );
+	static DeclarationNode * newPointer( DeclarationNode * qualifiers, OperKinds kind );
+	static DeclarationNode * newArray( ExpressionNode * size, DeclarationNode * qualifiers, bool isStatic );
+	static DeclarationNode * newVarArray( DeclarationNode * qualifiers );
+	static DeclarationNode * newBitfield( ExpressionNode * size );
+	static DeclarationNode * newTuple( DeclarationNode * members );
+	static DeclarationNode * newTypeof( ExpressionNode * expr, bool basetypeof = false );
+	static DeclarationNode * newAttribute( const std::string *, ExpressionNode * expr = nullptr ); // gcc attributes
+	static DeclarationNode * newDirectiveStmt( StatementNode * stmt ); // gcc external directive statement
+	static DeclarationNode * newAsmStmt( StatementNode * stmt ); // gcc external asm statement
+	static DeclarationNode * newStaticAssert( ExpressionNode * condition, ast::Expr * message );
+
+	DeclarationNode();
+	~DeclarationNode();
+	DeclarationNode * clone() const override;
+
+	DeclarationNode * addQualifiers( DeclarationNode * );
+	void checkQualifiers( const TypeData *, const TypeData * );
+	void checkSpecifiers( DeclarationNode * );
+	DeclarationNode * copySpecifiers( DeclarationNode *, bool = true );
+	DeclarationNode * addType( DeclarationNode *, bool = true );
+	DeclarationNode * addTypedef();
+	DeclarationNode * addEnumBase( DeclarationNode * );
+	DeclarationNode * addAssertions( DeclarationNode * );
+	DeclarationNode * addName( std::string * );
+	DeclarationNode * addAsmName( DeclarationNode * );
+	DeclarationNode * addBitfield( ExpressionNode * size );
+	DeclarationNode * addVarArgs();
+	DeclarationNode * addFunctionBody( StatementNode * body, ExpressionNode * with = nullptr );
+	DeclarationNode * addOldDeclList( DeclarationNode * list );
+	DeclarationNode * setBase( TypeData * newType );
+	DeclarationNode * copyAttribute( DeclarationNode * attr );
+	DeclarationNode * addPointer( DeclarationNode * qualifiers );
+	DeclarationNode * addArray( DeclarationNode * array );
+	DeclarationNode * addNewPointer( DeclarationNode * pointer );
+	DeclarationNode * addNewArray( DeclarationNode * array );
+	DeclarationNode * addParamList( DeclarationNode * list );
+	DeclarationNode * addIdList( DeclarationNode * list ); // old-style functions
+	DeclarationNode * addInitializer( InitializerNode * init );
+	DeclarationNode * addTypeInitializer( DeclarationNode * init );
+
+	DeclarationNode * cloneType( std::string * newName );
+	DeclarationNode * cloneBaseType( DeclarationNode * newdecl, bool = true );
+
+	virtual void print( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const override;
+	virtual void printList( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const override;
+
+	ast::Decl * build() const;
+	ast::Type * buildType() const;
+
+	ast::Linkage::Spec get_linkage() const { return linkage; }
+	DeclarationNode * extractAggregate() const;
+	bool has_enumeratorValue() const { return (bool)enumeratorValue; }
+	ExpressionNode * consume_enumeratorValue() const { return const_cast<DeclarationNode *>(this)->enumeratorValue.release(); }
+
+	bool get_extension() const { return extension; }
+	DeclarationNode * set_extension( bool exten ) { extension = exten; return this; }
+
+	bool get_inLine() const { return inLine; }
+	DeclarationNode * set_inLine( bool inL ) { inLine = inL; return this; }
+
+	const std::string * name = nullptr;
+
+	struct Variable_t {
+		ast::TypeDecl::Kind tyClass;
+		DeclarationNode * assertions;
+		DeclarationNode * initializer;
+	};
+	Variable_t variable;
+
+	struct StaticAssert_t {
+		ExpressionNode * condition;
+		ast::Expr * message;
+	};
+	StaticAssert_t assert;
+
+	TypeData * type = nullptr;
+
+	bool inLine = false;
+	bool enumInLine = false;
+	ast::Function::Specs funcSpecs;
+	ast::Storage::Classes storageClasses;
+
+	ExpressionNode * bitfieldWidth = nullptr;
+	std::unique_ptr<ExpressionNode> enumeratorValue;
+	bool hasEllipsis = false;
+	ast::Linkage::Spec linkage;
+	ast::Expr * asmName = nullptr;
+	std::vector<ast::ptr<ast::Attribute>> attributes;
+	InitializerNode * initializer = nullptr;
+	bool extension = false;
+	std::string error;
+	StatementNode * asmStmt = nullptr;
+	StatementNode * directiveStmt = nullptr;
+
+	static UniqueName anonymous;
+}; // DeclarationNode
+
+static inline ast::Type * maybeMoveBuildType( const DeclarationNode * orig ) {
+	ast::Type * ret = orig ? orig->buildType() : nullptr;
+	delete orig;
+	return ret;
+}
+
+// This generic buildList is here along side its overloads.
+template<typename AstType, typename NodeType,
+		template<typename, typename...> class Container, typename... Args>
+void buildList( NodeType * firstNode,
+		Container<ast::ptr<AstType>, Args...> & output ) {
+	SemanticErrorException errors;
+	std::back_insert_iterator<Container<ast::ptr<AstType>, Args...>> out( output );
+
+	for ( NodeType * cur = firstNode ; cur ; cur = cur->next ) {
+		try {
+			AstType * node = dynamic_cast<AstType *>( maybeBuild( cur ) );
+			assertf( node, "buildList: Did not build node of correct type." );
+			*out++ = node;
+		} catch ( SemanticErrorException & e ) {
+			errors.append( e );
+		} // try
+	} // for
+	if ( ! errors.isEmpty() ) {
+		throw errors;
+	} // if
+}
+
+void buildList( DeclarationNode * firstNode, std::vector<ast::ptr<ast::Decl>> & outputList );
+void buildList( DeclarationNode * firstNode, std::vector<ast::ptr<ast::DeclWithType>> & outputList );
+void buildTypeList( const DeclarationNode * firstNode, std::vector<ast::ptr<ast::Type>> & outputList );
+
+template<typename AstType, typename NodeType,
+		template<typename, typename...> class Container, typename... Args>
+void buildMoveList( NodeType * firstNode,
+		Container<ast::ptr<AstType>, Args...> & output ) {
+	buildList<AstType, NodeType, Container, Args...>( firstNode, output );
+	delete firstNode;
+}
Index: src/Parser/ExpressionNode.cc
===================================================================
--- src/Parser/ExpressionNode.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,784 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ExpressionNode.cc --
-//
-// Author           : Peter A. Buhr
-// Created On       : Sat May 16 13:17:07 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Thu Dec 14 18:57:07 2023
-// Update Count     : 1087
-//
-
-#include "ExpressionNode.h"
-
-#include <cassert>                 // for assert
-#include <stdio.h>                 // for sscanf, size_t
-#include <climits>                 // for LLONG_MAX, LONG_MAX, INT_MAX, UINT...
-#include <list>                    // for list
-#include <sstream>                 // for basic_istream::operator>>, basic_i...
-#include <string>                  // for string, operator+, operator==
-
-#include "AST/BasicKind.hpp"       // for BasicKind
-#include "AST/Expr.hpp"            // for NameExpr
-#include "AST/Type.hpp"            // for Type, LengthFlag, DimentionFlag
-#include "Common/SemanticError.h"  // for SemanticError
-#include "Common/utility.h"        // for maybeMoveBuild, maybeBuild, CodeLo...
-#include "DeclarationNode.h"       // for DeclarationNode
-#include "InitializerNode.h"       // for InitializerNode
-#include "TypeData.h"              // for addType, build_basic_type, build_c...
-#include "parserutility.h"         // for notZeroExpr
-
-using namespace std;
-
-//##############################################################################
-
-// Difficult to separate extra parts of constants during lexing because actions are not allow in the middle of patterns:
-//
-//		prefix action constant action suffix
-//
-// Alternatively, breaking a pattern using BEGIN does not work if the following pattern can be empty:
-//
-//		constant BEGIN CONT ...
-//		<CONT>(...)? BEGIN 0 ... // possible empty suffix
-//
-// because the CONT rule is NOT triggered if the pattern is empty. Hence, constants are reparsed here to determine their
-// type.
-
-// static inline bool checkH( char c ) { return c == 'h' || c == 'H'; }
-// static inline bool checkZ( char c ) { return c == 'z' || c == 'Z'; }
-// static inline bool checkU( char c ) { return c == 'u' || c == 'U'; }
-static inline bool checkF( char c ) { return c == 'f' || c == 'F'; }
-static inline bool checkD( char c ) { return c == 'd' || c == 'D'; }
-static inline bool checkF80( char c ) { return c == 'w' || c == 'W'; }
-static inline bool checkF128( char c ) { return c == 'q' || c == 'Q'; }
-static inline bool checkL( char c ) { return c == 'l' || c == 'L'; }
-static inline bool checkI( char c ) { return c == 'i' || c == 'I'; }
-static inline bool checkB( char c ) { return c == 'b' || c == 'B'; }
-static inline bool checkX( char c ) { return c == 'x' || c == 'X'; }
-// static inline bool checkN( char c ) { return c == 'n' || c == 'N'; }
-
-void lnthSuffix( string & str, int & type, int & ltype ) {
-	// 'u' can appear before or after length suffix
-	string::size_type posn = str.find_last_of( "lL" );
-
-	if ( posn == string::npos ) return;					// no suffix
-	size_t end = str.length() - 1;
-	if ( posn == end ) { type = 3; return; }			// no length after 'l' => long
-
-	string::size_type next = posn + 1;					// advance to length
-	if ( str[next] == '3' ) {							// 32
-		type = ltype = 2;
-	} else if ( str[next] == '6' ) {					// 64
-		type = ltype = 3;
-	} else if ( str[next] == '8' ) {					// 8
-		type = ltype = 1;
-	} else if ( str[next] == '1' ) {
-		if ( str[next + 1] == '6' ) {					// 16
-			type = ltype = 0;
-		} else {										// 128
-			type = 5; ltype = 6;
-		} // if
-	} // if
-
-	char fix = '\0';
-	if ( str[end] == 'u' || str[end] == 'U' ) fix = str[end]; // ends with 'uU' ?
-	str.erase( posn );									// remove length suffix and possibly uU
-	if ( type == 5 ) {									// L128 does not need uU
-		end = str.length() - 1;
-		if ( str[end] == 'u' || str[end] == 'U' ) str.erase( end ); // ends with 'uU' ? remove
-	} else if ( fix != '\0' ) str += fix;				// put 'uU' back if removed
-} // lnthSuffix
-
-void valueToType( unsigned long long int & v, bool dec, int & type, bool & Unsigned ) {
-	// use value to determine type
-	if ( v <= INT_MAX ) {								// signed int
-		type = 2;
-	} else if ( v <= UINT_MAX && ! dec ) {				// unsigned int
-		type = 2;
-		Unsigned = true;								// unsigned
-	} else if ( v <= LONG_MAX ) {						// signed long int
-		type = 3;
-	} else if ( v <= ULONG_MAX && ( ! dec || LONG_MAX == LLONG_MAX ) ) { // signed long int
-		type = 3;
-		Unsigned = true;								// unsigned long int
-	} else if ( v <= LLONG_MAX ) {						// signed long long int
-		type = 4;
-	} else {											// unsigned long long int
-		type = 4;
-		Unsigned = true;								// unsigned long long int
-	} // if
-} // valueToType
-
-static void scanbin( string & str, unsigned long long int & v ) {
-	v = 0;
-	size_t last = str.length() - 1;						// last subscript of constant
-	for ( unsigned int i = 2;; ) {						// ignore prefix
-		if ( str[i] == '1' ) v |= 1;
-		i += 1;
-		if ( i == last - 1 || (str[i] != '0' && str[i] != '1') ) break;
-		v <<= 1;
-	} // for
-} // scanbin
-
-ast::Expr * build_constantInteger(
-		const CodeLocation & location, string & str ) {
-	static const ast::BasicKind kind[2][6] = {
-		// short (h) must be before char (hh) because shorter type has the longer suffix
-		{ ast::BasicKind::ShortSignedInt, ast::BasicKind::SignedChar, ast::BasicKind::SignedInt, ast::BasicKind::LongSignedInt, ast::BasicKind::LongLongSignedInt, /* BasicKind::SignedInt128 */ ast::BasicKind::LongLongSignedInt, },
-		{ ast::BasicKind::ShortUnsignedInt, ast::BasicKind::UnsignedChar, ast::BasicKind::UnsignedInt, ast::BasicKind::LongUnsignedInt, ast::BasicKind::LongLongUnsignedInt, /* BasicKind::UnsignedInt128 */ ast::BasicKind::LongLongUnsignedInt, },
-	};
-
-	static const char * lnthsInt[2][6] = {
-		{ "int16_t",  "int8_t",  "int32_t",  "int64_t",  "size_t",  "uintptr_t", },
-		{ "uint16_t", "uint8_t", "uint32_t", "uint64_t", "size_t",  "uintptr_t", },
-	}; // lnthsInt
-
-	string str2( "0x0" );
-	unsigned long long int v, v2 = 0;					// converted integral value
-	ast::Expr * ret, * ret2;
-
-	int type = -1;										// 0 => short, 1 => char, 2 => int, 3 => long int, 4 => long long int, 5 => int128
-	int ltype = -1;										// 0 => 16 bits, 1 => 8 bits, 2 => 32 bits, 3 => 64 bits, 4 => size_t, 5 => intptr, 6 => pointer
-	bool dec = true, Unsigned = false;					// decimal, unsigned constant
-
-	// special constants
-	if ( str == "0" ) {
-		ret = new ast::ConstantExpr( location, new ast::ZeroType(), str, 0 );
-		goto CLEANUP;
-	} // if
-	if ( str == "1" ) {
-		ret = new ast::ConstantExpr( location, new ast::OneType(), str, 1 );
-		goto CLEANUP;
-	} // if
-
-	// 'u' can appear before or after length suffix
-	if ( str.find_last_of( "uU" ) != string::npos ) Unsigned = true;
-
-	if ( isdigit( str[str.length() - 1] ) ) {			// no suffix ?
-		lnthSuffix( str, type, ltype );					// could have length suffix
-	} else {
-		// At least one digit in integer constant, so safe to backup while looking for suffix.
-		// This declaration and the comma expressions in the conditions mimic
-		// the declare and check pattern allowed in later compiler versions.
-		// (Only some early compilers/C++ standards do not support it.)
-		string::size_type posn;
-		// pointer value
-		if ( posn = str.find_last_of( "pP" ), posn != string::npos ) {
-			ltype = 5; str.erase( posn, 1 );
-		// size_t
-		} else if ( posn = str.find_last_of( "zZ" ), posn != string::npos ) {
-			Unsigned = true; type = 2; ltype = 4; str.erase( posn, 1 );
-		// signed char
-		} else if ( posn = str.rfind( "hh" ), posn != string::npos ) {
-			type = 1; str.erase( posn, 2 );
-		// signed char
-		} else if ( posn = str.rfind( "HH" ), posn != string::npos ) {
-			type = 1; str.erase( posn, 2 );
-		// short
-		} else if ( posn = str.find_last_of( "hH" ), posn != string::npos ) {
-			type = 0; str.erase( posn, 1 );
-		// int (natural number)
-		} else if ( posn = str.find_last_of( "nN" ), posn != string::npos ) {
-			type = 2; str.erase( posn, 1 );
-		} else if ( str.rfind( "ll" ) != string::npos || str.rfind( "LL" ) != string::npos ) {
-			type = 4;
-		} else {
-			lnthSuffix( str, type, ltype );
-		} // if
-	} // if
-
-	// Cannot be just "0"/"1"; sscanf stops at the suffix, if any; value goes over the wall => always generate
-
-#if ! defined(__SIZEOF_INT128__)
-	if ( type == 5 ) SemanticError( yylloc, "int128 constant is not supported on this target \"%s\"", str.c_str() );
-#endif // ! __SIZEOF_INT128__
-
-	if ( str[0] == '0' ) {								// radix character ?
-		dec = false;
-		if ( checkX( str[1] ) ) {						// hex constant ?
-			if ( type < 5 ) {							// not L128 ?
-				sscanf( (char *)str.c_str(), "%llx", &v );
-#if defined(__SIZEOF_INT128__)
-			} else {									// hex int128 constant
-				unsigned int len = str.length();
-				if ( len > (2 + 16 + 16) ) SemanticError( yylloc, "128-bit hexadecimal constant to large \"%s\"", str.c_str() );
-				// hex digits < 2^64
-				if ( len > (2 + 16) ) {
-					str2 = "0x" + str.substr( len - 16 );
-					sscanf( (char *)str2.c_str(), "%llx", &v2 );
-					str = str.substr( 0, len - 16 );
-				} // if
-				sscanf( (char *)str.c_str(), "%llx", &v );
-#endif // __SIZEOF_INT128__
-			} // if
-			//printf( "%llx %llu\n", v, v );
-		} else if ( checkB( str[1] ) ) {				// binary constant ?
-#if defined(__SIZEOF_INT128__)
-			unsigned int len = str.length();
-			if ( type == 5 && len > 2 + 64 ) {
-				if ( len > 2 + 64 + 64 ) SemanticError( yylloc, "128-bit binary constant to large \"%s\".", str.c_str() );
-				str2 = "0b" + str.substr( len - 64 );
-				str = str.substr( 0, len - 64 );
-				scanbin( str2, v2 );
-			} // if
-#endif // __SIZEOF_INT128__
-			scanbin( str, v );
-			//printf( "%#llx %llu\n", v, v );
-		} else {										// octal constant
-			if ( type < 5 ) {							// not L128 ?
-				sscanf( (char *)str.c_str(), "%llo", &v );
-#if defined(__SIZEOF_INT128__)
-			} else {									// octal int128 constant
-				unsigned int len = str.length();
-				if ( len > 1 + 43 || (len == 1 + 43 && str[0] > '3') ) SemanticError( yylloc, "128-bit octal constant to large \"%s\"", str.c_str() );
-				char buf[32];
-				if ( len <= 1 + 21 ) {					// value < 21 octal digitis
-					sscanf( (char *)str.c_str(), "%llo", &v );
-				} else {
-					sscanf( &str[len - 21], "%llo", &v );
-					__int128 val = v;					// accumulate bits
-					str[len - 21] ='\0';				// shorten string
-					sscanf( &str[len == 43 ? 1 : 0], "%llo", &v );
-					val |= (__int128)v << 63;			// store bits
-					if ( len == 1 + 43 ) {				// most significant 2 bits ?
-						str[2] = '\0';					// shorten string
-						sscanf( &str[1], "%llo", &v );	// process most significant 2 bits
-						val |= (__int128)v << 126;		// store bits
-					} // if
-					v = val >> 64; v2 = (uint64_t)val;	// replace octal constant with 2 hex constants
-					sprintf( buf, "%#llx", v2 );
-					str2 = buf;
-				} // if
-				sprintf( buf, "%#llx", v );
-				str = buf;
-#endif // __SIZEOF_INT128__
-			} // if
-			//printf( "%#llo %llu\n", v, v );
-		} // if
-	} else {											// decimal constant ?
-		if ( type < 5 ) {								// not L128 ?
-			sscanf( (char *)str.c_str(), "%llu", &v );
-#if defined(__SIZEOF_INT128__)
-		} else {										// decimal int128 constant
-			#define P10_UINT64 10'000'000'000'000'000'000ULL // 19 zeroes
-			unsigned int len = str.length();
-			if ( str.length() == 39 && str > (Unsigned ? "340282366920938463463374607431768211455" : "170141183460469231731687303715884105727") )
-				SemanticError( yylloc, "128-bit decimal constant to large \"%s\".", str.c_str() );
-			char buf[32];
-			if ( len <= 19 ) {							// value < 19 decimal digitis
-				sscanf( (char *)str.c_str(), "%llu", &v );
-			} else {
-				sscanf( &str[len - 19], "%llu", &v );
-				__int128 val = v;						// accumulate bits
-				str[len - 19] ='\0';					// shorten string
-				sscanf( &str[len == 39 ? 1 : 0], "%llu", &v );
-				val += (__int128)v * (__int128)P10_UINT64; // store bits
-				if ( len == 39 ) {						// most significant 2 bits ?
-					str[1] = '\0';						// shorten string
-					sscanf( &str[0], "%llu", &v );		// process most significant 2 bits
-					val += (__int128)v * (__int128)P10_UINT64 * (__int128)P10_UINT64; // store bits
-				} // if
-				v = val >> 64; v2 = (uint64_t)val;		// replace decimal constant with 2 hex constants
-				sprintf( buf, "%#llx", v2 );
-				str2 = buf;
-			} // if
-			sprintf( buf, "%#llx", v );
-			str = buf;
-#endif // __SIZEOF_INT128__
-		} // if
-		//printf( "%llu\n", v );
-	} // if
-
-	if ( type == -1 ) {									// no suffix => determine type from value size
-		valueToType( v, dec, type, Unsigned );
-	} // if
-	/* printf( "%s %llo %s %llo\n", str.c_str(), v, str2.c_str(), v2 ); */
-
-	//if ( !( 0 <= type && type <= 6 ) ) { printf( "%s %lu %d %s\n", fred.c_str(), fred.length(), type, str.c_str() ); }
-	assert( 0 <= type && type <= 6 );
-
-	// Constant type is correct for overload resolving.
-	ret = new ast::ConstantExpr( location,
-		new ast::BasicType( kind[Unsigned][type] ), str, v );
-	if ( Unsigned && type < 2 ) {						// hh or h, less than int ?
-		// int i = -1uh => 65535 not -1, so cast is necessary for unsigned, which unfortunately eliminates warnings for large values.
-		ret = new ast::CastExpr( location,
-			ret,
-			new ast::BasicType( kind[Unsigned][type] ),
-			ast::ExplicitCast );
-	} else if ( ltype != -1 ) {							// explicit length ?
-		if ( ltype == 6 ) {								// int128, (int128)constant
-			ret2 = new ast::ConstantExpr( location,
-				new ast::BasicType( ast::BasicKind::LongLongSignedInt ),
-				str2,
-				v2 );
-			ret = build_compoundLiteral( location,
-				DeclarationNode::newFromTypeData(
-					addType(
-						build_basic_type( TypeData::Int128 ),
-						build_signedness( TypeData::Unsigned ) ) ),
-				new InitializerNode(
-					(new InitializerNode( new ExpressionNode( v2 == 0 ? ret2 : ret ) ))->set_last( new InitializerNode( new ExpressionNode( v2 == 0 ? ret : ret2 ) ) ), true )
-			);
-		} else {										// explicit length, (length_type)constant
-			ret = new ast::CastExpr( location,
-				ret,
-				new ast::TypeInstType( lnthsInt[Unsigned][ltype], ast::TypeDecl::Dtype ),
-				ast::ExplicitCast );
-			if ( ltype == 5 ) {							// pointer, intptr( (uintptr_t)constant )
-				ret = build_func( location,
-					new ExpressionNode(
-						build_varref( location, new string( "intptr" ) ) ),
-					new ExpressionNode( ret ) );
-			} // if
-		} // if
-	} // if
-
-  CLEANUP: ;
-	delete &str;										// created by lex
-	return ret;
-} // build_constantInteger
-
-
-static inline void checkFnxFloat( string & str, size_t last, bool & explnth, int & type ) {
-	string::size_type posn;
-	// floating-point constant has minimum of 2 characters, 1. or .1, so safe to look ahead
-	if ( str[1] == 'x' ) {								// hex ?
-		posn = str.find_last_of( "pP" );				// back for exponent (must have)
-		posn = str.find_first_of( "fF", posn + 1 );		// forward for size (fF allowed in hex constant)
-	} else {
-		posn = str.find_last_of( "fF" );				// back for size (fF not allowed)
-	} // if
-  if ( posn == string::npos ) return;
-	explnth = true;
-	posn += 1;											// advance to size
-	if ( str[posn] == '3' ) {							// 32
-		if ( str[last] != 'x' ) type = 6;
-		else type = 7;
-	} else if ( str[posn] == '6' ) {					// 64
-		if ( str[last] != 'x' ) type = 8;
-		else type = 9;
-	} else if ( str[posn] == '8' ) {					// 80
-		type = 3;
-	} else if ( str[posn] == '1' ) {					// 16/128
-		if ( str[posn + 1] == '6' ) {					// 16
-			type = 5;
-		} else {										// 128
-			if ( str[last] != 'x' ) type = 10;
-			else type = 11;
-		} // if
-	} else {
-		assertf( false, "internal error, bad floating point length %s", str.c_str() );
-	} // if
-} // checkFnxFloat
-
-
-ast::Expr * build_constantFloat(
-		const CodeLocation & location, string & str ) {
-	static const ast::BasicKind kind[2][12] = {
-		{ ast::BasicKind::Float, ast::BasicKind::Double, ast::BasicKind::LongDouble, ast::BasicKind::uuFloat80, ast::BasicKind::uuFloat128, ast::BasicKind::uFloat16, ast::BasicKind::uFloat32, ast::BasicKind::uFloat32x, ast::BasicKind::uFloat64, ast::BasicKind::uFloat64x, ast::BasicKind::uFloat128, ast::BasicKind::uFloat128x },
-		{ ast::BasicKind::FloatComplex, ast::BasicKind::DoubleComplex, ast::BasicKind::LongDoubleComplex, ast::BasicKind::NUMBER_OF_BASIC_TYPES, ast::BasicKind::NUMBER_OF_BASIC_TYPES, ast::BasicKind::uFloat16Complex, ast::BasicKind::uFloat32Complex, ast::BasicKind::uFloat32xComplex, ast::BasicKind::uFloat64Complex, ast::BasicKind::uFloat64xComplex, ast::BasicKind::uFloat128Complex, ast::BasicKind::uFloat128xComplex },
-	};
-
-	// floating-point constant has minimum of 2 characters 1. or .1
-	size_t last = str.length() - 1;
-	double v;
-	int type;											// 0 => float, 1 => double, 3 => long double, ...
-	bool complx = false;								// real, complex
-	bool explnth = false;								// explicit literal length
-
-	sscanf( str.c_str(), "%lg", &v );
-
-	if ( checkI( str[last] ) ) {						// imaginary ?
-		complx = true;
-		last -= 1;										// backup one character
-	} // if
-
-	if ( checkF( str[last] ) ) {						// float ?
-		type = 0;
-	} else if ( checkD( str[last] ) ) {					// double ?
-		type = 1;
-	} else if ( checkL( str[last] ) ) {					// long double ?
-		type = 2;
-	} else if ( checkF80( str[last] ) ) {				// __float80 ?
-		type = 3;
-	} else if ( checkF128( str[last] ) ) {				// __float128 ?
-		type = 4;
-	} else {
-		type = 1;										// double (default if no suffix)
-		checkFnxFloat( str, last, explnth, type );
-	} // if
-
-	if ( ! complx && checkI( str[last - 1] ) ) {		// imaginary ?
-		complx = true;
-	} // if
-
-	assert( 0 <= type && type < 12 );
-	ast::Expr * ret = new ast::ConstantExpr( location,
-		new ast::BasicType( kind[complx][type] ),
-		str,
-		v );
-	// explicit length ?
-	if ( explnth ) {
-		ret = new ast::CastExpr( location,
-			ret,
-			new ast::BasicType( kind[complx][type] ),
-			ast::ExplicitCast );
-	} // if
-
-	delete &str;										// created by lex
-	return ret;
-} // build_constantFloat
-
-static void sepString( string & str, string & units, char delimit ) {
-	string::size_type posn = str.find_last_of( delimit ) + 1;
-	if ( posn != str.length() ) {
-		units = "?" + str.substr( posn );				// extract units
-		str.erase( posn );								// remove units
-	} // if
-} // sepString
-
-ast::Expr * build_constantChar( const CodeLocation & location, string & str ) {
-	string units;										// units
-	sepString( str, units, '\'' );						// separate constant from units
-
-	ast::Expr * ret = new ast::ConstantExpr( location,
-		new ast::BasicType( ast::BasicKind::Char ),
-		str,
-		(unsigned long long int)(unsigned char)str[1] );
-	if ( units.length() != 0 ) {
-		ret = new ast::UntypedExpr( location,
-			new ast::NameExpr( location, units ),
-			{ ret } );
-	} // if
-
-	delete &str;										// created by lex
-	return ret;
-} // build_constantChar
-
-ast::Expr * build_constantStr(
-		const CodeLocation & location,
-		string & str ) {
-	assert( str.length() > 0 );
-	string units;										// units
-	sepString( str, units, '"' );						// separate constant from units
-
-	ast::Type * strtype;
-	switch ( str[0] ) {									// str has >= 2 characters, i.e, null string "" => safe to look at subscripts 0/1
-	case 'u':
-		if ( str[1] == '8' ) goto Default;				// utf-8 characters => array of char
-		// lookup type of associated typedef
-		strtype = new ast::TypeInstType( "char16_t", ast::TypeDecl::Dtype );
-		break;
-	case 'U':
-		strtype = new ast::TypeInstType( "char32_t", ast::TypeDecl::Dtype );
-		break;
-	case 'L':
-		strtype = new ast::TypeInstType( "wchar_t", ast::TypeDecl::Dtype );
-		break;
-	Default:											// char default string type
-	default:
-		strtype = new ast::BasicType( ast::BasicKind::Char );
-	} // switch
-	ast::ArrayType * at = new ast::ArrayType(
-		strtype,
-		// Length is adjusted: +1 for '\0' and -2 for '"'
-		ast::ConstantExpr::from_ulong( location, str.size() + 1 - 2 ),
-		ast::FixedLen,
-		ast::DynamicDim );
-	ast::Expr * ret = new ast::ConstantExpr( location, at, str, std::nullopt );
-	if ( units.length() != 0 ) {
-		ret = new ast::UntypedExpr( location,
-			new ast::NameExpr( location, units ),
-			{ ret } );
-	} // if
-
-	delete &str;										// created by lex
-	return ret;
-} // build_constantStr
-
-ast::Expr * build_field_name_FLOATING_FRACTIONconstant(
-		const CodeLocation & location, const string & str ) {
-	if ( str.find_first_not_of( "0123456789", 1 ) != string::npos ) SemanticError( yylloc, "invalid tuple index \"%s\".", str.c_str() );
-	ast::Expr * ret = build_constantInteger( location,
-		*new string( str.substr(1) ) );
-	delete &str;
-	return ret;
-} // build_field_name_FLOATING_FRACTIONconstant
-
-ast::Expr * build_field_name_FLOATING_DECIMALconstant(
-		const CodeLocation & location, const string & str ) {
-	if ( str[str.size() - 1] != '.' ) SemanticError( yylloc, "invalid tuple index \"%s\".", str.c_str() );
-	ast::Expr * ret = build_constantInteger(
-		location, *new string( str.substr( 0, str.size()-1 ) ) );
-	delete &str;
-	return ret;
-} // build_field_name_FLOATING_DECIMALconstant
-
-ast::Expr * build_field_name_FLOATINGconstant( const CodeLocation & location,
-		const string & str ) {
-	// str is of the form A.B -> separate at the . and return member expression
-	int a, b;
-	char dot;
-	stringstream ss( str );
-	ss >> a >> dot >> b;
-	auto ret = new ast::UntypedMemberExpr( location,
-		ast::ConstantExpr::from_int( location, b ),
-		ast::ConstantExpr::from_int( location, a )
-	);
-	delete &str;
-	return ret;
-} // build_field_name_FLOATINGconstant
-
-ast::Expr * make_field_name_fraction_constants( const CodeLocation & location,
-		ast::Expr * fieldName,
-		ast::Expr * fracts ) {
-	if ( nullptr == fracts ) {
-		return fieldName;
-	} else if ( auto memberExpr = dynamic_cast<ast::UntypedMemberExpr *>( fracts ) ) {
-		memberExpr->member = make_field_name_fraction_constants( location,
-			fieldName,
-			ast::mutate( memberExpr->aggregate.get() ) );
-		return memberExpr;
-	} else {
-		return new ast::UntypedMemberExpr( location, fracts, fieldName );
-	} // if
-} // make_field_name_fraction_constants
-
-ast::Expr * build_field_name_fraction_constants( const CodeLocation & location,
-		ast::Expr * fieldName,
-		ExpressionNode * fracts ) {
-	return make_field_name_fraction_constants( location, fieldName, maybeMoveBuild( fracts ) );
-} // build_field_name_fraction_constants
-
-ast::NameExpr * build_varref( const CodeLocation & location,
-		const string * name ) {
-	ast::NameExpr * expr = new ast::NameExpr( location, *name );
-	delete name;
-	return expr;
-} // build_varref
-
-ast::QualifiedNameExpr * build_qualified_expr( const CodeLocation & location,
-		const DeclarationNode * decl_node,
-		const ast::NameExpr * name ) {
-	ast::Decl * newDecl = maybeBuild( decl_node );
-	if ( ast::DeclWithType * newDeclWithType = dynamic_cast<ast::DeclWithType *>( newDecl ) ) {
-		if ( const ast::Type * t = newDeclWithType->get_type() ) {
-			if ( auto typeInst = dynamic_cast<const ast::TypeInstType *>( t ) ) {
-				newDecl = new ast::EnumDecl( location, typeInst->name );
-			}
-		}
-	}
-	return new ast::QualifiedNameExpr( location, newDecl, name->name );
-}
-
-ast::QualifiedNameExpr * build_qualified_expr( const CodeLocation & location,
-		const ast::EnumDecl * decl,
-		const ast::NameExpr * name ) {
-	return new ast::QualifiedNameExpr( location, decl, name->name );
-}
-
-ast::DimensionExpr * build_dimensionref( const CodeLocation & location,
-		const string * name ) {
-	ast::DimensionExpr * expr = new ast::DimensionExpr( location, *name );
-	delete name;
-	return expr;
-} // build_varref
-
-// TODO: get rid of this and OperKinds and reuse code from OperatorTable
-static const char * OperName[] = {						// must harmonize with OperKinds
-	// diadic
-	"SizeOf", "AlignOf", "OffsetOf", "?+?", "?-?", "?\\?", "?*?", "?/?", "?%?", "||", "&&",
-	"?|?", "?&?", "?^?", "Cast", "?<<?", "?>>?", "?<?", "?>?", "?<=?", "?>=?", "?==?", "?!=?",
-	"?=?", "?@=?", "?\\=?", "?*=?", "?/=?", "?%=?", "?+=?", "?-=?", "?<<=?", "?>>=?", "?&=?", "?^=?", "?|=?",
-	"?[?]", "...",
-	// monadic
-	"+?", "-?", "AddressOf", "*?", "!?", "~?", "++?", "?++", "--?", "?--",
-}; // OperName
-
-ast::Expr * build_cast( const CodeLocation & location,
-		DeclarationNode * decl_node,
-		ExpressionNode * expr_node,
-		ast::CastExpr::CastKind kind ) {
-	ast::Type * targetType = maybeMoveBuildType( decl_node );
-	if ( dynamic_cast<ast::VoidType *>( targetType ) ) {
-		delete targetType;
-		return new ast::CastExpr( location,
-			maybeMoveBuild( expr_node ),
-			ast::ExplicitCast, kind );
-	} else {
-		return new ast::CastExpr( location,
-			maybeMoveBuild( expr_node ),
-			targetType,
-			ast::ExplicitCast, kind );
-	} // if
-} // build_cast
-
-ast::Expr * build_keyword_cast( const CodeLocation & location,
-		ast::AggregateDecl::Aggregate target,
-		ExpressionNode * expr_node ) {
-	return new ast::KeywordCastExpr( location,
-		maybeMoveBuild( expr_node ),
-		target
-	);
-}
-
-ast::Expr * build_virtual_cast( const CodeLocation & location,
-		DeclarationNode * decl_node,
-		ExpressionNode * expr_node ) {
-	return new ast::VirtualCastExpr( location,
-		maybeMoveBuild( expr_node ),
-		maybeMoveBuildType( decl_node )
-	);
-} // build_virtual_cast
-
-ast::Expr * build_fieldSel( const CodeLocation & location,
-		ExpressionNode * expr_node,
-		ast::Expr * member ) {
-	return new ast::UntypedMemberExpr( location,
-		member,
-		maybeMoveBuild( expr_node )
-	);
-} // build_fieldSel
-
-ast::Expr * build_pfieldSel( const CodeLocation & location,
-		ExpressionNode * expr_node,
-		ast::Expr * member ) {
-	auto deref = new ast::UntypedExpr( location,
-		new ast::NameExpr( location, "*?" )
-	);
-	deref->location = expr_node->location;
-	deref->args.push_back( maybeMoveBuild( expr_node ) );
-	auto ret = new ast::UntypedMemberExpr( location, member, deref );
-	return ret;
-} // build_pfieldSel
-
-ast::Expr * build_offsetOf( const CodeLocation & location,
-		DeclarationNode * decl_node,
-		ast::NameExpr * member ) {
-	ast::Expr * ret = new ast::UntypedOffsetofExpr( location,
-		maybeMoveBuildType( decl_node ),
-		member->name
-	);
-	ret->result = new ast::BasicType( ast::BasicKind::LongUnsignedInt );
-	delete member;
-	return ret;
-} // build_offsetOf
-
-ast::Expr * build_and_or( const CodeLocation & location,
-		ExpressionNode * expr_node1,
-		ExpressionNode * expr_node2,
-		ast::LogicalFlag flag ) {
-	return new ast::LogicalExpr( location,
-		maybeMoveBuild( expr_node1 ),
-		maybeMoveBuild( expr_node2 ),
-		flag
-	);
-} // build_and_or
-
-ast::Expr * build_unary_val( const CodeLocation & location,
-		OperKinds op,
-		ExpressionNode * expr_node ) {
-	std::vector<ast::ptr<ast::Expr>> args;
-	args.push_back( maybeMoveBuild( expr_node ) );
-	return new ast::UntypedExpr( location,
-		new ast::NameExpr( location, OperName[ (int)op ] ),
-		std::move( args )
-	);
-} // build_unary_val
-
-ast::Expr * build_binary_val( const CodeLocation & location,
-		OperKinds op,
-		ExpressionNode * expr_node1,
-		ExpressionNode * expr_node2 ) {
-	std::vector<ast::ptr<ast::Expr>> args;
-	args.push_back( maybeMoveBuild( expr_node1 ) );
-	args.push_back( maybeMoveBuild( expr_node2 ) );
-	return new ast::UntypedExpr( location,
-		new ast::NameExpr( location, OperName[ (int)op ] ),
-		std::move( args )
-	);
-} // build_binary_val
-
-ast::Expr * build_cond( const CodeLocation & location,
-		ExpressionNode * expr_node1,
-		ExpressionNode * expr_node2,
-		ExpressionNode * expr_node3 ) {
-	return new ast::ConditionalExpr( location,
-		maybeMoveBuild( expr_node1 ),
-		maybeMoveBuild( expr_node2 ),
-		maybeMoveBuild( expr_node3 )
-	);
-} // build_cond
-
-ast::Expr * build_tuple( const CodeLocation & location,
-		ExpressionNode * expr_node ) {
-	std::vector<ast::ptr<ast::Expr>> exprs;
-	buildMoveList( expr_node, exprs );
-	return new ast::UntypedTupleExpr( location, std::move( exprs ) );
-} // build_tuple
-
-ast::Expr * build_func( const CodeLocation & location,
-		ExpressionNode * function,
-		ExpressionNode * expr_node ) {
-	std::vector<ast::ptr<ast::Expr>> args;
-	buildMoveList( expr_node, args );
-	return new ast::UntypedExpr( location,
-		maybeMoveBuild( function ),
-		std::move( args )
-	);
-} // build_func
-
-ast::Expr * build_compoundLiteral( const CodeLocation & location,
-		DeclarationNode * decl_node,
-		InitializerNode * kids ) {
-	// compound literal type
-	ast::Decl * newDecl = maybeBuild( decl_node );
-	// non-sue compound-literal type
-	if ( ast::DeclWithType * newDeclWithType = dynamic_cast<ast::DeclWithType *>( newDecl ) ) {
-		return new ast::CompoundLiteralExpr( location,
-			newDeclWithType->get_type(),
-			maybeMoveBuild( kids ) );
-	// these types do not have associated type information
-	} else if ( auto newDeclStructDecl = dynamic_cast<ast::StructDecl *>( newDecl ) ) {
-		if ( newDeclStructDecl->body ) {
-			return new ast::CompoundLiteralExpr( location,
-				new ast::StructInstType( newDeclStructDecl ),
-				maybeMoveBuild( kids ) );
-		} else {
-			return new ast::CompoundLiteralExpr( location,
-				new ast::StructInstType( newDeclStructDecl->name ),
-				maybeMoveBuild( kids ) );
-		} // if
-	} else if ( auto newDeclUnionDecl = dynamic_cast<ast::UnionDecl *>( newDecl )  ) {
-		if ( newDeclUnionDecl->body ) {
-			return new ast::CompoundLiteralExpr( location,
-				new ast::UnionInstType( newDeclUnionDecl ),
-				maybeMoveBuild( kids ) );
-		} else {
-			return new ast::CompoundLiteralExpr( location,
-				new ast::UnionInstType( newDeclUnionDecl->name ),
-				maybeMoveBuild( kids ) );
-		} // if
-	} else if ( auto newDeclEnumDecl = dynamic_cast<ast::EnumDecl *>( newDecl )  ) {
-		if ( newDeclEnumDecl->body ) {
-			return new ast::CompoundLiteralExpr( location,
-				new ast::EnumInstType( newDeclEnumDecl ),
-				maybeMoveBuild( kids ) );
-		} else {
-			return new ast::CompoundLiteralExpr( location,
-				new ast::EnumInstType( newDeclEnumDecl->name ),
-				maybeMoveBuild( kids ) );
-		} // if
-	} else {
-		assert( false );
-	} // if
-} // build_compoundLiteral
-
-// Local Variables: //
-// tab-width: 4 //
-// End: //
Index: src/Parser/ExpressionNode.cpp
===================================================================
--- src/Parser/ExpressionNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/ExpressionNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,784 @@
+//
+// 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.
+//
+// ExpressionNode.cpp --
+//
+// Author           : Peter A. Buhr
+// Created On       : Sat May 16 13:17:07 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Thu Dec 14 18:57:07 2023
+// Update Count     : 1087
+//
+
+#include "ExpressionNode.hpp"
+
+#include <cassert>                 // for assert
+#include <stdio.h>                 // for sscanf, size_t
+#include <climits>                 // for LLONG_MAX, LONG_MAX, INT_MAX, UINT...
+#include <list>                    // for list
+#include <sstream>                 // for basic_istream::operator>>, basic_i...
+#include <string>                  // for string, operator+, operator==
+
+#include "AST/BasicKind.hpp"       // for BasicKind
+#include "AST/Expr.hpp"            // for NameExpr
+#include "AST/Type.hpp"            // for Type, LengthFlag, DimentionFlag
+#include "Common/SemanticError.hpp"// for SemanticError
+#include "Common/Utility.hpp"      // for maybeMoveBuild, maybeBuild, CodeLo...
+#include "DeclarationNode.hpp"     // for DeclarationNode
+#include "InitializerNode.hpp"     // for InitializerNode
+#include "TypeData.hpp"            // for addType, build_basic_type, build_c...
+#include "ParserUtility.hpp"       // for notZeroExpr
+
+using namespace std;
+
+//##############################################################################
+
+// Difficult to separate extra parts of constants during lexing because actions are not allow in the middle of patterns:
+//
+//		prefix action constant action suffix
+//
+// Alternatively, breaking a pattern using BEGIN does not work if the following pattern can be empty:
+//
+//		constant BEGIN CONT ...
+//		<CONT>(...)? BEGIN 0 ... // possible empty suffix
+//
+// because the CONT rule is NOT triggered if the pattern is empty. Hence, constants are reparsed here to determine their
+// type.
+
+// static inline bool checkH( char c ) { return c == 'h' || c == 'H'; }
+// static inline bool checkZ( char c ) { return c == 'z' || c == 'Z'; }
+// static inline bool checkU( char c ) { return c == 'u' || c == 'U'; }
+static inline bool checkF( char c ) { return c == 'f' || c == 'F'; }
+static inline bool checkD( char c ) { return c == 'd' || c == 'D'; }
+static inline bool checkF80( char c ) { return c == 'w' || c == 'W'; }
+static inline bool checkF128( char c ) { return c == 'q' || c == 'Q'; }
+static inline bool checkL( char c ) { return c == 'l' || c == 'L'; }
+static inline bool checkI( char c ) { return c == 'i' || c == 'I'; }
+static inline bool checkB( char c ) { return c == 'b' || c == 'B'; }
+static inline bool checkX( char c ) { return c == 'x' || c == 'X'; }
+// static inline bool checkN( char c ) { return c == 'n' || c == 'N'; }
+
+void lnthSuffix( string & str, int & type, int & ltype ) {
+	// 'u' can appear before or after length suffix
+	string::size_type posn = str.find_last_of( "lL" );
+
+	if ( posn == string::npos ) return;					// no suffix
+	size_t end = str.length() - 1;
+	if ( posn == end ) { type = 3; return; }			// no length after 'l' => long
+
+	string::size_type next = posn + 1;					// advance to length
+	if ( str[next] == '3' ) {							// 32
+		type = ltype = 2;
+	} else if ( str[next] == '6' ) {					// 64
+		type = ltype = 3;
+	} else if ( str[next] == '8' ) {					// 8
+		type = ltype = 1;
+	} else if ( str[next] == '1' ) {
+		if ( str[next + 1] == '6' ) {					// 16
+			type = ltype = 0;
+		} else {										// 128
+			type = 5; ltype = 6;
+		} // if
+	} // if
+
+	char fix = '\0';
+	if ( str[end] == 'u' || str[end] == 'U' ) fix = str[end]; // ends with 'uU' ?
+	str.erase( posn );									// remove length suffix and possibly uU
+	if ( type == 5 ) {									// L128 does not need uU
+		end = str.length() - 1;
+		if ( str[end] == 'u' || str[end] == 'U' ) str.erase( end ); // ends with 'uU' ? remove
+	} else if ( fix != '\0' ) str += fix;				// put 'uU' back if removed
+} // lnthSuffix
+
+void valueToType( unsigned long long int & v, bool dec, int & type, bool & Unsigned ) {
+	// use value to determine type
+	if ( v <= INT_MAX ) {								// signed int
+		type = 2;
+	} else if ( v <= UINT_MAX && ! dec ) {				// unsigned int
+		type = 2;
+		Unsigned = true;								// unsigned
+	} else if ( v <= LONG_MAX ) {						// signed long int
+		type = 3;
+	} else if ( v <= ULONG_MAX && ( ! dec || LONG_MAX == LLONG_MAX ) ) { // signed long int
+		type = 3;
+		Unsigned = true;								// unsigned long int
+	} else if ( v <= LLONG_MAX ) {						// signed long long int
+		type = 4;
+	} else {											// unsigned long long int
+		type = 4;
+		Unsigned = true;								// unsigned long long int
+	} // if
+} // valueToType
+
+static void scanbin( string & str, unsigned long long int & v ) {
+	v = 0;
+	size_t last = str.length() - 1;						// last subscript of constant
+	for ( unsigned int i = 2;; ) {						// ignore prefix
+		if ( str[i] == '1' ) v |= 1;
+		i += 1;
+		if ( i == last - 1 || (str[i] != '0' && str[i] != '1') ) break;
+		v <<= 1;
+	} // for
+} // scanbin
+
+ast::Expr * build_constantInteger(
+		const CodeLocation & location, string & str ) {
+	static const ast::BasicKind kind[2][6] = {
+		// short (h) must be before char (hh) because shorter type has the longer suffix
+		{ ast::BasicKind::ShortSignedInt, ast::BasicKind::SignedChar, ast::BasicKind::SignedInt, ast::BasicKind::LongSignedInt, ast::BasicKind::LongLongSignedInt, /* BasicKind::SignedInt128 */ ast::BasicKind::LongLongSignedInt, },
+		{ ast::BasicKind::ShortUnsignedInt, ast::BasicKind::UnsignedChar, ast::BasicKind::UnsignedInt, ast::BasicKind::LongUnsignedInt, ast::BasicKind::LongLongUnsignedInt, /* BasicKind::UnsignedInt128 */ ast::BasicKind::LongLongUnsignedInt, },
+	};
+
+	static const char * lnthsInt[2][6] = {
+		{ "int16_t",  "int8_t",  "int32_t",  "int64_t",  "size_t",  "uintptr_t", },
+		{ "uint16_t", "uint8_t", "uint32_t", "uint64_t", "size_t",  "uintptr_t", },
+	}; // lnthsInt
+
+	string str2( "0x0" );
+	unsigned long long int v, v2 = 0;					// converted integral value
+	ast::Expr * ret, * ret2;
+
+	int type = -1;										// 0 => short, 1 => char, 2 => int, 3 => long int, 4 => long long int, 5 => int128
+	int ltype = -1;										// 0 => 16 bits, 1 => 8 bits, 2 => 32 bits, 3 => 64 bits, 4 => size_t, 5 => intptr, 6 => pointer
+	bool dec = true, Unsigned = false;					// decimal, unsigned constant
+
+	// special constants
+	if ( str == "0" ) {
+		ret = new ast::ConstantExpr( location, new ast::ZeroType(), str, 0 );
+		goto CLEANUP;
+	} // if
+	if ( str == "1" ) {
+		ret = new ast::ConstantExpr( location, new ast::OneType(), str, 1 );
+		goto CLEANUP;
+	} // if
+
+	// 'u' can appear before or after length suffix
+	if ( str.find_last_of( "uU" ) != string::npos ) Unsigned = true;
+
+	if ( isdigit( str[str.length() - 1] ) ) {			// no suffix ?
+		lnthSuffix( str, type, ltype );					// could have length suffix
+	} else {
+		// At least one digit in integer constant, so safe to backup while looking for suffix.
+		// This declaration and the comma expressions in the conditions mimic
+		// the declare and check pattern allowed in later compiler versions.
+		// (Only some early compilers/C++ standards do not support it.)
+		string::size_type posn;
+		// pointer value
+		if ( posn = str.find_last_of( "pP" ), posn != string::npos ) {
+			ltype = 5; str.erase( posn, 1 );
+		// size_t
+		} else if ( posn = str.find_last_of( "zZ" ), posn != string::npos ) {
+			Unsigned = true; type = 2; ltype = 4; str.erase( posn, 1 );
+		// signed char
+		} else if ( posn = str.rfind( "hh" ), posn != string::npos ) {
+			type = 1; str.erase( posn, 2 );
+		// signed char
+		} else if ( posn = str.rfind( "HH" ), posn != string::npos ) {
+			type = 1; str.erase( posn, 2 );
+		// short
+		} else if ( posn = str.find_last_of( "hH" ), posn != string::npos ) {
+			type = 0; str.erase( posn, 1 );
+		// int (natural number)
+		} else if ( posn = str.find_last_of( "nN" ), posn != string::npos ) {
+			type = 2; str.erase( posn, 1 );
+		} else if ( str.rfind( "ll" ) != string::npos || str.rfind( "LL" ) != string::npos ) {
+			type = 4;
+		} else {
+			lnthSuffix( str, type, ltype );
+		} // if
+	} // if
+
+	// Cannot be just "0"/"1"; sscanf stops at the suffix, if any; value goes over the wall => always generate
+
+#if ! defined(__SIZEOF_INT128__)
+	if ( type == 5 ) SemanticError( yylloc, "int128 constant is not supported on this target \"%s\"", str.c_str() );
+#endif // ! __SIZEOF_INT128__
+
+	if ( str[0] == '0' ) {								// radix character ?
+		dec = false;
+		if ( checkX( str[1] ) ) {						// hex constant ?
+			if ( type < 5 ) {							// not L128 ?
+				sscanf( (char *)str.c_str(), "%llx", &v );
+#if defined(__SIZEOF_INT128__)
+			} else {									// hex int128 constant
+				unsigned int len = str.length();
+				if ( len > (2 + 16 + 16) ) SemanticError( yylloc, "128-bit hexadecimal constant to large \"%s\"", str.c_str() );
+				// hex digits < 2^64
+				if ( len > (2 + 16) ) {
+					str2 = "0x" + str.substr( len - 16 );
+					sscanf( (char *)str2.c_str(), "%llx", &v2 );
+					str = str.substr( 0, len - 16 );
+				} // if
+				sscanf( (char *)str.c_str(), "%llx", &v );
+#endif // __SIZEOF_INT128__
+			} // if
+			//printf( "%llx %llu\n", v, v );
+		} else if ( checkB( str[1] ) ) {				// binary constant ?
+#if defined(__SIZEOF_INT128__)
+			unsigned int len = str.length();
+			if ( type == 5 && len > 2 + 64 ) {
+				if ( len > 2 + 64 + 64 ) SemanticError( yylloc, "128-bit binary constant to large \"%s\".", str.c_str() );
+				str2 = "0b" + str.substr( len - 64 );
+				str = str.substr( 0, len - 64 );
+				scanbin( str2, v2 );
+			} // if
+#endif // __SIZEOF_INT128__
+			scanbin( str, v );
+			//printf( "%#llx %llu\n", v, v );
+		} else {										// octal constant
+			if ( type < 5 ) {							// not L128 ?
+				sscanf( (char *)str.c_str(), "%llo", &v );
+#if defined(__SIZEOF_INT128__)
+			} else {									// octal int128 constant
+				unsigned int len = str.length();
+				if ( len > 1 + 43 || (len == 1 + 43 && str[0] > '3') ) SemanticError( yylloc, "128-bit octal constant to large \"%s\"", str.c_str() );
+				char buf[32];
+				if ( len <= 1 + 21 ) {					// value < 21 octal digitis
+					sscanf( (char *)str.c_str(), "%llo", &v );
+				} else {
+					sscanf( &str[len - 21], "%llo", &v );
+					__int128 val = v;					// accumulate bits
+					str[len - 21] ='\0';				// shorten string
+					sscanf( &str[len == 43 ? 1 : 0], "%llo", &v );
+					val |= (__int128)v << 63;			// store bits
+					if ( len == 1 + 43 ) {				// most significant 2 bits ?
+						str[2] = '\0';					// shorten string
+						sscanf( &str[1], "%llo", &v );	// process most significant 2 bits
+						val |= (__int128)v << 126;		// store bits
+					} // if
+					v = val >> 64; v2 = (uint64_t)val;	// replace octal constant with 2 hex constants
+					sprintf( buf, "%#llx", v2 );
+					str2 = buf;
+				} // if
+				sprintf( buf, "%#llx", v );
+				str = buf;
+#endif // __SIZEOF_INT128__
+			} // if
+			//printf( "%#llo %llu\n", v, v );
+		} // if
+	} else {											// decimal constant ?
+		if ( type < 5 ) {								// not L128 ?
+			sscanf( (char *)str.c_str(), "%llu", &v );
+#if defined(__SIZEOF_INT128__)
+		} else {										// decimal int128 constant
+			#define P10_UINT64 10'000'000'000'000'000'000ULL // 19 zeroes
+			unsigned int len = str.length();
+			if ( str.length() == 39 && str > (Unsigned ? "340282366920938463463374607431768211455" : "170141183460469231731687303715884105727") )
+				SemanticError( yylloc, "128-bit decimal constant to large \"%s\".", str.c_str() );
+			char buf[32];
+			if ( len <= 19 ) {							// value < 19 decimal digitis
+				sscanf( (char *)str.c_str(), "%llu", &v );
+			} else {
+				sscanf( &str[len - 19], "%llu", &v );
+				__int128 val = v;						// accumulate bits
+				str[len - 19] ='\0';					// shorten string
+				sscanf( &str[len == 39 ? 1 : 0], "%llu", &v );
+				val += (__int128)v * (__int128)P10_UINT64; // store bits
+				if ( len == 39 ) {						// most significant 2 bits ?
+					str[1] = '\0';						// shorten string
+					sscanf( &str[0], "%llu", &v );		// process most significant 2 bits
+					val += (__int128)v * (__int128)P10_UINT64 * (__int128)P10_UINT64; // store bits
+				} // if
+				v = val >> 64; v2 = (uint64_t)val;		// replace decimal constant with 2 hex constants
+				sprintf( buf, "%#llx", v2 );
+				str2 = buf;
+			} // if
+			sprintf( buf, "%#llx", v );
+			str = buf;
+#endif // __SIZEOF_INT128__
+		} // if
+		//printf( "%llu\n", v );
+	} // if
+
+	if ( type == -1 ) {									// no suffix => determine type from value size
+		valueToType( v, dec, type, Unsigned );
+	} // if
+	/* printf( "%s %llo %s %llo\n", str.c_str(), v, str2.c_str(), v2 ); */
+
+	//if ( !( 0 <= type && type <= 6 ) ) { printf( "%s %lu %d %s\n", fred.c_str(), fred.length(), type, str.c_str() ); }
+	assert( 0 <= type && type <= 6 );
+
+	// Constant type is correct for overload resolving.
+	ret = new ast::ConstantExpr( location,
+		new ast::BasicType( kind[Unsigned][type] ), str, v );
+	if ( Unsigned && type < 2 ) {						// hh or h, less than int ?
+		// int i = -1uh => 65535 not -1, so cast is necessary for unsigned, which unfortunately eliminates warnings for large values.
+		ret = new ast::CastExpr( location,
+			ret,
+			new ast::BasicType( kind[Unsigned][type] ),
+			ast::ExplicitCast );
+	} else if ( ltype != -1 ) {							// explicit length ?
+		if ( ltype == 6 ) {								// int128, (int128)constant
+			ret2 = new ast::ConstantExpr( location,
+				new ast::BasicType( ast::BasicKind::LongLongSignedInt ),
+				str2,
+				v2 );
+			ret = build_compoundLiteral( location,
+				DeclarationNode::newFromTypeData(
+					addType(
+						build_basic_type( TypeData::Int128 ),
+						build_signedness( TypeData::Unsigned ) ) ),
+				new InitializerNode(
+					(new InitializerNode( new ExpressionNode( v2 == 0 ? ret2 : ret ) ))->set_last( new InitializerNode( new ExpressionNode( v2 == 0 ? ret : ret2 ) ) ), true )
+			);
+		} else {										// explicit length, (length_type)constant
+			ret = new ast::CastExpr( location,
+				ret,
+				new ast::TypeInstType( lnthsInt[Unsigned][ltype], ast::TypeDecl::Dtype ),
+				ast::ExplicitCast );
+			if ( ltype == 5 ) {							// pointer, intptr( (uintptr_t)constant )
+				ret = build_func( location,
+					new ExpressionNode(
+						build_varref( location, new string( "intptr" ) ) ),
+					new ExpressionNode( ret ) );
+			} // if
+		} // if
+	} // if
+
+  CLEANUP: ;
+	delete &str;										// created by lex
+	return ret;
+} // build_constantInteger
+
+
+static inline void checkFnxFloat( string & str, size_t last, bool & explnth, int & type ) {
+	string::size_type posn;
+	// floating-point constant has minimum of 2 characters, 1. or .1, so safe to look ahead
+	if ( str[1] == 'x' ) {								// hex ?
+		posn = str.find_last_of( "pP" );				// back for exponent (must have)
+		posn = str.find_first_of( "fF", posn + 1 );		// forward for size (fF allowed in hex constant)
+	} else {
+		posn = str.find_last_of( "fF" );				// back for size (fF not allowed)
+	} // if
+  if ( posn == string::npos ) return;
+	explnth = true;
+	posn += 1;											// advance to size
+	if ( str[posn] == '3' ) {							// 32
+		if ( str[last] != 'x' ) type = 6;
+		else type = 7;
+	} else if ( str[posn] == '6' ) {					// 64
+		if ( str[last] != 'x' ) type = 8;
+		else type = 9;
+	} else if ( str[posn] == '8' ) {					// 80
+		type = 3;
+	} else if ( str[posn] == '1' ) {					// 16/128
+		if ( str[posn + 1] == '6' ) {					// 16
+			type = 5;
+		} else {										// 128
+			if ( str[last] != 'x' ) type = 10;
+			else type = 11;
+		} // if
+	} else {
+		assertf( false, "internal error, bad floating point length %s", str.c_str() );
+	} // if
+} // checkFnxFloat
+
+
+ast::Expr * build_constantFloat(
+		const CodeLocation & location, string & str ) {
+	static const ast::BasicKind kind[2][12] = {
+		{ ast::BasicKind::Float, ast::BasicKind::Double, ast::BasicKind::LongDouble, ast::BasicKind::uuFloat80, ast::BasicKind::uuFloat128, ast::BasicKind::uFloat16, ast::BasicKind::uFloat32, ast::BasicKind::uFloat32x, ast::BasicKind::uFloat64, ast::BasicKind::uFloat64x, ast::BasicKind::uFloat128, ast::BasicKind::uFloat128x },
+		{ ast::BasicKind::FloatComplex, ast::BasicKind::DoubleComplex, ast::BasicKind::LongDoubleComplex, ast::BasicKind::NUMBER_OF_BASIC_TYPES, ast::BasicKind::NUMBER_OF_BASIC_TYPES, ast::BasicKind::uFloat16Complex, ast::BasicKind::uFloat32Complex, ast::BasicKind::uFloat32xComplex, ast::BasicKind::uFloat64Complex, ast::BasicKind::uFloat64xComplex, ast::BasicKind::uFloat128Complex, ast::BasicKind::uFloat128xComplex },
+	};
+
+	// floating-point constant has minimum of 2 characters 1. or .1
+	size_t last = str.length() - 1;
+	double v;
+	int type;											// 0 => float, 1 => double, 3 => long double, ...
+	bool complx = false;								// real, complex
+	bool explnth = false;								// explicit literal length
+
+	sscanf( str.c_str(), "%lg", &v );
+
+	if ( checkI( str[last] ) ) {						// imaginary ?
+		complx = true;
+		last -= 1;										// backup one character
+	} // if
+
+	if ( checkF( str[last] ) ) {						// float ?
+		type = 0;
+	} else if ( checkD( str[last] ) ) {					// double ?
+		type = 1;
+	} else if ( checkL( str[last] ) ) {					// long double ?
+		type = 2;
+	} else if ( checkF80( str[last] ) ) {				// __float80 ?
+		type = 3;
+	} else if ( checkF128( str[last] ) ) {				// __float128 ?
+		type = 4;
+	} else {
+		type = 1;										// double (default if no suffix)
+		checkFnxFloat( str, last, explnth, type );
+	} // if
+
+	if ( ! complx && checkI( str[last - 1] ) ) {		// imaginary ?
+		complx = true;
+	} // if
+
+	assert( 0 <= type && type < 12 );
+	ast::Expr * ret = new ast::ConstantExpr( location,
+		new ast::BasicType( kind[complx][type] ),
+		str,
+		v );
+	// explicit length ?
+	if ( explnth ) {
+		ret = new ast::CastExpr( location,
+			ret,
+			new ast::BasicType( kind[complx][type] ),
+			ast::ExplicitCast );
+	} // if
+
+	delete &str;										// created by lex
+	return ret;
+} // build_constantFloat
+
+static void sepString( string & str, string & units, char delimit ) {
+	string::size_type posn = str.find_last_of( delimit ) + 1;
+	if ( posn != str.length() ) {
+		units = "?" + str.substr( posn );				// extract units
+		str.erase( posn );								// remove units
+	} // if
+} // sepString
+
+ast::Expr * build_constantChar( const CodeLocation & location, string & str ) {
+	string units;										// units
+	sepString( str, units, '\'' );						// separate constant from units
+
+	ast::Expr * ret = new ast::ConstantExpr( location,
+		new ast::BasicType( ast::BasicKind::Char ),
+		str,
+		(unsigned long long int)(unsigned char)str[1] );
+	if ( units.length() != 0 ) {
+		ret = new ast::UntypedExpr( location,
+			new ast::NameExpr( location, units ),
+			{ ret } );
+	} // if
+
+	delete &str;										// created by lex
+	return ret;
+} // build_constantChar
+
+ast::Expr * build_constantStr(
+		const CodeLocation & location,
+		string & str ) {
+	assert( str.length() > 0 );
+	string units;										// units
+	sepString( str, units, '"' );						// separate constant from units
+
+	ast::Type * strtype;
+	switch ( str[0] ) {									// str has >= 2 characters, i.e, null string "" => safe to look at subscripts 0/1
+	case 'u':
+		if ( str[1] == '8' ) goto Default;				// utf-8 characters => array of char
+		// lookup type of associated typedef
+		strtype = new ast::TypeInstType( "char16_t", ast::TypeDecl::Dtype );
+		break;
+	case 'U':
+		strtype = new ast::TypeInstType( "char32_t", ast::TypeDecl::Dtype );
+		break;
+	case 'L':
+		strtype = new ast::TypeInstType( "wchar_t", ast::TypeDecl::Dtype );
+		break;
+	Default:											// char default string type
+	default:
+		strtype = new ast::BasicType( ast::BasicKind::Char );
+	} // switch
+	ast::ArrayType * at = new ast::ArrayType(
+		strtype,
+		// Length is adjusted: +1 for '\0' and -2 for '"'
+		ast::ConstantExpr::from_ulong( location, str.size() + 1 - 2 ),
+		ast::FixedLen,
+		ast::DynamicDim );
+	ast::Expr * ret = new ast::ConstantExpr( location, at, str, std::nullopt );
+	if ( units.length() != 0 ) {
+		ret = new ast::UntypedExpr( location,
+			new ast::NameExpr( location, units ),
+			{ ret } );
+	} // if
+
+	delete &str;										// created by lex
+	return ret;
+} // build_constantStr
+
+ast::Expr * build_field_name_FLOATING_FRACTIONconstant(
+		const CodeLocation & location, const string & str ) {
+	if ( str.find_first_not_of( "0123456789", 1 ) != string::npos ) SemanticError( yylloc, "invalid tuple index \"%s\".", str.c_str() );
+	ast::Expr * ret = build_constantInteger( location,
+		*new string( str.substr(1) ) );
+	delete &str;
+	return ret;
+} // build_field_name_FLOATING_FRACTIONconstant
+
+ast::Expr * build_field_name_FLOATING_DECIMALconstant(
+		const CodeLocation & location, const string & str ) {
+	if ( str[str.size() - 1] != '.' ) SemanticError( yylloc, "invalid tuple index \"%s\".", str.c_str() );
+	ast::Expr * ret = build_constantInteger(
+		location, *new string( str.substr( 0, str.size()-1 ) ) );
+	delete &str;
+	return ret;
+} // build_field_name_FLOATING_DECIMALconstant
+
+ast::Expr * build_field_name_FLOATINGconstant( const CodeLocation & location,
+		const string & str ) {
+	// str is of the form A.B -> separate at the . and return member expression
+	int a, b;
+	char dot;
+	stringstream ss( str );
+	ss >> a >> dot >> b;
+	auto ret = new ast::UntypedMemberExpr( location,
+		ast::ConstantExpr::from_int( location, b ),
+		ast::ConstantExpr::from_int( location, a )
+	);
+	delete &str;
+	return ret;
+} // build_field_name_FLOATINGconstant
+
+ast::Expr * make_field_name_fraction_constants( const CodeLocation & location,
+		ast::Expr * fieldName,
+		ast::Expr * fracts ) {
+	if ( nullptr == fracts ) {
+		return fieldName;
+	} else if ( auto memberExpr = dynamic_cast<ast::UntypedMemberExpr *>( fracts ) ) {
+		memberExpr->member = make_field_name_fraction_constants( location,
+			fieldName,
+			ast::mutate( memberExpr->aggregate.get() ) );
+		return memberExpr;
+	} else {
+		return new ast::UntypedMemberExpr( location, fracts, fieldName );
+	} // if
+} // make_field_name_fraction_constants
+
+ast::Expr * build_field_name_fraction_constants( const CodeLocation & location,
+		ast::Expr * fieldName,
+		ExpressionNode * fracts ) {
+	return make_field_name_fraction_constants( location, fieldName, maybeMoveBuild( fracts ) );
+} // build_field_name_fraction_constants
+
+ast::NameExpr * build_varref( const CodeLocation & location,
+		const string * name ) {
+	ast::NameExpr * expr = new ast::NameExpr( location, *name );
+	delete name;
+	return expr;
+} // build_varref
+
+ast::QualifiedNameExpr * build_qualified_expr( const CodeLocation & location,
+		const DeclarationNode * decl_node,
+		const ast::NameExpr * name ) {
+	ast::Decl * newDecl = maybeBuild( decl_node );
+	if ( ast::DeclWithType * newDeclWithType = dynamic_cast<ast::DeclWithType *>( newDecl ) ) {
+		if ( const ast::Type * t = newDeclWithType->get_type() ) {
+			if ( auto typeInst = dynamic_cast<const ast::TypeInstType *>( t ) ) {
+				newDecl = new ast::EnumDecl( location, typeInst->name );
+			}
+		}
+	}
+	return new ast::QualifiedNameExpr( location, newDecl, name->name );
+}
+
+ast::QualifiedNameExpr * build_qualified_expr( const CodeLocation & location,
+		const ast::EnumDecl * decl,
+		const ast::NameExpr * name ) {
+	return new ast::QualifiedNameExpr( location, decl, name->name );
+}
+
+ast::DimensionExpr * build_dimensionref( const CodeLocation & location,
+		const string * name ) {
+	ast::DimensionExpr * expr = new ast::DimensionExpr( location, *name );
+	delete name;
+	return expr;
+} // build_varref
+
+// TODO: get rid of this and OperKinds and reuse code from OperatorTable
+static const char * OperName[] = {						// must harmonize with OperKinds
+	// diadic
+	"SizeOf", "AlignOf", "OffsetOf", "?+?", "?-?", "?\\?", "?*?", "?/?", "?%?", "||", "&&",
+	"?|?", "?&?", "?^?", "Cast", "?<<?", "?>>?", "?<?", "?>?", "?<=?", "?>=?", "?==?", "?!=?",
+	"?=?", "?@=?", "?\\=?", "?*=?", "?/=?", "?%=?", "?+=?", "?-=?", "?<<=?", "?>>=?", "?&=?", "?^=?", "?|=?",
+	"?[?]", "...",
+	// monadic
+	"+?", "-?", "AddressOf", "*?", "!?", "~?", "++?", "?++", "--?", "?--",
+}; // OperName
+
+ast::Expr * build_cast( const CodeLocation & location,
+		DeclarationNode * decl_node,
+		ExpressionNode * expr_node,
+		ast::CastExpr::CastKind kind ) {
+	ast::Type * targetType = maybeMoveBuildType( decl_node );
+	if ( dynamic_cast<ast::VoidType *>( targetType ) ) {
+		delete targetType;
+		return new ast::CastExpr( location,
+			maybeMoveBuild( expr_node ),
+			ast::ExplicitCast, kind );
+	} else {
+		return new ast::CastExpr( location,
+			maybeMoveBuild( expr_node ),
+			targetType,
+			ast::ExplicitCast, kind );
+	} // if
+} // build_cast
+
+ast::Expr * build_keyword_cast( const CodeLocation & location,
+		ast::AggregateDecl::Aggregate target,
+		ExpressionNode * expr_node ) {
+	return new ast::KeywordCastExpr( location,
+		maybeMoveBuild( expr_node ),
+		target
+	);
+}
+
+ast::Expr * build_virtual_cast( const CodeLocation & location,
+		DeclarationNode * decl_node,
+		ExpressionNode * expr_node ) {
+	return new ast::VirtualCastExpr( location,
+		maybeMoveBuild( expr_node ),
+		maybeMoveBuildType( decl_node )
+	);
+} // build_virtual_cast
+
+ast::Expr * build_fieldSel( const CodeLocation & location,
+		ExpressionNode * expr_node,
+		ast::Expr * member ) {
+	return new ast::UntypedMemberExpr( location,
+		member,
+		maybeMoveBuild( expr_node )
+	);
+} // build_fieldSel
+
+ast::Expr * build_pfieldSel( const CodeLocation & location,
+		ExpressionNode * expr_node,
+		ast::Expr * member ) {
+	auto deref = new ast::UntypedExpr( location,
+		new ast::NameExpr( location, "*?" )
+	);
+	deref->location = expr_node->location;
+	deref->args.push_back( maybeMoveBuild( expr_node ) );
+	auto ret = new ast::UntypedMemberExpr( location, member, deref );
+	return ret;
+} // build_pfieldSel
+
+ast::Expr * build_offsetOf( const CodeLocation & location,
+		DeclarationNode * decl_node,
+		ast::NameExpr * member ) {
+	ast::Expr * ret = new ast::UntypedOffsetofExpr( location,
+		maybeMoveBuildType( decl_node ),
+		member->name
+	);
+	ret->result = new ast::BasicType( ast::BasicKind::LongUnsignedInt );
+	delete member;
+	return ret;
+} // build_offsetOf
+
+ast::Expr * build_and_or( const CodeLocation & location,
+		ExpressionNode * expr_node1,
+		ExpressionNode * expr_node2,
+		ast::LogicalFlag flag ) {
+	return new ast::LogicalExpr( location,
+		maybeMoveBuild( expr_node1 ),
+		maybeMoveBuild( expr_node2 ),
+		flag
+	);
+} // build_and_or
+
+ast::Expr * build_unary_val( const CodeLocation & location,
+		OperKinds op,
+		ExpressionNode * expr_node ) {
+	std::vector<ast::ptr<ast::Expr>> args;
+	args.push_back( maybeMoveBuild( expr_node ) );
+	return new ast::UntypedExpr( location,
+		new ast::NameExpr( location, OperName[ (int)op ] ),
+		std::move( args )
+	);
+} // build_unary_val
+
+ast::Expr * build_binary_val( const CodeLocation & location,
+		OperKinds op,
+		ExpressionNode * expr_node1,
+		ExpressionNode * expr_node2 ) {
+	std::vector<ast::ptr<ast::Expr>> args;
+	args.push_back( maybeMoveBuild( expr_node1 ) );
+	args.push_back( maybeMoveBuild( expr_node2 ) );
+	return new ast::UntypedExpr( location,
+		new ast::NameExpr( location, OperName[ (int)op ] ),
+		std::move( args )
+	);
+} // build_binary_val
+
+ast::Expr * build_cond( const CodeLocation & location,
+		ExpressionNode * expr_node1,
+		ExpressionNode * expr_node2,
+		ExpressionNode * expr_node3 ) {
+	return new ast::ConditionalExpr( location,
+		maybeMoveBuild( expr_node1 ),
+		maybeMoveBuild( expr_node2 ),
+		maybeMoveBuild( expr_node3 )
+	);
+} // build_cond
+
+ast::Expr * build_tuple( const CodeLocation & location,
+		ExpressionNode * expr_node ) {
+	std::vector<ast::ptr<ast::Expr>> exprs;
+	buildMoveList( expr_node, exprs );
+	return new ast::UntypedTupleExpr( location, std::move( exprs ) );
+} // build_tuple
+
+ast::Expr * build_func( const CodeLocation & location,
+		ExpressionNode * function,
+		ExpressionNode * expr_node ) {
+	std::vector<ast::ptr<ast::Expr>> args;
+	buildMoveList( expr_node, args );
+	return new ast::UntypedExpr( location,
+		maybeMoveBuild( function ),
+		std::move( args )
+	);
+} // build_func
+
+ast::Expr * build_compoundLiteral( const CodeLocation & location,
+		DeclarationNode * decl_node,
+		InitializerNode * kids ) {
+	// compound literal type
+	ast::Decl * newDecl = maybeBuild( decl_node );
+	// non-sue compound-literal type
+	if ( ast::DeclWithType * newDeclWithType = dynamic_cast<ast::DeclWithType *>( newDecl ) ) {
+		return new ast::CompoundLiteralExpr( location,
+			newDeclWithType->get_type(),
+			maybeMoveBuild( kids ) );
+	// these types do not have associated type information
+	} else if ( auto newDeclStructDecl = dynamic_cast<ast::StructDecl *>( newDecl ) ) {
+		if ( newDeclStructDecl->body ) {
+			return new ast::CompoundLiteralExpr( location,
+				new ast::StructInstType( newDeclStructDecl ),
+				maybeMoveBuild( kids ) );
+		} else {
+			return new ast::CompoundLiteralExpr( location,
+				new ast::StructInstType( newDeclStructDecl->name ),
+				maybeMoveBuild( kids ) );
+		} // if
+	} else if ( auto newDeclUnionDecl = dynamic_cast<ast::UnionDecl *>( newDecl )  ) {
+		if ( newDeclUnionDecl->body ) {
+			return new ast::CompoundLiteralExpr( location,
+				new ast::UnionInstType( newDeclUnionDecl ),
+				maybeMoveBuild( kids ) );
+		} else {
+			return new ast::CompoundLiteralExpr( location,
+				new ast::UnionInstType( newDeclUnionDecl->name ),
+				maybeMoveBuild( kids ) );
+		} // if
+	} else if ( auto newDeclEnumDecl = dynamic_cast<ast::EnumDecl *>( newDecl )  ) {
+		if ( newDeclEnumDecl->body ) {
+			return new ast::CompoundLiteralExpr( location,
+				new ast::EnumInstType( newDeclEnumDecl ),
+				maybeMoveBuild( kids ) );
+		} else {
+			return new ast::CompoundLiteralExpr( location,
+				new ast::EnumInstType( newDeclEnumDecl->name ),
+				maybeMoveBuild( kids ) );
+		} // if
+	} else {
+		assert( false );
+	} // if
+} // build_compoundLiteral
+
+// Local Variables: //
+// tab-width: 4 //
+// End: //
Index: src/Parser/ExpressionNode.h
===================================================================
--- src/Parser/ExpressionNode.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,86 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ExpressionNode.h --
-//
-// Author           : Andrew Beach
-// Created On       : Wed Apr  5 11:34:00 2023
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Apr  5 11:50:00 2023
-// Update Count     : 0
-//
-
-#pragma once
-
-#include "ParseNode.h"
-
-struct InitializerNode;
-
-struct ExpressionNode final : public ParseList<ExpressionNode> {
-	ExpressionNode( ast::Expr * expr = nullptr ) : expr( expr ) {}
-	virtual ~ExpressionNode() {}
-	virtual ExpressionNode * clone() const override {
-		if ( nullptr == expr ) return nullptr;
-		ExpressionNode * node = new ExpressionNode( ast::shallowCopy( expr.get() ) );
-		node->next = maybeCopy( next );
-		return node;
-	}
-
-	bool get_extension() const { return extension; }
-	ExpressionNode * set_extension( bool exten ) { extension = exten; return this; }
-
-	virtual void print( std::ostream & os, __attribute__((unused)) int indent = 0 ) const override {
-		os << expr.get();
-	}
-	void printOneLine( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const {}
-
-	template<typename T>
-	bool isExpressionType() const {  return nullptr != dynamic_cast<T>(expr.get()); }
-
-	ast::Expr * build() {
-		ast::Expr * node = expr.release();
-		node->set_extension( this->get_extension() );
-		node->location = this->location;
-		return node;
-	}
-
-	// Public because of lifetime implications (what lifetime implications?)
-	std::unique_ptr<ast::Expr> expr;
-private:
-	bool extension = false;
-}; // ExpressionNode
-
-// These 4 routines modify the string:
-ast::Expr * build_constantInteger( const CodeLocation &, std::string & );
-ast::Expr * build_constantFloat( const CodeLocation &, std::string & );
-ast::Expr * build_constantChar( const CodeLocation &, std::string & );
-ast::Expr * build_constantStr( const CodeLocation &, std::string & );
-ast::Expr * build_field_name_FLOATING_FRACTIONconstant( const CodeLocation &, const std::string & str );
-ast::Expr * build_field_name_FLOATING_DECIMALconstant( const CodeLocation &, const std::string & str );
-ast::Expr * build_field_name_FLOATINGconstant( const CodeLocation &, const std::string & str );
-ast::Expr * build_field_name_fraction_constants( const CodeLocation &, ast::Expr * fieldName, ExpressionNode * fracts );
-
-ast::NameExpr * build_varref( const CodeLocation &, const std::string * name );
-ast::QualifiedNameExpr * build_qualified_expr( const CodeLocation &, const DeclarationNode * decl_node, const ast::NameExpr * name );
-ast::QualifiedNameExpr * build_qualified_expr( const CodeLocation &, const ast::EnumDecl * decl, const ast::NameExpr * name );
-ast::DimensionExpr * build_dimensionref( const CodeLocation &, const std::string * name );
-
-ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node, ast::CastExpr::CastKind kind = ast::CastExpr::Default );
-ast::Expr * build_keyword_cast( const CodeLocation &, ast::AggregateDecl::Aggregate target, ExpressionNode * expr_node );
-ast::Expr * build_virtual_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node );
-ast::Expr * build_fieldSel( const CodeLocation &, ExpressionNode * expr_node, ast::Expr * member );
-ast::Expr * build_pfieldSel( const CodeLocation &, ExpressionNode * expr_node, ast::Expr * member );
-ast::Expr * build_offsetOf( const CodeLocation &, DeclarationNode * decl_node, ast::NameExpr * member );
-ast::Expr * build_and( const CodeLocation &, ExpressionNode * expr_node1, ExpressionNode * expr_node2 );
-ast::Expr * build_and_or( const CodeLocation &, ExpressionNode * expr_node1, ExpressionNode * expr_node2, ast::LogicalFlag flag );
-ast::Expr * build_unary_val( const CodeLocation &, OperKinds op, ExpressionNode * expr_node );
-ast::Expr * build_binary_val( const CodeLocation &, OperKinds op, ExpressionNode * expr_node1, ExpressionNode * expr_node2 );
-ast::Expr * build_cond( const CodeLocation &, ExpressionNode * expr_node1, ExpressionNode * expr_node2, ExpressionNode * expr_node3 );
-ast::Expr * build_tuple( const CodeLocation &, ExpressionNode * expr_node = nullptr );
-ast::Expr * build_func( const CodeLocation &, ExpressionNode * function, ExpressionNode * expr_node );
-ast::Expr * build_compoundLiteral( const CodeLocation &, DeclarationNode * decl_node, InitializerNode * kids );
-
-ast::Expr * build_enum_pos_expr( const CodeLocation &, ast::Expr * expr_node );
Index: src/Parser/ExpressionNode.hpp
===================================================================
--- src/Parser/ExpressionNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/ExpressionNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,86 @@
+//
+// 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.
+//
+// ExpressionNode.hpp --
+//
+// Author           : Andrew Beach
+// Created On       : Wed Apr  5 11:34:00 2023
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Apr  5 11:50:00 2023
+// Update Count     : 0
+//
+
+#pragma once
+
+#include "ParseNode.hpp"
+
+struct InitializerNode;
+
+struct ExpressionNode final : public ParseList<ExpressionNode> {
+	ExpressionNode( ast::Expr * expr = nullptr ) : expr( expr ) {}
+	virtual ~ExpressionNode() {}
+	virtual ExpressionNode * clone() const override {
+		if ( nullptr == expr ) return nullptr;
+		ExpressionNode * node = new ExpressionNode( ast::shallowCopy( expr.get() ) );
+		node->next = maybeCopy( next );
+		return node;
+	}
+
+	bool get_extension() const { return extension; }
+	ExpressionNode * set_extension( bool exten ) { extension = exten; return this; }
+
+	virtual void print( std::ostream & os, __attribute__((unused)) int indent = 0 ) const override {
+		os << expr.get();
+	}
+	void printOneLine( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const {}
+
+	template<typename T>
+	bool isExpressionType() const {  return nullptr != dynamic_cast<T>(expr.get()); }
+
+	ast::Expr * build() {
+		ast::Expr * node = expr.release();
+		node->set_extension( this->get_extension() );
+		node->location = this->location;
+		return node;
+	}
+
+	// Public because of lifetime implications (what lifetime implications?)
+	std::unique_ptr<ast::Expr> expr;
+private:
+	bool extension = false;
+}; // ExpressionNode
+
+// These 4 routines modify the string:
+ast::Expr * build_constantInteger( const CodeLocation &, std::string & );
+ast::Expr * build_constantFloat( const CodeLocation &, std::string & );
+ast::Expr * build_constantChar( const CodeLocation &, std::string & );
+ast::Expr * build_constantStr( const CodeLocation &, std::string & );
+ast::Expr * build_field_name_FLOATING_FRACTIONconstant( const CodeLocation &, const std::string & str );
+ast::Expr * build_field_name_FLOATING_DECIMALconstant( const CodeLocation &, const std::string & str );
+ast::Expr * build_field_name_FLOATINGconstant( const CodeLocation &, const std::string & str );
+ast::Expr * build_field_name_fraction_constants( const CodeLocation &, ast::Expr * fieldName, ExpressionNode * fracts );
+
+ast::NameExpr * build_varref( const CodeLocation &, const std::string * name );
+ast::QualifiedNameExpr * build_qualified_expr( const CodeLocation &, const DeclarationNode * decl_node, const ast::NameExpr * name );
+ast::QualifiedNameExpr * build_qualified_expr( const CodeLocation &, const ast::EnumDecl * decl, const ast::NameExpr * name );
+ast::DimensionExpr * build_dimensionref( const CodeLocation &, const std::string * name );
+
+ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node, ast::CastExpr::CastKind kind = ast::CastExpr::Default );
+ast::Expr * build_keyword_cast( const CodeLocation &, ast::AggregateDecl::Aggregate target, ExpressionNode * expr_node );
+ast::Expr * build_virtual_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node );
+ast::Expr * build_fieldSel( const CodeLocation &, ExpressionNode * expr_node, ast::Expr * member );
+ast::Expr * build_pfieldSel( const CodeLocation &, ExpressionNode * expr_node, ast::Expr * member );
+ast::Expr * build_offsetOf( const CodeLocation &, DeclarationNode * decl_node, ast::NameExpr * member );
+ast::Expr * build_and( const CodeLocation &, ExpressionNode * expr_node1, ExpressionNode * expr_node2 );
+ast::Expr * build_and_or( const CodeLocation &, ExpressionNode * expr_node1, ExpressionNode * expr_node2, ast::LogicalFlag flag );
+ast::Expr * build_unary_val( const CodeLocation &, OperKinds op, ExpressionNode * expr_node );
+ast::Expr * build_binary_val( const CodeLocation &, OperKinds op, ExpressionNode * expr_node1, ExpressionNode * expr_node2 );
+ast::Expr * build_cond( const CodeLocation &, ExpressionNode * expr_node1, ExpressionNode * expr_node2, ExpressionNode * expr_node3 );
+ast::Expr * build_tuple( const CodeLocation &, ExpressionNode * expr_node = nullptr );
+ast::Expr * build_func( const CodeLocation &, ExpressionNode * function, ExpressionNode * expr_node );
+ast::Expr * build_compoundLiteral( const CodeLocation &, DeclarationNode * decl_node, InitializerNode * kids );
+
+ast::Expr * build_enum_pos_expr( const CodeLocation &, ast::Expr * expr_node );
Index: src/Parser/InitializerNode.cc
===================================================================
--- src/Parser/InitializerNode.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,127 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// InitializerNode.cc --
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Sat May 16 13:20:24 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Apr  4 11:18:00 2023
-// Update Count     : 27
-//
-
-#include "InitializerNode.h"
-
-#include <iostream>                // for operator<<, ostream, basic_ostream
-#include <list>                    // for list
-#include <string>                  // for operator<<, string
-
-#include "AST/Expr.hpp"            // for Expr
-#include "AST/Init.hpp"            // for Designator, Init, ListInit, Sing...
-#include "Common/SemanticError.h"  // for SemanticError
-#include "Common/utility.h"        // for maybeBuild
-#include "ExpressionNode.h"        // for ExpressionNode
-#include "DeclarationNode.h"       // for buildList
-
-using namespace std;
-
-static ast::ConstructFlag toConstructFlag( bool maybeConstructed ) {
-	return maybeConstructed ? ast::MaybeConstruct : ast::NoConstruct;
-}
-
-InitializerNode::InitializerNode( ExpressionNode * _expr, bool aggrp, ExpressionNode * des )
-		: expr( _expr ), aggregate( aggrp ), designator( des ), kids( nullptr ), maybeConstructed( true ), isDelete( false ) {
-	if ( aggrp )
-		kids = next;
-
-	if ( kids )
-		set_last( nullptr );
-} // InitializerNode::InitializerNode
-
-InitializerNode::InitializerNode( InitializerNode * init, bool aggrp, ExpressionNode * des )
-		: expr( nullptr ), aggregate( aggrp ), designator( des ), kids( nullptr ), maybeConstructed( true ), isDelete( false ) {
-	if ( init )
-		set_last( init );
-
-	if ( aggrp )
-		kids = next;
-
-	if ( kids )
-		next = nullptr;
-} // InitializerNode::InitializerNode
-
-InitializerNode::InitializerNode( bool isDelete ) : expr( nullptr ), aggregate( false ), designator( nullptr ), kids( nullptr ), maybeConstructed( false ), isDelete( isDelete ) {}
-
-InitializerNode::~InitializerNode() {
-	delete expr;
-	delete designator;
-	delete kids;
-} // InitializerNode::~InitializerNode
-
-void InitializerNode::print( std::ostream &os, int indent ) const {
-	os << std::string( indent, ' ' ) << "Initializer expression" << std::endl;
-} // InitializerNode::print
-
-void InitializerNode::printOneLine( std::ostream &os ) const {
-	if ( ! aggregate ) {
-		if ( designator ) {
-			os << "designated by: (";
-			ExpressionNode *curdes = designator;
-			while ( curdes != nullptr) {
-				curdes->printOneLine(os);
-				curdes = curdes->next;
-				if ( curdes ) os << ", ";
-			} // while
-			os << ")";
-		} // if
-		if ( expr ) expr->printOneLine( os );
-	} else {  // It's an aggregate
-		os << "[--";
-		if ( next_init() != nullptr )
-			next_init()->printOneLine( os );
-		if (aggregate) os << "--]";
-	} // if
-
-	InitializerNode *moreInit;
-	if ( ( moreInit = next ) ) {
-		moreInit->printOneLine( os );
-	} // if
-} // InitializerNode::printOneLine
-
-ast::Init * InitializerNode::build() const {
-	assertf( ! isDelete, "Should not build delete stmt InitializerNode" );
-	if ( aggregate ) {
-		// steal designators from children
-		std::vector<ast::ptr<ast::Designation>> designlist;
-		InitializerNode * child = next_init();
-		for ( ; child != nullptr ; child = child->next ) {
-			std::deque<ast::ptr<ast::Expr>> desList;
-			buildList( child->designator, desList );
-			designlist.push_back(
-				new ast::Designation( location, std::move( desList ) ) );
-		} // for
-		std::vector<ast::ptr<ast::Init>> initlist;
-		buildList( next_init(), initlist );
-		return new ast::ListInit( location,
-			std::move( initlist ),
-			std::move( designlist ),
-			toConstructFlag( maybeConstructed )
-		);
-	} else if ( get_expression() ) {
-		assertf( get_expression()->expr, "The expression of initializer must have value" );
-		return new ast::SingleInit( location,
-			maybeBuild( get_expression() ),
-			toConstructFlag( maybeConstructed )
-		);
-	} // if
-	return nullptr;
-} // InitializerNode::build
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/InitializerNode.cpp
===================================================================
--- src/Parser/InitializerNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/InitializerNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,127 @@
+//
+// 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.
+//
+// InitializerNode.cpp --
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Sat May 16 13:20:24 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Apr  4 11:18:00 2023
+// Update Count     : 27
+//
+
+#include "InitializerNode.hpp"
+
+#include <iostream>                  // for operator<<, ostream, basic_ostream
+#include <list>                      // for list
+#include <string>                    // for operator<<, string
+
+#include "AST/Expr.hpp"              // for Expr
+#include "AST/Init.hpp"              // for Designator, Init, ListInit, Sing...
+#include "Common/SemanticError.hpp"  // for SemanticError
+#include "Common/Utility.hpp"        // for maybeBuild
+#include "ExpressionNode.hpp"        // for ExpressionNode
+#include "DeclarationNode.hpp"       // for buildList
+
+using namespace std;
+
+static ast::ConstructFlag toConstructFlag( bool maybeConstructed ) {
+	return maybeConstructed ? ast::MaybeConstruct : ast::NoConstruct;
+}
+
+InitializerNode::InitializerNode( ExpressionNode * _expr, bool aggrp, ExpressionNode * des )
+		: expr( _expr ), aggregate( aggrp ), designator( des ), kids( nullptr ), maybeConstructed( true ), isDelete( false ) {
+	if ( aggrp )
+		kids = next;
+
+	if ( kids )
+		set_last( nullptr );
+} // InitializerNode::InitializerNode
+
+InitializerNode::InitializerNode( InitializerNode * init, bool aggrp, ExpressionNode * des )
+		: expr( nullptr ), aggregate( aggrp ), designator( des ), kids( nullptr ), maybeConstructed( true ), isDelete( false ) {
+	if ( init )
+		set_last( init );
+
+	if ( aggrp )
+		kids = next;
+
+	if ( kids )
+		next = nullptr;
+} // InitializerNode::InitializerNode
+
+InitializerNode::InitializerNode( bool isDelete ) : expr( nullptr ), aggregate( false ), designator( nullptr ), kids( nullptr ), maybeConstructed( false ), isDelete( isDelete ) {}
+
+InitializerNode::~InitializerNode() {
+	delete expr;
+	delete designator;
+	delete kids;
+} // InitializerNode::~InitializerNode
+
+void InitializerNode::print( std::ostream &os, int indent ) const {
+	os << std::string( indent, ' ' ) << "Initializer expression" << std::endl;
+} // InitializerNode::print
+
+void InitializerNode::printOneLine( std::ostream &os ) const {
+	if ( ! aggregate ) {
+		if ( designator ) {
+			os << "designated by: (";
+			ExpressionNode *curdes = designator;
+			while ( curdes != nullptr) {
+				curdes->printOneLine(os);
+				curdes = curdes->next;
+				if ( curdes ) os << ", ";
+			} // while
+			os << ")";
+		} // if
+		if ( expr ) expr->printOneLine( os );
+	} else {  // It's an aggregate
+		os << "[--";
+		if ( next_init() != nullptr )
+			next_init()->printOneLine( os );
+		if (aggregate) os << "--]";
+	} // if
+
+	InitializerNode *moreInit;
+	if ( ( moreInit = next ) ) {
+		moreInit->printOneLine( os );
+	} // if
+} // InitializerNode::printOneLine
+
+ast::Init * InitializerNode::build() const {
+	assertf( ! isDelete, "Should not build delete stmt InitializerNode" );
+	if ( aggregate ) {
+		// steal designators from children
+		std::vector<ast::ptr<ast::Designation>> designlist;
+		InitializerNode * child = next_init();
+		for ( ; child != nullptr ; child = child->next ) {
+			std::deque<ast::ptr<ast::Expr>> desList;
+			buildList( child->designator, desList );
+			designlist.push_back(
+				new ast::Designation( location, std::move( desList ) ) );
+		} // for
+		std::vector<ast::ptr<ast::Init>> initlist;
+		buildList( next_init(), initlist );
+		return new ast::ListInit( location,
+			std::move( initlist ),
+			std::move( designlist ),
+			toConstructFlag( maybeConstructed )
+		);
+	} else if ( get_expression() ) {
+		assertf( get_expression()->expr, "The expression of initializer must have value" );
+		return new ast::SingleInit( location,
+			maybeBuild( get_expression() ),
+			toConstructFlag( maybeConstructed )
+		);
+	} // if
+	return nullptr;
+} // InitializerNode::build
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/InitializerNode.h
===================================================================
--- src/Parser/InitializerNode.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,50 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// InitializerNode.h --
-//
-// Author           : Andrew Beach
-// Created On       : Wed Apr  5 11:31:00 2023
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Apr  5 11:48:00 2023
-// Update Count     : 0
-//
-
-#pragma once
-
-#include "ParseNode.h"
-
-struct InitializerNode final : public ParseList<InitializerNode> {
-	InitializerNode( ExpressionNode *, bool aggrp = false, ExpressionNode * des = nullptr );
-	InitializerNode( InitializerNode *, bool aggrp = false, ExpressionNode * des = nullptr );
-	InitializerNode( bool isDelete );
-	~InitializerNode();
-	virtual InitializerNode * clone() const { assert( false ); return nullptr; }
-
-	ExpressionNode * get_expression() const { return expr; }
-
-	InitializerNode * set_designators( ExpressionNode * des ) { designator = des; return this; }
-	ExpressionNode * get_designators() const { return designator; }
-
-	InitializerNode * set_maybeConstructed( bool value ) { maybeConstructed = value; return this; }
-	bool get_maybeConstructed() const { return maybeConstructed; }
-
-	bool get_isDelete() const { return isDelete; }
-
-	InitializerNode * next_init() const { return kids; }
-
-	void print( std::ostream & os, int indent = 0 ) const;
-	void printOneLine( std::ostream & ) const;
-
-	virtual ast::Init * build() const;
-private:
-	ExpressionNode * expr;
-	bool aggregate;
-	ExpressionNode * designator;                        // may be list
-	InitializerNode * kids;
-	bool maybeConstructed;
-	bool isDelete;
-}; // InitializerNode
Index: src/Parser/InitializerNode.hpp
===================================================================
--- src/Parser/InitializerNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/InitializerNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,50 @@
+//
+// 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.
+//
+// InitializerNode.hpp --
+//
+// Author           : Andrew Beach
+// Created On       : Wed Apr  5 11:31:00 2023
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Apr  5 11:48:00 2023
+// Update Count     : 0
+//
+
+#pragma once
+
+#include "ParseNode.hpp"
+
+struct InitializerNode final : public ParseList<InitializerNode> {
+	InitializerNode( ExpressionNode *, bool aggrp = false, ExpressionNode * des = nullptr );
+	InitializerNode( InitializerNode *, bool aggrp = false, ExpressionNode * des = nullptr );
+	InitializerNode( bool isDelete );
+	~InitializerNode();
+	virtual InitializerNode * clone() const { assert( false ); return nullptr; }
+
+	ExpressionNode * get_expression() const { return expr; }
+
+	InitializerNode * set_designators( ExpressionNode * des ) { designator = des; return this; }
+	ExpressionNode * get_designators() const { return designator; }
+
+	InitializerNode * set_maybeConstructed( bool value ) { maybeConstructed = value; return this; }
+	bool get_maybeConstructed() const { return maybeConstructed; }
+
+	bool get_isDelete() const { return isDelete; }
+
+	InitializerNode * next_init() const { return kids; }
+
+	void print( std::ostream & os, int indent = 0 ) const;
+	void printOneLine( std::ostream & ) const;
+
+	virtual ast::Init * build() const;
+private:
+	ExpressionNode * expr;
+	bool aggregate;
+	ExpressionNode * designator;                        // may be list
+	InitializerNode * kids;
+	bool maybeConstructed;
+	bool isDelete;
+}; // InitializerNode
Index: src/Parser/ParseNode.cc
===================================================================
--- src/Parser/ParseNode.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,30 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ParseNode.cc --
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Sat May 16 13:26:29 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Oct  1 23:10:43 2016
-// Update Count     : 127
-//
-
-#include "ParseNode.h"
-using namespace std;
-
-int ParseNode::indent_by = 4;
-
-std::ostream & operator<<( std::ostream & out, const ParseNode * node ) {
-	node->print( out );
-	return out;
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/ParseNode.cpp
===================================================================
--- src/Parser/ParseNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/ParseNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,30 @@
+//
+// 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.
+//
+// ParseNode.cpp --
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Sat May 16 13:26:29 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Oct  1 23:10:43 2016
+// Update Count     : 127
+//
+
+#include "ParseNode.hpp"
+using namespace std;
+
+int ParseNode::indent_by = 4;
+
+std::ostream & operator<<( std::ostream & out, const ParseNode * node ) {
+	node->print( out );
+	return out;
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/ParseNode.h
===================================================================
--- src/Parser/ParseNode.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,121 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ParseNode.h --
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Sat May 16 13:28:16 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Dec  9 17:39:34 2023
-// Update Count     : 945
-//
-
-#pragma once
-
-#include <algorithm>               // for move
-#include <cassert>                 // for assert, assertf
-#include <iosfwd>                  // for ostream
-#include <iterator>                // for back_insert_iterator
-#include <list>                    // for list
-#include <memory>                  // for unique_ptr, pointer_traits
-#include <string>                  // for string
-
-#include "AST/Expr.hpp"            // for Expr, NameExpr LogicalFlag
-#include "AST/Fwd.hpp"             // for ptr, Decl, DeclWithType,
-#include "AST/Stmt.hpp"            // for Stmt
-#include "Common/CodeLocation.h"   // for CodeLocation
-#include "Common/SemanticError.h"  // for SemanticError
-#include "Common/UniqueName.h"     // for UniqueName
-#include "Parser/parserutility.h"  // for maybeBuild, maybeCopy
-
-struct DeclarationNode;
-struct InitializerNode;
-struct ExpressionNode;
-struct StatementNode;
-
-
-//##############################################################################
-
-typedef CodeLocation YYLTYPE;
-#define YYLTYPE_IS_DECLARED 1 /* alert the parser that we have our own definition */
-
-extern YYLTYPE yylloc;
-
-struct ParseNode {
-	ParseNode() {}
-	virtual ~ParseNode() {}
-	virtual ParseNode * clone() const = 0;
-
-	virtual void print( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const {}
-
-	static int indent_by;
-
-	CodeLocation location = yylloc;
-}; // ParseNode
-
-/// Only ever use in the form `struct NAME final : public ParseList<NAME>`!
-template<typename Next>
-struct ParseList : public ParseNode {
-	ParseList() {}
-	virtual ~ParseList() { delete next; };
-	virtual ParseList<Next> * clone() const = 0;
-
-	Next * get_last() {
-		Next * current = static_cast<Next *>( this );
-		while ( current->next != nullptr ) current = current->next;
-		return current;
-	}
-	Next * set_last( Next * newlast ) {
-		if ( newlast != nullptr ) get_last()->next = newlast;
-		return static_cast<Next *>( this );
-	}
-
-	virtual void printList( std::ostream & os, int indent = 0 ) const {
-		print( os, indent );
-		if ( next ) next->print( os, indent );
-	}
-
-	Next * next = nullptr;
-};
-
-template<typename Node>
-void extend( Node *& list, Node * value ) {
-	if ( list ) {
-		extend( list->next, value );
-	} else {
-		list = value;
-	}
-}
-
-// Must harmonize with OperName.
-enum class OperKinds {
-	// diadic
-	SizeOf, AlignOf, OffsetOf, Plus, Minus, Exp, Mul, Div, Mod, Or, And,
-	BitOr, BitAnd, Xor, Cast, LShift, RShift, LThan, GThan, LEThan, GEThan, Eq, Neq,
-	Assign, AtAssn, ExpAssn, MulAssn, DivAssn, ModAssn, PlusAssn, MinusAssn, LSAssn, RSAssn, AndAssn, ERAssn, OrAssn,
-	Index, Range,
-	// monadic
-	UnPlus, UnMinus, AddressOf, PointTo, Neg, BitNeg, Incr, IncrPost, Decr, DecrPost,
-	Ctor, Dtor,
-}; // OperKinds
-
-enum class EnumHiding { Visible, Hide };
-
-struct LabelNode {
-	std::vector<ast::Label> labels;
-};
-
-std::ostream & operator<<( std::ostream & out, const ParseNode * node );
-
-__attribute__((noreturn)) static inline void SemanticError( const ParseNode * obj, const std::string & error ) {
-	SemanticError( obj->location, toString( error, obj ) );
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/ParseNode.hpp
===================================================================
--- src/Parser/ParseNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/ParseNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,121 @@
+//
+// 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.
+//
+// ParseNode.hpp --
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Sat May 16 13:28:16 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Dec  9 17:39:34 2023
+// Update Count     : 945
+//
+
+#pragma once
+
+#include <algorithm>                 // for move
+#include <cassert>                   // for assert, assertf
+#include <iosfwd>                    // for ostream
+#include <iterator>                  // for back_insert_iterator
+#include <list>                      // for list
+#include <memory>                    // for unique_ptr, pointer_traits
+#include <string>                    // for string
+
+#include "AST/Expr.hpp"              // for Expr, NameExpr, LogicalFlag
+#include "AST/Fwd.hpp"               // for ptr, Decl, DeclWithType,
+#include "AST/Stmt.hpp"              // for Stmt
+#include "Common/CodeLocation.hpp"   // for CodeLocation
+#include "Common/SemanticError.hpp"  // for SemanticError
+#include "Common/UniqueName.hpp"     // for UniqueName
+#include "Parser/ParserUtility.hpp"  // for maybeBuild, maybeCopy
+
+struct DeclarationNode;
+struct InitializerNode;
+struct ExpressionNode;
+struct StatementNode;
+
+
+//##############################################################################
+
+typedef CodeLocation YYLTYPE;
+#define YYLTYPE_IS_DECLARED 1 /* alert the parser that we have our own definition */
+
+extern YYLTYPE yylloc;
+
+struct ParseNode {
+	ParseNode() {}
+	virtual ~ParseNode() {}
+	virtual ParseNode * clone() const = 0;
+
+	virtual void print( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const {}
+
+	static int indent_by;
+
+	CodeLocation location = yylloc;
+}; // ParseNode
+
+/// Only ever use in the form `struct NAME final : public ParseList<NAME>`!
+template<typename Next>
+struct ParseList : public ParseNode {
+	ParseList() {}
+	virtual ~ParseList() { delete next; };
+	virtual ParseList<Next> * clone() const = 0;
+
+	Next * get_last() {
+		Next * current = static_cast<Next *>( this );
+		while ( current->next != nullptr ) current = current->next;
+		return current;
+	}
+	Next * set_last( Next * newlast ) {
+		if ( newlast != nullptr ) get_last()->next = newlast;
+		return static_cast<Next *>( this );
+	}
+
+	virtual void printList( std::ostream & os, int indent = 0 ) const {
+		print( os, indent );
+		if ( next ) next->print( os, indent );
+	}
+
+	Next * next = nullptr;
+};
+
+template<typename Node>
+void extend( Node *& list, Node * value ) {
+	if ( list ) {
+		extend( list->next, value );
+	} else {
+		list = value;
+	}
+}
+
+// Must harmonize with OperName.
+enum class OperKinds {
+	// diadic
+	SizeOf, AlignOf, OffsetOf, Plus, Minus, Exp, Mul, Div, Mod, Or, And,
+	BitOr, BitAnd, Xor, Cast, LShift, RShift, LThan, GThan, LEThan, GEThan, Eq, Neq,
+	Assign, AtAssn, ExpAssn, MulAssn, DivAssn, ModAssn, PlusAssn, MinusAssn, LSAssn, RSAssn, AndAssn, ERAssn, OrAssn,
+	Index, Range,
+	// monadic
+	UnPlus, UnMinus, AddressOf, PointTo, Neg, BitNeg, Incr, IncrPost, Decr, DecrPost,
+	Ctor, Dtor,
+}; // OperKinds
+
+enum class EnumHiding { Visible, Hide };
+
+struct LabelNode {
+	std::vector<ast::Label> labels;
+};
+
+std::ostream & operator<<( std::ostream & out, const ParseNode * node );
+
+__attribute__((noreturn)) static inline void SemanticError( const ParseNode * obj, const std::string & error ) {
+	SemanticError( obj->location, toString( error, obj ) );
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/ParserTypes.h
===================================================================
--- src/Parser/ParserTypes.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,47 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// parser.hh --
-//
-// Author           : Peter A. Buhr
-// Created On       : Sat Sep 22 08:58:10 2001
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Feb 15 11:04:40 2020
-// Update Count     : 351
-//
-
-#pragma once
-
-int yylex();
-void yyerror( const char * );
-
-#include <string>
-#include "ParseNode.h"
-// External declarations for information sharing between lexer and scanner
-class TypedefTable;
-extern TypedefTable typedefTable;
-
-// current location in the input
-extern int yylineno;
-extern char * yyfilename;
-
-struct Location {
-    char * file;
-    int line;
-}; // Location
-
-struct Token {
-    std::string * str;									// must be pointer as used in union
-    Location loc;
-
-    operator std::string *() { return str; }
-}; // Token
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/ParserTypes.hpp
===================================================================
--- src/Parser/ParserTypes.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/ParserTypes.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+// ParserTypes.hpp --
+//
+// Author           : Peter A. Buhr
+// Created On       : Sat Sep 22 08:58:10 2001
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Feb 15 11:04:40 2020
+// Update Count     : 351
+//
+
+#pragma once
+
+int yylex();
+void yyerror( const char * );
+
+#include <string>
+#include "ParseNode.hpp"
+// External declarations for information sharing between lexer and scanner
+class TypedefTable;
+extern TypedefTable typedefTable;
+
+// current location in the input
+extern int yylineno;
+extern char * yyfilename;
+
+struct Location {
+    char * file;
+    int line;
+}; // Location
+
+struct Token {
+    std::string * str;									// must be pointer as used in union
+    Location loc;
+
+    operator std::string *() { return str; }
+}; // Token
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/ParserUtility.hpp
===================================================================
--- src/Parser/ParserUtility.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/ParserUtility.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,41 @@
+//
+// 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.
+//
+// ParserUtility.hpp -- Collected utilities for the parser.
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Sat May 16 15:31:46 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Apr  4 14:03:00 2023
+// Update Count     : 7
+//
+
+#pragma once
+
+#include "AST/Copy.hpp"            // for shallowCopy
+
+template< typename T >
+static inline auto maybeBuild( T * orig ) -> decltype(orig->build()) {
+	return (orig) ? orig->build() : nullptr;
+}
+
+template< typename T >
+static inline auto maybeMoveBuild( T * orig ) -> decltype(orig->build()) {
+	auto ret = maybeBuild<T>(orig);
+	delete orig;
+	return ret;
+}
+
+template<typename node_t>
+static inline node_t * maybeCopy( node_t const * node ) {
+	return node ? ast::shallowCopy( node ) : nullptr;
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/RunParser.cpp
===================================================================
--- src/Parser/RunParser.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Parser/RunParser.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -18,6 +18,6 @@
 #include "AST/TranslationUnit.hpp"          // for TranslationUnit
 #include "Common/CodeLocationTools.hpp"     // for forceFillCodeLocations
-#include "Parser/DeclarationNode.h"         // for DeclarationNode, buildList
-#include "Parser/TypedefTable.h"            // for TypedefTable
+#include "Parser/DeclarationNode.hpp"       // for DeclarationNode, buildList
+#include "Parser/TypedefTable.hpp"          // for TypedefTable
 
 // Variables global to the parsing code.
Index: src/Parser/StatementNode.cc
===================================================================
--- src/Parser/StatementNode.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,527 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// StatementNode.cc -- Transform from parse data-structures to AST data-structures, usually deleting the parse
-//     data-structure after the transformation.
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Sat May 16 14:59:41 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Aug 11 11:44:15 2023
-// Update Count     : 429
-//
-
-#include "StatementNode.h"
-
-#include <cassert>                 // for assert, strict_dynamic_cast, assertf
-#include <memory>                  // for unique_ptr
-#include <string>                  // for string
-
-#include "AST/Label.hpp"           // for Label
-#include "AST/Stmt.hpp"            // for Stmt, AsmStmt, BranchStmt, CaseCla...
-#include "Common/SemanticError.h"  // for SemanticError
-#include "Common/utility.h"        // for maybeMoveBuild, maybeBuild
-#include "DeclarationNode.h"       // for DeclarationNode
-#include "ExpressionNode.h"        // for ExpressionNode
-#include "parserutility.h"         // for notZeroExpr
-
-class Declaration;
-
-using namespace std;
-
-// Some helpers for cases that really want a single node but check for lists.
-static const ast::Stmt * buildMoveSingle( StatementNode * node ) {
-	std::vector<ast::ptr<ast::Stmt>> list;
-	buildMoveList( node, list );
-	assertf( list.size() == 1, "CFA Internal Error: Extra/Missing Nodes" );
-	return list.front().release();
-}
-
-static const ast::Stmt * buildMoveOptional( StatementNode * node ) {
-	std::vector<ast::ptr<ast::Stmt>> list;
-	buildMoveList( node, list );
-	assertf( list.size() <= 1, "CFA Internal Error: Extra Nodes" );
-	return list.empty() ? nullptr : list.front().release();
-}
-
-StatementNode::StatementNode( DeclarationNode * decl ) {
-	assert( decl );
-	DeclarationNode * agg = decl->extractAggregate();
-	if ( agg ) {
-		StatementNode * nextStmt = new StatementNode(
-			new ast::DeclStmt( decl->location, maybeBuild( decl ) ) );
-		next = nextStmt;
-		if ( decl->next ) {
-			next->next = new StatementNode( decl->next );
-			decl->next = nullptr;
-		} // if
-	} else {
-		if ( decl->next ) {
-			next = new StatementNode( decl->next );
-			decl->next = nullptr;
-		} // if
-		agg = decl;
-	} // if
-	// Local copy to avoid accessing the pointer after it is moved from.
-	CodeLocation declLocation = agg->location;
-	stmt.reset( new ast::DeclStmt( declLocation, maybeMoveBuild( agg ) ) );
-} // StatementNode::StatementNode
-
-StatementNode * StatementNode::add_label(
-		const CodeLocation & location,
-		const std::string * name,
-		DeclarationNode * attr ) {
-	stmt->labels.emplace_back( location,
-		*name,
-		attr ? std::move( attr->attributes )
-			: std::vector<ast::ptr<ast::Attribute>>{} );
-	delete attr;
-	delete name;
-	return this;
-}
-
-ClauseNode * ClauseNode::append_last_case( StatementNode * stmt ) {
-	ClauseNode * prev = this;
-	// find end of list and maintain previous pointer
-	for ( ClauseNode * curr = prev; curr != nullptr; curr = curr->next ) {
-		ClauseNode * node = curr;
-		assert( dynamic_cast<ast::CaseClause *>( node->clause.get() ) );
-		prev = curr;
-	} // for
-	ClauseNode * node = prev;
-	// convert from StatementNode list to Statement list
-	std::vector<ast::ptr<ast::Stmt>> stmts;
-	buildMoveList( stmt, stmts );
-	// splice any new Statements to end of current Statements
-	auto caseStmt = strict_dynamic_cast<ast::CaseClause *>( node->clause.get() );
-	for ( auto const & newStmt : stmts ) {
-		caseStmt->stmts.emplace_back( newStmt );
-	}
-	stmts.clear();
-	return this;
-} // ClauseNode::append_last_case
-
-ast::Stmt * build_expr( CodeLocation const & location, ExpressionNode * ctl ) {
-	if ( ast::Expr * e = maybeMoveBuild( ctl ) ) {
-		return new ast::ExprStmt( location, e );
-	} else {
-		return new ast::NullStmt( location );
-	}
-} // build_expr
-
-static ast::Expr * build_if_control( CondCtl * ctl,
-		std::vector<ast::ptr<ast::Stmt>> & inits ) {
-	assert( inits.empty() );
-	if ( nullptr != ctl->init ) {
-		buildMoveList( ctl->init, inits );
-	} // if
-
-	ast::Expr * cond = nullptr;
-	if ( ctl->condition ) {
-		cond = maybeMoveBuild( ctl->condition );
-	} else {
-		for ( ast::ptr<ast::Stmt> & stmt : inits ) {
-			// build the && of all of the declared variables compared against 0
-			auto declStmt = stmt.strict_as<ast::DeclStmt>();
-			auto dwt = declStmt->decl.strict_as<ast::DeclWithType>();
-			ast::Expr * nze = new ast::VariableExpr( dwt->location, dwt );
-			cond = cond ? new ast::LogicalExpr( dwt->location, cond, nze, ast::AndExpr ) : nze;
-		}
-	}
-	delete ctl;
-	return cond;
-} // build_if_control
-
-ast::Stmt * build_if( const CodeLocation & location, CondCtl * ctl, StatementNode * then, StatementNode * else_ ) {
-	std::vector<ast::ptr<ast::Stmt>> astinit;						// maybe empty
-	ast::Expr * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
-
-	ast::Stmt const * astthen = buildMoveSingle( then );
-	ast::Stmt const * astelse = buildMoveOptional( else_ );
-
-	return new ast::IfStmt( location, astcond, astthen, astelse,
-		std::move( astinit )
-	);
-} // build_if
-
-ast::Stmt * build_switch( const CodeLocation & location, bool isSwitch, ExpressionNode * ctl, ClauseNode * stmt ) {
-	std::vector<ast::ptr<ast::CaseClause>> aststmt;
-	buildMoveList( stmt, aststmt );
-	// If it is not a switch it is a choose statement.
-	if ( ! isSwitch ) {
-		for ( ast::ptr<ast::CaseClause> & stmt : aststmt ) {
-			// Code after "case" is the end of case list.
-			if ( !stmt->stmts.empty() ) {
-				auto mutStmt = ast::mutate( stmt.get() );
-				// I believe the stmts are actually always one block.
-				auto stmts = mutStmt->stmts.front().get_and_mutate();
-				auto block = strict_dynamic_cast<ast::CompoundStmt *>( stmts );
-				block->kids.push_back( new ast::BranchStmt( block->location,
-					ast::BranchStmt::Break,
-					ast::Label( block->location ) ) );
-				stmt = mutStmt;
-			} // if
-		} // for
-	} // if
-	// aststmt.size() == 0 for switch (...) {}, i.e., no declaration or statements
-	return new ast::SwitchStmt( location,
-		maybeMoveBuild( ctl ), std::move( aststmt ) );
-} // build_switch
-
-ast::CaseClause * build_case( const CodeLocation & location, ExpressionNode * ctl ) {
-	// stmt starts empty and then added to
-	auto expr = maybeMoveBuild( ctl );
-	return new ast::CaseClause( location, expr, {} );
-} // build_case
-
-ast::CaseClause * build_default( const CodeLocation & location ) {
-	// stmt starts empty and then added to
-	return new ast::CaseClause( location, nullptr, {} );
-} // build_default
-
-ast::Stmt * build_while( const CodeLocation & location, CondCtl * ctl, StatementNode * stmt, StatementNode * else_ ) {
-	std::vector<ast::ptr<ast::Stmt>> astinit;						// maybe empty
-	ast::Expr * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
-
-	return new ast::WhileDoStmt( location,
-		astcond,
-		buildMoveSingle( stmt ),
-		buildMoveOptional( else_ ),
-		std::move( astinit ),
-		ast::While
-	);
-} // build_while
-
-ast::Stmt * build_do_while( const CodeLocation & location, ExpressionNode * ctl, StatementNode * stmt, StatementNode * else_ ) {
-	// do-while cannot have declarations in the contitional, so init is always empty
-	return new ast::WhileDoStmt( location,
-		maybeMoveBuild( ctl ),
-		buildMoveSingle( stmt ),
-		buildMoveOptional( else_ ),
-		{},
-		ast::DoWhile
-	);
-} // build_do_while
-
-ast::Stmt * build_for( const CodeLocation & location, ForCtrl * forctl, StatementNode * stmt, StatementNode * else_ ) {
-	std::vector<ast::ptr<ast::Stmt>> astinit;						// maybe empty
-	buildMoveList( forctl->init, astinit );
-
-	ast::Expr * astcond = nullptr;						// maybe empty
-	astcond = maybeMoveBuild( forctl->condition );
-
-	ast::Expr * astincr = nullptr;						// maybe empty
-	astincr = maybeMoveBuild( forctl->change );
-	delete forctl;
-
-	return new ast::ForStmt( location,
-		std::move( astinit ),
-		astcond,
-		astincr,
-		buildMoveSingle( stmt ),
-		buildMoveOptional( else_ )
-	);
-} // build_for
-
-ast::Stmt * build_branch( const CodeLocation & location, ast::BranchStmt::Kind kind ) {
-	return new ast::BranchStmt( location,
-		kind,
-		ast::Label( location )
-	);
-} // build_branch
-
-ast::Stmt * build_branch( const CodeLocation & location, string * identifier, ast::BranchStmt::Kind kind ) {
-	ast::Stmt * ret = new ast::BranchStmt( location,
-		kind,
-		ast::Label( location, *identifier )
-	);
-	delete identifier; 									// allocated by lexer
-	return ret;
-} // build_branch
-
-ast::Stmt * build_computedgoto( ExpressionNode * ctl ) {
-	ast::Expr * expr = maybeMoveBuild( ctl );
-	return new ast::BranchStmt( expr->location, expr );
-} // build_computedgoto
-
-ast::Stmt * build_return( const CodeLocation & location, ExpressionNode * ctl ) {
-	std::vector<ast::ptr<ast::Expr>> exps;
-	buildMoveList( ctl, exps );
-	return new ast::ReturnStmt( location,
-		exps.size() > 0 ? exps.back().release() : nullptr
-	);
-} // build_return
-
-static ast::Stmt * build_throw_stmt(
-		const CodeLocation & location,
-		ExpressionNode * ctl,
-		ast::ExceptionKind kind ) {
-	std::vector<ast::ptr<ast::Expr>> exps;
-	buildMoveList( ctl, exps );
-	assertf( exps.size() < 2, "CFA internal error: leaking memory" );
-	return new ast::ThrowStmt( location,
-		kind,
-		!exps.empty() ? exps.back().release() : nullptr,
-		(ast::Expr *)nullptr
-	);
-}
-
-ast::Stmt * build_throw( const CodeLocation & loc, ExpressionNode * ctl ) {
-	return build_throw_stmt( loc, ctl, ast::Terminate );
-} // build_throw
-
-ast::Stmt * build_resume( const CodeLocation & loc, ExpressionNode * ctl ) {
-	return build_throw_stmt( loc, ctl, ast::Resume );
-} // build_resume
-
-ast::Stmt * build_resume_at( ExpressionNode * ctl, ExpressionNode * target ) {
-	(void)ctl;
-	(void)target;
-	assertf( false, "resume at (non-local throw) is not yet supported," );
-} // build_resume_at
-
-ast::Stmt * build_try( const CodeLocation & location, StatementNode * try_, ClauseNode * catch_, ClauseNode * finally_ ) {
-	std::vector<ast::ptr<ast::CatchClause>> aststmt;
-	buildMoveList( catch_, aststmt );
-	ast::CompoundStmt * tryBlock = strict_dynamic_cast<ast::CompoundStmt *>( maybeMoveBuild( try_ ) );
-	ast::FinallyClause * finallyBlock = nullptr;
-	if ( finally_ ) {
-		finallyBlock = dynamic_cast<ast::FinallyClause *>( finally_->clause.release() );
-	}
-	return new ast::TryStmt( location,
-		tryBlock,
-		std::move( aststmt ),
-		finallyBlock
-	);
-} // build_try
-
-ast::CatchClause * build_catch( const CodeLocation & location, ast::ExceptionKind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body ) {
-	return new ast::CatchClause( location,
-		kind,
-		maybeMoveBuild( decl ),
-		maybeMoveBuild( cond ),
-		buildMoveSingle( body )
-	);
-} // build_catch
-
-ast::FinallyClause * build_finally( const CodeLocation & location, StatementNode * stmt ) {
-	return new ast::FinallyClause( location,
-		strict_dynamic_cast<const ast::CompoundStmt *>(
-			buildMoveSingle( stmt )
-		)
-	);
-} // build_finally
-
-ast::SuspendStmt * build_suspend( const CodeLocation & location, StatementNode * then, ast::SuspendStmt::Kind kind ) {
-	return new ast::SuspendStmt( location,
-		strict_dynamic_cast<const ast::CompoundStmt *, nullptr>(
-			buildMoveOptional( then )
-		),
-		kind
-	);
-} // build_suspend
-
-ast::WaitForStmt * build_waitfor( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
-	auto clause = new ast::WaitForClause( location );
-	clause->target = maybeBuild( targetExpr );
-	clause->stmt = maybeMoveBuild( stmt );
-	clause->when_cond = maybeMoveBuild( when );
-
-	ExpressionNode * next = targetExpr->next;
-	targetExpr->next = nullptr;
-	buildMoveList( next, clause->target_args );
-
-	delete targetExpr;
-
-	existing->clauses.insert( existing->clauses.begin(), clause );
-
-	return existing;
-} // build_waitfor
-
-ast::WaitForStmt * build_waitfor_else( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt ) {
-	existing->else_stmt = maybeMoveBuild( stmt );
-	existing->else_cond = maybeMoveBuild( when );
-
-	(void)location;
-	return existing;
-} // build_waitfor_else
-
-ast::WaitForStmt * build_waitfor_timeout( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt ) {
-	existing->timeout_time = maybeMoveBuild( timeout );
-	existing->timeout_stmt = maybeMoveBuild( stmt );
-	existing->timeout_cond = maybeMoveBuild( when );
-
-	(void)location;
-	return existing;
-} // build_waitfor_timeout
-
-ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
-	ast::WhenClause * clause = new ast::WhenClause( loc );
-	clause->when_cond = maybeMoveBuild( when );
-	clause->stmt = maybeMoveBuild( stmt );
-	clause->target = maybeMoveBuild( targetExpr );
-	return new ast::WaitUntilStmt::ClauseNode( clause );
-}
-ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation & loc, ExpressionNode * when, StatementNode * stmt ) {
-	ast::WhenClause * clause = new ast::WhenClause( loc );
-	clause->when_cond = maybeMoveBuild( when );
-	clause->stmt = maybeMoveBuild( stmt );
-	return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::ELSE, clause );
-}
-
-ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation & loc, ast::WaitUntilStmt::ClauseNode * root ) {
-	ast::WaitUntilStmt * retStmt = new ast::WaitUntilStmt( loc );
-	retStmt->predicateTree = root;
-
-	// iterative tree traversal
-	std::vector<ast::WaitUntilStmt::ClauseNode *> nodeStack; // stack needed for iterative traversal
-	ast::WaitUntilStmt::ClauseNode * currNode = nullptr;
-	ast::WaitUntilStmt::ClauseNode * lastInternalNode = nullptr;
-	ast::WaitUntilStmt::ClauseNode * cleanup = nullptr; // used to cleanup removed else/timeout
-	nodeStack.push_back(root);
-
-	do {
-		currNode = nodeStack.back();
-		nodeStack.pop_back(); // remove node since it will be processed
-
-		switch (currNode->op) {
-		case ast::WaitUntilStmt::ClauseNode::LEAF:
-			retStmt->clauses.push_back(currNode->leaf);
-			break;
-		case ast::WaitUntilStmt::ClauseNode::ELSE:
-			retStmt->else_stmt = currNode->leaf->stmt
-				? ast::deepCopy( currNode->leaf->stmt )
-				: nullptr;
-			retStmt->else_cond = currNode->leaf->when_cond
-				? ast::deepCopy( currNode->leaf->when_cond )
-				: nullptr;
-
-			delete currNode->leaf;
-			break;
-		case ast::WaitUntilStmt::ClauseNode::TIMEOUT:
-			retStmt->timeout_time = currNode->leaf->target
-				? ast::deepCopy( currNode->leaf->target )
-				: nullptr;
-			retStmt->timeout_stmt = currNode->leaf->stmt
-				? ast::deepCopy( currNode->leaf->stmt )
-				: nullptr;
-			retStmt->timeout_cond = currNode->leaf->when_cond
-				? ast::deepCopy( currNode->leaf->when_cond )
-				: nullptr;
-
-			delete currNode->leaf;
-			break;
-		default:
-			nodeStack.push_back( currNode->right ); // process right after left
-			nodeStack.push_back( currNode->left );
-
-			// Cut else/timeout out of the tree
-			if ( currNode->op == ast::WaitUntilStmt::ClauseNode::LEFT_OR ) {
-				if ( lastInternalNode )
-					lastInternalNode->right = currNode->left;
-				else // if not set then root is LEFT_OR
-					retStmt->predicateTree = currNode->left;
-
-				currNode->left = nullptr;
-				cleanup = currNode;
-			}
-
-			lastInternalNode = currNode;
-			break;
-		}
-	} while ( !nodeStack.empty() );
-
-	if ( cleanup ) delete cleanup;
-
-	return retStmt;
-}
-
-ast::Stmt * build_with( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
-	std::vector<ast::ptr<ast::Expr>> e;
-	buildMoveList( exprs, e );
-	ast::Stmt * s = maybeMoveBuild( stmt );
-	return new ast::DeclStmt( location, new ast::WithStmt( location, std::move( e ), s ) );
-} // build_with
-
-ast::Stmt * build_compound( const CodeLocation & location, StatementNode * first ) {
-	auto cs = new ast::CompoundStmt( location );
-	buildMoveList( first, cs->kids );
-	return cs;
-} // build_compound
-
-// A single statement in a control structure is always converted to a compound statement so subsequent generated code
-// can be placed within this compound statement. Otherwise, code generation has to constantly check for a single
-// statement and wrap it into a compound statement to insert additional code. Hence, all control structures have a
-// conical form for code generation.
-StatementNode * maybe_build_compound( const CodeLocation & location, StatementNode * first ) {
-	// Optimization: if the control-structure statement is a compound statement, do not wrap it.
-	// e.g., if (...) {...} do not wrap the existing compound statement.
-	if ( !dynamic_cast<ast::CompoundStmt *>( first->stmt.get() ) ) { // unique_ptr
-		return new StatementNode( build_compound( location, first ) );
-	} // if
-	return first;
-} // maybe_build_compound
-
-// Question
-ast::Stmt * build_asm( const CodeLocation & location, bool is_volatile, ExpressionNode * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) {
-	std::vector<ast::ptr<ast::Expr>> out, in;
-	std::vector<ast::ptr<ast::ConstantExpr>> clob;
-
-	buildMoveList( output, out );
-	buildMoveList( input, in );
-	buildMoveList( clobber, clob );
-	return new ast::AsmStmt( location,
-		is_volatile,
-		maybeMoveBuild( instruction ),
-		std::move( out ),
-		std::move( in ),
-		std::move( clob ),
-		gotolabels ? gotolabels->labels : std::vector<ast::Label>()
-	);
-} // build_asm
-
-ast::Stmt * build_directive( const CodeLocation & location, string * directive ) {
-	auto stmt = new ast::DirectiveStmt( location, *directive );
-	delete directive;
-	return stmt;
-} // build_directive
-
-ast::Stmt * build_mutex( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
-	std::vector<ast::ptr<ast::Expr>> expList;
-	buildMoveList( exprs, expList );
-	ast::Stmt * body = maybeMoveBuild( stmt );
-	return new ast::MutexStmt( location, body, std::move( expList ) );
-} // build_mutex
-
-ast::Stmt * build_corun( const CodeLocation & location, StatementNode * stmt ) {
-	ast::Stmt * body = maybeMoveBuild( stmt );
-	return new ast::CorunStmt( location, body );
-} // build_corun
-
-ast::Stmt * build_cofor( const CodeLocation & location, ForCtrl * forctl, StatementNode * stmt ) {
-	std::vector<ast::ptr<ast::Stmt>> astinit;						// maybe empty
-	buildMoveList( forctl->init, astinit );
-
-	ast::Expr * astcond = nullptr;						// maybe empty
-	astcond = maybeMoveBuild( forctl->condition );
-
-	ast::Expr * astincr = nullptr;						// maybe empty
-	astincr = maybeMoveBuild( forctl->change );
-	delete forctl;
-
-	return new ast::CoforStmt( location,
-		std::move( astinit ),
-		astcond,
-		astincr,
-		buildMoveSingle( stmt )
-	);
-} // build_cofor
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/StatementNode.cpp
===================================================================
--- src/Parser/StatementNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/StatementNode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,527 @@
+//
+// 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.
+//
+// StatementNode.cpp -- Transform from parse data-structures to AST data-structures, usually deleting the parse
+//     data-structure after the transformation.
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Sat May 16 14:59:41 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri Aug 11 11:44:15 2023
+// Update Count     : 429
+//
+
+#include "StatementNode.hpp"
+
+#include <cassert>                 // for assert, strict_dynamic_cast, assertf
+#include <memory>                  // for unique_ptr
+#include <string>                  // for string
+
+#include "AST/Label.hpp"           // for Label
+#include "AST/Stmt.hpp"            // for Stmt, AsmStmt, BranchStmt, CaseCla...
+#include "Common/SemanticError.hpp"// for SemanticError
+#include "Common/Utility.hpp"      // for maybeMoveBuild, maybeBuild
+#include "DeclarationNode.hpp"     // for DeclarationNode
+#include "ExpressionNode.hpp"      // for ExpressionNode
+#include "ParserUtility.hpp"       // for notZeroExpr
+
+class Declaration;
+
+using namespace std;
+
+// Some helpers for cases that really want a single node but check for lists.
+static const ast::Stmt * buildMoveSingle( StatementNode * node ) {
+	std::vector<ast::ptr<ast::Stmt>> list;
+	buildMoveList( node, list );
+	assertf( list.size() == 1, "CFA Internal Error: Extra/Missing Nodes" );
+	return list.front().release();
+}
+
+static const ast::Stmt * buildMoveOptional( StatementNode * node ) {
+	std::vector<ast::ptr<ast::Stmt>> list;
+	buildMoveList( node, list );
+	assertf( list.size() <= 1, "CFA Internal Error: Extra Nodes" );
+	return list.empty() ? nullptr : list.front().release();
+}
+
+StatementNode::StatementNode( DeclarationNode * decl ) {
+	assert( decl );
+	DeclarationNode * agg = decl->extractAggregate();
+	if ( agg ) {
+		StatementNode * nextStmt = new StatementNode(
+			new ast::DeclStmt( decl->location, maybeBuild( decl ) ) );
+		next = nextStmt;
+		if ( decl->next ) {
+			next->next = new StatementNode( decl->next );
+			decl->next = nullptr;
+		} // if
+	} else {
+		if ( decl->next ) {
+			next = new StatementNode( decl->next );
+			decl->next = nullptr;
+		} // if
+		agg = decl;
+	} // if
+	// Local copy to avoid accessing the pointer after it is moved from.
+	CodeLocation declLocation = agg->location;
+	stmt.reset( new ast::DeclStmt( declLocation, maybeMoveBuild( agg ) ) );
+} // StatementNode::StatementNode
+
+StatementNode * StatementNode::add_label(
+		const CodeLocation & location,
+		const std::string * name,
+		DeclarationNode * attr ) {
+	stmt->labels.emplace_back( location,
+		*name,
+		attr ? std::move( attr->attributes )
+			: std::vector<ast::ptr<ast::Attribute>>{} );
+	delete attr;
+	delete name;
+	return this;
+}
+
+ClauseNode * ClauseNode::append_last_case( StatementNode * stmt ) {
+	ClauseNode * prev = this;
+	// find end of list and maintain previous pointer
+	for ( ClauseNode * curr = prev; curr != nullptr; curr = curr->next ) {
+		ClauseNode * node = curr;
+		assert( dynamic_cast<ast::CaseClause *>( node->clause.get() ) );
+		prev = curr;
+	} // for
+	ClauseNode * node = prev;
+	// convert from StatementNode list to Statement list
+	std::vector<ast::ptr<ast::Stmt>> stmts;
+	buildMoveList( stmt, stmts );
+	// splice any new Statements to end of current Statements
+	auto caseStmt = strict_dynamic_cast<ast::CaseClause *>( node->clause.get() );
+	for ( auto const & newStmt : stmts ) {
+		caseStmt->stmts.emplace_back( newStmt );
+	}
+	stmts.clear();
+	return this;
+} // ClauseNode::append_last_case
+
+ast::Stmt * build_expr( CodeLocation const & location, ExpressionNode * ctl ) {
+	if ( ast::Expr * e = maybeMoveBuild( ctl ) ) {
+		return new ast::ExprStmt( location, e );
+	} else {
+		return new ast::NullStmt( location );
+	}
+} // build_expr
+
+static ast::Expr * build_if_control( CondCtl * ctl,
+		std::vector<ast::ptr<ast::Stmt>> & inits ) {
+	assert( inits.empty() );
+	if ( nullptr != ctl->init ) {
+		buildMoveList( ctl->init, inits );
+	} // if
+
+	ast::Expr * cond = nullptr;
+	if ( ctl->condition ) {
+		cond = maybeMoveBuild( ctl->condition );
+	} else {
+		for ( ast::ptr<ast::Stmt> & stmt : inits ) {
+			// build the && of all of the declared variables compared against 0
+			auto declStmt = stmt.strict_as<ast::DeclStmt>();
+			auto dwt = declStmt->decl.strict_as<ast::DeclWithType>();
+			ast::Expr * nze = new ast::VariableExpr( dwt->location, dwt );
+			cond = cond ? new ast::LogicalExpr( dwt->location, cond, nze, ast::AndExpr ) : nze;
+		}
+	}
+	delete ctl;
+	return cond;
+} // build_if_control
+
+ast::Stmt * build_if( const CodeLocation & location, CondCtl * ctl, StatementNode * then, StatementNode * else_ ) {
+	std::vector<ast::ptr<ast::Stmt>> astinit;						// maybe empty
+	ast::Expr * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
+
+	ast::Stmt const * astthen = buildMoveSingle( then );
+	ast::Stmt const * astelse = buildMoveOptional( else_ );
+
+	return new ast::IfStmt( location, astcond, astthen, astelse,
+		std::move( astinit )
+	);
+} // build_if
+
+ast::Stmt * build_switch( const CodeLocation & location, bool isSwitch, ExpressionNode * ctl, ClauseNode * stmt ) {
+	std::vector<ast::ptr<ast::CaseClause>> aststmt;
+	buildMoveList( stmt, aststmt );
+	// If it is not a switch it is a choose statement.
+	if ( ! isSwitch ) {
+		for ( ast::ptr<ast::CaseClause> & stmt : aststmt ) {
+			// Code after "case" is the end of case list.
+			if ( !stmt->stmts.empty() ) {
+				auto mutStmt = ast::mutate( stmt.get() );
+				// I believe the stmts are actually always one block.
+				auto stmts = mutStmt->stmts.front().get_and_mutate();
+				auto block = strict_dynamic_cast<ast::CompoundStmt *>( stmts );
+				block->kids.push_back( new ast::BranchStmt( block->location,
+					ast::BranchStmt::Break,
+					ast::Label( block->location ) ) );
+				stmt = mutStmt;
+			} // if
+		} // for
+	} // if
+	// aststmt.size() == 0 for switch (...) {}, i.e., no declaration or statements
+	return new ast::SwitchStmt( location,
+		maybeMoveBuild( ctl ), std::move( aststmt ) );
+} // build_switch
+
+ast::CaseClause * build_case( const CodeLocation & location, ExpressionNode * ctl ) {
+	// stmt starts empty and then added to
+	auto expr = maybeMoveBuild( ctl );
+	return new ast::CaseClause( location, expr, {} );
+} // build_case
+
+ast::CaseClause * build_default( const CodeLocation & location ) {
+	// stmt starts empty and then added to
+	return new ast::CaseClause( location, nullptr, {} );
+} // build_default
+
+ast::Stmt * build_while( const CodeLocation & location, CondCtl * ctl, StatementNode * stmt, StatementNode * else_ ) {
+	std::vector<ast::ptr<ast::Stmt>> astinit;						// maybe empty
+	ast::Expr * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
+
+	return new ast::WhileDoStmt( location,
+		astcond,
+		buildMoveSingle( stmt ),
+		buildMoveOptional( else_ ),
+		std::move( astinit ),
+		ast::While
+	);
+} // build_while
+
+ast::Stmt * build_do_while( const CodeLocation & location, ExpressionNode * ctl, StatementNode * stmt, StatementNode * else_ ) {
+	// do-while cannot have declarations in the contitional, so init is always empty
+	return new ast::WhileDoStmt( location,
+		maybeMoveBuild( ctl ),
+		buildMoveSingle( stmt ),
+		buildMoveOptional( else_ ),
+		{},
+		ast::DoWhile
+	);
+} // build_do_while
+
+ast::Stmt * build_for( const CodeLocation & location, ForCtrl * forctl, StatementNode * stmt, StatementNode * else_ ) {
+	std::vector<ast::ptr<ast::Stmt>> astinit;						// maybe empty
+	buildMoveList( forctl->init, astinit );
+
+	ast::Expr * astcond = nullptr;						// maybe empty
+	astcond = maybeMoveBuild( forctl->condition );
+
+	ast::Expr * astincr = nullptr;						// maybe empty
+	astincr = maybeMoveBuild( forctl->change );
+	delete forctl;
+
+	return new ast::ForStmt( location,
+		std::move( astinit ),
+		astcond,
+		astincr,
+		buildMoveSingle( stmt ),
+		buildMoveOptional( else_ )
+	);
+} // build_for
+
+ast::Stmt * build_branch( const CodeLocation & location, ast::BranchStmt::Kind kind ) {
+	return new ast::BranchStmt( location,
+		kind,
+		ast::Label( location )
+	);
+} // build_branch
+
+ast::Stmt * build_branch( const CodeLocation & location, string * identifier, ast::BranchStmt::Kind kind ) {
+	ast::Stmt * ret = new ast::BranchStmt( location,
+		kind,
+		ast::Label( location, *identifier )
+	);
+	delete identifier; 									// allocated by lexer
+	return ret;
+} // build_branch
+
+ast::Stmt * build_computedgoto( ExpressionNode * ctl ) {
+	ast::Expr * expr = maybeMoveBuild( ctl );
+	return new ast::BranchStmt( expr->location, expr );
+} // build_computedgoto
+
+ast::Stmt * build_return( const CodeLocation & location, ExpressionNode * ctl ) {
+	std::vector<ast::ptr<ast::Expr>> exps;
+	buildMoveList( ctl, exps );
+	return new ast::ReturnStmt( location,
+		exps.size() > 0 ? exps.back().release() : nullptr
+	);
+} // build_return
+
+static ast::Stmt * build_throw_stmt(
+		const CodeLocation & location,
+		ExpressionNode * ctl,
+		ast::ExceptionKind kind ) {
+	std::vector<ast::ptr<ast::Expr>> exps;
+	buildMoveList( ctl, exps );
+	assertf( exps.size() < 2, "CFA internal error: leaking memory" );
+	return new ast::ThrowStmt( location,
+		kind,
+		!exps.empty() ? exps.back().release() : nullptr,
+		(ast::Expr *)nullptr
+	);
+}
+
+ast::Stmt * build_throw( const CodeLocation & loc, ExpressionNode * ctl ) {
+	return build_throw_stmt( loc, ctl, ast::Terminate );
+} // build_throw
+
+ast::Stmt * build_resume( const CodeLocation & loc, ExpressionNode * ctl ) {
+	return build_throw_stmt( loc, ctl, ast::Resume );
+} // build_resume
+
+ast::Stmt * build_resume_at( ExpressionNode * ctl, ExpressionNode * target ) {
+	(void)ctl;
+	(void)target;
+	assertf( false, "resume at (non-local throw) is not yet supported," );
+} // build_resume_at
+
+ast::Stmt * build_try( const CodeLocation & location, StatementNode * try_, ClauseNode * catch_, ClauseNode * finally_ ) {
+	std::vector<ast::ptr<ast::CatchClause>> aststmt;
+	buildMoveList( catch_, aststmt );
+	ast::CompoundStmt * tryBlock = strict_dynamic_cast<ast::CompoundStmt *>( maybeMoveBuild( try_ ) );
+	ast::FinallyClause * finallyBlock = nullptr;
+	if ( finally_ ) {
+		finallyBlock = dynamic_cast<ast::FinallyClause *>( finally_->clause.release() );
+	}
+	return new ast::TryStmt( location,
+		tryBlock,
+		std::move( aststmt ),
+		finallyBlock
+	);
+} // build_try
+
+ast::CatchClause * build_catch( const CodeLocation & location, ast::ExceptionKind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body ) {
+	return new ast::CatchClause( location,
+		kind,
+		maybeMoveBuild( decl ),
+		maybeMoveBuild( cond ),
+		buildMoveSingle( body )
+	);
+} // build_catch
+
+ast::FinallyClause * build_finally( const CodeLocation & location, StatementNode * stmt ) {
+	return new ast::FinallyClause( location,
+		strict_dynamic_cast<const ast::CompoundStmt *>(
+			buildMoveSingle( stmt )
+		)
+	);
+} // build_finally
+
+ast::SuspendStmt * build_suspend( const CodeLocation & location, StatementNode * then, ast::SuspendStmt::Kind kind ) {
+	return new ast::SuspendStmt( location,
+		strict_dynamic_cast<const ast::CompoundStmt *, nullptr>(
+			buildMoveOptional( then )
+		),
+		kind
+	);
+} // build_suspend
+
+ast::WaitForStmt * build_waitfor( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
+	auto clause = new ast::WaitForClause( location );
+	clause->target = maybeBuild( targetExpr );
+	clause->stmt = maybeMoveBuild( stmt );
+	clause->when_cond = maybeMoveBuild( when );
+
+	ExpressionNode * next = targetExpr->next;
+	targetExpr->next = nullptr;
+	buildMoveList( next, clause->target_args );
+
+	delete targetExpr;
+
+	existing->clauses.insert( existing->clauses.begin(), clause );
+
+	return existing;
+} // build_waitfor
+
+ast::WaitForStmt * build_waitfor_else( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt ) {
+	existing->else_stmt = maybeMoveBuild( stmt );
+	existing->else_cond = maybeMoveBuild( when );
+
+	(void)location;
+	return existing;
+} // build_waitfor_else
+
+ast::WaitForStmt * build_waitfor_timeout( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt ) {
+	existing->timeout_time = maybeMoveBuild( timeout );
+	existing->timeout_stmt = maybeMoveBuild( stmt );
+	existing->timeout_cond = maybeMoveBuild( when );
+
+	(void)location;
+	return existing;
+} // build_waitfor_timeout
+
+ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
+	ast::WhenClause * clause = new ast::WhenClause( loc );
+	clause->when_cond = maybeMoveBuild( when );
+	clause->stmt = maybeMoveBuild( stmt );
+	clause->target = maybeMoveBuild( targetExpr );
+	return new ast::WaitUntilStmt::ClauseNode( clause );
+}
+ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation & loc, ExpressionNode * when, StatementNode * stmt ) {
+	ast::WhenClause * clause = new ast::WhenClause( loc );
+	clause->when_cond = maybeMoveBuild( when );
+	clause->stmt = maybeMoveBuild( stmt );
+	return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::ELSE, clause );
+}
+
+ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation & loc, ast::WaitUntilStmt::ClauseNode * root ) {
+	ast::WaitUntilStmt * retStmt = new ast::WaitUntilStmt( loc );
+	retStmt->predicateTree = root;
+
+	// iterative tree traversal
+	std::vector<ast::WaitUntilStmt::ClauseNode *> nodeStack; // stack needed for iterative traversal
+	ast::WaitUntilStmt::ClauseNode * currNode = nullptr;
+	ast::WaitUntilStmt::ClauseNode * lastInternalNode = nullptr;
+	ast::WaitUntilStmt::ClauseNode * cleanup = nullptr; // used to cleanup removed else/timeout
+	nodeStack.push_back(root);
+
+	do {
+		currNode = nodeStack.back();
+		nodeStack.pop_back(); // remove node since it will be processed
+
+		switch (currNode->op) {
+		case ast::WaitUntilStmt::ClauseNode::LEAF:
+			retStmt->clauses.push_back(currNode->leaf);
+			break;
+		case ast::WaitUntilStmt::ClauseNode::ELSE:
+			retStmt->else_stmt = currNode->leaf->stmt
+				? ast::deepCopy( currNode->leaf->stmt )
+				: nullptr;
+			retStmt->else_cond = currNode->leaf->when_cond
+				? ast::deepCopy( currNode->leaf->when_cond )
+				: nullptr;
+
+			delete currNode->leaf;
+			break;
+		case ast::WaitUntilStmt::ClauseNode::TIMEOUT:
+			retStmt->timeout_time = currNode->leaf->target
+				? ast::deepCopy( currNode->leaf->target )
+				: nullptr;
+			retStmt->timeout_stmt = currNode->leaf->stmt
+				? ast::deepCopy( currNode->leaf->stmt )
+				: nullptr;
+			retStmt->timeout_cond = currNode->leaf->when_cond
+				? ast::deepCopy( currNode->leaf->when_cond )
+				: nullptr;
+
+			delete currNode->leaf;
+			break;
+		default:
+			nodeStack.push_back( currNode->right ); // process right after left
+			nodeStack.push_back( currNode->left );
+
+			// Cut else/timeout out of the tree
+			if ( currNode->op == ast::WaitUntilStmt::ClauseNode::LEFT_OR ) {
+				if ( lastInternalNode )
+					lastInternalNode->right = currNode->left;
+				else // if not set then root is LEFT_OR
+					retStmt->predicateTree = currNode->left;
+
+				currNode->left = nullptr;
+				cleanup = currNode;
+			}
+
+			lastInternalNode = currNode;
+			break;
+		}
+	} while ( !nodeStack.empty() );
+
+	if ( cleanup ) delete cleanup;
+
+	return retStmt;
+}
+
+ast::Stmt * build_with( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
+	std::vector<ast::ptr<ast::Expr>> e;
+	buildMoveList( exprs, e );
+	ast::Stmt * s = maybeMoveBuild( stmt );
+	return new ast::DeclStmt( location, new ast::WithStmt( location, std::move( e ), s ) );
+} // build_with
+
+ast::Stmt * build_compound( const CodeLocation & location, StatementNode * first ) {
+	auto cs = new ast::CompoundStmt( location );
+	buildMoveList( first, cs->kids );
+	return cs;
+} // build_compound
+
+// A single statement in a control structure is always converted to a compound statement so subsequent generated code
+// can be placed within this compound statement. Otherwise, code generation has to constantly check for a single
+// statement and wrap it into a compound statement to insert additional code. Hence, all control structures have a
+// conical form for code generation.
+StatementNode * maybe_build_compound( const CodeLocation & location, StatementNode * first ) {
+	// Optimization: if the control-structure statement is a compound statement, do not wrap it.
+	// e.g., if (...) {...} do not wrap the existing compound statement.
+	if ( !dynamic_cast<ast::CompoundStmt *>( first->stmt.get() ) ) { // unique_ptr
+		return new StatementNode( build_compound( location, first ) );
+	} // if
+	return first;
+} // maybe_build_compound
+
+// Question
+ast::Stmt * build_asm( const CodeLocation & location, bool is_volatile, ExpressionNode * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) {
+	std::vector<ast::ptr<ast::Expr>> out, in;
+	std::vector<ast::ptr<ast::ConstantExpr>> clob;
+
+	buildMoveList( output, out );
+	buildMoveList( input, in );
+	buildMoveList( clobber, clob );
+	return new ast::AsmStmt( location,
+		is_volatile,
+		maybeMoveBuild( instruction ),
+		std::move( out ),
+		std::move( in ),
+		std::move( clob ),
+		gotolabels ? gotolabels->labels : std::vector<ast::Label>()
+	);
+} // build_asm
+
+ast::Stmt * build_directive( const CodeLocation & location, string * directive ) {
+	auto stmt = new ast::DirectiveStmt( location, *directive );
+	delete directive;
+	return stmt;
+} // build_directive
+
+ast::Stmt * build_mutex( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
+	std::vector<ast::ptr<ast::Expr>> expList;
+	buildMoveList( exprs, expList );
+	ast::Stmt * body = maybeMoveBuild( stmt );
+	return new ast::MutexStmt( location, body, std::move( expList ) );
+} // build_mutex
+
+ast::Stmt * build_corun( const CodeLocation & location, StatementNode * stmt ) {
+	ast::Stmt * body = maybeMoveBuild( stmt );
+	return new ast::CorunStmt( location, body );
+} // build_corun
+
+ast::Stmt * build_cofor( const CodeLocation & location, ForCtrl * forctl, StatementNode * stmt ) {
+	std::vector<ast::ptr<ast::Stmt>> astinit;						// maybe empty
+	buildMoveList( forctl->init, astinit );
+
+	ast::Expr * astcond = nullptr;						// maybe empty
+	astcond = maybeMoveBuild( forctl->condition );
+
+	ast::Expr * astincr = nullptr;						// maybe empty
+	astincr = maybeMoveBuild( forctl->change );
+	delete forctl;
+
+	return new ast::CoforStmt( location,
+		std::move( astinit ),
+		astcond,
+		astincr,
+		buildMoveSingle( stmt )
+	);
+} // build_cofor
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/StatementNode.h
===================================================================
--- src/Parser/StatementNode.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,103 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// StatementNode.h --
-//
-// Author           : Andrew Beach
-// Created On       : Wed Apr  5 11:42:00 2023
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Aug 11 11:44:07 2023
-// Update Count     : 2
-//
-
-#pragma once
-
-#include "ParseNode.h"
-
-struct StatementNode final : public ParseList<StatementNode> {
-	StatementNode() : stmt( nullptr ) {}
-	StatementNode( ast::Stmt * stmt ) : stmt( stmt ) {}
-	StatementNode( DeclarationNode * decl );
-	virtual ~StatementNode() {}
-
-	virtual StatementNode * clone() const final { assert( false ); return nullptr; }
-	ast::Stmt * build() { return stmt.release(); }
-
-	StatementNode * add_label(
-			const CodeLocation & location,
-			const std::string * name,
-			DeclarationNode * attr = nullptr );
-
-	virtual void print( std::ostream & os, __attribute__((unused)) int indent = 0 ) const override {
-		os << stmt.get() << std::endl;
-	}
-
-	std::unique_ptr<ast::Stmt> stmt;
-}; // StatementNode
-
-struct ClauseNode final : public ParseList<ClauseNode> {
-	ClauseNode( ast::StmtClause * clause ) : clause( clause ) {}
-	virtual ~ClauseNode() {}
-
-	virtual ClauseNode * clone() const final { assert( false ); return nullptr; }
-	ast::StmtClause * build() { return clause.release(); }
-
-	virtual ClauseNode * append_last_case( StatementNode * );
-
-	std::unique_ptr<ast::StmtClause> clause;
-};
-
-ast::Stmt * build_expr( CodeLocation const &, ExpressionNode * ctl );
-
-struct CondCtl {
-	CondCtl( DeclarationNode * decl, ExpressionNode * condition ) :
-		init( decl ? new StatementNode( decl ) : nullptr ), condition( condition ) {}
-
-	StatementNode * init;
-	ExpressionNode * condition;
-};
-
-struct ForCtrl {
-	ForCtrl( StatementNode * stmt, ExpressionNode * condition, ExpressionNode * change ) :
-		init( stmt ), condition( condition ), change( change ) {}
-
-	StatementNode * init;
-	ExpressionNode * condition;
-	ExpressionNode * change;
-};
-
-ast::Stmt * build_if( const CodeLocation &, CondCtl * ctl, StatementNode * then, StatementNode * else_ );
-ast::Stmt * build_switch( const CodeLocation &, bool isSwitch, ExpressionNode * ctl, ClauseNode * stmt );
-ast::CaseClause * build_case( const CodeLocation &, ExpressionNode * ctl );
-ast::CaseClause * build_default( const CodeLocation & );
-ast::Stmt * build_while( const CodeLocation &, CondCtl * ctl, StatementNode * stmt, StatementNode * else_ = nullptr );
-ast::Stmt * build_do_while( const CodeLocation &, ExpressionNode * ctl, StatementNode * stmt, StatementNode * else_ = nullptr );
-ast::Stmt * build_for( const CodeLocation &, ForCtrl * forctl, StatementNode * stmt, StatementNode * else_ = nullptr );
-ast::Stmt * build_branch( const CodeLocation &, ast::BranchStmt::Kind kind );
-ast::Stmt * build_branch( const CodeLocation &, std::string * identifier, ast::BranchStmt::Kind kind );
-ast::Stmt * build_computedgoto( ExpressionNode * ctl );
-ast::Stmt * build_return( const CodeLocation &, ExpressionNode * ctl );
-ast::Stmt * build_throw( const CodeLocation &, ExpressionNode * ctl );
-ast::Stmt * build_resume( const CodeLocation &, ExpressionNode * ctl );
-ast::Stmt * build_resume_at( ExpressionNode * ctl , ExpressionNode * target );
-ast::Stmt * build_try( const CodeLocation &, StatementNode * try_, ClauseNode * catch_, ClauseNode * finally_ );
-ast::CatchClause * build_catch( const CodeLocation &, ast::ExceptionKind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body );
-ast::FinallyClause * build_finally( const CodeLocation &, StatementNode * stmt );
-ast::Stmt * build_compound( const CodeLocation &, StatementNode * first );
-StatementNode * maybe_build_compound( const CodeLocation &, StatementNode * first );
-ast::Stmt * build_asm( const CodeLocation &, bool is_volatile, ExpressionNode * instruction, ExpressionNode * output = nullptr, ExpressionNode * input = nullptr, ExpressionNode * clobber = nullptr, LabelNode * gotolabels = nullptr );
-ast::Stmt * build_directive( const CodeLocation &, std::string * directive );
-ast::SuspendStmt * build_suspend( const CodeLocation &, StatementNode *, ast::SuspendStmt::Kind );
-ast::WaitForStmt * build_waitfor( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt );
-ast::WaitForStmt * build_waitfor_else( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt );
-ast::WaitForStmt * build_waitfor_timeout( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt );
-ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation &, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt );
-ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation &, ExpressionNode * when, StatementNode * stmt );
-ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation &, ast::WaitUntilStmt::ClauseNode * root );
-ast::Stmt * build_with( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt );
-ast::Stmt * build_mutex( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt );
-ast::Stmt * build_corun( const CodeLocation &, StatementNode * stmt );
-ast::Stmt * build_cofor( const CodeLocation & location, ForCtrl * forctl, StatementNode * stmt );
Index: src/Parser/StatementNode.hpp
===================================================================
--- src/Parser/StatementNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/StatementNode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,103 @@
+//
+// 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.
+//
+// StatementNode.hpp --
+//
+// Author           : Andrew Beach
+// Created On       : Wed Apr  5 11:42:00 2023
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri Aug 11 11:44:07 2023
+// Update Count     : 2
+//
+
+#pragma once
+
+#include "ParseNode.hpp"
+
+struct StatementNode final : public ParseList<StatementNode> {
+	StatementNode() : stmt( nullptr ) {}
+	StatementNode( ast::Stmt * stmt ) : stmt( stmt ) {}
+	StatementNode( DeclarationNode * decl );
+	virtual ~StatementNode() {}
+
+	virtual StatementNode * clone() const final { assert( false ); return nullptr; }
+	ast::Stmt * build() { return stmt.release(); }
+
+	StatementNode * add_label(
+			const CodeLocation & location,
+			const std::string * name,
+			DeclarationNode * attr = nullptr );
+
+	virtual void print( std::ostream & os, __attribute__((unused)) int indent = 0 ) const override {
+		os << stmt.get() << std::endl;
+	}
+
+	std::unique_ptr<ast::Stmt> stmt;
+}; // StatementNode
+
+struct ClauseNode final : public ParseList<ClauseNode> {
+	ClauseNode( ast::StmtClause * clause ) : clause( clause ) {}
+	virtual ~ClauseNode() {}
+
+	virtual ClauseNode * clone() const final { assert( false ); return nullptr; }
+	ast::StmtClause * build() { return clause.release(); }
+
+	virtual ClauseNode * append_last_case( StatementNode * );
+
+	std::unique_ptr<ast::StmtClause> clause;
+};
+
+ast::Stmt * build_expr( CodeLocation const &, ExpressionNode * ctl );
+
+struct CondCtl {
+	CondCtl( DeclarationNode * decl, ExpressionNode * condition ) :
+		init( decl ? new StatementNode( decl ) : nullptr ), condition( condition ) {}
+
+	StatementNode * init;
+	ExpressionNode * condition;
+};
+
+struct ForCtrl {
+	ForCtrl( StatementNode * stmt, ExpressionNode * condition, ExpressionNode * change ) :
+		init( stmt ), condition( condition ), change( change ) {}
+
+	StatementNode * init;
+	ExpressionNode * condition;
+	ExpressionNode * change;
+};
+
+ast::Stmt * build_if( const CodeLocation &, CondCtl * ctl, StatementNode * then, StatementNode * else_ );
+ast::Stmt * build_switch( const CodeLocation &, bool isSwitch, ExpressionNode * ctl, ClauseNode * stmt );
+ast::CaseClause * build_case( const CodeLocation &, ExpressionNode * ctl );
+ast::CaseClause * build_default( const CodeLocation & );
+ast::Stmt * build_while( const CodeLocation &, CondCtl * ctl, StatementNode * stmt, StatementNode * else_ = nullptr );
+ast::Stmt * build_do_while( const CodeLocation &, ExpressionNode * ctl, StatementNode * stmt, StatementNode * else_ = nullptr );
+ast::Stmt * build_for( const CodeLocation &, ForCtrl * forctl, StatementNode * stmt, StatementNode * else_ = nullptr );
+ast::Stmt * build_branch( const CodeLocation &, ast::BranchStmt::Kind kind );
+ast::Stmt * build_branch( const CodeLocation &, std::string * identifier, ast::BranchStmt::Kind kind );
+ast::Stmt * build_computedgoto( ExpressionNode * ctl );
+ast::Stmt * build_return( const CodeLocation &, ExpressionNode * ctl );
+ast::Stmt * build_throw( const CodeLocation &, ExpressionNode * ctl );
+ast::Stmt * build_resume( const CodeLocation &, ExpressionNode * ctl );
+ast::Stmt * build_resume_at( ExpressionNode * ctl , ExpressionNode * target );
+ast::Stmt * build_try( const CodeLocation &, StatementNode * try_, ClauseNode * catch_, ClauseNode * finally_ );
+ast::CatchClause * build_catch( const CodeLocation &, ast::ExceptionKind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body );
+ast::FinallyClause * build_finally( const CodeLocation &, StatementNode * stmt );
+ast::Stmt * build_compound( const CodeLocation &, StatementNode * first );
+StatementNode * maybe_build_compound( const CodeLocation &, StatementNode * first );
+ast::Stmt * build_asm( const CodeLocation &, bool is_volatile, ExpressionNode * instruction, ExpressionNode * output = nullptr, ExpressionNode * input = nullptr, ExpressionNode * clobber = nullptr, LabelNode * gotolabels = nullptr );
+ast::Stmt * build_directive( const CodeLocation &, std::string * directive );
+ast::SuspendStmt * build_suspend( const CodeLocation &, StatementNode *, ast::SuspendStmt::Kind );
+ast::WaitForStmt * build_waitfor( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt );
+ast::WaitForStmt * build_waitfor_else( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt );
+ast::WaitForStmt * build_waitfor_timeout( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt );
+ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation &, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt );
+ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation &, ExpressionNode * when, StatementNode * stmt );
+ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation &, ast::WaitUntilStmt::ClauseNode * root );
+ast::Stmt * build_with( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt );
+ast::Stmt * build_mutex( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt );
+ast::Stmt * build_corun( const CodeLocation &, StatementNode * stmt );
+ast::Stmt * build_cofor( const CodeLocation & location, ForCtrl * forctl, StatementNode * stmt );
Index: src/Parser/TypeData.cc
===================================================================
--- src/Parser/TypeData.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,1733 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// TypeData.cc --
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Sat May 16 15:12:51 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Feb 23 08:58:30 2024
-// Update Count     : 734
-//
-
-#include "TypeData.h"
-
-#include <cassert>                 // for assert
-#include <ostream>                 // for operator<<, ostream, basic_ostream
-
-#include "AST/Attribute.hpp"       // for Attribute
-#include "AST/Decl.hpp"            // for AggregateDecl, ObjectDecl, TypeDe...
-#include "AST/Init.hpp"            // for SingleInit, ListInit
-#include "AST/Print.hpp"           // for print
-#include "AST/Type.hpp"            // for Type
-#include "Common/SemanticError.h"  // for SemanticError
-#include "Common/utility.h"        // for splice, spliceBegin
-#include "Common/Iterate.hpp"      // for reverseIterate
-#include "Parser/ExpressionNode.h" // for ExpressionNode
-#include "Parser/StatementNode.h"  // for StatementNode
-
-class Attribute;
-
-using namespace std;
-
-// These must harmonize with the corresponding enumerations in the header.
-const char * TypeData::basicTypeNames[] = {
-	"void", "_Bool", "char", "int", "int128",
-	"float", "double", "long double", "float80", "float128",
-	"_float16", "_float32", "_float32x", "_float64", "_float64x", "_float128", "_float128x",
-	"NoBasicTypeNames"
-};
-const char * TypeData::complexTypeNames[] = {
-	"_Complex", "NoComplexTypeNames", "_Imaginary"
-}; // Imaginary unsupported => parse, but make invisible and print error message
-const char * TypeData::signednessNames[] = {
-	"signed", "unsigned", "NoSignednessNames"
-};
-const char * TypeData::lengthNames[] = {
-	"short", "long", "long long", "NoLengthNames"
-};
-const char * TypeData::builtinTypeNames[] = {
-	"__builtin_va_list", "__auto_type", "zero_t", "one_t", "NoBuiltinTypeNames"
-};
-
-TypeData::TypeData( Kind k ) : location( yylloc ), kind( k ), base( nullptr ), forall( nullptr ) /*, PTR1( (void*)(0xdeadbeefdeadbeef)), PTR2( (void*)(0xdeadbeefdeadbeef) ) */ {
-	switch ( kind ) {
-	case Unknown:
-	case Pointer:
-	case Reference:
-	case EnumConstant:
-	case GlobalScope:
-	case Basic:
-		// No unique data to initialize.
-		break;
-	case Array:
-		array.dimension = nullptr;
-		array.isVarLen = false;
-		array.isStatic = false;
-		break;
-	case Function:
-		function.params = nullptr;
-		function.idList = nullptr;
-		function.oldDeclList = nullptr;
-		function.body = nullptr;
-		function.withExprs = nullptr;
-		break;
-	case Aggregate:
-		aggregate.kind = ast::AggregateDecl::NoAggregate;
-		aggregate.name = nullptr;
-		aggregate.params = nullptr;
-		aggregate.actuals = nullptr;
-		aggregate.fields = nullptr;
-		aggregate.body = false;
-		aggregate.anon = false;
-		aggregate.typed = false;
-		aggregate.hiding = EnumHiding::Visible;
-		break;
-	case AggregateInst:
-		aggInst.aggregate = nullptr;
-		aggInst.params = nullptr;
-		aggInst.hoistType = false;
-		break;
-	case Symbolic:
-	case SymbolicInst:
-		symbolic.name = nullptr;
-		symbolic.params = nullptr;
-		symbolic.actuals = nullptr;
-		symbolic.assertions = nullptr;
-		break;
-	case Tuple:
-		tuple = nullptr;
-		break;
-	case Typeof:
-	case Basetypeof:
-		typeexpr = nullptr;
-		break;
-	case Vtable:
-	case Builtin:
-		// No unique data to initialize.
-		break;
-	case Qualified:
-		qualified.parent = nullptr;
-		qualified.child = nullptr;
-		break;
-	} // switch
-} // TypeData::TypeData
-
-
-TypeData::~TypeData() {
-	delete base;
-	delete forall;
-
-	switch ( kind ) {
-	case Unknown:
-	case Pointer:
-	case Reference:
-	case EnumConstant:
-	case GlobalScope:
-	case Basic:
-		// No unique data to deconstruct.
-		break;
-	case Array:
-		delete array.dimension;
-		break;
-	case Function:
-		delete function.params;
-		delete function.idList;
-		delete function.oldDeclList;
-		delete function.body;
-		delete function.withExprs;
-		break;
-	case Aggregate:
-		delete aggregate.name;
-		delete aggregate.params;
-		delete aggregate.actuals;
-		delete aggregate.fields;
-		break;
-	case AggregateInst:
-		delete aggInst.aggregate;
-		delete aggInst.params;
-		break;
-	case Symbolic:
-	case SymbolicInst:
-		delete symbolic.name;
-		delete symbolic.params;
-		delete symbolic.actuals;
-		delete symbolic.assertions;
-		break;
-	case Tuple:
-		delete tuple;
-		break;
-	case Typeof:
-	case Basetypeof:
-		delete typeexpr;
-		break;
-	case Vtable:
-	case Builtin:
-		// No unique data to deconstruct.
-		break;
-	case Qualified:
-		delete qualified.parent;
-		delete qualified.child;
-		break;
-	} // switch
-} // TypeData::~TypeData
-
-
-TypeData * TypeData::clone() const {
-	TypeData * newtype = new TypeData( kind );
-	newtype->qualifiers = qualifiers;
-	newtype->base = maybeCopy( base );
-	newtype->forall = maybeCopy( forall );
-
-	switch ( kind ) {
-	case Unknown:
-	case EnumConstant:
-	case Pointer:
-	case Reference:
-	case GlobalScope:
-		// nothing else to copy
-		break;
-	case Basic:
-		newtype->basictype = basictype;
-		newtype->complextype = complextype;
-		newtype->signedness = signedness;
-		newtype->length = length;
-		break;
-	case Array:
-		newtype->array.dimension = maybeCopy( array.dimension );
-		newtype->array.isVarLen = array.isVarLen;
-		newtype->array.isStatic = array.isStatic;
-		break;
-	case Function:
-		newtype->function.params = maybeCopy( function.params );
-		newtype->function.idList = maybeCopy( function.idList );
-		newtype->function.oldDeclList = maybeCopy( function.oldDeclList );
-		newtype->function.body = maybeCopy( function.body );
-		newtype->function.withExprs = maybeCopy( function.withExprs );
-		break;
-	case Aggregate:
-		newtype->aggregate.kind = aggregate.kind;
-		newtype->aggregate.name = aggregate.name ? new string( *aggregate.name ) : nullptr;
-		newtype->aggregate.params = maybeCopy( aggregate.params );
-		newtype->aggregate.actuals = maybeCopy( aggregate.actuals );
-		newtype->aggregate.fields = maybeCopy( aggregate.fields );
-		newtype->aggregate.attributes = aggregate.attributes;
-		newtype->aggregate.body = aggregate.body;
-		newtype->aggregate.anon = aggregate.anon;
-		break;
-	case AggregateInst:
-		newtype->aggInst.aggregate = maybeCopy( aggInst.aggregate );
-		newtype->aggInst.params = maybeCopy( aggInst.params );
-		newtype->aggInst.hoistType = aggInst.hoistType;
-		break;
-	case Symbolic:
-	case SymbolicInst:
-		newtype->symbolic.name = symbolic.name ? new string( *symbolic.name ) : nullptr;
-		newtype->symbolic.params = maybeCopy( symbolic.params );
-		newtype->symbolic.actuals = maybeCopy( symbolic.actuals );
-		newtype->symbolic.assertions = maybeCopy( symbolic.assertions );
-		newtype->symbolic.isTypedef = symbolic.isTypedef;
-		break;
-	case Tuple:
-		newtype->tuple = maybeCopy( tuple );
-		break;
-	case Typeof:
-	case Basetypeof:
-		newtype->typeexpr = maybeCopy( typeexpr );
-		break;
-	case Vtable:
-		break;
-	case Builtin:
-		assert( builtintype == Zero || builtintype == One );
-		newtype->builtintype = builtintype;
-		break;
-	case Qualified:
-		newtype->qualified.parent = maybeCopy( qualified.parent );
-		newtype->qualified.child = maybeCopy( qualified.child );
-		break;
-	} // switch
-	return newtype;
-} // TypeData::clone
-
-
-void TypeData::print( ostream &os, int indent ) const {
-	ast::print( os, qualifiers );
-
-	if ( forall ) {
-		os << "forall " << endl;
-		forall->printList( os, indent + 4 );
-	} // if
-
-	switch ( kind ) {
-	case Basic:
-		if ( signedness != NoSignedness ) os << signednessNames[ signedness ] << " ";
-		if ( length != NoLength ) os << lengthNames[ length ] << " ";
-		if ( complextype != NoComplexType ) os << complexTypeNames[ complextype ] << " ";
-		if ( basictype != NoBasicType ) os << basicTypeNames[ basictype ] << " ";
-		break;
-	case Pointer:
-		os << "pointer ";
-		if ( base ) {
-			os << "to ";
-			base->print( os, indent );
-		} // if
-		break;
-	case Reference:
-		os << "reference ";
-		if ( base ) {
-			os << "to ";
-			base->print( os, indent );
-		} // if
-		break;
-	case Array:
-		if ( array.isStatic ) {
-			os << "static ";
-		} // if
-		if ( array.dimension ) {
-			os << "array of ";
-			array.dimension->printOneLine( os, indent );
-		} else if ( array.isVarLen ) {
-			os << "variable-length array of ";
-		} else {
-			os << "open array of ";
-		} // if
-		if ( base ) {
-			base->print( os, indent );
-		} // if
-		break;
-	case Function:
-		os << "function" << endl;
-		if ( function.params ) {
-			os << string( indent + 2, ' ' ) << "with parameters " << endl;
-			function.params->printList( os, indent + 4 );
-		} else {
-			os << string( indent + 2, ' ' ) << "with no parameters" << endl;
-		} // if
-		if ( function.idList ) {
-			os << string( indent + 2, ' ' ) << "with old-style identifier list " << endl;
-			function.idList->printList( os, indent + 4 );
-		} // if
-		if ( function.oldDeclList ) {
-			os << string( indent + 2, ' ' ) << "with old-style declaration list " << endl;
-			function.oldDeclList->printList( os, indent + 4 );
-		} // if
-		os << string( indent + 2, ' ' ) << "returning ";
-		if ( base ) {
-			base->print( os, indent + 4 );
-		} else {
-			os << "nothing ";
-		} // if
-		os << endl;
-		if ( function.body ) {
-			os << string( indent + 2, ' ' ) << "with body " << endl;
-			function.body->printList( os, indent + 2 );
-		} // if
-		break;
-	case Aggregate:
-		os << ast::AggregateDecl::aggrString( aggregate.kind ) << ' ' << *aggregate.name << endl;
-		if ( aggregate.params ) {
-			os << string( indent + 2, ' ' ) << "with type parameters" << endl;
-			aggregate.params->printList( os, indent + 4 );
-		} // if
-		if ( aggregate.actuals ) {
-			os << string( indent + 2, ' ' ) << "instantiated with actual parameters" << endl;
-			aggregate.actuals->printList( os, indent + 4 );
-		} // if
-		if ( aggregate.fields ) {
-			os << string( indent + 2, ' ' ) << "with members" << endl;
-			aggregate.fields->printList( os, indent + 4 );
-		} // if
-		if ( aggregate.body ) {
-			os << string( indent + 2, ' ' ) << "with body" << endl;
-		} // if
-		if ( ! aggregate.attributes.empty() ) {
-			os << string( indent + 2, ' ' ) << "with attributes" << endl;
-			for ( ast::ptr<ast::Attribute> const & attr : reverseIterate( aggregate.attributes ) ) {
-				os << string( indent + 4, ' ' );
-				ast::print( os, attr, indent + 2 );
-			} // for
-		} // if
-		break;
-	case AggregateInst:
-		if ( aggInst.aggregate ) {
-			os << "instance of " ;
-			aggInst.aggregate->print( os, indent );
-		} else {
-			os << "instance of an unspecified aggregate ";
-		} // if
-		if ( aggInst.params ) {
-			os << string( indent + 2, ' ' ) << "with parameters" << endl;
-			aggInst.params->printList( os, indent + 2 );
-		} // if
-		break;
-	case EnumConstant:
-		os << "enumeration constant ";
-		break;
-	case Symbolic:
-		if ( symbolic.isTypedef ) {
-			os << "typedef definition ";
-		} else {
-			os << "type definition ";
-		} // if
-		if ( symbolic.params ) {
-			os << endl << string( indent + 2, ' ' ) << "with parameters" << endl;
-			symbolic.params->printList( os, indent + 2 );
-		} // if
-		if ( symbolic.assertions ) {
-			os << endl << string( indent + 2, ' ' ) << "with assertions" << endl;
-			symbolic.assertions->printList( os, indent + 4 );
-			os << string( indent + 2, ' ' );
-		} // if
-		if ( base ) {
-			os << "for ";
-			base->print( os, indent + 2 );
-		} // if
-		break;
-	case SymbolicInst:
-		os << *symbolic.name;
-		if ( symbolic.actuals ) {
-			os << "(";
-			symbolic.actuals->printList( os, indent + 2 );
-			os << ")";
-		} // if
-		break;
-	case Tuple:
-		os << "tuple ";
-		if ( tuple ) {
-			os << "with members" << endl;
-			tuple->printList( os, indent + 2 );
-		} // if
-		break;
-	case Basetypeof:
-		os << "base-";
-		#if defined(__GNUC__) && __GNUC__ >= 7
-			__attribute__((fallthrough));
-		#endif
-		// FALL THROUGH
-	case Typeof:
-		os << "type-of expression ";
-		if ( typeexpr ) {
-			typeexpr->print( os, indent + 2 );
-		} // if
-		break;
-	case Vtable:
-		os << "vtable";
-		break;
-	case Builtin:
-		os << builtinTypeNames[builtintype];
-		break;
-	case GlobalScope:
-		break;
-	case Qualified:
-		qualified.parent->print( os );
-		os << ".";
-		qualified.child->print( os );
-		break;
-	case Unknown:
-		os << "entity of unknown type ";
-		break;
-	default:
-		os << "internal error: TypeData::print " << kind << endl;
-		assert( false );
-	} // switch
-} // TypeData::print
-
-const std::string * TypeData::leafName() const {
-	switch ( kind ) {
-	case Unknown:
-	case Pointer:
-	case Reference:
-	case EnumConstant:
-	case GlobalScope:
-	case Array:
-	case Basic:
-	case Function:
-	case AggregateInst:
-	case Tuple:
-	case Typeof:
-	case Basetypeof:
-	case Builtin:
-	case Vtable:
-		assertf(false, "Tried to get leaf name from kind without a name: %d", kind);
-		break;
-	case Aggregate:
-		return aggregate.name;
-	case Symbolic:
-	case SymbolicInst:
-		return symbolic.name;
-	case Qualified:
-		return qualified.child->leafName();
-	} // switch
-	assert(false);
-}
-
-TypeData * TypeData::getLastBase() {
-	TypeData * cur = this;
-	while ( cur->base ) cur = cur->base;
-	return cur;
-}
-
-void TypeData::setLastBase( TypeData * newBase ) {
-	getLastBase()->base = newBase;
-}
-
-
-// Wrap an aggregate up in an instance. Takes and gives ownership.
-static TypeData * makeInstance( TypeData * type ) {
-	assert( TypeData::Aggregate == type->kind );
-	TypeData * out = new TypeData( TypeData::AggregateInst );
-	out->aggInst.aggregate = type;
-	out->aggInst.params = maybeCopy( type->aggregate.actuals );
-	out->aggInst.hoistType = type->aggregate.body;
-	out->qualifiers |= type->qualifiers;
-	return out;
-}
-
-
-TypeData * build_type_qualifier( ast::CV::Qualifiers tq ) {
-	TypeData * type = new TypeData;
-	type->qualifiers = tq;
-	return type;
-}
-
-TypeData * build_basic_type( TypeData::BasicType basic ) {
-	TypeData * type = new TypeData( TypeData::Basic );
-	type->basictype = basic;
-	return type;
-}
-
-TypeData * build_complex_type( TypeData::ComplexType complex ) {
-	TypeData * type = new TypeData( TypeData::Basic );
-	type->complextype = complex;
-	return type;
-}
-
-TypeData * build_signedness( TypeData::Signedness signedness ) {
-	TypeData * type = new TypeData( TypeData::Basic );
-	type->signedness = signedness;
-	return type;
-}
-
-TypeData * build_builtin_type( TypeData::BuiltinType bit ) {
-	TypeData * type = new TypeData( TypeData::Builtin );
-	type->builtintype = bit;
-	return type;
-}
-
-TypeData * build_length( TypeData::Length length ) {
-	TypeData * type = new TypeData( TypeData::Basic );
-	type->length = length;
-	return type;
-}
-
-TypeData * build_forall( DeclarationNode * forall ) {
-	TypeData * type = new TypeData( TypeData::Unknown );
-	type->forall = forall;
-	return type;
-}
-
-TypeData * build_global_scope() {
-	return new TypeData( TypeData::GlobalScope );
-}
-
-TypeData * build_qualified_type( TypeData * parent, TypeData * child ) {
-	TypeData * type = new TypeData( TypeData::Qualified );
-	type->qualified.parent = parent;
-	type->qualified.child = child;
-	return type;
-}
-
-TypeData * build_typedef( const std::string * name ) {
-	TypeData * type = new TypeData( TypeData::SymbolicInst );
-	type->symbolic.name = name;
-	type->symbolic.isTypedef = true;
-	type->symbolic.actuals = nullptr;
-	return type;
-}
-
-TypeData * build_type_gen( const std::string * name, ExpressionNode * params ) {
-	TypeData * type = new TypeData( TypeData::SymbolicInst );
-	type->symbolic.name = name;
-	type->symbolic.isTypedef = false;
-	type->symbolic.actuals = params;
-	return type;
-}
-
-TypeData * build_vtable_type( TypeData * base ) {
-	TypeData * type = new TypeData( TypeData::Vtable );
-	type->base = base;
-	return type;
-}
-
-// Takes ownership of src.
-static void addQualifiersToType( TypeData * dst, TypeData * src ) {
-	if ( dst->base ) {
-		addQualifiersToType( dst->base, src );
-	} else if ( dst->kind == TypeData::Function ) {
-		dst->base = src;
-		src = nullptr;
-    } else {
-		dst->qualifiers |= src->qualifiers;
-		delete src;
-	} // if
-}
-
-// Takes ownership of all arguments, gives ownership of return value.
-TypeData * addQualifiers( TypeData * dst, TypeData * src ) {
-	if ( src->forall ) {
-		if ( dst->forall || TypeData::Aggregate != dst->kind ) {
-			extend( dst->forall, src->forall );
-		} else {
-			extend( dst->aggregate.params, src->forall );
-		}
-		src->forall = nullptr;
-	}
-
-	addQualifiersToType( dst, src );
-	return dst;
-}
-
-// Helper for addType and cloneBaseType.
-static void addTypeToType( TypeData *& dst, TypeData *& src ) {
-	if ( src->forall && dst->kind == TypeData::Function ) {
-		extend( dst->forall, src->forall );
-		src->forall = nullptr;
-	} // if
-	if ( dst->base ) {
-		addTypeToType( dst->base, src );
-		return;
-	}
-	switch ( dst->kind ) {
-	case TypeData::Unknown:
-		src->qualifiers |= dst->qualifiers;
-		// LEAKS dst?
-		dst = src;
-		src = nullptr;
-		break;
-	case TypeData::Basic:
-		dst->qualifiers |= src->qualifiers;
-		if ( src->kind != TypeData::Unknown ) {
-			assert( src->kind == TypeData::Basic );
-
-			if ( dst->basictype == TypeData::NoBasicType ) {
-				dst->basictype = src->basictype;
-			} else if ( src->basictype != TypeData::NoBasicType ) {
-				SemanticError( yylloc, "multiple declaration types \"%s\" and \"%s\".",
-					TypeData::basicTypeNames[ dst->basictype ],
-					TypeData::basicTypeNames[ src->basictype ] );
-			}
-			if ( dst->complextype == TypeData::NoComplexType ) {
-				dst->complextype = src->complextype;
-			} else if ( src->complextype != TypeData::NoComplexType ) {
-				SemanticError( yylloc, "multiple declaration types \"%s\" and \"%s\".",
-					TypeData::complexTypeNames[ src->complextype ],
-					TypeData::complexTypeNames[ src->complextype ] );
-			}
-			if ( dst->signedness == TypeData::NoSignedness ) {
-				dst->signedness = src->signedness;
-			} else if ( src->signedness != TypeData::NoSignedness ) {
-				SemanticError( yylloc, "conflicting type specifier \"%s\" and \"%s\".",
-					TypeData::signednessNames[ dst->signedness ],
-					TypeData::signednessNames[ src->signedness ] );
-			}
-			if ( dst->length == TypeData::NoLength ) {
-				dst->length = src->length;
-			} else if ( dst->length == TypeData::Long && src->length == TypeData::Long ) {
-				dst->length = TypeData::LongLong;
-			} else if ( src->length != TypeData::NoLength ) {
-				SemanticError( yylloc, "conflicting type specifier \"%s\" and \"%s\".",
-					TypeData::lengthNames[ dst->length ],
-					TypeData::lengthNames[ src->length ] );
-			}
-		} // if
-		break;
-	default:
-		if ( TypeData::Aggregate == src->kind ) {
-			dst->base = makeInstance( src );
-		} else {
-			extend( dst->forall, src->forall );
-			src->forall = nullptr;
-			dst->base = src;
-		}
-		src = nullptr;
-	} // switch
-}
-
-// Takes ownership of all arguments, gives ownership of return value.
-TypeData * addType( TypeData * dst, TypeData * src, std::vector<ast::ptr<ast::Attribute>> & attributes ) {
-	if ( dst ) {
-		addTypeToType( dst, src );
-	} else if ( src->kind == TypeData::Aggregate ) {
-		// Hide type information aggregate instances.
-		dst = makeInstance( src );
-		dst->aggInst.aggregate->aggregate.attributes.swap( attributes );
-	} else {
-		dst = src;
-	} // if
-	return dst;
-}
-
-TypeData * addType( TypeData * dst, TypeData * src ) {
-	std::vector<ast::ptr<ast::Attribute>> attributes;
-	return addType( dst, src, attributes );
-}
-
-// Takes ownership of both arguments, gives ownership of return value.
-TypeData * cloneBaseType( TypeData * type, TypeData * other ) {
-	TypeData * newType = type->getLastBase()->clone();
-	if ( newType->kind == TypeData::AggregateInst ) {
-		// don't duplicate members
-		assert( newType->aggInst.aggregate->kind == TypeData::Aggregate );
-		delete newType->aggInst.aggregate->aggregate.fields;
-		newType->aggInst.aggregate->aggregate.fields = nullptr;
-		newType->aggInst.aggregate->aggregate.body = false;
-		// don't hoist twice
-		newType->aggInst.hoistType = false;
-	} // if
-	newType->forall = maybeCopy( type->forall );
-
-	if ( other ) {
-		addTypeToType( other, newType );
-		delete newType;
-		return other;
-	} // if
-	return newType;
-}
-
-TypeData * makeNewBase( TypeData * type ) {
-	return ( TypeData::Aggregate == type->kind ) ? makeInstance( type ) : type;
-}
-
-
-void buildForall(
-		const DeclarationNode * firstNode,
-		std::vector<ast::ptr<ast::TypeInstType>> &outputList ) {
-	{
-		std::vector<ast::ptr<ast::Type>> tmpList;
-		buildTypeList( firstNode, tmpList );
-		for ( auto tmp : tmpList ) {
-			outputList.emplace_back(
-				strict_dynamic_cast<const ast::TypeInstType *>(
-					tmp.release() ) );
-		}
-	}
-	auto n = firstNode;
-	for ( auto i = outputList.begin() ;
-			i != outputList.end() ;
-			++i, n = n->next ) {
-		// Only the object type class adds additional assertions.
-		if ( n->variable.tyClass != ast::TypeDecl::Otype ) {
-			continue;
-		}
-
-		ast::TypeDecl const * td = i->strict_as<ast::TypeDecl>();
-		std::vector<ast::ptr<ast::DeclWithType>> newAssertions;
-		auto mutTypeDecl = ast::mutate( td );
-		const CodeLocation & location = mutTypeDecl->location;
-		*i = mutTypeDecl;
-
-		// add assertion parameters to `type' tyvars in reverse order
-		// add assignment operator:  T * ?=?(T *, T)
-		newAssertions.push_back( new ast::FunctionDecl(
-			location,
-			"?=?",
-			{}, // forall
-			{}, // assertions
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::ReferenceType( i->get() ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-				new ast::ObjectDecl(
-					location,
-					"",
-					i->get(),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // params
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					i->get(),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // returns
-			(ast::CompoundStmt *)nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		) );
-
-		// add default ctor:  void ?{}(T *)
-		newAssertions.push_back( new ast::FunctionDecl(
-			location,
-			"?{}",
-			{}, // forall
-			{}, // assertions
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::ReferenceType( i->get() ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // params
-			{}, // returns
-			(ast::CompoundStmt *)nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		) );
-
-		// add copy ctor:  void ?{}(T *, T)
-		newAssertions.push_back( new ast::FunctionDecl(
-			location,
-			"?{}",
-			{}, // forall
-			{}, // assertions
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::ReferenceType( i->get() ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-				new ast::ObjectDecl(
-					location,
-					"",
-					i->get(),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // params
-			{}, // returns
-			(ast::CompoundStmt *)nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		) );
-
-		// add dtor:  void ^?{}(T *)
-		newAssertions.push_back( new ast::FunctionDecl(
-			location,
-			"^?{}",
-			{}, // forall
-			{}, // assertions
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::ReferenceType( i->get() ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // params
-			{}, // returns
-			(ast::CompoundStmt *)nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		) );
-
-		spliceBegin( mutTypeDecl->assertions, newAssertions );
-	} // for
-}
-
-
-void buildForall(
-		const DeclarationNode * firstNode,
-		std::vector<ast::ptr<ast::TypeDecl>> &outputForall ) {
-	buildList( firstNode, outputForall );
-	auto n = firstNode;
-	for ( auto i = outputForall.begin() ;
-			i != outputForall.end() ;
-			++i, n = n->next ) {
-		// Only the object type class adds additional assertions.
-		if ( n->variable.tyClass != ast::TypeDecl::Otype ) {
-			continue;
-		}
-
-		ast::TypeDecl const * td = i->strict_as<ast::TypeDecl>();
-		std::vector<ast::ptr<ast::DeclWithType>> newAssertions;
-		auto mutTypeDecl = ast::mutate( td );
-		const CodeLocation & location = mutTypeDecl->location;
-		*i = mutTypeDecl;
-
-		// add assertion parameters to `type' tyvars in reverse order
-		// add assignment operator:  T * ?=?(T *, T)
-		newAssertions.push_back( new ast::FunctionDecl(
-			location,
-			"?=?",
-			{}, // forall
-			{}, // assertions
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::ReferenceType( new ast::TypeInstType( td->name, *i ) ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::TypeInstType( td->name, *i ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // params
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::TypeInstType( td->name, *i ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // returns
-			(ast::CompoundStmt *)nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		) );
-
-		// add default ctor:  void ?{}(T *)
-		newAssertions.push_back( new ast::FunctionDecl(
-			location,
-			"?{}",
-			{}, // forall
-			{}, // assertions
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::ReferenceType(
-						new ast::TypeInstType( td->name, i->get() ) ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // params
-			{}, // returns
-			(ast::CompoundStmt *)nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		) );
-
-		// add copy ctor:  void ?{}(T *, T)
-		newAssertions.push_back( new ast::FunctionDecl(
-			location,
-			"?{}",
-			{}, // forall
-			{}, // assertions
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::ReferenceType(
-						new ast::TypeInstType( td->name, *i ) ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::TypeInstType( td->name, *i ),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // params
-			{}, // returns
-			(ast::CompoundStmt *)nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		) );
-
-		// add dtor:  void ^?{}(T *)
-		newAssertions.push_back( new ast::FunctionDecl(
-			location,
-			"^?{}",
-			{}, // forall
-			{}, // assertions
-			{
-				new ast::ObjectDecl(
-					location,
-					"",
-					new ast::ReferenceType(
-						new ast::TypeInstType( i->get() )
-					),
-					(ast::Init *)nullptr,
-					ast::Storage::Classes(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr
-				),
-			}, // params
-			{}, // returns
-			(ast::CompoundStmt *)nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		) );
-
-		spliceBegin( mutTypeDecl->assertions, newAssertions );
-	} // for
-} // buildForall
-
-
-ast::Type * typebuild( const TypeData * td ) {
-	assert( td );
-	switch ( td->kind ) {
-	case TypeData::Unknown:
-		// fill in implicit int
-		return new ast::BasicType(
-			ast::BasicKind::SignedInt,
-			buildQualifiers( td )
-		);
-	case TypeData::Basic:
-		return buildBasicType( td );
-	case TypeData::Pointer:
-		return buildPointer( td );
-	case TypeData::Array:
-		return buildArray( td );
-	case TypeData::Reference:
-		return buildReference( td );
-	case TypeData::Function:
-		return buildFunctionType( td );
-	case TypeData::AggregateInst:
-		return buildAggInst( td );
-	case TypeData::EnumConstant:
-		return new ast::EnumInstType( "", buildQualifiers( td ) );
-	case TypeData::SymbolicInst:
-		return buildSymbolicInst( td );
-	case TypeData::Tuple:
-		return buildTuple( td );
-	case TypeData::Typeof:
-	case TypeData::Basetypeof:
-		return buildTypeof( td );
-	case TypeData::Vtable:
-		return buildVtable( td );
-	case TypeData::Builtin:
-		switch ( td->builtintype ) {
-		case TypeData::Zero:
-			return new ast::ZeroType();
-		case TypeData::One:
-			return new ast::OneType();
-		default:
-			return new ast::VarArgsType( buildQualifiers( td ) );
-		} // switch
-	case TypeData::GlobalScope:
-		return new ast::GlobalScopeType();
-	case TypeData::Qualified:
-		return new ast::QualifiedType(
-			typebuild( td->qualified.parent ),
-			typebuild( td->qualified.child ),
-			buildQualifiers( td )
-		);
-	case TypeData::Symbolic:
-	case TypeData::Aggregate:
-		assert( false );
-	} // switch
-
-	return nullptr;
-} // typebuild
-
-
-TypeData * typeextractAggregate( const TypeData * td, bool toplevel ) {
-	TypeData * ret = nullptr;
-
-	switch ( td->kind ) {
-	case TypeData::Aggregate:
-		if ( ! toplevel && td->aggregate.body ) {
-			ret = td->clone();
-		} // if
-		break;
-	case TypeData::AggregateInst:
-		if ( td->aggInst.aggregate ) {
-			ret = typeextractAggregate( td->aggInst.aggregate, false );
-		} // if
-		break;
-	default:
-		if ( td->base ) {
-			ret = typeextractAggregate( td->base, false );
-		} // if
-	} // switch
-	return ret;
-} // typeextractAggregate
-
-
-ast::CV::Qualifiers buildQualifiers( const TypeData * td ) {
-	return td->qualifiers;
-} // buildQualifiers
-
-
-static string genTSError( string msg, TypeData::BasicType basictype ) {
-	SemanticError( yylloc, "invalid type specifier \"%s\" for type \"%s\".", msg.c_str(), TypeData::basicTypeNames[basictype] );
-} // genTSError
-
-ast::Type * buildBasicType( const TypeData * td ) {
-	ast::BasicKind ret;
-
-	switch ( td->basictype ) {
-	case TypeData::Void:
-		if ( td->signedness != TypeData::NoSignedness ) {
-			genTSError( TypeData::signednessNames[ td->signedness ], td->basictype );
-		} // if
-		if ( td->length != TypeData::NoLength ) {
-			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
-		} // if
-		return new ast::VoidType( buildQualifiers( td ) );
-		break;
-
-	case TypeData::Bool:
-		if ( td->signedness != TypeData::NoSignedness ) {
-			genTSError( TypeData::signednessNames[ td->signedness ], td->basictype );
-		} // if
-		if ( td->length != TypeData::NoLength ) {
-			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
-		} // if
-
-		ret = ast::BasicKind::Bool;
-		break;
-
-	case TypeData::Char:
-		// C11 Standard 6.2.5.15: The three types char, signed char, and unsigned char are collectively called the
-		// character types. The implementation shall define char to have the same range, representation, and behavior as
-		// either signed char or unsigned char.
-		static ast::BasicKind chartype[] = { ast::BasicKind::SignedChar, ast::BasicKind::UnsignedChar, ast::BasicKind::Char };
-
-		if ( td->length != TypeData::NoLength ) {
-			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
-		} // if
-
-		ret = chartype[ td->signedness ];
-		break;
-
-	case TypeData::Int:
-		static ast::BasicKind inttype[2][4] = {
-			{ ast::BasicKind::ShortSignedInt, ast::BasicKind::LongSignedInt, ast::BasicKind::LongLongSignedInt, ast::BasicKind::SignedInt },
-			{ ast::BasicKind::ShortUnsignedInt, ast::BasicKind::LongUnsignedInt, ast::BasicKind::LongLongUnsignedInt, ast::BasicKind::UnsignedInt },
-		};
-
-	Integral: ;
-		if ( td->signedness == TypeData::NoSignedness ) {
-			const_cast<TypeData *>(td)->signedness = TypeData::Signed;
-		} // if
-		ret = inttype[ td->signedness ][ td->length ];
-		break;
-
-	case TypeData::Int128:
-		ret = td->signedness == TypeData::Unsigned ? ast::BasicKind::UnsignedInt128 : ast::BasicKind::SignedInt128;
-		if ( td->length != TypeData::NoLength ) {
-			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
-		} // if
-		break;
-
-	case TypeData::Float:
-	case TypeData::Double:
-	case TypeData::LongDouble:					// not set until below
-	case TypeData::uuFloat80:
-	case TypeData::uuFloat128:
-	case TypeData::uFloat16:
-	case TypeData::uFloat32:
-	case TypeData::uFloat32x:
-	case TypeData::uFloat64:
-	case TypeData::uFloat64x:
-	case TypeData::uFloat128:
-	case TypeData::uFloat128x:
-		static ast::BasicKind floattype[2][12] = {
-			{ ast::BasicKind::FloatComplex, ast::BasicKind::DoubleComplex, ast::BasicKind::LongDoubleComplex, (ast::BasicKind)-1, (ast::BasicKind)-1, ast::BasicKind::uFloat16Complex, ast::BasicKind::uFloat32Complex, ast::BasicKind::uFloat32xComplex, ast::BasicKind::uFloat64Complex, ast::BasicKind::uFloat64xComplex, ast::BasicKind::uFloat128Complex, ast::BasicKind::uFloat128xComplex, },
-			{ ast::BasicKind::Float, ast::BasicKind::Double, ast::BasicKind::LongDouble, ast::BasicKind::uuFloat80, ast::BasicKind::uuFloat128, ast::BasicKind::uFloat16, ast::BasicKind::uFloat32, ast::BasicKind::uFloat32x, ast::BasicKind::uFloat64, ast::BasicKind::uFloat64x, ast::BasicKind::uFloat128, ast::BasicKind::uFloat128x, },
-		};
-
-	FloatingPoint: ;
-		if ( td->signedness != TypeData::NoSignedness ) {
-			genTSError( TypeData::signednessNames[ td->signedness ], td->basictype );
-		} // if
-		if ( td->length == TypeData::Short || td->length == TypeData::LongLong ) {
-			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
-		} // if
-		if ( td->basictype != TypeData::Double && td->length == TypeData::Long ) {
-			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
-		} // if
-		if ( td->complextype == TypeData::Imaginary ) {
-			genTSError( TypeData::complexTypeNames[ td->complextype ], td->basictype );
-		} // if
-		if ( (td->basictype == TypeData::uuFloat80 || td->basictype == TypeData::uuFloat128) && td->complextype == TypeData::Complex ) { // gcc unsupported
-			genTSError( TypeData::complexTypeNames[ td->complextype ], td->basictype );
-		} // if
-		if ( td->length == TypeData::Long ) {
-			const_cast<TypeData *>(td)->basictype = TypeData::LongDouble;
-		} // if
-
-		ret = floattype[ td->complextype ][ td->basictype - TypeData::Float ];
-		//printf( "XXXX %d %d %d %d\n", td->complextype, td->basictype, TypeData::Float, ret );
-		break;
-
-	case TypeData::NoBasicType:
-		// No basic type in declaration => default double for Complex/Imaginary and int type for integral types
-		if ( td->complextype == TypeData::Complex || td->complextype == TypeData::Imaginary ) {
-			const_cast<TypeData *>(td)->basictype = TypeData::Double;
-			goto FloatingPoint;
-		} // if
-
-		const_cast<TypeData *>(td)->basictype = TypeData::Int;
-		goto Integral;
-	default:
-		assertf( false, "unknown basic type" );
-		return nullptr;
-	} // switch
-
-	ast::BasicType * bt = new ast::BasicType( ret, buildQualifiers( td ) );
-	return bt;
-} // buildBasicType
-
-
-static ast::Type * buildDefaultType( const TypeData * td ) {
-	return ( td ) ? typebuild( td ) : new ast::BasicType( ast::BasicKind::SignedInt );
-} // buildDefaultType
-
-
-ast::PointerType * buildPointer( const TypeData * td ) {
-	return new ast::PointerType(
-		buildDefaultType( td->base ),
-		buildQualifiers( td )
-	);
-} // buildPointer
-
-
-ast::ArrayType * buildArray( const TypeData * td ) {
-	return new ast::ArrayType(
-		buildDefaultType( td->base ),
-		maybeBuild( td->array.dimension ),
-		td->array.isVarLen ? ast::VariableLen : ast::FixedLen,
-		td->array.isStatic ? ast::StaticDim : ast::DynamicDim,
-		buildQualifiers( td )
-	);
-} // buildArray
-
-
-ast::ReferenceType * buildReference( const TypeData * td ) {
-	return new ast::ReferenceType(
-		buildDefaultType( td->base ),
-		buildQualifiers( td )
-	);
-} // buildReference
-
-
-ast::AggregateDecl * buildAggregate( const TypeData * td, std::vector<ast::ptr<ast::Attribute>> attributes, ast::Linkage::Spec linkage ) {
-	assert( td->kind == TypeData::Aggregate );
-	ast::AggregateDecl * at;
-	switch ( td->aggregate.kind ) {
-	case ast::AggregateDecl::Struct:
-	case ast::AggregateDecl::Coroutine:
-	case ast::AggregateDecl::Exception:
-	case ast::AggregateDecl::Generator:
-	case ast::AggregateDecl::Monitor:
-	case ast::AggregateDecl::Thread:
-		at = new ast::StructDecl( td->location,
-			*td->aggregate.name,
-			td->aggregate.kind,
-			std::move( attributes ),
-			linkage
-		);
-		buildForall( td->aggregate.params, at->params );
-		break;
-	case ast::AggregateDecl::Union:
-		at = new ast::UnionDecl( td->location,
-			*td->aggregate.name,
-			std::move( attributes ),
-			linkage
-		);
-		buildForall( td->aggregate.params, at->params );
-		break;
-	case ast::AggregateDecl::Enum:
-		return buildEnum( td, std::move( attributes ), linkage );
-	case ast::AggregateDecl::Trait:
-		at = new ast::TraitDecl( td->location,
-			*td->aggregate.name,
-			std::move( attributes ),
-			linkage
-		);
-		buildList( td->aggregate.params, at->params );
-		break;
-	default:
-		assert( false );
-	} // switch
-
-	buildList( td->aggregate.fields, at->members );
-	at->set_body( td->aggregate.body );
-
-	return at;
-} // buildAggregate
-
-
-ast::BaseInstType * buildComAggInst(
-		const TypeData * td,
-		std::vector<ast::ptr<ast::Attribute>> && attributes,
-		ast::Linkage::Spec linkage ) {
-	switch ( td->kind ) {
-	case TypeData::Aggregate:
-		if ( td->aggregate.body ) {
-			ast::AggregateDecl * typedecl =
-				buildAggregate( td, std::move( attributes ), linkage );
-			switch ( td->aggregate.kind ) {
-			case ast::AggregateDecl::Struct:
-			case ast::AggregateDecl::Coroutine:
-			case ast::AggregateDecl::Monitor:
-			case ast::AggregateDecl::Thread:
-				return new ast::StructInstType(
-					strict_dynamic_cast<ast::StructDecl *>( typedecl ),
-					buildQualifiers( td )
-				);
-			case ast::AggregateDecl::Union:
-				return new ast::UnionInstType(
-					strict_dynamic_cast<ast::UnionDecl *>( typedecl ),
-					buildQualifiers( td )
-				);
-			case ast::AggregateDecl::Enum:
-				return new ast::EnumInstType(
-					strict_dynamic_cast<ast::EnumDecl *>( typedecl ),
-					buildQualifiers( td )
-				);
-			case ast::AggregateDecl::Trait:
-				assert( false );
-				break;
-			default:
-				assert( false );
-			} // switch
-		} else {
-			switch ( td->aggregate.kind ) {
-			case ast::AggregateDecl::Struct:
-			case ast::AggregateDecl::Coroutine:
-			case ast::AggregateDecl::Monitor:
-			case ast::AggregateDecl::Thread:
-				return new ast::StructInstType(
-					*td->aggregate.name,
-					buildQualifiers( td )
-				);
-			case ast::AggregateDecl::Union:
-				return new ast::UnionInstType(
-					*td->aggregate.name,
-					buildQualifiers( td )
-				);
-			case ast::AggregateDecl::Enum:
-				return new ast::EnumInstType(
-					*td->aggregate.name,
-					buildQualifiers( td )
-				);
-			case ast::AggregateDecl::Trait:
-				return new ast::TraitInstType(
-					*td->aggregate.name,
-					buildQualifiers( td )
-				);
-			default:
-				assert( false );
-			} // switch
-			break;
-		} // if
-		break;
-	default:
-		assert( false );
-	} // switch
-	assert( false );
-} // buildAggInst
-
-
-ast::BaseInstType * buildAggInst( const TypeData * td ) {
-	assert( td->kind == TypeData::AggregateInst );
-
-	ast::BaseInstType * ret = nullptr;
-	TypeData * type = td->aggInst.aggregate;
-	switch ( type->kind ) {
-	case TypeData::Aggregate:
-		switch ( type->aggregate.kind ) {
-		case ast::AggregateDecl::Struct:
-		case ast::AggregateDecl::Coroutine:
-		case ast::AggregateDecl::Monitor:
-		case ast::AggregateDecl::Thread:
-			ret = new ast::StructInstType(
-				*type->aggregate.name,
-				buildQualifiers( type )
-			);
-			break;
-		case ast::AggregateDecl::Union:
-			ret = new ast::UnionInstType(
-				*type->aggregate.name,
-				buildQualifiers( type )
-			);
-			break;
-		case ast::AggregateDecl::Enum:
-			ret = new ast::EnumInstType(
-				*type->aggregate.name,
-				buildQualifiers( type )
-			);
-			break;
-		case ast::AggregateDecl::Trait:
-			ret = new ast::TraitInstType(
-				*type->aggregate.name,
-				buildQualifiers( type )
-			);
-			break;
-		default:
-			assert( false );
-		} // switch
-		break;
-	default:
-		assert( false );
-	} // switch
-
-	ret->hoistType = td->aggInst.hoistType;
-	buildList( td->aggInst.params, ret->params );
-	return ret;
-} // buildAggInst
-
-
-ast::NamedTypeDecl * buildSymbolic(
-		const TypeData * td,
-		std::vector<ast::ptr<ast::Attribute>> attributes,
-		const std::string & name,
-		ast::Storage::Classes scs,
-		ast::Linkage::Spec linkage ) {
-	assert( td->kind == TypeData::Symbolic );
-	ast::NamedTypeDecl * ret;
-	assert( td->base );
-	if ( td->symbolic.isTypedef ) {
-		ret = new ast::TypedefDecl(
-			td->location,
-			name,
-			scs,
-			typebuild( td->base ),
-			linkage
-		);
-	} else {
-		ret = new ast::TypeDecl(
-			td->location,
-			name,
-			scs,
-			typebuild( td->base ),
-			ast::TypeDecl::Dtype,
-			true
-		);
-	} // if
-	buildList( td->symbolic.assertions, ret->assertions );
-	splice( ret->base.get_and_mutate()->attributes, attributes );
-	return ret;
-} // buildSymbolic
-
-
-ast::EnumDecl * buildEnum(
-		const TypeData * td,
-		std::vector<ast::ptr<ast::Attribute>> && attributes,
-		ast::Linkage::Spec linkage ) {
-	assert( td->kind == TypeData::Aggregate );
-	assert( td->aggregate.kind == ast::AggregateDecl::Enum );
-	ast::Type * baseType = td->base ? typebuild(td->base) : nullptr;
-	ast::EnumDecl * ret = new ast::EnumDecl(
-		td->location,
-		*td->aggregate.name,
-		td->aggregate.typed,
-		std::move( attributes ),
-		linkage,
-		baseType
-	);
-	buildList( td->aggregate.fields, ret->members );
-	auto members = ret->members.begin();
-	ret->hide = td->aggregate.hiding == EnumHiding::Hide ? ast::EnumDecl::EnumHiding::Hide : ast::EnumDecl::EnumHiding::Visible;
-	for ( const DeclarationNode * cur = td->aggregate.fields ; cur != nullptr ; cur = cur->next, ++members ) {
-		if ( cur->enumInLine ) {
-			// Do Nothing
-		} else if ( ret->isTyped && !ret->base && cur->has_enumeratorValue() ) {
-			SemanticError( td->location, "Enumerator of enum(void) cannot have an explicit initializer value." );
-		} else if ( cur->has_enumeratorValue() ) {
-			ast::Decl * member = members->get_and_mutate();
-			ast::ObjectDecl * object = strict_dynamic_cast<ast::ObjectDecl *>( member );
-			object->init = new ast::SingleInit(
-				td->location,
-				maybeMoveBuild( cur->consume_enumeratorValue() ),
-				ast::NoConstruct
-			);
-		} else if ( !cur->initializer ) {
-			if ( baseType && (!dynamic_cast<ast::BasicType *>(baseType) || !dynamic_cast<ast::BasicType *>(baseType)->isInteger())) {
-				SemanticError( td->location, "Enumerators of an non-integer typed enum must be explicitly initialized." );
-			}
-		}
-		// else cur is a List Initializer and has been set as init in buildList()
-		// if
-	} // for
-	ret->body = td->aggregate.body;
-	return ret;
-} // buildEnum
-
-
-ast::TypeInstType * buildSymbolicInst( const TypeData * td ) {
-	assert( td->kind == TypeData::SymbolicInst );
-	ast::TypeInstType * ret = new ast::TypeInstType(
-		*td->symbolic.name,
-		ast::TypeDecl::Dtype,
-		buildQualifiers( td )
-	);
-	buildList( td->symbolic.actuals, ret->params );
-	return ret;
-} // buildSymbolicInst
-
-
-ast::TupleType * buildTuple( const TypeData * td ) {
-	assert( td->kind == TypeData::Tuple );
-	std::vector<ast::ptr<ast::Type>> types;
-	buildTypeList( td->tuple, types );
-	ast::TupleType * ret = new ast::TupleType(
-		std::move( types ),
-		buildQualifiers( td )
-	);
-	return ret;
-} // buildTuple
-
-
-ast::TypeofType * buildTypeof( const TypeData * td ) {
-	assert( td->kind == TypeData::Typeof || td->kind == TypeData::Basetypeof );
-	assert( td->typeexpr );
-	return new ast::TypeofType(
-		td->typeexpr->build(),
-		td->kind == TypeData::Typeof
-			? ast::TypeofType::Typeof : ast::TypeofType::Basetypeof,
-		buildQualifiers( td )
-	);
-} // buildTypeof
-
-
-ast::VTableType * buildVtable( const TypeData * td ) {
-	assert( td->base );
-	return new ast::VTableType(
-		typebuild( td->base ),
-		buildQualifiers( td )
-	);
-} // buildVtable
-
-
-ast::FunctionDecl * buildFunctionDecl(
-		const TypeData * td,
-		const string &name,
-		ast::Storage::Classes scs,
-		ast::Function::Specs funcSpec,
-		ast::Linkage::Spec linkage,
-		ast::Expr * asmName,
-		std::vector<ast::ptr<ast::Attribute>> && attributes ) {
-	assert( td->kind == TypeData::Function );
-	// For some reason FunctionDecl takes a bool instead of an ArgumentFlag.
-	bool isVarArgs = !td->function.params || td->function.params->hasEllipsis;
-	ast::CV::Qualifiers cvq = buildQualifiers( td );
-	std::vector<ast::ptr<ast::TypeDecl>> forall;
-	std::vector<ast::ptr<ast::DeclWithType>> assertions;
-	std::vector<ast::ptr<ast::DeclWithType>> params;
-	std::vector<ast::ptr<ast::DeclWithType>> returns;
-	buildList( td->function.params, params );
-	buildForall( td->forall, forall );
-	// Functions do not store their assertions there anymore.
-	for ( ast::ptr<ast::TypeDecl> & type_param : forall ) {
-		auto mut = type_param.get_and_mutate();
-		splice( assertions, mut->assertions );
-	}
-	if ( td->base ) {
-		switch ( td->base->kind ) {
-		case TypeData::Tuple:
-			buildList( td->base->tuple, returns );
-			break;
-		default:
-			returns.push_back( dynamic_cast<ast::DeclWithType *>(
-				buildDecl(
-					td->base,
-					"",
-					ast::Storage::Classes(),
-					(ast::Expr *)nullptr, // bitfieldWidth
-					ast::Function::Specs(),
-					ast::Linkage::Cforall,
-					(ast::Expr *)nullptr // asmName
-				)
-			) );
-		} // switch
-	} else {
-		returns.push_back( new ast::ObjectDecl(
-			td->location,
-			"",
-			new ast::BasicType( ast::BasicKind::SignedInt ),
-			(ast::Init *)nullptr,
-			ast::Storage::Classes(),
-			ast::Linkage::Cforall
-		) );
-	} // if
-	ast::Stmt * stmt = maybeBuild( td->function.body );
-	ast::CompoundStmt * body = dynamic_cast<ast::CompoundStmt *>( stmt );
-	ast::FunctionDecl * decl = new ast::FunctionDecl( td->location,
-		name,
-		std::move( forall ),
-		std::move( assertions ),
-		std::move( params ),
-		std::move( returns ),
-		body,
-		scs,
-		linkage,
-		std::move( attributes ),
-		funcSpec,
-		(isVarArgs) ? ast::VariableArgs : ast::FixedArgs
-	);
-	buildList( td->function.withExprs, decl->withExprs );
-	decl->asmName = asmName;
-	// This may be redundant on a declaration.
-	decl->type.get_and_mutate()->qualifiers = cvq;
-	return decl;
-} // buildFunctionDecl
-
-
-ast::Decl * buildDecl(
-		const TypeData * td,
-		const string &name,
-		ast::Storage::Classes scs,
-		ast::Expr * bitfieldWidth,
-		ast::Function::Specs funcSpec,
-		ast::Linkage::Spec linkage,
-		ast::Expr * asmName,
-		ast::Init * init,
-		std::vector<ast::ptr<ast::Attribute>> && attributes ) {
-	if ( td->kind == TypeData::Function ) {
-		if ( td->function.idList ) {					// KR function ?
-			buildKRFunction( td->function );			// transform into C11 function
-		} // if
-
-		return buildFunctionDecl(
-			td, name, scs, funcSpec, linkage,
-			asmName, std::move( attributes ) );
-	} else if ( td->kind == TypeData::Aggregate ) {
-		return buildAggregate( td, std::move( attributes ), linkage );
-	} else if ( td->kind == TypeData::Symbolic ) {
-		return buildSymbolic( td, std::move( attributes ), name, scs, linkage );
-	} else {
-		auto ret = new ast::ObjectDecl( td->location,
-			name,
-			typebuild( td ),
-			init,
-			scs,
-			linkage,
-			bitfieldWidth,
-			std::move( attributes )
-		);
-		ret->asmName = asmName;
-		return ret;
-	} // if
-	return nullptr;
-} // buildDecl
-
-
-ast::FunctionType * buildFunctionType( const TypeData * td ) {
-	assert( td->kind == TypeData::Function );
-	ast::FunctionType * ft = new ast::FunctionType(
-		( !td->function.params || td->function.params->hasEllipsis )
-			? ast::VariableArgs : ast::FixedArgs,
-		buildQualifiers( td )
-	);
-	buildTypeList( td->function.params, ft->params );
-	buildForall( td->forall, ft->forall );
-	if ( td->base ) {
-		switch ( td->base->kind ) {
-		case TypeData::Tuple:
-			buildTypeList( td->base->tuple, ft->returns );
-			break;
-		default:
-			ft->returns.push_back( typebuild( td->base ) );
-			break;
-		} // switch
-	} else {
-		ft->returns.push_back(
-			new ast::BasicType( ast::BasicKind::SignedInt ) );
-	} // if
-	return ft;
-} // buildFunctionType
-
-
-// Transform KR routine declarations into C99 routine declarations:
-//
-//    rtn( a, b, c ) int a, c; double b {}  =>  int rtn( int a, double c, int b ) {}
-//
-// The type information for each post-declaration is moved to the corresponding pre-parameter and the post-declaration
-// is deleted. Note, the order of the parameter names may not be the same as the declaration names. Duplicate names and
-// extra names are disallowed.
-//
-// Note, there is no KR routine-prototype syntax:
-//
-//    rtn( a, b, c ) int a, c; double b; // invalid KR prototype
-//    rtn(); // valid KR prototype
-
-void buildKRFunction( const TypeData::Function_t & function ) {
-	assert( ! function.params );
-	// loop over declaration first as it is easier to spot errors
-	for ( DeclarationNode * decl = function.oldDeclList; decl != nullptr; decl = decl->next ) {
-		// scan ALL parameter names for each declaration name to check for duplicates
-		for ( DeclarationNode * param = function.idList; param != nullptr; param = param->next ) {
-			if ( *decl->name == *param->name ) {
-				// type set => parameter name already transformed by a declaration names so there is a duplicate
-				// declaration name attempting a second transformation
-				if ( param->type ) SemanticError( param->location, "duplicate declaration name \"%s\".", param->name->c_str() );
-				// declaration type reset => declaration already transformed by a parameter name so there is a duplicate
-				// parameter name attempting a second transformation
-				if ( ! decl->type ) SemanticError( param->location, "duplicate parameter name \"%s\".", param->name->c_str() );
-				param->type = decl->type;				// set copy declaration type to parameter type
-				decl->type = nullptr;					// reset declaration type
-				// Copy and reset attributes from declaration to parameter:
-				splice( param->attributes, decl->attributes );
-			} // if
-		} // for
-		// declaration type still set => type not moved to a matching parameter so there is a missing parameter name
-		if ( decl->type ) SemanticError( decl->location, "missing name in parameter list %s", decl->name->c_str() );
-	} // for
-
-	// Parameter names without a declaration default to type int:
-	//
-	//    rtb( a, b, c ) const char * b; {} => int rtn( int a, const char * b, int c ) {}
-
-	for ( DeclarationNode * param = function.idList; param != nullptr; param = param->next ) {
-		if ( ! param->type ) {							// generate type int for empty parameter type
-			param->type = new TypeData( TypeData::Basic );
-			param->type->basictype = TypeData::Int;
-		} // if
-	} // for
-
-	function.params = function.idList;					// newly modified idList becomes parameters
-	function.idList = nullptr;							// idList now empty
-	delete function.oldDeclList;						// deletes entire list
-	function.oldDeclList = nullptr;						// reset
-} // buildKRFunction
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/TypeData.cpp
===================================================================
--- src/Parser/TypeData.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/TypeData.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,1733 @@
+//
+// 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.
+//
+// TypeData.cpp --
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Sat May 16 15:12:51 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri Feb 23 08:58:30 2024
+// Update Count     : 734
+//
+
+#include "TypeData.hpp"
+
+#include <cassert>                   // for assert
+#include <ostream>                   // for operator<<, ostream, basic_ostream
+
+#include "AST/Attribute.hpp"         // for Attribute
+#include "AST/Decl.hpp"              // for AggregateDecl, ObjectDecl, Type...
+#include "AST/Init.hpp"              // for SingleInit, ListInit
+#include "AST/Print.hpp"             // for print
+#include "AST/Type.hpp"              // for Type
+#include "Common/SemanticError.hpp"  // for SemanticError
+#include "Common/Utility.hpp"        // for splice, spliceBegin
+#include "Common/Iterate.hpp"        // for reverseIterate
+#include "Parser/ExpressionNode.hpp" // for ExpressionNode
+#include "Parser/StatementNode.hpp"  // for StatementNode
+
+class Attribute;
+
+using namespace std;
+
+// These must harmonize with the corresponding enumerations in the header.
+const char * TypeData::basicTypeNames[] = {
+	"void", "_Bool", "char", "int", "int128",
+	"float", "double", "long double", "float80", "float128",
+	"_float16", "_float32", "_float32x", "_float64", "_float64x", "_float128", "_float128x",
+	"NoBasicTypeNames"
+};
+const char * TypeData::complexTypeNames[] = {
+	"_Complex", "NoComplexTypeNames", "_Imaginary"
+}; // Imaginary unsupported => parse, but make invisible and print error message
+const char * TypeData::signednessNames[] = {
+	"signed", "unsigned", "NoSignednessNames"
+};
+const char * TypeData::lengthNames[] = {
+	"short", "long", "long long", "NoLengthNames"
+};
+const char * TypeData::builtinTypeNames[] = {
+	"__builtin_va_list", "__auto_type", "zero_t", "one_t", "NoBuiltinTypeNames"
+};
+
+TypeData::TypeData( Kind k ) : location( yylloc ), kind( k ), base( nullptr ), forall( nullptr ) /*, PTR1( (void*)(0xdeadbeefdeadbeef)), PTR2( (void*)(0xdeadbeefdeadbeef) ) */ {
+	switch ( kind ) {
+	case Unknown:
+	case Pointer:
+	case Reference:
+	case EnumConstant:
+	case GlobalScope:
+	case Basic:
+		// No unique data to initialize.
+		break;
+	case Array:
+		array.dimension = nullptr;
+		array.isVarLen = false;
+		array.isStatic = false;
+		break;
+	case Function:
+		function.params = nullptr;
+		function.idList = nullptr;
+		function.oldDeclList = nullptr;
+		function.body = nullptr;
+		function.withExprs = nullptr;
+		break;
+	case Aggregate:
+		aggregate.kind = ast::AggregateDecl::NoAggregate;
+		aggregate.name = nullptr;
+		aggregate.params = nullptr;
+		aggregate.actuals = nullptr;
+		aggregate.fields = nullptr;
+		aggregate.body = false;
+		aggregate.anon = false;
+		aggregate.typed = false;
+		aggregate.hiding = EnumHiding::Visible;
+		break;
+	case AggregateInst:
+		aggInst.aggregate = nullptr;
+		aggInst.params = nullptr;
+		aggInst.hoistType = false;
+		break;
+	case Symbolic:
+	case SymbolicInst:
+		symbolic.name = nullptr;
+		symbolic.params = nullptr;
+		symbolic.actuals = nullptr;
+		symbolic.assertions = nullptr;
+		break;
+	case Tuple:
+		tuple = nullptr;
+		break;
+	case Typeof:
+	case Basetypeof:
+		typeexpr = nullptr;
+		break;
+	case Vtable:
+	case Builtin:
+		// No unique data to initialize.
+		break;
+	case Qualified:
+		qualified.parent = nullptr;
+		qualified.child = nullptr;
+		break;
+	} // switch
+} // TypeData::TypeData
+
+
+TypeData::~TypeData() {
+	delete base;
+	delete forall;
+
+	switch ( kind ) {
+	case Unknown:
+	case Pointer:
+	case Reference:
+	case EnumConstant:
+	case GlobalScope:
+	case Basic:
+		// No unique data to deconstruct.
+		break;
+	case Array:
+		delete array.dimension;
+		break;
+	case Function:
+		delete function.params;
+		delete function.idList;
+		delete function.oldDeclList;
+		delete function.body;
+		delete function.withExprs;
+		break;
+	case Aggregate:
+		delete aggregate.name;
+		delete aggregate.params;
+		delete aggregate.actuals;
+		delete aggregate.fields;
+		break;
+	case AggregateInst:
+		delete aggInst.aggregate;
+		delete aggInst.params;
+		break;
+	case Symbolic:
+	case SymbolicInst:
+		delete symbolic.name;
+		delete symbolic.params;
+		delete symbolic.actuals;
+		delete symbolic.assertions;
+		break;
+	case Tuple:
+		delete tuple;
+		break;
+	case Typeof:
+	case Basetypeof:
+		delete typeexpr;
+		break;
+	case Vtable:
+	case Builtin:
+		// No unique data to deconstruct.
+		break;
+	case Qualified:
+		delete qualified.parent;
+		delete qualified.child;
+		break;
+	} // switch
+} // TypeData::~TypeData
+
+
+TypeData * TypeData::clone() const {
+	TypeData * newtype = new TypeData( kind );
+	newtype->qualifiers = qualifiers;
+	newtype->base = maybeCopy( base );
+	newtype->forall = maybeCopy( forall );
+
+	switch ( kind ) {
+	case Unknown:
+	case EnumConstant:
+	case Pointer:
+	case Reference:
+	case GlobalScope:
+		// nothing else to copy
+		break;
+	case Basic:
+		newtype->basictype = basictype;
+		newtype->complextype = complextype;
+		newtype->signedness = signedness;
+		newtype->length = length;
+		break;
+	case Array:
+		newtype->array.dimension = maybeCopy( array.dimension );
+		newtype->array.isVarLen = array.isVarLen;
+		newtype->array.isStatic = array.isStatic;
+		break;
+	case Function:
+		newtype->function.params = maybeCopy( function.params );
+		newtype->function.idList = maybeCopy( function.idList );
+		newtype->function.oldDeclList = maybeCopy( function.oldDeclList );
+		newtype->function.body = maybeCopy( function.body );
+		newtype->function.withExprs = maybeCopy( function.withExprs );
+		break;
+	case Aggregate:
+		newtype->aggregate.kind = aggregate.kind;
+		newtype->aggregate.name = aggregate.name ? new string( *aggregate.name ) : nullptr;
+		newtype->aggregate.params = maybeCopy( aggregate.params );
+		newtype->aggregate.actuals = maybeCopy( aggregate.actuals );
+		newtype->aggregate.fields = maybeCopy( aggregate.fields );
+		newtype->aggregate.attributes = aggregate.attributes;
+		newtype->aggregate.body = aggregate.body;
+		newtype->aggregate.anon = aggregate.anon;
+		break;
+	case AggregateInst:
+		newtype->aggInst.aggregate = maybeCopy( aggInst.aggregate );
+		newtype->aggInst.params = maybeCopy( aggInst.params );
+		newtype->aggInst.hoistType = aggInst.hoistType;
+		break;
+	case Symbolic:
+	case SymbolicInst:
+		newtype->symbolic.name = symbolic.name ? new string( *symbolic.name ) : nullptr;
+		newtype->symbolic.params = maybeCopy( symbolic.params );
+		newtype->symbolic.actuals = maybeCopy( symbolic.actuals );
+		newtype->symbolic.assertions = maybeCopy( symbolic.assertions );
+		newtype->symbolic.isTypedef = symbolic.isTypedef;
+		break;
+	case Tuple:
+		newtype->tuple = maybeCopy( tuple );
+		break;
+	case Typeof:
+	case Basetypeof:
+		newtype->typeexpr = maybeCopy( typeexpr );
+		break;
+	case Vtable:
+		break;
+	case Builtin:
+		assert( builtintype == Zero || builtintype == One );
+		newtype->builtintype = builtintype;
+		break;
+	case Qualified:
+		newtype->qualified.parent = maybeCopy( qualified.parent );
+		newtype->qualified.child = maybeCopy( qualified.child );
+		break;
+	} // switch
+	return newtype;
+} // TypeData::clone
+
+
+void TypeData::print( ostream &os, int indent ) const {
+	ast::print( os, qualifiers );
+
+	if ( forall ) {
+		os << "forall " << endl;
+		forall->printList( os, indent + 4 );
+	} // if
+
+	switch ( kind ) {
+	case Basic:
+		if ( signedness != NoSignedness ) os << signednessNames[ signedness ] << " ";
+		if ( length != NoLength ) os << lengthNames[ length ] << " ";
+		if ( complextype != NoComplexType ) os << complexTypeNames[ complextype ] << " ";
+		if ( basictype != NoBasicType ) os << basicTypeNames[ basictype ] << " ";
+		break;
+	case Pointer:
+		os << "pointer ";
+		if ( base ) {
+			os << "to ";
+			base->print( os, indent );
+		} // if
+		break;
+	case Reference:
+		os << "reference ";
+		if ( base ) {
+			os << "to ";
+			base->print( os, indent );
+		} // if
+		break;
+	case Array:
+		if ( array.isStatic ) {
+			os << "static ";
+		} // if
+		if ( array.dimension ) {
+			os << "array of ";
+			array.dimension->printOneLine( os, indent );
+		} else if ( array.isVarLen ) {
+			os << "variable-length array of ";
+		} else {
+			os << "open array of ";
+		} // if
+		if ( base ) {
+			base->print( os, indent );
+		} // if
+		break;
+	case Function:
+		os << "function" << endl;
+		if ( function.params ) {
+			os << string( indent + 2, ' ' ) << "with parameters " << endl;
+			function.params->printList( os, indent + 4 );
+		} else {
+			os << string( indent + 2, ' ' ) << "with no parameters" << endl;
+		} // if
+		if ( function.idList ) {
+			os << string( indent + 2, ' ' ) << "with old-style identifier list " << endl;
+			function.idList->printList( os, indent + 4 );
+		} // if
+		if ( function.oldDeclList ) {
+			os << string( indent + 2, ' ' ) << "with old-style declaration list " << endl;
+			function.oldDeclList->printList( os, indent + 4 );
+		} // if
+		os << string( indent + 2, ' ' ) << "returning ";
+		if ( base ) {
+			base->print( os, indent + 4 );
+		} else {
+			os << "nothing ";
+		} // if
+		os << endl;
+		if ( function.body ) {
+			os << string( indent + 2, ' ' ) << "with body " << endl;
+			function.body->printList( os, indent + 2 );
+		} // if
+		break;
+	case Aggregate:
+		os << ast::AggregateDecl::aggrString( aggregate.kind ) << ' ' << *aggregate.name << endl;
+		if ( aggregate.params ) {
+			os << string( indent + 2, ' ' ) << "with type parameters" << endl;
+			aggregate.params->printList( os, indent + 4 );
+		} // if
+		if ( aggregate.actuals ) {
+			os << string( indent + 2, ' ' ) << "instantiated with actual parameters" << endl;
+			aggregate.actuals->printList( os, indent + 4 );
+		} // if
+		if ( aggregate.fields ) {
+			os << string( indent + 2, ' ' ) << "with members" << endl;
+			aggregate.fields->printList( os, indent + 4 );
+		} // if
+		if ( aggregate.body ) {
+			os << string( indent + 2, ' ' ) << "with body" << endl;
+		} // if
+		if ( ! aggregate.attributes.empty() ) {
+			os << string( indent + 2, ' ' ) << "with attributes" << endl;
+			for ( ast::ptr<ast::Attribute> const & attr : reverseIterate( aggregate.attributes ) ) {
+				os << string( indent + 4, ' ' );
+				ast::print( os, attr, indent + 2 );
+			} // for
+		} // if
+		break;
+	case AggregateInst:
+		if ( aggInst.aggregate ) {
+			os << "instance of " ;
+			aggInst.aggregate->print( os, indent );
+		} else {
+			os << "instance of an unspecified aggregate ";
+		} // if
+		if ( aggInst.params ) {
+			os << string( indent + 2, ' ' ) << "with parameters" << endl;
+			aggInst.params->printList( os, indent + 2 );
+		} // if
+		break;
+	case EnumConstant:
+		os << "enumeration constant ";
+		break;
+	case Symbolic:
+		if ( symbolic.isTypedef ) {
+			os << "typedef definition ";
+		} else {
+			os << "type definition ";
+		} // if
+		if ( symbolic.params ) {
+			os << endl << string( indent + 2, ' ' ) << "with parameters" << endl;
+			symbolic.params->printList( os, indent + 2 );
+		} // if
+		if ( symbolic.assertions ) {
+			os << endl << string( indent + 2, ' ' ) << "with assertions" << endl;
+			symbolic.assertions->printList( os, indent + 4 );
+			os << string( indent + 2, ' ' );
+		} // if
+		if ( base ) {
+			os << "for ";
+			base->print( os, indent + 2 );
+		} // if
+		break;
+	case SymbolicInst:
+		os << *symbolic.name;
+		if ( symbolic.actuals ) {
+			os << "(";
+			symbolic.actuals->printList( os, indent + 2 );
+			os << ")";
+		} // if
+		break;
+	case Tuple:
+		os << "tuple ";
+		if ( tuple ) {
+			os << "with members" << endl;
+			tuple->printList( os, indent + 2 );
+		} // if
+		break;
+	case Basetypeof:
+		os << "base-";
+		#if defined(__GNUC__) && __GNUC__ >= 7
+			__attribute__((fallthrough));
+		#endif
+		// FALL THROUGH
+	case Typeof:
+		os << "type-of expression ";
+		if ( typeexpr ) {
+			typeexpr->print( os, indent + 2 );
+		} // if
+		break;
+	case Vtable:
+		os << "vtable";
+		break;
+	case Builtin:
+		os << builtinTypeNames[builtintype];
+		break;
+	case GlobalScope:
+		break;
+	case Qualified:
+		qualified.parent->print( os );
+		os << ".";
+		qualified.child->print( os );
+		break;
+	case Unknown:
+		os << "entity of unknown type ";
+		break;
+	default:
+		os << "internal error: TypeData::print " << kind << endl;
+		assert( false );
+	} // switch
+} // TypeData::print
+
+const std::string * TypeData::leafName() const {
+	switch ( kind ) {
+	case Unknown:
+	case Pointer:
+	case Reference:
+	case EnumConstant:
+	case GlobalScope:
+	case Array:
+	case Basic:
+	case Function:
+	case AggregateInst:
+	case Tuple:
+	case Typeof:
+	case Basetypeof:
+	case Builtin:
+	case Vtable:
+		assertf(false, "Tried to get leaf name from kind without a name: %d", kind);
+		break;
+	case Aggregate:
+		return aggregate.name;
+	case Symbolic:
+	case SymbolicInst:
+		return symbolic.name;
+	case Qualified:
+		return qualified.child->leafName();
+	} // switch
+	assert(false);
+}
+
+TypeData * TypeData::getLastBase() {
+	TypeData * cur = this;
+	while ( cur->base ) cur = cur->base;
+	return cur;
+}
+
+void TypeData::setLastBase( TypeData * newBase ) {
+	getLastBase()->base = newBase;
+}
+
+
+// Wrap an aggregate up in an instance. Takes and gives ownership.
+static TypeData * makeInstance( TypeData * type ) {
+	assert( TypeData::Aggregate == type->kind );
+	TypeData * out = new TypeData( TypeData::AggregateInst );
+	out->aggInst.aggregate = type;
+	out->aggInst.params = maybeCopy( type->aggregate.actuals );
+	out->aggInst.hoistType = type->aggregate.body;
+	out->qualifiers |= type->qualifiers;
+	return out;
+}
+
+
+TypeData * build_type_qualifier( ast::CV::Qualifiers tq ) {
+	TypeData * type = new TypeData;
+	type->qualifiers = tq;
+	return type;
+}
+
+TypeData * build_basic_type( TypeData::BasicType basic ) {
+	TypeData * type = new TypeData( TypeData::Basic );
+	type->basictype = basic;
+	return type;
+}
+
+TypeData * build_complex_type( TypeData::ComplexType complex ) {
+	TypeData * type = new TypeData( TypeData::Basic );
+	type->complextype = complex;
+	return type;
+}
+
+TypeData * build_signedness( TypeData::Signedness signedness ) {
+	TypeData * type = new TypeData( TypeData::Basic );
+	type->signedness = signedness;
+	return type;
+}
+
+TypeData * build_builtin_type( TypeData::BuiltinType bit ) {
+	TypeData * type = new TypeData( TypeData::Builtin );
+	type->builtintype = bit;
+	return type;
+}
+
+TypeData * build_length( TypeData::Length length ) {
+	TypeData * type = new TypeData( TypeData::Basic );
+	type->length = length;
+	return type;
+}
+
+TypeData * build_forall( DeclarationNode * forall ) {
+	TypeData * type = new TypeData( TypeData::Unknown );
+	type->forall = forall;
+	return type;
+}
+
+TypeData * build_global_scope() {
+	return new TypeData( TypeData::GlobalScope );
+}
+
+TypeData * build_qualified_type( TypeData * parent, TypeData * child ) {
+	TypeData * type = new TypeData( TypeData::Qualified );
+	type->qualified.parent = parent;
+	type->qualified.child = child;
+	return type;
+}
+
+TypeData * build_typedef( const std::string * name ) {
+	TypeData * type = new TypeData( TypeData::SymbolicInst );
+	type->symbolic.name = name;
+	type->symbolic.isTypedef = true;
+	type->symbolic.actuals = nullptr;
+	return type;
+}
+
+TypeData * build_type_gen( const std::string * name, ExpressionNode * params ) {
+	TypeData * type = new TypeData( TypeData::SymbolicInst );
+	type->symbolic.name = name;
+	type->symbolic.isTypedef = false;
+	type->symbolic.actuals = params;
+	return type;
+}
+
+TypeData * build_vtable_type( TypeData * base ) {
+	TypeData * type = new TypeData( TypeData::Vtable );
+	type->base = base;
+	return type;
+}
+
+// Takes ownership of src.
+static void addQualifiersToType( TypeData * dst, TypeData * src ) {
+	if ( dst->base ) {
+		addQualifiersToType( dst->base, src );
+	} else if ( dst->kind == TypeData::Function ) {
+		dst->base = src;
+		src = nullptr;
+    } else {
+		dst->qualifiers |= src->qualifiers;
+		delete src;
+	} // if
+}
+
+// Takes ownership of all arguments, gives ownership of return value.
+TypeData * addQualifiers( TypeData * dst, TypeData * src ) {
+	if ( src->forall ) {
+		if ( dst->forall || TypeData::Aggregate != dst->kind ) {
+			extend( dst->forall, src->forall );
+		} else {
+			extend( dst->aggregate.params, src->forall );
+		}
+		src->forall = nullptr;
+	}
+
+	addQualifiersToType( dst, src );
+	return dst;
+}
+
+// Helper for addType and cloneBaseType.
+static void addTypeToType( TypeData *& dst, TypeData *& src ) {
+	if ( src->forall && dst->kind == TypeData::Function ) {
+		extend( dst->forall, src->forall );
+		src->forall = nullptr;
+	} // if
+	if ( dst->base ) {
+		addTypeToType( dst->base, src );
+		return;
+	}
+	switch ( dst->kind ) {
+	case TypeData::Unknown:
+		src->qualifiers |= dst->qualifiers;
+		// LEAKS dst?
+		dst = src;
+		src = nullptr;
+		break;
+	case TypeData::Basic:
+		dst->qualifiers |= src->qualifiers;
+		if ( src->kind != TypeData::Unknown ) {
+			assert( src->kind == TypeData::Basic );
+
+			if ( dst->basictype == TypeData::NoBasicType ) {
+				dst->basictype = src->basictype;
+			} else if ( src->basictype != TypeData::NoBasicType ) {
+				SemanticError( yylloc, "multiple declaration types \"%s\" and \"%s\".",
+					TypeData::basicTypeNames[ dst->basictype ],
+					TypeData::basicTypeNames[ src->basictype ] );
+			}
+			if ( dst->complextype == TypeData::NoComplexType ) {
+				dst->complextype = src->complextype;
+			} else if ( src->complextype != TypeData::NoComplexType ) {
+				SemanticError( yylloc, "multiple declaration types \"%s\" and \"%s\".",
+					TypeData::complexTypeNames[ src->complextype ],
+					TypeData::complexTypeNames[ src->complextype ] );
+			}
+			if ( dst->signedness == TypeData::NoSignedness ) {
+				dst->signedness = src->signedness;
+			} else if ( src->signedness != TypeData::NoSignedness ) {
+				SemanticError( yylloc, "conflicting type specifier \"%s\" and \"%s\".",
+					TypeData::signednessNames[ dst->signedness ],
+					TypeData::signednessNames[ src->signedness ] );
+			}
+			if ( dst->length == TypeData::NoLength ) {
+				dst->length = src->length;
+			} else if ( dst->length == TypeData::Long && src->length == TypeData::Long ) {
+				dst->length = TypeData::LongLong;
+			} else if ( src->length != TypeData::NoLength ) {
+				SemanticError( yylloc, "conflicting type specifier \"%s\" and \"%s\".",
+					TypeData::lengthNames[ dst->length ],
+					TypeData::lengthNames[ src->length ] );
+			}
+		} // if
+		break;
+	default:
+		if ( TypeData::Aggregate == src->kind ) {
+			dst->base = makeInstance( src );
+		} else {
+			extend( dst->forall, src->forall );
+			src->forall = nullptr;
+			dst->base = src;
+		}
+		src = nullptr;
+	} // switch
+}
+
+// Takes ownership of all arguments, gives ownership of return value.
+TypeData * addType( TypeData * dst, TypeData * src, std::vector<ast::ptr<ast::Attribute>> & attributes ) {
+	if ( dst ) {
+		addTypeToType( dst, src );
+	} else if ( src->kind == TypeData::Aggregate ) {
+		// Hide type information aggregate instances.
+		dst = makeInstance( src );
+		dst->aggInst.aggregate->aggregate.attributes.swap( attributes );
+	} else {
+		dst = src;
+	} // if
+	return dst;
+}
+
+TypeData * addType( TypeData * dst, TypeData * src ) {
+	std::vector<ast::ptr<ast::Attribute>> attributes;
+	return addType( dst, src, attributes );
+}
+
+// Takes ownership of both arguments, gives ownership of return value.
+TypeData * cloneBaseType( TypeData * type, TypeData * other ) {
+	TypeData * newType = type->getLastBase()->clone();
+	if ( newType->kind == TypeData::AggregateInst ) {
+		// don't duplicate members
+		assert( newType->aggInst.aggregate->kind == TypeData::Aggregate );
+		delete newType->aggInst.aggregate->aggregate.fields;
+		newType->aggInst.aggregate->aggregate.fields = nullptr;
+		newType->aggInst.aggregate->aggregate.body = false;
+		// don't hoist twice
+		newType->aggInst.hoistType = false;
+	} // if
+	newType->forall = maybeCopy( type->forall );
+
+	if ( other ) {
+		addTypeToType( other, newType );
+		delete newType;
+		return other;
+	} // if
+	return newType;
+}
+
+TypeData * makeNewBase( TypeData * type ) {
+	return ( TypeData::Aggregate == type->kind ) ? makeInstance( type ) : type;
+}
+
+
+void buildForall(
+		const DeclarationNode * firstNode,
+		std::vector<ast::ptr<ast::TypeInstType>> &outputList ) {
+	{
+		std::vector<ast::ptr<ast::Type>> tmpList;
+		buildTypeList( firstNode, tmpList );
+		for ( auto tmp : tmpList ) {
+			outputList.emplace_back(
+				strict_dynamic_cast<const ast::TypeInstType *>(
+					tmp.release() ) );
+		}
+	}
+	auto n = firstNode;
+	for ( auto i = outputList.begin() ;
+			i != outputList.end() ;
+			++i, n = n->next ) {
+		// Only the object type class adds additional assertions.
+		if ( n->variable.tyClass != ast::TypeDecl::Otype ) {
+			continue;
+		}
+
+		ast::TypeDecl const * td = i->strict_as<ast::TypeDecl>();
+		std::vector<ast::ptr<ast::DeclWithType>> newAssertions;
+		auto mutTypeDecl = ast::mutate( td );
+		const CodeLocation & location = mutTypeDecl->location;
+		*i = mutTypeDecl;
+
+		// add assertion parameters to `type' tyvars in reverse order
+		// add assignment operator:  T * ?=?(T *, T)
+		newAssertions.push_back( new ast::FunctionDecl(
+			location,
+			"?=?",
+			{}, // forall
+			{}, // assertions
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::ReferenceType( i->get() ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+				new ast::ObjectDecl(
+					location,
+					"",
+					i->get(),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // params
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					i->get(),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // returns
+			(ast::CompoundStmt *)nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		) );
+
+		// add default ctor:  void ?{}(T *)
+		newAssertions.push_back( new ast::FunctionDecl(
+			location,
+			"?{}",
+			{}, // forall
+			{}, // assertions
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::ReferenceType( i->get() ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // params
+			{}, // returns
+			(ast::CompoundStmt *)nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		) );
+
+		// add copy ctor:  void ?{}(T *, T)
+		newAssertions.push_back( new ast::FunctionDecl(
+			location,
+			"?{}",
+			{}, // forall
+			{}, // assertions
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::ReferenceType( i->get() ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+				new ast::ObjectDecl(
+					location,
+					"",
+					i->get(),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // params
+			{}, // returns
+			(ast::CompoundStmt *)nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		) );
+
+		// add dtor:  void ^?{}(T *)
+		newAssertions.push_back( new ast::FunctionDecl(
+			location,
+			"^?{}",
+			{}, // forall
+			{}, // assertions
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::ReferenceType( i->get() ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // params
+			{}, // returns
+			(ast::CompoundStmt *)nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		) );
+
+		spliceBegin( mutTypeDecl->assertions, newAssertions );
+	} // for
+}
+
+
+void buildForall(
+		const DeclarationNode * firstNode,
+		std::vector<ast::ptr<ast::TypeDecl>> &outputForall ) {
+	buildList( firstNode, outputForall );
+	auto n = firstNode;
+	for ( auto i = outputForall.begin() ;
+			i != outputForall.end() ;
+			++i, n = n->next ) {
+		// Only the object type class adds additional assertions.
+		if ( n->variable.tyClass != ast::TypeDecl::Otype ) {
+			continue;
+		}
+
+		ast::TypeDecl const * td = i->strict_as<ast::TypeDecl>();
+		std::vector<ast::ptr<ast::DeclWithType>> newAssertions;
+		auto mutTypeDecl = ast::mutate( td );
+		const CodeLocation & location = mutTypeDecl->location;
+		*i = mutTypeDecl;
+
+		// add assertion parameters to `type' tyvars in reverse order
+		// add assignment operator:  T * ?=?(T *, T)
+		newAssertions.push_back( new ast::FunctionDecl(
+			location,
+			"?=?",
+			{}, // forall
+			{}, // assertions
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::ReferenceType( new ast::TypeInstType( td->name, *i ) ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::TypeInstType( td->name, *i ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // params
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::TypeInstType( td->name, *i ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // returns
+			(ast::CompoundStmt *)nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		) );
+
+		// add default ctor:  void ?{}(T *)
+		newAssertions.push_back( new ast::FunctionDecl(
+			location,
+			"?{}",
+			{}, // forall
+			{}, // assertions
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::ReferenceType(
+						new ast::TypeInstType( td->name, i->get() ) ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // params
+			{}, // returns
+			(ast::CompoundStmt *)nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		) );
+
+		// add copy ctor:  void ?{}(T *, T)
+		newAssertions.push_back( new ast::FunctionDecl(
+			location,
+			"?{}",
+			{}, // forall
+			{}, // assertions
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::ReferenceType(
+						new ast::TypeInstType( td->name, *i ) ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::TypeInstType( td->name, *i ),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // params
+			{}, // returns
+			(ast::CompoundStmt *)nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		) );
+
+		// add dtor:  void ^?{}(T *)
+		newAssertions.push_back( new ast::FunctionDecl(
+			location,
+			"^?{}",
+			{}, // forall
+			{}, // assertions
+			{
+				new ast::ObjectDecl(
+					location,
+					"",
+					new ast::ReferenceType(
+						new ast::TypeInstType( i->get() )
+					),
+					(ast::Init *)nullptr,
+					ast::Storage::Classes(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr
+				),
+			}, // params
+			{}, // returns
+			(ast::CompoundStmt *)nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		) );
+
+		spliceBegin( mutTypeDecl->assertions, newAssertions );
+	} // for
+} // buildForall
+
+
+ast::Type * typebuild( const TypeData * td ) {
+	assert( td );
+	switch ( td->kind ) {
+	case TypeData::Unknown:
+		// fill in implicit int
+		return new ast::BasicType(
+			ast::BasicKind::SignedInt,
+			buildQualifiers( td )
+		);
+	case TypeData::Basic:
+		return buildBasicType( td );
+	case TypeData::Pointer:
+		return buildPointer( td );
+	case TypeData::Array:
+		return buildArray( td );
+	case TypeData::Reference:
+		return buildReference( td );
+	case TypeData::Function:
+		return buildFunctionType( td );
+	case TypeData::AggregateInst:
+		return buildAggInst( td );
+	case TypeData::EnumConstant:
+		return new ast::EnumInstType( "", buildQualifiers( td ) );
+	case TypeData::SymbolicInst:
+		return buildSymbolicInst( td );
+	case TypeData::Tuple:
+		return buildTuple( td );
+	case TypeData::Typeof:
+	case TypeData::Basetypeof:
+		return buildTypeof( td );
+	case TypeData::Vtable:
+		return buildVtable( td );
+	case TypeData::Builtin:
+		switch ( td->builtintype ) {
+		case TypeData::Zero:
+			return new ast::ZeroType();
+		case TypeData::One:
+			return new ast::OneType();
+		default:
+			return new ast::VarArgsType( buildQualifiers( td ) );
+		} // switch
+	case TypeData::GlobalScope:
+		return new ast::GlobalScopeType();
+	case TypeData::Qualified:
+		return new ast::QualifiedType(
+			typebuild( td->qualified.parent ),
+			typebuild( td->qualified.child ),
+			buildQualifiers( td )
+		);
+	case TypeData::Symbolic:
+	case TypeData::Aggregate:
+		assert( false );
+	} // switch
+
+	return nullptr;
+} // typebuild
+
+
+TypeData * typeextractAggregate( const TypeData * td, bool toplevel ) {
+	TypeData * ret = nullptr;
+
+	switch ( td->kind ) {
+	case TypeData::Aggregate:
+		if ( ! toplevel && td->aggregate.body ) {
+			ret = td->clone();
+		} // if
+		break;
+	case TypeData::AggregateInst:
+		if ( td->aggInst.aggregate ) {
+			ret = typeextractAggregate( td->aggInst.aggregate, false );
+		} // if
+		break;
+	default:
+		if ( td->base ) {
+			ret = typeextractAggregate( td->base, false );
+		} // if
+	} // switch
+	return ret;
+} // typeextractAggregate
+
+
+ast::CV::Qualifiers buildQualifiers( const TypeData * td ) {
+	return td->qualifiers;
+} // buildQualifiers
+
+
+static string genTSError( string msg, TypeData::BasicType basictype ) {
+	SemanticError( yylloc, "invalid type specifier \"%s\" for type \"%s\".", msg.c_str(), TypeData::basicTypeNames[basictype] );
+} // genTSError
+
+ast::Type * buildBasicType( const TypeData * td ) {
+	ast::BasicKind ret;
+
+	switch ( td->basictype ) {
+	case TypeData::Void:
+		if ( td->signedness != TypeData::NoSignedness ) {
+			genTSError( TypeData::signednessNames[ td->signedness ], td->basictype );
+		} // if
+		if ( td->length != TypeData::NoLength ) {
+			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
+		} // if
+		return new ast::VoidType( buildQualifiers( td ) );
+		break;
+
+	case TypeData::Bool:
+		if ( td->signedness != TypeData::NoSignedness ) {
+			genTSError( TypeData::signednessNames[ td->signedness ], td->basictype );
+		} // if
+		if ( td->length != TypeData::NoLength ) {
+			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
+		} // if
+
+		ret = ast::BasicKind::Bool;
+		break;
+
+	case TypeData::Char:
+		// C11 Standard 6.2.5.15: The three types char, signed char, and unsigned char are collectively called the
+		// character types. The implementation shall define char to have the same range, representation, and behavior as
+		// either signed char or unsigned char.
+		static ast::BasicKind chartype[] = { ast::BasicKind::SignedChar, ast::BasicKind::UnsignedChar, ast::BasicKind::Char };
+
+		if ( td->length != TypeData::NoLength ) {
+			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
+		} // if
+
+		ret = chartype[ td->signedness ];
+		break;
+
+	case TypeData::Int:
+		static ast::BasicKind inttype[2][4] = {
+			{ ast::BasicKind::ShortSignedInt, ast::BasicKind::LongSignedInt, ast::BasicKind::LongLongSignedInt, ast::BasicKind::SignedInt },
+			{ ast::BasicKind::ShortUnsignedInt, ast::BasicKind::LongUnsignedInt, ast::BasicKind::LongLongUnsignedInt, ast::BasicKind::UnsignedInt },
+		};
+
+	Integral: ;
+		if ( td->signedness == TypeData::NoSignedness ) {
+			const_cast<TypeData *>(td)->signedness = TypeData::Signed;
+		} // if
+		ret = inttype[ td->signedness ][ td->length ];
+		break;
+
+	case TypeData::Int128:
+		ret = td->signedness == TypeData::Unsigned ? ast::BasicKind::UnsignedInt128 : ast::BasicKind::SignedInt128;
+		if ( td->length != TypeData::NoLength ) {
+			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
+		} // if
+		break;
+
+	case TypeData::Float:
+	case TypeData::Double:
+	case TypeData::LongDouble:					// not set until below
+	case TypeData::uuFloat80:
+	case TypeData::uuFloat128:
+	case TypeData::uFloat16:
+	case TypeData::uFloat32:
+	case TypeData::uFloat32x:
+	case TypeData::uFloat64:
+	case TypeData::uFloat64x:
+	case TypeData::uFloat128:
+	case TypeData::uFloat128x:
+		static ast::BasicKind floattype[2][12] = {
+			{ ast::BasicKind::FloatComplex, ast::BasicKind::DoubleComplex, ast::BasicKind::LongDoubleComplex, (ast::BasicKind)-1, (ast::BasicKind)-1, ast::BasicKind::uFloat16Complex, ast::BasicKind::uFloat32Complex, ast::BasicKind::uFloat32xComplex, ast::BasicKind::uFloat64Complex, ast::BasicKind::uFloat64xComplex, ast::BasicKind::uFloat128Complex, ast::BasicKind::uFloat128xComplex, },
+			{ ast::BasicKind::Float, ast::BasicKind::Double, ast::BasicKind::LongDouble, ast::BasicKind::uuFloat80, ast::BasicKind::uuFloat128, ast::BasicKind::uFloat16, ast::BasicKind::uFloat32, ast::BasicKind::uFloat32x, ast::BasicKind::uFloat64, ast::BasicKind::uFloat64x, ast::BasicKind::uFloat128, ast::BasicKind::uFloat128x, },
+		};
+
+	FloatingPoint: ;
+		if ( td->signedness != TypeData::NoSignedness ) {
+			genTSError( TypeData::signednessNames[ td->signedness ], td->basictype );
+		} // if
+		if ( td->length == TypeData::Short || td->length == TypeData::LongLong ) {
+			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
+		} // if
+		if ( td->basictype != TypeData::Double && td->length == TypeData::Long ) {
+			genTSError( TypeData::lengthNames[ td->length ], td->basictype );
+		} // if
+		if ( td->complextype == TypeData::Imaginary ) {
+			genTSError( TypeData::complexTypeNames[ td->complextype ], td->basictype );
+		} // if
+		if ( (td->basictype == TypeData::uuFloat80 || td->basictype == TypeData::uuFloat128) && td->complextype == TypeData::Complex ) { // gcc unsupported
+			genTSError( TypeData::complexTypeNames[ td->complextype ], td->basictype );
+		} // if
+		if ( td->length == TypeData::Long ) {
+			const_cast<TypeData *>(td)->basictype = TypeData::LongDouble;
+		} // if
+
+		ret = floattype[ td->complextype ][ td->basictype - TypeData::Float ];
+		//printf( "XXXX %d %d %d %d\n", td->complextype, td->basictype, TypeData::Float, ret );
+		break;
+
+	case TypeData::NoBasicType:
+		// No basic type in declaration => default double for Complex/Imaginary and int type for integral types
+		if ( td->complextype == TypeData::Complex || td->complextype == TypeData::Imaginary ) {
+			const_cast<TypeData *>(td)->basictype = TypeData::Double;
+			goto FloatingPoint;
+		} // if
+
+		const_cast<TypeData *>(td)->basictype = TypeData::Int;
+		goto Integral;
+	default:
+		assertf( false, "unknown basic type" );
+		return nullptr;
+	} // switch
+
+	ast::BasicType * bt = new ast::BasicType( ret, buildQualifiers( td ) );
+	return bt;
+} // buildBasicType
+
+
+static ast::Type * buildDefaultType( const TypeData * td ) {
+	return ( td ) ? typebuild( td ) : new ast::BasicType( ast::BasicKind::SignedInt );
+} // buildDefaultType
+
+
+ast::PointerType * buildPointer( const TypeData * td ) {
+	return new ast::PointerType(
+		buildDefaultType( td->base ),
+		buildQualifiers( td )
+	);
+} // buildPointer
+
+
+ast::ArrayType * buildArray( const TypeData * td ) {
+	return new ast::ArrayType(
+		buildDefaultType( td->base ),
+		maybeBuild( td->array.dimension ),
+		td->array.isVarLen ? ast::VariableLen : ast::FixedLen,
+		td->array.isStatic ? ast::StaticDim : ast::DynamicDim,
+		buildQualifiers( td )
+	);
+} // buildArray
+
+
+ast::ReferenceType * buildReference( const TypeData * td ) {
+	return new ast::ReferenceType(
+		buildDefaultType( td->base ),
+		buildQualifiers( td )
+	);
+} // buildReference
+
+
+ast::AggregateDecl * buildAggregate( const TypeData * td, std::vector<ast::ptr<ast::Attribute>> attributes, ast::Linkage::Spec linkage ) {
+	assert( td->kind == TypeData::Aggregate );
+	ast::AggregateDecl * at;
+	switch ( td->aggregate.kind ) {
+	case ast::AggregateDecl::Struct:
+	case ast::AggregateDecl::Coroutine:
+	case ast::AggregateDecl::Exception:
+	case ast::AggregateDecl::Generator:
+	case ast::AggregateDecl::Monitor:
+	case ast::AggregateDecl::Thread:
+		at = new ast::StructDecl( td->location,
+			*td->aggregate.name,
+			td->aggregate.kind,
+			std::move( attributes ),
+			linkage
+		);
+		buildForall( td->aggregate.params, at->params );
+		break;
+	case ast::AggregateDecl::Union:
+		at = new ast::UnionDecl( td->location,
+			*td->aggregate.name,
+			std::move( attributes ),
+			linkage
+		);
+		buildForall( td->aggregate.params, at->params );
+		break;
+	case ast::AggregateDecl::Enum:
+		return buildEnum( td, std::move( attributes ), linkage );
+	case ast::AggregateDecl::Trait:
+		at = new ast::TraitDecl( td->location,
+			*td->aggregate.name,
+			std::move( attributes ),
+			linkage
+		);
+		buildList( td->aggregate.params, at->params );
+		break;
+	default:
+		assert( false );
+	} // switch
+
+	buildList( td->aggregate.fields, at->members );
+	at->set_body( td->aggregate.body );
+
+	return at;
+} // buildAggregate
+
+
+ast::BaseInstType * buildComAggInst(
+		const TypeData * td,
+		std::vector<ast::ptr<ast::Attribute>> && attributes,
+		ast::Linkage::Spec linkage ) {
+	switch ( td->kind ) {
+	case TypeData::Aggregate:
+		if ( td->aggregate.body ) {
+			ast::AggregateDecl * typedecl =
+				buildAggregate( td, std::move( attributes ), linkage );
+			switch ( td->aggregate.kind ) {
+			case ast::AggregateDecl::Struct:
+			case ast::AggregateDecl::Coroutine:
+			case ast::AggregateDecl::Monitor:
+			case ast::AggregateDecl::Thread:
+				return new ast::StructInstType(
+					strict_dynamic_cast<ast::StructDecl *>( typedecl ),
+					buildQualifiers( td )
+				);
+			case ast::AggregateDecl::Union:
+				return new ast::UnionInstType(
+					strict_dynamic_cast<ast::UnionDecl *>( typedecl ),
+					buildQualifiers( td )
+				);
+			case ast::AggregateDecl::Enum:
+				return new ast::EnumInstType(
+					strict_dynamic_cast<ast::EnumDecl *>( typedecl ),
+					buildQualifiers( td )
+				);
+			case ast::AggregateDecl::Trait:
+				assert( false );
+				break;
+			default:
+				assert( false );
+			} // switch
+		} else {
+			switch ( td->aggregate.kind ) {
+			case ast::AggregateDecl::Struct:
+			case ast::AggregateDecl::Coroutine:
+			case ast::AggregateDecl::Monitor:
+			case ast::AggregateDecl::Thread:
+				return new ast::StructInstType(
+					*td->aggregate.name,
+					buildQualifiers( td )
+				);
+			case ast::AggregateDecl::Union:
+				return new ast::UnionInstType(
+					*td->aggregate.name,
+					buildQualifiers( td )
+				);
+			case ast::AggregateDecl::Enum:
+				return new ast::EnumInstType(
+					*td->aggregate.name,
+					buildQualifiers( td )
+				);
+			case ast::AggregateDecl::Trait:
+				return new ast::TraitInstType(
+					*td->aggregate.name,
+					buildQualifiers( td )
+				);
+			default:
+				assert( false );
+			} // switch
+			break;
+		} // if
+		break;
+	default:
+		assert( false );
+	} // switch
+	assert( false );
+} // buildAggInst
+
+
+ast::BaseInstType * buildAggInst( const TypeData * td ) {
+	assert( td->kind == TypeData::AggregateInst );
+
+	ast::BaseInstType * ret = nullptr;
+	TypeData * type = td->aggInst.aggregate;
+	switch ( type->kind ) {
+	case TypeData::Aggregate:
+		switch ( type->aggregate.kind ) {
+		case ast::AggregateDecl::Struct:
+		case ast::AggregateDecl::Coroutine:
+		case ast::AggregateDecl::Monitor:
+		case ast::AggregateDecl::Thread:
+			ret = new ast::StructInstType(
+				*type->aggregate.name,
+				buildQualifiers( type )
+			);
+			break;
+		case ast::AggregateDecl::Union:
+			ret = new ast::UnionInstType(
+				*type->aggregate.name,
+				buildQualifiers( type )
+			);
+			break;
+		case ast::AggregateDecl::Enum:
+			ret = new ast::EnumInstType(
+				*type->aggregate.name,
+				buildQualifiers( type )
+			);
+			break;
+		case ast::AggregateDecl::Trait:
+			ret = new ast::TraitInstType(
+				*type->aggregate.name,
+				buildQualifiers( type )
+			);
+			break;
+		default:
+			assert( false );
+		} // switch
+		break;
+	default:
+		assert( false );
+	} // switch
+
+	ret->hoistType = td->aggInst.hoistType;
+	buildList( td->aggInst.params, ret->params );
+	return ret;
+} // buildAggInst
+
+
+ast::NamedTypeDecl * buildSymbolic(
+		const TypeData * td,
+		std::vector<ast::ptr<ast::Attribute>> attributes,
+		const std::string & name,
+		ast::Storage::Classes scs,
+		ast::Linkage::Spec linkage ) {
+	assert( td->kind == TypeData::Symbolic );
+	ast::NamedTypeDecl * ret;
+	assert( td->base );
+	if ( td->symbolic.isTypedef ) {
+		ret = new ast::TypedefDecl(
+			td->location,
+			name,
+			scs,
+			typebuild( td->base ),
+			linkage
+		);
+	} else {
+		ret = new ast::TypeDecl(
+			td->location,
+			name,
+			scs,
+			typebuild( td->base ),
+			ast::TypeDecl::Dtype,
+			true
+		);
+	} // if
+	buildList( td->symbolic.assertions, ret->assertions );
+	splice( ret->base.get_and_mutate()->attributes, attributes );
+	return ret;
+} // buildSymbolic
+
+
+ast::EnumDecl * buildEnum(
+		const TypeData * td,
+		std::vector<ast::ptr<ast::Attribute>> && attributes,
+		ast::Linkage::Spec linkage ) {
+	assert( td->kind == TypeData::Aggregate );
+	assert( td->aggregate.kind == ast::AggregateDecl::Enum );
+	ast::Type * baseType = td->base ? typebuild(td->base) : nullptr;
+	ast::EnumDecl * ret = new ast::EnumDecl(
+		td->location,
+		*td->aggregate.name,
+		td->aggregate.typed,
+		std::move( attributes ),
+		linkage,
+		baseType
+	);
+	buildList( td->aggregate.fields, ret->members );
+	auto members = ret->members.begin();
+	ret->hide = td->aggregate.hiding == EnumHiding::Hide ? ast::EnumDecl::EnumHiding::Hide : ast::EnumDecl::EnumHiding::Visible;
+	for ( const DeclarationNode * cur = td->aggregate.fields ; cur != nullptr ; cur = cur->next, ++members ) {
+		if ( cur->enumInLine ) {
+			// Do Nothing
+		} else if ( ret->isTyped && !ret->base && cur->has_enumeratorValue() ) {
+			SemanticError( td->location, "Enumerator of enum(void) cannot have an explicit initializer value." );
+		} else if ( cur->has_enumeratorValue() ) {
+			ast::Decl * member = members->get_and_mutate();
+			ast::ObjectDecl * object = strict_dynamic_cast<ast::ObjectDecl *>( member );
+			object->init = new ast::SingleInit(
+				td->location,
+				maybeMoveBuild( cur->consume_enumeratorValue() ),
+				ast::NoConstruct
+			);
+		} else if ( !cur->initializer ) {
+			if ( baseType && (!dynamic_cast<ast::BasicType *>(baseType) || !dynamic_cast<ast::BasicType *>(baseType)->isInteger())) {
+				SemanticError( td->location, "Enumerators of an non-integer typed enum must be explicitly initialized." );
+			}
+		}
+		// else cur is a List Initializer and has been set as init in buildList()
+		// if
+	} // for
+	ret->body = td->aggregate.body;
+	return ret;
+} // buildEnum
+
+
+ast::TypeInstType * buildSymbolicInst( const TypeData * td ) {
+	assert( td->kind == TypeData::SymbolicInst );
+	ast::TypeInstType * ret = new ast::TypeInstType(
+		*td->symbolic.name,
+		ast::TypeDecl::Dtype,
+		buildQualifiers( td )
+	);
+	buildList( td->symbolic.actuals, ret->params );
+	return ret;
+} // buildSymbolicInst
+
+
+ast::TupleType * buildTuple( const TypeData * td ) {
+	assert( td->kind == TypeData::Tuple );
+	std::vector<ast::ptr<ast::Type>> types;
+	buildTypeList( td->tuple, types );
+	ast::TupleType * ret = new ast::TupleType(
+		std::move( types ),
+		buildQualifiers( td )
+	);
+	return ret;
+} // buildTuple
+
+
+ast::TypeofType * buildTypeof( const TypeData * td ) {
+	assert( td->kind == TypeData::Typeof || td->kind == TypeData::Basetypeof );
+	assert( td->typeexpr );
+	return new ast::TypeofType(
+		td->typeexpr->build(),
+		td->kind == TypeData::Typeof
+			? ast::TypeofType::Typeof : ast::TypeofType::Basetypeof,
+		buildQualifiers( td )
+	);
+} // buildTypeof
+
+
+ast::VTableType * buildVtable( const TypeData * td ) {
+	assert( td->base );
+	return new ast::VTableType(
+		typebuild( td->base ),
+		buildQualifiers( td )
+	);
+} // buildVtable
+
+
+ast::FunctionDecl * buildFunctionDecl(
+		const TypeData * td,
+		const string &name,
+		ast::Storage::Classes scs,
+		ast::Function::Specs funcSpec,
+		ast::Linkage::Spec linkage,
+		ast::Expr * asmName,
+		std::vector<ast::ptr<ast::Attribute>> && attributes ) {
+	assert( td->kind == TypeData::Function );
+	// For some reason FunctionDecl takes a bool instead of an ArgumentFlag.
+	bool isVarArgs = !td->function.params || td->function.params->hasEllipsis;
+	ast::CV::Qualifiers cvq = buildQualifiers( td );
+	std::vector<ast::ptr<ast::TypeDecl>> forall;
+	std::vector<ast::ptr<ast::DeclWithType>> assertions;
+	std::vector<ast::ptr<ast::DeclWithType>> params;
+	std::vector<ast::ptr<ast::DeclWithType>> returns;
+	buildList( td->function.params, params );
+	buildForall( td->forall, forall );
+	// Functions do not store their assertions there anymore.
+	for ( ast::ptr<ast::TypeDecl> & type_param : forall ) {
+		auto mut = type_param.get_and_mutate();
+		splice( assertions, mut->assertions );
+	}
+	if ( td->base ) {
+		switch ( td->base->kind ) {
+		case TypeData::Tuple:
+			buildList( td->base->tuple, returns );
+			break;
+		default:
+			returns.push_back( dynamic_cast<ast::DeclWithType *>(
+				buildDecl(
+					td->base,
+					"",
+					ast::Storage::Classes(),
+					(ast::Expr *)nullptr, // bitfieldWidth
+					ast::Function::Specs(),
+					ast::Linkage::Cforall,
+					(ast::Expr *)nullptr // asmName
+				)
+			) );
+		} // switch
+	} else {
+		returns.push_back( new ast::ObjectDecl(
+			td->location,
+			"",
+			new ast::BasicType( ast::BasicKind::SignedInt ),
+			(ast::Init *)nullptr,
+			ast::Storage::Classes(),
+			ast::Linkage::Cforall
+		) );
+	} // if
+	ast::Stmt * stmt = maybeBuild( td->function.body );
+	ast::CompoundStmt * body = dynamic_cast<ast::CompoundStmt *>( stmt );
+	ast::FunctionDecl * decl = new ast::FunctionDecl( td->location,
+		name,
+		std::move( forall ),
+		std::move( assertions ),
+		std::move( params ),
+		std::move( returns ),
+		body,
+		scs,
+		linkage,
+		std::move( attributes ),
+		funcSpec,
+		(isVarArgs) ? ast::VariableArgs : ast::FixedArgs
+	);
+	buildList( td->function.withExprs, decl->withExprs );
+	decl->asmName = asmName;
+	// This may be redundant on a declaration.
+	decl->type.get_and_mutate()->qualifiers = cvq;
+	return decl;
+} // buildFunctionDecl
+
+
+ast::Decl * buildDecl(
+		const TypeData * td,
+		const string &name,
+		ast::Storage::Classes scs,
+		ast::Expr * bitfieldWidth,
+		ast::Function::Specs funcSpec,
+		ast::Linkage::Spec linkage,
+		ast::Expr * asmName,
+		ast::Init * init,
+		std::vector<ast::ptr<ast::Attribute>> && attributes ) {
+	if ( td->kind == TypeData::Function ) {
+		if ( td->function.idList ) {					// KR function ?
+			buildKRFunction( td->function );			// transform into C11 function
+		} // if
+
+		return buildFunctionDecl(
+			td, name, scs, funcSpec, linkage,
+			asmName, std::move( attributes ) );
+	} else if ( td->kind == TypeData::Aggregate ) {
+		return buildAggregate( td, std::move( attributes ), linkage );
+	} else if ( td->kind == TypeData::Symbolic ) {
+		return buildSymbolic( td, std::move( attributes ), name, scs, linkage );
+	} else {
+		auto ret = new ast::ObjectDecl( td->location,
+			name,
+			typebuild( td ),
+			init,
+			scs,
+			linkage,
+			bitfieldWidth,
+			std::move( attributes )
+		);
+		ret->asmName = asmName;
+		return ret;
+	} // if
+	return nullptr;
+} // buildDecl
+
+
+ast::FunctionType * buildFunctionType( const TypeData * td ) {
+	assert( td->kind == TypeData::Function );
+	ast::FunctionType * ft = new ast::FunctionType(
+		( !td->function.params || td->function.params->hasEllipsis )
+			? ast::VariableArgs : ast::FixedArgs,
+		buildQualifiers( td )
+	);
+	buildTypeList( td->function.params, ft->params );
+	buildForall( td->forall, ft->forall );
+	if ( td->base ) {
+		switch ( td->base->kind ) {
+		case TypeData::Tuple:
+			buildTypeList( td->base->tuple, ft->returns );
+			break;
+		default:
+			ft->returns.push_back( typebuild( td->base ) );
+			break;
+		} // switch
+	} else {
+		ft->returns.push_back(
+			new ast::BasicType( ast::BasicKind::SignedInt ) );
+	} // if
+	return ft;
+} // buildFunctionType
+
+
+// Transform KR routine declarations into C99 routine declarations:
+//
+//    rtn( a, b, c ) int a, c; double b {}  =>  int rtn( int a, double c, int b ) {}
+//
+// The type information for each post-declaration is moved to the corresponding pre-parameter and the post-declaration
+// is deleted. Note, the order of the parameter names may not be the same as the declaration names. Duplicate names and
+// extra names are disallowed.
+//
+// Note, there is no KR routine-prototype syntax:
+//
+//    rtn( a, b, c ) int a, c; double b; // invalid KR prototype
+//    rtn(); // valid KR prototype
+
+void buildKRFunction( const TypeData::Function_t & function ) {
+	assert( ! function.params );
+	// loop over declaration first as it is easier to spot errors
+	for ( DeclarationNode * decl = function.oldDeclList; decl != nullptr; decl = decl->next ) {
+		// scan ALL parameter names for each declaration name to check for duplicates
+		for ( DeclarationNode * param = function.idList; param != nullptr; param = param->next ) {
+			if ( *decl->name == *param->name ) {
+				// type set => parameter name already transformed by a declaration names so there is a duplicate
+				// declaration name attempting a second transformation
+				if ( param->type ) SemanticError( param->location, "duplicate declaration name \"%s\".", param->name->c_str() );
+				// declaration type reset => declaration already transformed by a parameter name so there is a duplicate
+				// parameter name attempting a second transformation
+				if ( ! decl->type ) SemanticError( param->location, "duplicate parameter name \"%s\".", param->name->c_str() );
+				param->type = decl->type;				// set copy declaration type to parameter type
+				decl->type = nullptr;					// reset declaration type
+				// Copy and reset attributes from declaration to parameter:
+				splice( param->attributes, decl->attributes );
+			} // if
+		} // for
+		// declaration type still set => type not moved to a matching parameter so there is a missing parameter name
+		if ( decl->type ) SemanticError( decl->location, "missing name in parameter list %s", decl->name->c_str() );
+	} // for
+
+	// Parameter names without a declaration default to type int:
+	//
+	//    rtb( a, b, c ) const char * b; {} => int rtn( int a, const char * b, int c ) {}
+
+	for ( DeclarationNode * param = function.idList; param != nullptr; param = param->next ) {
+		if ( ! param->type ) {							// generate type int for empty parameter type
+			param->type = new TypeData( TypeData::Basic );
+			param->type->basictype = TypeData::Int;
+		} // if
+	} // for
+
+	function.params = function.idList;					// newly modified idList becomes parameters
+	function.idList = nullptr;							// idList now empty
+	delete function.oldDeclList;						// deletes entire list
+	function.oldDeclList = nullptr;						// reset
+} // buildKRFunction
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/TypeData.h
===================================================================
--- src/Parser/TypeData.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,187 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// TypeData.h --
-//
-// Author           : Peter A. Buhr
-// Created On       : Sat May 16 15:18:36 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Thu Feb 22 16:30:31 2024
-// Update Count     : 210
-//
-
-#pragma once
-
-#include <iosfwd>                                   // for ostream
-#include <list>                                     // for list
-#include <string>                                   // for string
-
-#include "AST/CVQualifiers.hpp"                     // for CV
-#include "AST/Fwd.hpp"                              // for Type
-#include "DeclarationNode.h"                        // for DeclarationNode
-
-struct TypeData {
-	// Type flags used in this type, and there names (harmonize with implementation).
-	enum BasicType {
-		Void, Bool, Char, Int, Int128,
-		Float, Double, LongDouble, uuFloat80, uuFloat128,
-		uFloat16, uFloat32, uFloat32x, uFloat64, uFloat64x, uFloat128, uFloat128x,
-		NoBasicType
-	};
-	static const char * basicTypeNames[];
-	enum ComplexType { Complex, NoComplexType, Imaginary };
-	// Imaginary unsupported => parse, but make invisible and print error message
-	static const char * complexTypeNames[];
-	enum Signedness { Signed, Unsigned, NoSignedness };
-	static const char * signednessNames[];
-	enum Length { Short, Long, LongLong, NoLength };
-	static const char * lengthNames[];
-	enum BuiltinType { Valist, AutoType, Zero, One, NoBuiltinType };
-	static const char * builtinTypeNames[];
-
-	enum Kind { Basic, Pointer, Reference, Array, Function, Aggregate, AggregateInst, EnumConstant, Symbolic,
-				SymbolicInst, Tuple, Basetypeof, Typeof, Vtable, Builtin, GlobalScope, Qualified, Unknown };
-
-	struct Aggregate_t {
-		ast::AggregateDecl::Aggregate kind;
-		const std::string * name = nullptr;
-		// Polymorphics parameters. (Polymorphic types only.)
-		DeclarationNode * params = nullptr;
-		// Arguments later applied to AggInst. (Polymorphic types only.)
-		ExpressionNode * actuals = nullptr;
-		// Only set if body is true. (Constants for enumerations.)
-		DeclarationNode * fields = nullptr;
-		std::vector<ast::ptr<ast::Attribute>> attributes;
-		// Is this a declaration with a body (may have fields)?
-		bool body;
-		// Is this type anonymous? (Name can still be set to generated name.)
-		bool anon;
-		// Is this a typed enumeration? Type may be stored in base.
-		bool typed;
-		EnumHiding hiding;
-	};
-
-	struct AggInst_t {
-		TypeData * aggregate = nullptr;
-		ExpressionNode * params = nullptr;
-		bool hoistType;
-	};
-
-	struct Array_t {
-		ExpressionNode * dimension = nullptr;
-		bool isVarLen;
-		bool isStatic;
-	};
-
-	struct Function_t {
-		mutable DeclarationNode * params = nullptr;		// mutables modified in buildKRFunction
-		mutable DeclarationNode * idList = nullptr;		// old-style
-		mutable DeclarationNode * oldDeclList = nullptr;
-		StatementNode * body = nullptr;
-		ExpressionNode * withExprs = nullptr;			// expressions from function's with_clause
-	};
-
-	struct Symbolic_t {
-		const std::string * name = nullptr;
-		bool isTypedef;									// false => TYPEGENname, true => TYPEDEFname
-		DeclarationNode * params = nullptr;
-		ExpressionNode * actuals = nullptr;
-		DeclarationNode * assertions = nullptr;
-	};
-
-	struct Qualified_t {								// qualified type S.T
-		TypeData * parent = nullptr;
-		TypeData * child = nullptr;
-	};
-
-	CodeLocation location;
-
-	Kind kind;
-	TypeData * base;
-	BasicType basictype = NoBasicType;
-	ComplexType complextype = NoComplexType;
-	Signedness signedness = NoSignedness;
-	Length length = NoLength;
-	BuiltinType builtintype = NoBuiltinType;
-
-	ast::CV::Qualifiers qualifiers;
-	DeclarationNode * forall = nullptr;
-
-	Aggregate_t aggregate;
-	AggInst_t aggInst;
-	Array_t array;
-	Function_t function;
-	Symbolic_t symbolic;
-	Qualified_t qualified;
-	DeclarationNode * tuple = nullptr;
-	ExpressionNode * typeexpr = nullptr;
-
-	TypeData( Kind k = Unknown );
-	~TypeData();
-	void print( std::ostream &, int indent = 0 ) const;
-	TypeData * clone() const;
-
-	const std::string * leafName() const;
-
-	TypeData * getLastBase();
-	void setLastBase( TypeData * );
-};
-
-
-TypeData * build_type_qualifier( ast::CV::Qualifiers );
-TypeData * build_basic_type( TypeData::BasicType );
-TypeData * build_complex_type( TypeData::ComplexType );
-TypeData * build_signedness( TypeData::Signedness );
-TypeData * build_builtin_type( TypeData::BuiltinType );
-TypeData * build_length( TypeData::Length );
-TypeData * build_forall( DeclarationNode * );
-TypeData * build_global_scope();
-TypeData * build_qualified_type( TypeData *, TypeData * );
-TypeData * build_typedef( const std::string * name );
-TypeData * build_type_gen( const std::string * name, ExpressionNode * params );
-TypeData * build_vtable_type( TypeData * );
-
-TypeData * addQualifiers( TypeData * ltype, TypeData * rtype );
-TypeData * addType( TypeData * ltype, TypeData * rtype, std::vector<ast::ptr<ast::Attribute>> & );
-TypeData * addType( TypeData * ltype, TypeData * rtype );
-TypeData * cloneBaseType( TypeData * type, TypeData * other );
-TypeData * makeNewBase( TypeData * type );
-
-
-ast::Type * typebuild( const TypeData * );
-TypeData * typeextractAggregate( const TypeData * td, bool toplevel = true );
-ast::CV::Qualifiers buildQualifiers( const TypeData * td );
-ast::Type * buildBasicType( const TypeData * );
-ast::PointerType * buildPointer( const TypeData * );
-ast::ArrayType * buildArray( const TypeData * );
-ast::ReferenceType * buildReference( const TypeData * );
-ast::AggregateDecl * buildAggregate( const TypeData *, std::vector<ast::ptr<ast::Attribute>> );
-ast::BaseInstType * buildComAggInst( const TypeData *, std::vector<ast::ptr<ast::Attribute>> && attributes, ast::Linkage::Spec linkage );
-ast::BaseInstType * buildAggInst( const TypeData * );
-ast::TypeDecl * buildVariable( const TypeData * );
-ast::EnumDecl * buildEnum( const TypeData *, std::vector<ast::ptr<ast::Attribute>> &&, ast::Linkage::Spec );
-ast::TypeInstType * buildSymbolicInst( const TypeData * );
-ast::TupleType * buildTuple( const TypeData * );
-ast::TypeofType * buildTypeof( const TypeData * );
-ast::VTableType * buildVtable( const TypeData * );
-ast::Decl * buildDecl(
-	const TypeData *, const std::string &, ast::Storage::Classes, ast::Expr *,
-	ast::Function::Specs funcSpec, ast::Linkage::Spec, ast::Expr * asmName,
-	ast::Init * init = nullptr, std::vector<ast::ptr<ast::Attribute>> && attributes = std::vector<ast::ptr<ast::Attribute>>() );
-ast::FunctionType * buildFunctionType( const TypeData * );
-void buildKRFunction( const TypeData::Function_t & function );
-
-static inline ast::Type * maybeMoveBuildType( TypeData * type ) {
-	ast::Type * ret = type ? typebuild( type ) : nullptr;
-	delete type;
-	return ret;
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/TypeData.hpp
===================================================================
--- src/Parser/TypeData.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/TypeData.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,187 @@
+//
+// 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.
+//
+// TypeData.hpp --
+//
+// Author           : Peter A. Buhr
+// Created On       : Sat May 16 15:18:36 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Thu Feb 22 16:30:31 2024
+// Update Count     : 210
+//
+
+#pragma once
+
+#include <iosfwd>                                   // for ostream
+#include <list>                                     // for list
+#include <string>                                   // for string
+
+#include "AST/CVQualifiers.hpp"                     // for CV
+#include "AST/Fwd.hpp"                              // for Type
+#include "DeclarationNode.hpp"                      // for DeclarationNode
+
+struct TypeData {
+	// Type flags used in this type, and there names (harmonize with implementation).
+	enum BasicType {
+		Void, Bool, Char, Int, Int128,
+		Float, Double, LongDouble, uuFloat80, uuFloat128,
+		uFloat16, uFloat32, uFloat32x, uFloat64, uFloat64x, uFloat128, uFloat128x,
+		NoBasicType
+	};
+	static const char * basicTypeNames[];
+	enum ComplexType { Complex, NoComplexType, Imaginary };
+	// Imaginary unsupported => parse, but make invisible and print error message
+	static const char * complexTypeNames[];
+	enum Signedness { Signed, Unsigned, NoSignedness };
+	static const char * signednessNames[];
+	enum Length { Short, Long, LongLong, NoLength };
+	static const char * lengthNames[];
+	enum BuiltinType { Valist, AutoType, Zero, One, NoBuiltinType };
+	static const char * builtinTypeNames[];
+
+	enum Kind { Basic, Pointer, Reference, Array, Function, Aggregate, AggregateInst, EnumConstant, Symbolic,
+				SymbolicInst, Tuple, Basetypeof, Typeof, Vtable, Builtin, GlobalScope, Qualified, Unknown };
+
+	struct Aggregate_t {
+		ast::AggregateDecl::Aggregate kind;
+		const std::string * name = nullptr;
+		// Polymorphics parameters. (Polymorphic types only.)
+		DeclarationNode * params = nullptr;
+		// Arguments later applied to AggInst. (Polymorphic types only.)
+		ExpressionNode * actuals = nullptr;
+		// Only set if body is true. (Constants for enumerations.)
+		DeclarationNode * fields = nullptr;
+		std::vector<ast::ptr<ast::Attribute>> attributes;
+		// Is this a declaration with a body (may have fields)?
+		bool body;
+		// Is this type anonymous? (Name can still be set to generated name.)
+		bool anon;
+		// Is this a typed enumeration? Type may be stored in base.
+		bool typed;
+		EnumHiding hiding;
+	};
+
+	struct AggInst_t {
+		TypeData * aggregate = nullptr;
+		ExpressionNode * params = nullptr;
+		bool hoistType;
+	};
+
+	struct Array_t {
+		ExpressionNode * dimension = nullptr;
+		bool isVarLen;
+		bool isStatic;
+	};
+
+	struct Function_t {
+		mutable DeclarationNode * params = nullptr;		// mutables modified in buildKRFunction
+		mutable DeclarationNode * idList = nullptr;		// old-style
+		mutable DeclarationNode * oldDeclList = nullptr;
+		StatementNode * body = nullptr;
+		ExpressionNode * withExprs = nullptr;			// expressions from function's with_clause
+	};
+
+	struct Symbolic_t {
+		const std::string * name = nullptr;
+		bool isTypedef;									// false => TYPEGENname, true => TYPEDEFname
+		DeclarationNode * params = nullptr;
+		ExpressionNode * actuals = nullptr;
+		DeclarationNode * assertions = nullptr;
+	};
+
+	struct Qualified_t {								// qualified type S.T
+		TypeData * parent = nullptr;
+		TypeData * child = nullptr;
+	};
+
+	CodeLocation location;
+
+	Kind kind;
+	TypeData * base;
+	BasicType basictype = NoBasicType;
+	ComplexType complextype = NoComplexType;
+	Signedness signedness = NoSignedness;
+	Length length = NoLength;
+	BuiltinType builtintype = NoBuiltinType;
+
+	ast::CV::Qualifiers qualifiers;
+	DeclarationNode * forall = nullptr;
+
+	Aggregate_t aggregate;
+	AggInst_t aggInst;
+	Array_t array;
+	Function_t function;
+	Symbolic_t symbolic;
+	Qualified_t qualified;
+	DeclarationNode * tuple = nullptr;
+	ExpressionNode * typeexpr = nullptr;
+
+	TypeData( Kind k = Unknown );
+	~TypeData();
+	void print( std::ostream &, int indent = 0 ) const;
+	TypeData * clone() const;
+
+	const std::string * leafName() const;
+
+	TypeData * getLastBase();
+	void setLastBase( TypeData * );
+};
+
+
+TypeData * build_type_qualifier( ast::CV::Qualifiers );
+TypeData * build_basic_type( TypeData::BasicType );
+TypeData * build_complex_type( TypeData::ComplexType );
+TypeData * build_signedness( TypeData::Signedness );
+TypeData * build_builtin_type( TypeData::BuiltinType );
+TypeData * build_length( TypeData::Length );
+TypeData * build_forall( DeclarationNode * );
+TypeData * build_global_scope();
+TypeData * build_qualified_type( TypeData *, TypeData * );
+TypeData * build_typedef( const std::string * name );
+TypeData * build_type_gen( const std::string * name, ExpressionNode * params );
+TypeData * build_vtable_type( TypeData * );
+
+TypeData * addQualifiers( TypeData * ltype, TypeData * rtype );
+TypeData * addType( TypeData * ltype, TypeData * rtype, std::vector<ast::ptr<ast::Attribute>> & );
+TypeData * addType( TypeData * ltype, TypeData * rtype );
+TypeData * cloneBaseType( TypeData * type, TypeData * other );
+TypeData * makeNewBase( TypeData * type );
+
+
+ast::Type * typebuild( const TypeData * );
+TypeData * typeextractAggregate( const TypeData * td, bool toplevel = true );
+ast::CV::Qualifiers buildQualifiers( const TypeData * td );
+ast::Type * buildBasicType( const TypeData * );
+ast::PointerType * buildPointer( const TypeData * );
+ast::ArrayType * buildArray( const TypeData * );
+ast::ReferenceType * buildReference( const TypeData * );
+ast::AggregateDecl * buildAggregate( const TypeData *, std::vector<ast::ptr<ast::Attribute>> );
+ast::BaseInstType * buildComAggInst( const TypeData *, std::vector<ast::ptr<ast::Attribute>> && attributes, ast::Linkage::Spec linkage );
+ast::BaseInstType * buildAggInst( const TypeData * );
+ast::TypeDecl * buildVariable( const TypeData * );
+ast::EnumDecl * buildEnum( const TypeData *, std::vector<ast::ptr<ast::Attribute>> &&, ast::Linkage::Spec );
+ast::TypeInstType * buildSymbolicInst( const TypeData * );
+ast::TupleType * buildTuple( const TypeData * );
+ast::TypeofType * buildTypeof( const TypeData * );
+ast::VTableType * buildVtable( const TypeData * );
+ast::Decl * buildDecl(
+	const TypeData *, const std::string &, ast::Storage::Classes, ast::Expr *,
+	ast::Function::Specs funcSpec, ast::Linkage::Spec, ast::Expr * asmName,
+	ast::Init * init = nullptr, std::vector<ast::ptr<ast::Attribute>> && attributes = std::vector<ast::ptr<ast::Attribute>>() );
+ast::FunctionType * buildFunctionType( const TypeData * );
+void buildKRFunction( const TypeData::Function_t & function );
+
+static inline ast::Type * maybeMoveBuildType( TypeData * type ) {
+	ast::Type * ret = type ? typebuild( type ) : nullptr;
+	delete type;
+	return ret;
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/TypedefTable.cc
===================================================================
--- src/Parser/TypedefTable.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,151 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// TypedefTable.cc --
-//
-// Author           : Peter A. Buhr
-// Created On       : Sat May 16 15:20:13 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Wed Jul 12 06:11:28 2023
-// Update Count     : 276
-//
-
-
-#include "TypedefTable.h"
-
-#include <cassert>										// for assert
-#include <string>										// for string
-#include <iostream>										// for iostream
-
-struct TypeData;
-
-#include "ExpressionNode.h"								// for LabelNode
-#include "ParserTypes.h"								// for Token
-#include "StatementNode.h"								// for CondCtl, ForCtrl
-// This (generated) header must come late as it is missing includes.
-#include "parser.hh"									// for IDENTIFIER, TYPEDEFname, TYPEGENname
-
-using namespace std;
-
-#if 0
-#define debugPrint( code ) code
-
-static const char *kindName( int kind ) {
-	switch ( kind ) {
-	case IDENTIFIER: return "identifier";
-	case TYPEDIMname: return "typedim";
-	case TYPEDEFname: return "typedef";
-	case TYPEGENname: return "typegen";
-	default:
-		cerr << "Error: cfa-cpp internal error, invalid kind of identifier" << endl;
-		abort();
-	} // switch
-} // kindName
-#else
-#define debugPrint( code )
-#endif
-
-TypedefTable::~TypedefTable() {
-	if ( ! SemanticErrorThrow && kindTable.currentScope() != 0 ) {
-		cerr << "Error: cfa-cpp internal error, scope failure " << kindTable.currentScope() << endl;
-		abort();
-	} // if
-} // TypedefTable::~TypedefTable
-
-bool TypedefTable::exists( const string & identifier ) const {
-	return kindTable.find( identifier ) != kindTable.end();
-} // TypedefTable::exists
-
-bool TypedefTable::existsCurr( const string & identifier ) const {
-	return kindTable.findAt( kindTable.currentScope() - 1, identifier ) != kindTable.end();
-} // TypedefTable::exists
-
-int TypedefTable::isKind( const string & identifier ) const {
-	KindTable::const_iterator posn = kindTable.find( identifier );
-	// Name lookup defaults to identifier, and then the identifier's kind is set by the parser.
-	if ( posn == kindTable.end() ) return IDENTIFIER;
-	return posn->second;
-} // TypedefTable::isKind
-
-// SKULLDUGGERY: Generate a typedef for the aggregate name so the aggregate does not have to be qualified by
-// "struct". Only generate the typedef, if the name is not in use. The typedef is implicitly (silently) removed if the
-// name is explicitly used.
-void TypedefTable::makeTypedef( const string & name, int kind, const char * locn __attribute__((unused)) ) {
-//    Check for existence is necessary to handle:
-//        struct Fred {};
-//        void Fred();
-//        void fred() {
-//           struct Fred act; // do not add as type in this scope
-//           Fred();
-//        }
-	debugPrint( cerr << "Make typedef at " << locn << " \"" << name << "\" as " << kindName( kind ) << " scope " << kindTable.currentScope() << endl );
-	if ( ! typedefTable.exists( name ) ) {
-		typedefTable.addToEnclosingScope( name, kind, "MTD" );
-	} // if
-} // TypedefTable::makeTypedef
-
-void TypedefTable::makeTypedef( const string & name, const char * locn __attribute__((unused)) ) {
-	debugPrint( cerr << "Make typedef at " << locn << " \"" << name << " scope " << kindTable.currentScope() << endl );
-	return makeTypedef( name, TYPEDEFname, "makeTypede" );
-} // TypedefTable::makeTypedef
-
-void TypedefTable::addToScope( const string & identifier, int kind, const char * locn __attribute__((unused)) ) {
-	KindTable::size_type scope = kindTable.currentScope();
-	debugPrint( cerr << "Adding current at " << locn << " \"" << identifier << "\" as " << kindName( kind ) << " scope " << scope << endl );
-	kindTable.insertAt( scope, identifier, kind );
-} // TypedefTable::addToScope
-
-void TypedefTable::addToEnclosingScope( const string & identifier, int kind, const char * locn __attribute__((unused)) ) {
-	KindTable::size_type scope = kindTable.currentScope() - 1 - kindTable.getNote( kindTable.currentScope() - 1 ).level;
-//	size_type scope = level - kindTable.getNote( kindTable.currentScope() - 1 ).level;
-	debugPrint( cerr << "Adding enclosing at " << locn << " \"" << identifier << "\" as " << kindName( kind ) << " scope " << scope << " level " << level << " note " << kindTable.getNote( kindTable.currentScope() - 1 ).level << endl );
-	pair< KindTable::iterator, bool > ret = kindTable.insertAt( scope, identifier, kind );
-	if ( ! ret.second ) ret.first->second = kind;		// exists => update
-} // TypedefTable::addToEnclosingScope
-
-void TypedefTable::enterScope() {
-	kindTable.beginScope( (Note){ 0, false } );
-	debugPrint( cerr << "Entering scope " << kindTable.currentScope() << " level " << level << endl; print() );
-} // TypedefTable::enterScope
-
-void TypedefTable::leaveScope() {
-	debugPrint( cerr << "Leaving scope " << kindTable.currentScope() << endl; print() );
-	kindTable.endScope();
-} // TypedefTable::leaveScope
-
-void TypedefTable::up( bool forall ) {
-	level += 1;
-	kindTable.getNote( kindTable.currentScope() ) = (Note){ level, forall || getEnclForall() };
-	debugPrint( cerr << "Up " << " level " << level << " note " << kindTable.getNote( level ).level << ", " << kindTable.getNote( level ).forall << endl; );
-} // TypedefTable::up
-
-void TypedefTable::down() {
-	level -= 1;
-	debugPrint( cerr << "Down " << " level " << level << " note " << kindTable.getNote( level ).level << endl; );
-} // TypedefTable::down
-
-void TypedefTable::print( void ) const {
-	KindTable::size_type scope = kindTable.currentScope();
-	debugPrint( cerr << "[" << scope << "] " << kindTable.getNote( scope ).level << ", " << kindTable.getNote( scope ).forall << ":" );
-	for ( KindTable::const_iterator i = kindTable.begin(); i != kindTable.end(); i++ ) {
-		while ( i.get_level() != scope ) {
-			--scope;
-			debugPrint( cerr << endl << "[" << scope << "] " << kindTable.getNote( scope ).level << ", " << kindTable.getNote( scope ).forall << ":" );
-		} // while
-		debugPrint( cerr << " " << (*i).first << ":" << kindName( (*i).second ) );
-	} // for
-	while ( scope > 0 ) {
-		--scope;
-		debugPrint( cerr << endl << "[" << scope << "] " << kindTable.getNote( scope ).level << ", " << kindTable.getNote( scope ).forall << ":" );
-	} // while
-	debugPrint( cerr << endl );
-} // TypedefTable::print
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/TypedefTable.cpp
===================================================================
--- src/Parser/TypedefTable.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/TypedefTable.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,151 @@
+//
+// 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.
+//
+// TypedefTable.cpp --
+//
+// Author           : Peter A. Buhr
+// Created On       : Sat May 16 15:20:13 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Jul 12 06:11:28 2023
+// Update Count     : 276
+//
+
+
+#include "TypedefTable.hpp"
+
+#include <cassert>										// for assert
+#include <string>										// for string
+#include <iostream>										// for iostream
+
+struct TypeData;
+
+#include "ExpressionNode.hpp"							// for LabelNode
+#include "ParserTypes.hpp"								// for Token
+#include "StatementNode.hpp"							// for CondCtl, ForCtrl
+// This (generated) header must come late as it is missing includes.
+#include "parser.hh"									// for IDENTIFIER, TYPEDEFname, TYPEGENname
+
+using namespace std;
+
+#if 0
+#define debugPrint( code ) code
+
+static const char *kindName( int kind ) {
+	switch ( kind ) {
+	case IDENTIFIER: return "identifier";
+	case TYPEDIMname: return "typedim";
+	case TYPEDEFname: return "typedef";
+	case TYPEGENname: return "typegen";
+	default:
+		cerr << "Error: cfa-cpp internal error, invalid kind of identifier" << endl;
+		abort();
+	} // switch
+} // kindName
+#else
+#define debugPrint( code )
+#endif
+
+TypedefTable::~TypedefTable() {
+	if ( ! SemanticErrorThrow && kindTable.currentScope() != 0 ) {
+		cerr << "Error: cfa-cpp internal error, scope failure " << kindTable.currentScope() << endl;
+		abort();
+	} // if
+} // TypedefTable::~TypedefTable
+
+bool TypedefTable::exists( const string & identifier ) const {
+	return kindTable.find( identifier ) != kindTable.end();
+} // TypedefTable::exists
+
+bool TypedefTable::existsCurr( const string & identifier ) const {
+	return kindTable.findAt( kindTable.currentScope() - 1, identifier ) != kindTable.end();
+} // TypedefTable::exists
+
+int TypedefTable::isKind( const string & identifier ) const {
+	KindTable::const_iterator posn = kindTable.find( identifier );
+	// Name lookup defaults to identifier, and then the identifier's kind is set by the parser.
+	if ( posn == kindTable.end() ) return IDENTIFIER;
+	return posn->second;
+} // TypedefTable::isKind
+
+// SKULLDUGGERY: Generate a typedef for the aggregate name so the aggregate does not have to be qualified by
+// "struct". Only generate the typedef, if the name is not in use. The typedef is implicitly (silently) removed if the
+// name is explicitly used.
+void TypedefTable::makeTypedef( const string & name, int kind, const char * locn __attribute__((unused)) ) {
+//    Check for existence is necessary to handle:
+//        struct Fred {};
+//        void Fred();
+//        void fred() {
+//           struct Fred act; // do not add as type in this scope
+//           Fred();
+//        }
+	debugPrint( cerr << "Make typedef at " << locn << " \"" << name << "\" as " << kindName( kind ) << " scope " << kindTable.currentScope() << endl );
+	if ( ! typedefTable.exists( name ) ) {
+		typedefTable.addToEnclosingScope( name, kind, "MTD" );
+	} // if
+} // TypedefTable::makeTypedef
+
+void TypedefTable::makeTypedef( const string & name, const char * locn __attribute__((unused)) ) {
+	debugPrint( cerr << "Make typedef at " << locn << " \"" << name << " scope " << kindTable.currentScope() << endl );
+	return makeTypedef( name, TYPEDEFname, "makeTypede" );
+} // TypedefTable::makeTypedef
+
+void TypedefTable::addToScope( const string & identifier, int kind, const char * locn __attribute__((unused)) ) {
+	KindTable::size_type scope = kindTable.currentScope();
+	debugPrint( cerr << "Adding current at " << locn << " \"" << identifier << "\" as " << kindName( kind ) << " scope " << scope << endl );
+	kindTable.insertAt( scope, identifier, kind );
+} // TypedefTable::addToScope
+
+void TypedefTable::addToEnclosingScope( const string & identifier, int kind, const char * locn __attribute__((unused)) ) {
+	KindTable::size_type scope = kindTable.currentScope() - 1 - kindTable.getNote( kindTable.currentScope() - 1 ).level;
+//	size_type scope = level - kindTable.getNote( kindTable.currentScope() - 1 ).level;
+	debugPrint( cerr << "Adding enclosing at " << locn << " \"" << identifier << "\" as " << kindName( kind ) << " scope " << scope << " level " << level << " note " << kindTable.getNote( kindTable.currentScope() - 1 ).level << endl );
+	pair< KindTable::iterator, bool > ret = kindTable.insertAt( scope, identifier, kind );
+	if ( ! ret.second ) ret.first->second = kind;		// exists => update
+} // TypedefTable::addToEnclosingScope
+
+void TypedefTable::enterScope() {
+	kindTable.beginScope( (Note){ 0, false } );
+	debugPrint( cerr << "Entering scope " << kindTable.currentScope() << " level " << level << endl; print() );
+} // TypedefTable::enterScope
+
+void TypedefTable::leaveScope() {
+	debugPrint( cerr << "Leaving scope " << kindTable.currentScope() << endl; print() );
+	kindTable.endScope();
+} // TypedefTable::leaveScope
+
+void TypedefTable::up( bool forall ) {
+	level += 1;
+	kindTable.getNote( kindTable.currentScope() ) = (Note){ level, forall || getEnclForall() };
+	debugPrint( cerr << "Up " << " level " << level << " note " << kindTable.getNote( level ).level << ", " << kindTable.getNote( level ).forall << endl; );
+} // TypedefTable::up
+
+void TypedefTable::down() {
+	level -= 1;
+	debugPrint( cerr << "Down " << " level " << level << " note " << kindTable.getNote( level ).level << endl; );
+} // TypedefTable::down
+
+void TypedefTable::print( void ) const {
+	KindTable::size_type scope = kindTable.currentScope();
+	debugPrint( cerr << "[" << scope << "] " << kindTable.getNote( scope ).level << ", " << kindTable.getNote( scope ).forall << ":" );
+	for ( KindTable::const_iterator i = kindTable.begin(); i != kindTable.end(); i++ ) {
+		while ( i.get_level() != scope ) {
+			--scope;
+			debugPrint( cerr << endl << "[" << scope << "] " << kindTable.getNote( scope ).level << ", " << kindTable.getNote( scope ).forall << ":" );
+		} // while
+		debugPrint( cerr << " " << (*i).first << ":" << kindName( (*i).second ) );
+	} // for
+	while ( scope > 0 ) {
+		--scope;
+		debugPrint( cerr << endl << "[" << scope << "] " << kindTable.getNote( scope ).level << ", " << kindTable.getNote( scope ).forall << ":" );
+	} // while
+	debugPrint( cerr << endl );
+} // TypedefTable::print
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/TypedefTable.h
===================================================================
--- src/Parser/TypedefTable.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,55 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// TypedefTable.h --
-//
-// Author           : Peter A. Buhr
-// Created On       : Sat May 16 15:24:36 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Wed Jul 12 06:09:37 2023
-// Update Count     : 118
-//
-
-#pragma once
-
-#include <string>										// for string
-
-#include "Common/ScopedMap.h"							// for ScopedMap
-
-class TypedefTable {
-	struct Note {
-		size_t level;
-		bool forall;
-	};
-	typedef ScopedMap< std::string, int, Note > KindTable;
-	KindTable kindTable;
-	unsigned int level = 0;
-  public:
-	~TypedefTable();
-
-	bool exists( const std::string & identifier ) const;
-	bool existsCurr( const std::string & identifier ) const;
-	int isKind( const std::string & identifier ) const;
-	void makeTypedef( const std::string & name, int kind, const char * );
-	void makeTypedef( const std::string & name, const char * );
-	void addToScope( const std::string & identifier, int kind, const char * );
-	void addToEnclosingScope( const std::string & identifier, int kind, const char * );
-	bool getEnclForall() { return kindTable.getNote( kindTable.currentScope() -  1 ).forall; }
-
-	void enterScope();
-	void leaveScope();
-
-	void up( bool );
-	void down();
-
-	void print( void ) const;
-}; // TypedefTable
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Parser/TypedefTable.hpp
===================================================================
--- src/Parser/TypedefTable.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Parser/TypedefTable.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,55 @@
+//
+// 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.
+//
+// TypedefTable.hpp --
+//
+// Author           : Peter A. Buhr
+// Created On       : Sat May 16 15:24:36 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Jul 12 06:09:37 2023
+// Update Count     : 118
+//
+
+#pragma once
+
+#include <string>										// for string
+
+#include "Common/ScopedMap.hpp"							// for ScopedMap
+
+class TypedefTable {
+	struct Note {
+		size_t level;
+		bool forall;
+	};
+	typedef ScopedMap< std::string, int, Note > KindTable;
+	KindTable kindTable;
+	unsigned int level = 0;
+  public:
+	~TypedefTable();
+
+	bool exists( const std::string & identifier ) const;
+	bool existsCurr( const std::string & identifier ) const;
+	int isKind( const std::string & identifier ) const;
+	void makeTypedef( const std::string & name, int kind, const char * );
+	void makeTypedef( const std::string & name, const char * );
+	void addToScope( const std::string & identifier, int kind, const char * );
+	void addToEnclosingScope( const std::string & identifier, int kind, const char * );
+	bool getEnclForall() { return kindTable.getNote( kindTable.currentScope() -  1 ).forall; }
+
+	void enterScope();
+	void leaveScope();
+
+	void up( bool );
+	void down();
+
+	void print( void ) const;
+}; // TypedefTable
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Parser/lex.ll
===================================================================
--- src/Parser/lex.ll	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Parser/lex.ll	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -44,11 +44,11 @@
 
 #include "config.h"										// configure info
-#include "DeclarationNode.h"                            // for DeclarationNode
-#include "ExpressionNode.h"                             // for LabelNode
-#include "InitializerNode.h"                            // for InitializerNode
-#include "ParseNode.h"
-#include "ParserTypes.h"                                // for Token
-#include "StatementNode.h"                              // for CondCtl, ForCtrl
-#include "TypedefTable.h"
+#include "DeclarationNode.hpp"                          // for DeclarationNode
+#include "ExpressionNode.hpp"                           // for LabelNode
+#include "InitializerNode.hpp"                          // for InitializerNode
+#include "ParseNode.hpp"
+#include "ParserTypes.hpp"                              // for Token
+#include "StatementNode.hpp"                            // for CondCtl, ForCtrl
+#include "TypedefTable.hpp"
 // This (generated) header must come late as it is missing includes.
 #include "parser.hh"                                    // generated info
Index: src/Parser/module.mk
===================================================================
--- src/Parser/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Parser/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,24 +20,28 @@
 
 SRC += \
-       Parser/DeclarationNode.cc \
-       Parser/DeclarationNode.h \
-       Parser/ExpressionNode.cc \
-       Parser/ExpressionNode.h \
-       Parser/InitializerNode.cc \
-       Parser/InitializerNode.h \
+       Parser/DeclarationNode.cpp \
+       Parser/DeclarationNode.hpp \
+       Parser/ExpressionNode.cpp \
+       Parser/ExpressionNode.hpp \
+       Parser/InitializerNode.cpp \
+       Parser/InitializerNode.hpp \
        Parser/lex.ll \
-       Parser/ParseNode.cc \
-       Parser/ParseNode.h \
+       Parser/ParseNode.cpp \
+       Parser/ParseNode.hpp \
        Parser/parser.yy \
-       Parser/ParserTypes.h \
-       Parser/parserutility.h \
+       Parser/ParserTypes.hpp \
+       Parser/parserutility.hpp \
        Parser/RunParser.cpp \
        Parser/RunParser.hpp \
-       Parser/StatementNode.cc \
-       Parser/StatementNode.h \
-       Parser/TypeData.cc \
-       Parser/TypeData.h \
-       Parser/TypedefTable.cc \
-       Parser/TypedefTable.h
+       Parser/StatementNode.cpp \
+       Parser/StatementNode.hpp \
+       Parser/TypeData.cpp \
+       Parser/TypeData.hpp \
+       Parser/TypedefTable.cpp \
+       Parser/TypedefTable.hpp
 
-MOSTLYCLEANFILES += Parser/lex.cc Parser/parser.cc Parser/parser.hh Parser/parser.output
+MOSTLYCLEANFILES += \
+       Parser/lex.cc \
+       Parser/parser.cc \
+       Parser/parser.hh \
+       Parser/parser.output
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Parser/parser.yy	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -48,14 +48,14 @@
 using namespace std;
 
-#include "DeclarationNode.h"                            // for DeclarationNode, ...
-#include "ExpressionNode.h"                             // for ExpressionNode, ...
-#include "InitializerNode.h"                            // for InitializerNode, ...
-#include "ParserTypes.h"
-#include "StatementNode.h"                              // for build_...
-#include "TypedefTable.h"
-#include "TypeData.h"
+#include "DeclarationNode.hpp"                          // for DeclarationNode, ...
+#include "ExpressionNode.hpp"                           // for ExpressionNode, ...
+#include "InitializerNode.hpp"                          // for InitializerNode, ...
+#include "ParserTypes.hpp"
+#include "StatementNode.hpp"                            // for build_...
+#include "TypedefTable.hpp"
+#include "TypeData.hpp"
 #include "AST/Type.hpp"                                 // for BasicType, BasicKind
-#include "Common/SemanticError.h"						// error_str
-#include "Common/utility.h"								// for maybeMoveBuild, maybeBuild, CodeLo...
+#include "Common/SemanticError.hpp"                     // error_str
+#include "Common/Utility.hpp"                           // for maybeMoveBuild, maybeBuild, CodeLo...
 
 // lex uses __null in a boolean context, it's fine.
Index: src/Parser/parserutility.h
===================================================================
--- src/Parser/parserutility.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,41 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// parserutility.h -- Collected utilities for the parser.
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Sat May 16 15:31:46 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Apr  4 14:03:00 2023
-// Update Count     : 7
-//
-
-#pragma once
-
-#include "AST/Copy.hpp"            // for shallowCopy
-
-template< typename T >
-static inline auto maybeBuild( T * orig ) -> decltype(orig->build()) {
-	return (orig) ? orig->build() : nullptr;
-}
-
-template< typename T >
-static inline auto maybeMoveBuild( T * orig ) -> decltype(orig->build()) {
-	auto ret = maybeBuild<T>(orig);
-	delete orig;
-	return ret;
-}
-
-template<typename node_t>
-static inline node_t * maybeCopy( node_t const * node ) {
-	return node ? ast::shallowCopy( node ) : nullptr;
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/AdjustExprType.cc
===================================================================
--- src/ResolvExpr/AdjustExprType.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,89 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// AdjustExprType_old.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sat May 16 23:41:42 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Wed Dec 11 21:43:56 2019
-// Update Count     : 6
-//
-
-#include "AST/Node.hpp"
-#include "AST/Pass.hpp"
-#include "AST/SymbolTable.hpp"
-#include "AST/Type.hpp"
-#include "AST/TypeEnvironment.hpp"
-
-namespace ResolvExpr {
-
-namespace {
-
-class AdjustExprType final : public ast::WithShortCircuiting {
-	const ast::SymbolTable & symtab;
-public:
-	const ast::TypeEnvironment & tenv;
-
-	AdjustExprType( const ast::TypeEnvironment & e, const ast::SymbolTable & syms )
-	: symtab( syms ), tenv( e ) {}
-
-	void previsit( const ast::VoidType * ) { visit_children = false; }
-	void previsit( const ast::BasicType * ) { visit_children = false; }
-	void previsit( const ast::PointerType * ) { visit_children = false; }
-	void previsit( const ast::ArrayType * ) { visit_children = false; }
-	void previsit( const ast::FunctionType * ) { visit_children = false; }
-	void previsit( const ast::StructInstType * ) { visit_children = false; }
-	void previsit( const ast::UnionInstType * ) { visit_children = false; }
-	void previsit( const ast::EnumInstType * ) { visit_children = false; }
-	void previsit( const ast::TraitInstType * ) { visit_children = false; }
-	void previsit( const ast::TypeInstType * ) { visit_children = false; }
-	void previsit( const ast::TupleType * ) { visit_children = false; }
-	void previsit( const ast::VarArgsType * ) { visit_children = false; }
-	void previsit( const ast::ZeroType * ) { visit_children = false; }
-	void previsit( const ast::OneType * ) { visit_children = false; }
-
-	const ast::Type * postvisit( const ast::ArrayType * at ) {
-		return new ast::PointerType( at->base, at->qualifiers );
-	}
-
-	const ast::Type * postvisit( const ast::FunctionType * ft ) {
-		return new ast::PointerType( ft );
-	}
-
-	const ast::Type * postvisit( const ast::TypeInstType * inst ) {
-		// replace known function-type-variables with pointer-to-function
-		if ( const ast::EqvClass * eqvClass = tenv.lookup( *inst ) ) {
-			if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
-				return new ast::PointerType( inst );
-			}
-		} else if ( const ast::NamedTypeDecl * ntDecl = symtab.lookupType( inst->name ) ) {
-			if ( auto tyDecl = dynamic_cast< const ast::TypeDecl * >( ntDecl ) ) {
-				if ( tyDecl->kind == ast::TypeDecl::Ftype ) {
-					return new ast::PointerType( inst );
-				}
-			}
-		}
-		return inst;
-	}
-};
-
-} // anonymous namespace
-
-const ast::Type * adjustExprType(
-	const ast::Type * type, const ast::TypeEnvironment & env, const ast::SymbolTable & symtab
-) {
-	ast::Pass<AdjustExprType> adjuster{ env, symtab };
-	return type->accept( adjuster );
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/AdjustExprType.cpp
===================================================================
--- src/ResolvExpr/AdjustExprType.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/AdjustExprType.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,89 @@
+//
+// 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.
+//
+// AdjustExprType_old.cc --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sat May 16 23:41:42 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Dec 11 21:43:56 2019
+// Update Count     : 6
+//
+
+#include "AST/Node.hpp"
+#include "AST/Pass.hpp"
+#include "AST/SymbolTable.hpp"
+#include "AST/Type.hpp"
+#include "AST/TypeEnvironment.hpp"
+
+namespace ResolvExpr {
+
+namespace {
+
+class AdjustExprType final : public ast::WithShortCircuiting {
+	const ast::SymbolTable & symtab;
+public:
+	const ast::TypeEnvironment & tenv;
+
+	AdjustExprType( const ast::TypeEnvironment & e, const ast::SymbolTable & syms )
+	: symtab( syms ), tenv( e ) {}
+
+	void previsit( const ast::VoidType * ) { visit_children = false; }
+	void previsit( const ast::BasicType * ) { visit_children = false; }
+	void previsit( const ast::PointerType * ) { visit_children = false; }
+	void previsit( const ast::ArrayType * ) { visit_children = false; }
+	void previsit( const ast::FunctionType * ) { visit_children = false; }
+	void previsit( const ast::StructInstType * ) { visit_children = false; }
+	void previsit( const ast::UnionInstType * ) { visit_children = false; }
+	void previsit( const ast::EnumInstType * ) { visit_children = false; }
+	void previsit( const ast::TraitInstType * ) { visit_children = false; }
+	void previsit( const ast::TypeInstType * ) { visit_children = false; }
+	void previsit( const ast::TupleType * ) { visit_children = false; }
+	void previsit( const ast::VarArgsType * ) { visit_children = false; }
+	void previsit( const ast::ZeroType * ) { visit_children = false; }
+	void previsit( const ast::OneType * ) { visit_children = false; }
+
+	const ast::Type * postvisit( const ast::ArrayType * at ) {
+		return new ast::PointerType( at->base, at->qualifiers );
+	}
+
+	const ast::Type * postvisit( const ast::FunctionType * ft ) {
+		return new ast::PointerType( ft );
+	}
+
+	const ast::Type * postvisit( const ast::TypeInstType * inst ) {
+		// replace known function-type-variables with pointer-to-function
+		if ( const ast::EqvClass * eqvClass = tenv.lookup( *inst ) ) {
+			if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
+				return new ast::PointerType( inst );
+			}
+		} else if ( const ast::NamedTypeDecl * ntDecl = symtab.lookupType( inst->name ) ) {
+			if ( auto tyDecl = dynamic_cast< const ast::TypeDecl * >( ntDecl ) ) {
+				if ( tyDecl->kind == ast::TypeDecl::Ftype ) {
+					return new ast::PointerType( inst );
+				}
+			}
+		}
+		return inst;
+	}
+};
+
+} // anonymous namespace
+
+const ast::Type * adjustExprType(
+	const ast::Type * type, const ast::TypeEnvironment & env, const ast::SymbolTable & symtab
+) {
+	ast::Pass<AdjustExprType> adjuster{ env, symtab };
+	return type->accept( adjuster );
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/Candidate.hpp
===================================================================
--- src/ResolvExpr/Candidate.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ResolvExpr/Candidate.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,8 +20,8 @@
 #include <vector>
 
-#include "Cost.h"
+#include "Cost.hpp"
 #include "AST/Node.hpp"
 #include "AST/TypeEnvironment.hpp"
-#include "Common/Indenter.h"
+#include "Common/Indenter.hpp"
 
 namespace ast {
Index: src/ResolvExpr/CandidateFinder.cpp
===================================================================
--- src/ResolvExpr/CandidateFinder.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -17,5 +17,5 @@
 
 #include <deque>
-#include <iterator>               // for back_inserter
+#include <iterator>                 // for back_inserter
 #include <sstream>
 #include <string>
@@ -25,18 +25,18 @@
 #include "AdjustExprType.hpp"
 #include "Candidate.hpp"
-#include "CastCost.hpp"           // for castCost
+#include "CastCost.hpp"             // for castCost
 #include "CompilationState.hpp"
-#include "ConversionCost.h"       // for conversionCast
-#include "Cost.h"
+#include "ConversionCost.hpp"       // for conversionCast
+#include "Cost.hpp"
 #include "ExplodedArg.hpp"
 #include "PolyCost.hpp"
-#include "RenameVars.h"           // for renameTyVars
-#include "Resolver.h"
-#include "ResolveTypeof.h"
+#include "RenameVars.hpp"           // for renameTyVars
+#include "Resolver.hpp"
+#include "ResolveTypeof.hpp"
 #include "SatisfyAssertions.hpp"
 #include "SpecCost.hpp"
-#include "typeops.h"              // for combos
-#include "Unify.h"
-#include "WidenMode.h"
+#include "Typeops.hpp"              // for combos
+#include "Unify.hpp"
+#include "WidenMode.hpp"
 #include "AST/Expr.hpp"
 #include "AST/Node.hpp"
@@ -45,10 +45,10 @@
 #include "AST/SymbolTable.hpp"
 #include "AST/Type.hpp"
-#include "Common/utility.h"       // for move, copy
-#include "SymTab/Mangler.h"
-#include "Tuples/Tuples.h"        // for handleTupleAssignment
-#include "InitTweak/InitTweak.h"  // for getPointerBase
-
-#include "Common/Stats/Counter.h"
+#include "Common/Utility.hpp"       // for move, copy
+#include "SymTab/Mangler.hpp"
+#include "Tuples/Tuples.hpp"        // for handleTupleAssignment
+#include "InitTweak/InitTweak.hpp"  // for getPointerBase
+
+#include "Common/Stats/Counter.hpp"
 
 #include "AST/Inspect.hpp"             // for getFunctionName
Index: src/ResolvExpr/CandidatePrinter.cpp
===================================================================
--- src/ResolvExpr/CandidatePrinter.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ResolvExpr/CandidatePrinter.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -24,5 +24,5 @@
 #include "AST/TranslationUnit.hpp"
 #include "ResolvExpr/CandidateFinder.hpp"
-#include "ResolvExpr/Resolver.h"
+#include "ResolvExpr/Resolver.hpp"
 
 namespace ResolvExpr {
Index: src/ResolvExpr/CastCost.cc
===================================================================
--- src/ResolvExpr/CastCost.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,174 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// CastCost.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 06:57:43 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Oct  4 15:00:00 2019
-// Update Count     : 9
-//
-
-#include "CastCost.hpp"
-
-#include <cassert>                       // for assert
-
-#include "AST/Print.hpp"
-#include "AST/SymbolTable.hpp"
-#include "AST/Type.hpp"
-#include "AST/TypeEnvironment.hpp"
-#include "ConversionCost.h"              // for ConversionCost
-#include "Cost.h"                        // for Cost, Cost::infinity
-#include "ResolvExpr/ConversionCost.h"   // for conversionCost
-#include "ResolvExpr/PtrsCastable.hpp"   // for ptrsCastable
-#include "ResolvExpr/Unify.h"            // for typesCompatibleIgnoreQualifiers
-
-#if 0
-#define PRINT(x) x
-#else
-#define PRINT(x)
-#endif
-
-namespace ResolvExpr {
-
-namespace {
-	struct CastCost : public ConversionCost {
-		using ConversionCost::previsit;
-		using ConversionCost::postvisit;
-
-		CastCost(
-			const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab,
-			const ast::TypeEnvironment & env, CostCalculation costFunc )
-		: ConversionCost( dst, srcIsLvalue, symtab, env, costFunc ) {}
-
-		void postvisit( const ast::BasicType * basicType ) {
-			auto ptr = dynamic_cast< const ast::PointerType * >( dst );
-			if ( ptr && basicType->isInteger() ) {
-				// needed for, e.g. unsigned long => void *
-				cost = Cost::unsafe;
-			} else {
-				cost = conversionCost( basicType, dst, srcIsLvalue, symtab, env );
-				if ( Cost::unsafe < cost ) {
-					if (auto enumInst = dynamic_cast<const ast::EnumInstType *>(dst)) {
-						// Always explict cast only for typed enum
-						if (enumInst->base->isTyped) cost = Cost::unsafe;
-					}
-				}
-			}
-		}
-
-		void postvisit( const ast::ZeroType * zero ) {
-			cost = conversionCost( zero, dst, srcIsLvalue, symtab, env );
-			if ( Cost::unsafe < cost ) {
-				if (auto enumInst =  dynamic_cast<const ast::EnumInstType *>(dst)) {
-					if (enumInst->base->isTyped) cost = Cost::unsafe;
-				}
-			}
-		}
-
-		void postvisit( const ast::OneType * one ) {
-			cost = conversionCost( one, dst, srcIsLvalue, symtab, env );
-			if ( Cost::unsafe < cost ) {
-				if (auto enumInst = dynamic_cast<const ast::EnumInstType *>(dst)) {
-					if (enumInst->base->isTyped) cost = Cost::unsafe;
-				}
-			}
-		}
-
-		void postvisit( const ast::PointerType * pointerType ) {
-			if ( auto ptr = dynamic_cast< const ast::PointerType * >( dst ) ) {
-				if (
-					pointerType->qualifiers <= ptr->qualifiers
-					&& typesCompatibleIgnoreQualifiers( pointerType->base, ptr->base, env )
-				) {
-					cost = Cost::safe;
-				} else {
-					ast::TypeEnvironment newEnv{ env };
-					if ( auto wParams = pointerType->base.as< ast::FunctionType >() ) {
-						newEnv.add( wParams->forall );
-					}
-					int castResult = ptrsCastable( pointerType->base, ptr->base, symtab, newEnv );
-					if ( castResult > 0 ) {
-						cost = Cost::safe;
-					} else if ( castResult < 0 ) {
-						cost = Cost::infinity;
-					}
-				}
-			} else if ( auto basic = dynamic_cast< const ast::BasicType * >( dst ) ) {
-				if ( basic->isInteger() ) {
-					// necessary for, e.g. void * => unsigned long
-					cost = Cost::unsafe;
-				}
-			}
-		}
-
-		void postvist( const ast::EnumInstType * ) {
-			if ( auto basic = dynamic_cast< const ast::BasicType * >(dst) ) {
-				if ( basic->isInteger() ) cost = Cost::unsafe;
-			}
-		}
-	};
-
-} // anonymous namespace
-
-Cost castCost(
-	const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
-	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
-) {
-	if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( const ast::EqvClass * eqvClass = env.lookup( *typeInst ) ) {
-			// check cast cost against bound type, if present
-			if ( eqvClass->bound ) {
-				return castCost( src, eqvClass->bound, srcIsLvalue, symtab, env );
-			} else {
-				return Cost::infinity;
-			}
-		} else if ( const ast::NamedTypeDecl * named = symtab.lookupType( typeInst->name ) ) {
-			// all typedefs should be gone by now
-			auto type = strict_dynamic_cast< const ast::TypeDecl * >( named );
-			if ( type->base ) {
-				return castCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe;
-			}
-		}
-	}
-
-	PRINT(
-		std::cerr << "castCost ::: src is ";
-		ast::print( std::cerr, src );
-		std::cerr << std::endl << "dest is ";
-		ast::print( std::cerr, dst );
-		std::cerr << std::endl << "env is" << std::endl;
-		ast::print( std::cerr, env, 2 );
-	)
-
-	if ( typesCompatibleIgnoreQualifiers( src, dst, env ) ) {
-		PRINT( std::cerr << "compatible!" << std::endl; )
-		if (dynamic_cast<const ast::ZeroType *>(dst) || dynamic_cast<const ast::OneType *>(dst)) {
-			return Cost::spec;
-		}
-		return Cost::zero;
-	} else if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
-		return Cost::safe;
-	} else if ( auto refType = dynamic_cast< const ast::ReferenceType * >( dst ) ) {
-		PRINT( std::cerr << "conversionCost: dest is reference" << std::endl; )
-		return convertToReferenceCost(
-			src, refType, srcIsLvalue, symtab, env, ptrsCastable );
-	} else {
-		ast::Pass< CastCost > converter(
-			dst, srcIsLvalue, symtab, env, castCost );
-		src->accept( converter );
-		return converter.core.cost;
-	}
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/CastCost.cpp
===================================================================
--- src/ResolvExpr/CastCost.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/CastCost.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,174 @@
+//
+// 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.
+//
+// CastCost.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 06:57:43 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Oct  4 15:00:00 2019
+// Update Count     : 9
+//
+
+#include "CastCost.hpp"
+
+#include <cassert>                       // for assert
+
+#include "AST/Print.hpp"
+#include "AST/SymbolTable.hpp"
+#include "AST/Type.hpp"
+#include "AST/TypeEnvironment.hpp"
+#include "ConversionCost.hpp"            // for ConversionCost
+#include "Cost.hpp"                      // for Cost, Cost::infinity
+#include "ResolvExpr/ConversionCost.hpp" // for conversionCost
+#include "ResolvExpr/PtrsCastable.hpp"   // for ptrsCastable
+#include "ResolvExpr/Unify.hpp"          // for typesCompatibleIgnoreQualifiers
+
+#if 0
+#define PRINT(x) x
+#else
+#define PRINT(x)
+#endif
+
+namespace ResolvExpr {
+
+namespace {
+	struct CastCost : public ConversionCost {
+		using ConversionCost::previsit;
+		using ConversionCost::postvisit;
+
+		CastCost(
+			const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab,
+			const ast::TypeEnvironment & env, CostCalculation costFunc )
+		: ConversionCost( dst, srcIsLvalue, symtab, env, costFunc ) {}
+
+		void postvisit( const ast::BasicType * basicType ) {
+			auto ptr = dynamic_cast< const ast::PointerType * >( dst );
+			if ( ptr && basicType->isInteger() ) {
+				// needed for, e.g. unsigned long => void *
+				cost = Cost::unsafe;
+			} else {
+				cost = conversionCost( basicType, dst, srcIsLvalue, symtab, env );
+				if ( Cost::unsafe < cost ) {
+					if (auto enumInst = dynamic_cast<const ast::EnumInstType *>(dst)) {
+						// Always explict cast only for typed enum
+						if (enumInst->base->isTyped) cost = Cost::unsafe;
+					}
+				}
+			}
+		}
+
+		void postvisit( const ast::ZeroType * zero ) {
+			cost = conversionCost( zero, dst, srcIsLvalue, symtab, env );
+			if ( Cost::unsafe < cost ) {
+				if (auto enumInst =  dynamic_cast<const ast::EnumInstType *>(dst)) {
+					if (enumInst->base->isTyped) cost = Cost::unsafe;
+				}
+			}
+		}
+
+		void postvisit( const ast::OneType * one ) {
+			cost = conversionCost( one, dst, srcIsLvalue, symtab, env );
+			if ( Cost::unsafe < cost ) {
+				if (auto enumInst = dynamic_cast<const ast::EnumInstType *>(dst)) {
+					if (enumInst->base->isTyped) cost = Cost::unsafe;
+				}
+			}
+		}
+
+		void postvisit( const ast::PointerType * pointerType ) {
+			if ( auto ptr = dynamic_cast< const ast::PointerType * >( dst ) ) {
+				if (
+					pointerType->qualifiers <= ptr->qualifiers
+					&& typesCompatibleIgnoreQualifiers( pointerType->base, ptr->base, env )
+				) {
+					cost = Cost::safe;
+				} else {
+					ast::TypeEnvironment newEnv{ env };
+					if ( auto wParams = pointerType->base.as< ast::FunctionType >() ) {
+						newEnv.add( wParams->forall );
+					}
+					int castResult = ptrsCastable( pointerType->base, ptr->base, symtab, newEnv );
+					if ( castResult > 0 ) {
+						cost = Cost::safe;
+					} else if ( castResult < 0 ) {
+						cost = Cost::infinity;
+					}
+				}
+			} else if ( auto basic = dynamic_cast< const ast::BasicType * >( dst ) ) {
+				if ( basic->isInteger() ) {
+					// necessary for, e.g. void * => unsigned long
+					cost = Cost::unsafe;
+				}
+			}
+		}
+
+		void postvist( const ast::EnumInstType * ) {
+			if ( auto basic = dynamic_cast< const ast::BasicType * >(dst) ) {
+				if ( basic->isInteger() ) cost = Cost::unsafe;
+			}
+		}
+	};
+
+} // anonymous namespace
+
+Cost castCost(
+	const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
+) {
+	if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
+		if ( const ast::EqvClass * eqvClass = env.lookup( *typeInst ) ) {
+			// check cast cost against bound type, if present
+			if ( eqvClass->bound ) {
+				return castCost( src, eqvClass->bound, srcIsLvalue, symtab, env );
+			} else {
+				return Cost::infinity;
+			}
+		} else if ( const ast::NamedTypeDecl * named = symtab.lookupType( typeInst->name ) ) {
+			// all typedefs should be gone by now
+			auto type = strict_dynamic_cast< const ast::TypeDecl * >( named );
+			if ( type->base ) {
+				return castCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe;
+			}
+		}
+	}
+
+	PRINT(
+		std::cerr << "castCost ::: src is ";
+		ast::print( std::cerr, src );
+		std::cerr << std::endl << "dest is ";
+		ast::print( std::cerr, dst );
+		std::cerr << std::endl << "env is" << std::endl;
+		ast::print( std::cerr, env, 2 );
+	)
+
+	if ( typesCompatibleIgnoreQualifiers( src, dst, env ) ) {
+		PRINT( std::cerr << "compatible!" << std::endl; )
+		if (dynamic_cast<const ast::ZeroType *>(dst) || dynamic_cast<const ast::OneType *>(dst)) {
+			return Cost::spec;
+		}
+		return Cost::zero;
+	} else if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
+		return Cost::safe;
+	} else if ( auto refType = dynamic_cast< const ast::ReferenceType * >( dst ) ) {
+		PRINT( std::cerr << "conversionCost: dest is reference" << std::endl; )
+		return convertToReferenceCost(
+			src, refType, srcIsLvalue, symtab, env, ptrsCastable );
+	} else {
+		ast::Pass< CastCost > converter(
+			dst, srcIsLvalue, symtab, env, castCost );
+		src->accept( converter );
+		return converter.core.cost;
+	}
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/CastCost.hpp
===================================================================
--- src/ResolvExpr/CastCost.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ResolvExpr/CastCost.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,6 +16,4 @@
 #pragma once
 
-#include "ResolvExpr/Cost.h"     // for Cost
-
 namespace ast {
 	class SymbolTable;
@@ -25,4 +23,6 @@
 
 namespace ResolvExpr {
+
+class Cost;
 
 Cost castCost(
Index: src/ResolvExpr/CommonType.cc
===================================================================
--- src/ResolvExpr/CommonType.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,795 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// CommonType.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 06:59:27 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Thu Feb 14 17:10:10 2019
-// Update Count     : 24
-//
-
-#include "CommonType.hpp"
-
-#include <cassert>                       // for strict_dynamic_cast
-#include <map>                           // for _Rb_tree_const_iterator
-#include <utility>                       // for pair
-
-#include "AST/Decl.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-#include "Unify.h"                       // for unifyExact, WidenMode
-#include "typeops.h"                     // for isFtype
-#include "Tuples/Tuples.h"
-
-// #define DEBUG
-#ifdef DEBUG
-#define PRINT(x) x
-#else
-#define PRINT(x)
-#endif
-
-namespace ResolvExpr {
-
-namespace {
-
-	// GENERATED START, DO NOT EDIT
-	// GENERATED BY BasicTypes-gen.cpp
-	#define BT ast::BasicKind::
-	static const ast::BasicKind commonTypes[BT NUMBER_OF_BASIC_TYPES][BT NUMBER_OF_BASIC_TYPES] = { // nearest common ancestor
-		/*		                        B                       C                      SC                      UC                      SI                     SUI
-				                        I                      UI                      LI                     LUI                     LLI                    LLUI
-				                       IB                     UIB                     _FH                     _FH                      _F                     _FC
-				                        F                      FC                     _FX                    _FXC                      FD                    _FDC
-				                        D                      DC                    F80X                   _FDXC                     F80                     _FB
-				                    _FLDC                      FB                      LD                     LDC                    _FBX                  _FLDXC
-				 */
-				  {
-		/*      B */                BT Bool,                BT Char,          BT SignedChar,        BT UnsignedChar,      BT ShortSignedInt,    BT ShortUnsignedInt,
-				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*      C */                BT Char,                BT Char,          BT SignedChar,        BT UnsignedChar,      BT ShortSignedInt,    BT ShortUnsignedInt,
-				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     SC */          BT SignedChar,          BT SignedChar,          BT SignedChar,        BT UnsignedChar,      BT ShortSignedInt,    BT ShortUnsignedInt,
-				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     UC */        BT UnsignedChar,        BT UnsignedChar,        BT UnsignedChar,        BT UnsignedChar,      BT ShortSignedInt,    BT ShortUnsignedInt,
-				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     SI */      BT ShortSignedInt,      BT ShortSignedInt,      BT ShortSignedInt,      BT ShortSignedInt,      BT ShortSignedInt,    BT ShortUnsignedInt,
-				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    SUI */    BT ShortUnsignedInt,    BT ShortUnsignedInt,    BT ShortUnsignedInt,    BT ShortUnsignedInt,    BT ShortUnsignedInt,    BT ShortUnsignedInt,
-				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*      I */           BT SignedInt,           BT SignedInt,           BT SignedInt,           BT SignedInt,           BT SignedInt,           BT SignedInt,
-				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     UI */         BT UnsignedInt,         BT UnsignedInt,         BT UnsignedInt,         BT UnsignedInt,         BT UnsignedInt,         BT UnsignedInt,
-				           BT UnsignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     LI */       BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,
-				         BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    LUI */     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,
-				       BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    LLI */   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,
-				     BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*   LLUI */ BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt,
-				   BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     IB */        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,
-				          BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,
-				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    UIB */      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,
-				        BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,
-				        BT UnsignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    _FH */            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,
-				              BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,
-				              BT uFloat16,            BT uFloat16,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    _FH */     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,
-				       BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,
-				       BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat32Complex,     BT uFloat32Complex,
-				          BT FloatComplex,        BT FloatComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,     BT uFloat64Complex,     BT uFloat64Complex,
-				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     _F */            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,
-				              BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,
-				              BT uFloat32,            BT uFloat32,            BT uFloat32,     BT uFloat32Complex,            BT uFloat32,     BT uFloat32Complex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    _FC */     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,
-				       BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,
-				       BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,
-				          BT FloatComplex,        BT FloatComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,     BT uFloat64Complex,     BT uFloat64Complex,
-				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-				  {
-		/*      F */               BT Float,               BT Float,               BT Float,               BT Float,               BT Float,               BT Float,
-				                 BT Float,               BT Float,               BT Float,               BT Float,               BT Float,               BT Float,
-				                 BT Float,               BT Float,               BT Float,        BT FloatComplex,               BT Float,        BT FloatComplex,
-				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     FC */        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,
-				          BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,
-				          BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,
-				          BT FloatComplex,        BT FloatComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,     BT uFloat64Complex,     BT uFloat64Complex,
-				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    _FX */           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,
-				             BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,
-				             BT uFloat32x,           BT uFloat32x,           BT uFloat32x,    BT uFloat32xComplex,           BT uFloat32x,    BT uFloat32xComplex,
-				             BT uFloat32x,    BT uFloat32xComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*   _FXC */    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,
-				      BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,
-				      BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,
-				      BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,     BT uFloat64Complex,     BT uFloat64Complex,
-				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     FD */            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,
-				              BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,
-				              BT uFloat64,            BT uFloat64,            BT uFloat64,     BT uFloat64Complex,            BT uFloat64,     BT uFloat64Complex,
-				              BT uFloat64,     BT uFloat64Complex,            BT uFloat64,     BT uFloat64Complex,            BT uFloat64,     BT uFloat64Complex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*   _FDC */     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,
-				       BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,
-				       BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,
-				       BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,
-				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-				  {
-		/*      D */              BT Double,              BT Double,              BT Double,              BT Double,              BT Double,              BT Double,
-				                BT Double,              BT Double,              BT Double,              BT Double,              BT Double,              BT Double,
-				                BT Double,              BT Double,              BT Double,       BT DoubleComplex,              BT Double,       BT DoubleComplex,
-				                BT Double,       BT DoubleComplex,              BT Double,       BT DoubleComplex,              BT Double,       BT DoubleComplex,
-				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     DC */       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,
-				         BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,
-				         BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,
-				         BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,
-				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-				  {
-		/*   F80X */           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,
-				             BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,
-				             BT uFloat64x,           BT uFloat64x,           BT uFloat64x,    BT uFloat64xComplex,           BT uFloat64x,    BT uFloat64xComplex,
-				             BT uFloat64x,    BT uFloat64xComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uFloat64x,    BT uFloat64xComplex,
-				             BT uFloat64x,    BT uFloat64xComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*  _FDXC */    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,
-				      BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,
-				      BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,
-				      BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,
-				      BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    F80 */           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,
-				             BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,
-				             BT uuFloat80,           BT uuFloat80,           BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,    BT uFloat64xComplex,
-				             BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,    BT uFloat64xComplex,
-				             BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    _FB */           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,
-				             BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,
-				             BT uFloat128,           BT uFloat128,           BT uFloat128,    BT uFloat128Complex,           BT uFloat128,    BT uFloat128Complex,
-				             BT uFloat128,    BT uFloat128Complex,           BT uFloat128,    BT uFloat128Complex,           BT uFloat128,    BT uFloat128Complex,
-				             BT uFloat128,    BT uFloat128Complex,           BT uFloat128,    BT uFloat128Complex,           BT uFloat128,           BT uFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*  _FLDC */    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
-				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     FB */          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,
-				            BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,
-				            BT uuFloat128,          BT uuFloat128,          BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,    BT uFloat128Complex,
-				            BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,    BT uFloat128Complex,
-				            BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,          BT uuFloat128,
-				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*     LD */          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,
-				            BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,
-				            BT LongDouble,          BT LongDouble,          BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,   BT LongDoubleComplex,
-				            BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,   BT LongDoubleComplex,
-				            BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,          BT LongDouble,
-				     BT LongDoubleComplex,          BT LongDouble,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/*    LDC */   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
-				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
-				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
-				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
-				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
-				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-				  {
-		/*   _FBX */          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,
-				            BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,
-				            BT uFloat128x,          BT uFloat128x,          BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				            BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				            BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,          BT uFloat128x,
-				     BT uFloat128xComplex,          BT uFloat128x,          BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,
-				  },
-				  {
-		/* _FLDXC */   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
-				  },
-	}; // commonTypes
-	#undef BT
-	// GENERATED END
-	static_assert(
-		sizeof(commonTypes)/sizeof(commonTypes[0][0]) == ast::BasicKind::NUMBER_OF_BASIC_TYPES * ast::BasicKind::NUMBER_OF_BASIC_TYPES,
-		"Each basic type kind should have a corresponding row in the combined type matrix"
-	);
-
-class CommonType final : public ast::WithShortCircuiting {
-	const ast::Type * type2;
-	WidenMode widen;
-	ast::TypeEnvironment & tenv;
-	const ast::OpenVarSet & open;
-	ast::AssertionSet & need;
-	ast::AssertionSet & have;
-public:
-	static size_t traceId;
-	ast::ptr< ast::Type > result;
-
-	CommonType(
-		const ast::Type * t2, WidenMode w,
-		ast::TypeEnvironment & env, const ast::OpenVarSet & o,
-		ast::AssertionSet & need, ast::AssertionSet & have )
-	: type2( t2 ), widen( w ), tenv( env ), open( o ), need (need), have (have) ,result() {}
-
-	void previsit( const ast::Node * ) { visit_children = false; }
-
-	void postvisit( const ast::VoidType * ) {}
-
-	void postvisit( const ast::BasicType * basic ) {
-		if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) {
-			ast::BasicKind kind;
-			if (basic->kind != basic2->kind && !widen.first && !widen.second) return;
-			else if (!widen.first) kind = basic->kind; // widen.second
-			else if (!widen.second) kind = basic2->kind;
-			else kind = commonTypes[ basic->kind ][ basic2->kind ];
-			// xxx - what does qualifiers even do here??
-			if ( (basic->qualifiers >= basic2->qualifiers || widen.first)
-				&& (basic->qualifiers <= basic2->qualifiers || widen.second) ) {
-				result = new ast::BasicType{ kind, basic->qualifiers | basic2->qualifiers };
-			}
-		} else if (
-			dynamic_cast< const ast::ZeroType * >( type2 )
-			|| dynamic_cast< const ast::OneType * >( type2 )
-		) {
-			if (widen.second) {
-				result = new ast::BasicType{ basic->kind, basic->qualifiers | type2->qualifiers };
-			}
-		} else if ( const ast::EnumInstType * enumInst = dynamic_cast< const ast::EnumInstType * >( type2 ) ) {
-			const ast::EnumDecl* enumDecl = enumInst->base;
-			if ( !enumDecl->base ) {
-				ast::BasicKind kind = commonTypes[ basic->kind ][ ast::BasicKind::SignedInt ];
-				if (
-					( ( kind == basic->kind && basic->qualifiers >= type2->qualifiers )
-						|| widen.first )
-					&& ( ( kind != basic->kind && basic->qualifiers <= type2->qualifiers )
-						|| widen.second )
-				) {
-					result = new ast::BasicType{ kind, basic->qualifiers | type2->qualifiers };
-				}
-			}
-		} else if ( auto type2AsAttr = dynamic_cast< const ast::EnumAttrType * >( type2 ) ) {
-			if ( type2AsAttr->attr == ast::EnumAttribute::Posn ) {
-				ast::BasicKind kind = commonTypes[ basic->kind ][ ast::BasicKind::SignedInt ];
-				if (
-					( ( kind == basic->kind && basic->qualifiers >= type2->qualifiers )
-						|| widen.first )
-					&& ( ( kind != basic->kind && basic->qualifiers <= type2->qualifiers )
-						|| widen.second )
-				) {
-					result = new ast::BasicType{ kind, basic->qualifiers | type2->qualifiers };
-				}
-			}
-		}
-	}
-
-private:
-	template< typename Pointer >
-	void getCommonWithVoidPointer( const Pointer * voidPtr, const Pointer * oPtr ) {
-		const ast::Type * base = oPtr->base;
-		if ( auto var = dynamic_cast< const ast::TypeInstType * >( base ) ) {
-			auto entry = open.find( *var );
-			if ( entry != open.end() ) {
-				ast::AssertionSet need, have;
-				if ( ! tenv.bindVar(
-					var, voidPtr->base, entry->second, need, have, open, widen )
-				) return;
-			}
-		}
-		result = voidPtr;
-		add_qualifiers( result, oPtr->qualifiers );
-	}
-
-public:
-	void postvisit( const ast::PointerType * pointer ) {
-		if ( auto pointer2 = dynamic_cast< const ast::PointerType * >( type2 ) ) {
-			if (
-				widen.first
-				&& pointer2->base.as< ast::VoidType >()
-				&& ! ast::isFtype( pointer->base )
-			) {
-				getCommonWithVoidPointer( pointer2, pointer );
-			} else if (
-				widen.second
-				&& pointer->base.as< ast::VoidType >()
-				&& ! ast::isFtype( pointer2->base )
-			) {
-				getCommonWithVoidPointer( pointer, pointer2 );
-			} else if (
-				( pointer->base->qualifiers >= pointer2->base->qualifiers || widen.first )
-				&& ( pointer->base->qualifiers <= pointer2->base->qualifiers || widen.second )
-			) {
-				ast::CV::Qualifiers q1 = pointer->base->qualifiers;
-				ast::CV::Qualifiers q2 = pointer2->base->qualifiers;
-
-				// force t{1,2} to be cloned if their qualifiers must be stripped, so that
-				// pointer{,2}->base are unchanged
-				ast::ptr< ast::Type > t1{ pointer->base }, t2{ pointer2->base };
-				reset_qualifiers( t1 );
-				reset_qualifiers( t2 );
-
-				ast::OpenVarSet newOpen{ open };
-				if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden() ) ) {
-					result = pointer;
-					if ( q1.val != q2.val ) {
-						// reset result->base->qualifiers to be union of two base qualifiers
-						strict_dynamic_cast< ast::PointerType * >(
-							result.get_and_mutate()
-						)->base.get_and_mutate()->qualifiers = q1 | q2;
-					}
-				} else if ( isFtype (t1) && isFtype (t2) ) {
-					auto f1 = t1.as<ast::FunctionType>();
-					if (!f1) return;
-					auto f2 = t2.strict_as<ast::FunctionType>();
-
-					assertf(f1->returns.size() <= 1, "Function return should not be a list");
-					assertf(f2->returns.size() <= 1, "Function return should not be a list");
-
-					if (
-						( f1->params.size() != f2->params.size() || f1->returns.size() != f2->returns.size() )
-						&& ! f1->isTtype()
-						&& ! f2->isTtype()
-					) return;
-
-					auto params1 = flattenList( f1->params, tenv );
-					auto params2 = flattenList( f2->params, tenv );
-
-					auto crnt1 = params1.begin();
-					auto crnt2 = params2.begin();
-					auto end1 = params1.end();
-					auto end2 = params2.end();
-
-					while (crnt1 != end1 && crnt2 != end2 ) {
-						const ast::Type * arg1 = *crnt1;
-						const ast::Type * arg2 = *crnt2;
-
-						bool isTuple1 = Tuples::isTtype( t1 );
-						bool isTuple2 = Tuples::isTtype( t2 );
-
-						// assumes here that ttype *must* be last parameter
-						if ( isTuple1 && ! isTuple2 ) {
-							// combine remainder of list2, then unify
-							if (unifyExact(
-								arg1, tupleFromTypes( crnt2, end2 ), tenv, need, have, open,
-								noWiden() )) {
-									break;
-							} else return;
-						} else if ( ! isTuple1 && isTuple2 ) {
-							// combine remainder of list1, then unify
-							if (unifyExact(
-								tupleFromTypes( crnt1, end1 ), arg2, tenv, need, have, open,
-								noWiden() )) {
-									break;
-							} else return;
-						}
-
-						// allow qualifiers of pointer and reference base to become more specific
-						if (auto ref1 = dynamic_cast<const ast::ReferenceType *> (arg1)) {
-							if (auto ref2 = dynamic_cast<const ast::ReferenceType *> (arg2)) {
-								ast::ptr<ast::Type> base1 = ref1->base;
-								ast::ptr<ast::Type> base2 = ref2->base;
-
-								// xxx - assume LHS is always the target type
-
-								if ( ! ((widen.second && ref2->qualifiers.is_mutex)
-								|| (ref1->qualifiers.is_mutex == ref2->qualifiers.is_mutex ))) return;
-
-								if ( (widen.second && base1->qualifiers <= base2->qualifiers ) || (base2->qualifiers == base1->qualifiers) ) {
-
-									reset_qualifiers(base1);
-									reset_qualifiers(base2);
-
-									if ( !unifyExact(
-										base1, base2, tenv, need, have, open, noWiden() )
-									) return;
-								}
-							} else return;
-						} else if (auto ptr1 = dynamic_cast<const ast::PointerType *> (arg1)) {
-							if (auto ptr2 = dynamic_cast<const ast::PointerType *> (arg2)) {
-								ast::ptr<ast::Type> base1 = ptr1->base;
-								ast::ptr<ast::Type> base2 = ptr2->base;
-
-								// xxx - assume LHS is always the target type
-								// a function accepting const can always be called by non-const arg
-
-								if ( (widen.second && base1->qualifiers <= base2->qualifiers ) || (base2->qualifiers == base1->qualifiers) ) {
-
-									reset_qualifiers(base1);
-									reset_qualifiers(base2);
-
-									if ( ! unifyExact(
-										base1, base2, tenv, need, have, open, noWiden() )
-									) return;
-								}
-							} else return;
-						} else if (! unifyExact(
-								arg1, arg2, tenv, need, have, open, noWiden() )) {
-							return;
-						}
-						++crnt1; ++crnt2;
-					}
-					if ( crnt1 != end1 ) {
-						// try unifying empty tuple with ttype
-						const ast::Type * t1 = *crnt1;
-						if (! Tuples::isTtype( t1 ) ) return;
-						if (! unifyExact(
-							t1, tupleFromTypes( crnt2, end2 ), tenv, need, have, open,
-							noWiden() )) return;
-					} else if ( crnt2 != end2 ) {
-						// try unifying empty tuple with ttype
-						const ast::Type * t2 = *crnt2;
-						if ( !Tuples::isTtype( t2 ) ) return;
-						if ( !unifyExact(
-							tupleFromTypes( crnt1, end1 ), t2, tenv, need, have, open,
-							noWiden() )) return;
-					}
-					if ((f1->returns.size() == 0 && f2->returns.size() == 0)
-					  || (f1->returns.size() == 1 && f2->returns.size() == 1 && unifyExact(f1->returns[0], f2->returns[0], tenv, need, have, open, noWiden()))) {
-						result = pointer;
-
-						for (auto & assn : f1->assertions) {
-							auto i = need.find(assn);
-							if (i != need.end()) i->second.isUsed = true;
-							auto j = have.find(assn);
-							if (j != have.end()) j->second.isUsed = true;
-						}
-
-						for (auto & assn : f2->assertions) {
-							auto i = need.find(assn);
-							if (i != need.end()) i->second.isUsed = true;
-							auto j = have.find(assn);
-							if (j != have.end()) j->second.isUsed = true;
-						}
-					}
-				} // if ftype
-			}
-		} else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
-			result = pointer;
-			add_qualifiers( result, type2->qualifiers );
-		}
-	}
-
-	void postvisit( const ast::ArrayType * ) {}
-
-	void postvisit( const ast::ReferenceType * ref ) {
-		if ( auto ref2 = dynamic_cast< const ast::ReferenceType * >( type2 ) ) {
-			if (
-				widen.first && ref2->base.as< ast::VoidType >() && ! ast::isFtype( ref->base )
-			) {
-				getCommonWithVoidPointer( ref2, ref );
-			} else if (
-				widen.second && ref->base.as< ast::VoidType>() && ! ast::isFtype( ref2->base )
-			) {
-				getCommonWithVoidPointer( ref, ref2 );
-			} else if (
-				( ref->base->qualifiers >= ref2->base->qualifiers || widen.first )
-				&& ( ref->base->qualifiers <= ref2->base->qualifiers || widen.second )
-			) {
-				ast::CV::Qualifiers q1 = ref->base->qualifiers, q2 = ref2->base->qualifiers;
-
-				// force t{1,2} to be cloned if their qualifiers must be stripped, so that
-				// ref{,2}->base are unchanged
-				ast::ptr< ast::Type > t1{ ref->base }, t2{ ref2->base };
-				reset_qualifiers( t1 );
-				reset_qualifiers( t2 );
-
-				ast::OpenVarSet newOpen{ open };
-				if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden() ) ) {
-					result = ref;
-					if ( q1.val != q2.val ) {
-						// reset result->base->qualifiers to be union of two base qualifiers
-						strict_dynamic_cast< ast::ReferenceType * >(
-							result.get_and_mutate()
-						)->base.get_and_mutate()->qualifiers = q1 | q2;
-					}
-				}
-			}
-		} else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
-			result = ref;
-			add_qualifiers( result, type2->qualifiers );
-		} else {
-			if (!dynamic_cast<const ast::EnumInstType *>(type2))
-				result = commonType( type2, ref, tenv, need, have, open, widen );
-		}
-	}
-
-	void postvisit( const ast::FunctionType * ) {}
-
-	void postvisit( const ast::StructInstType * ) {}
-
-	void postvisit( const ast::UnionInstType * ) {}
-
-	void postvisit( const ast::EnumInstType * enumInst ) {
-		if ( enumInst->base && !enumInst->base->isTyped ) {
-			auto basicType = new ast::BasicType( ast::BasicKind::UnsignedInt );
-			result = commonType( basicType, type2, tenv, need, have, open, widen);
-		}
-	}
-
-	void postvisit( const ast::EnumAttrType * ) {}
-
-	void postvisit( const ast::TraitInstType * ) {}
-
-	void postvisit( const ast::TypeInstType * ) {}
-
-	void postvisit( const ast::TupleType * ) {}
-
-	void postvisit( const ast::VarArgsType * ) {}
-
-	void postvisit( const ast::ZeroType * zero ) {
-		if ( !widen.first ) return;
-		if ( dynamic_cast< const ast::BasicType * >( type2 )
-				|| dynamic_cast< const ast::PointerType * >( type2 ) ) {
-			if ( widen.second || zero->qualifiers <= type2->qualifiers ) {
-				result = type2;
-				add_qualifiers( result, zero->qualifiers );
-			}
-		} else if ( widen.second && dynamic_cast< const ast::OneType * >( type2 ) ) {
-			result = new ast::BasicType{
-				ast::BasicKind::SignedInt, zero->qualifiers | type2->qualifiers };
-		} else if ( const ast::EnumInstType * enumInst = dynamic_cast< const ast::EnumInstType * >( type2 ) ) {
-			const ast::EnumDecl * enumDecl = enumInst->base;
-			if ( !enumDecl->base ) {
-				if ( widen.second || zero->qualifiers <= type2->qualifiers ) {
-					result = type2;
-					add_qualifiers( result, zero->qualifiers );
-				}
-			}
-		}
-	}
-
-	void postvisit( const ast::OneType * one ) {
-		if ( !widen.first ) return;
-		if ( dynamic_cast< const ast::BasicType * >( type2 ) ) {
-			if ( widen.second || one->qualifiers <= type2->qualifiers ) {
-				result = type2;
-				add_qualifiers( result, one->qualifiers );
-			}
-		} else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
-			result = new ast::BasicType{
-				ast::BasicKind::SignedInt, one->qualifiers | type2->qualifiers };
-		} else if ( const ast::EnumInstType * enumInst = dynamic_cast< const ast::EnumInstType * >( type2 ) ) {
-			const ast::EnumDecl * enumDecl = enumInst->base;
-			if ( !enumDecl->base ) {
-				if ( widen.second || one->qualifiers <= type2->qualifiers ) {
-					result = type2;
-					add_qualifiers( result, one->qualifiers );
-				}
-			}
-		}
-	}
-};
-
-// size_t CommonType::traceId = Stats::Heap::new_stacktrace_id("CommonType");
-
-ast::ptr< ast::Type > handleReference(
-	const ast::ptr< ast::Type > & t1, const ast::ptr< ast::Type > & t2, WidenMode widen,
-	ast::TypeEnvironment & env,
-	const ast::OpenVarSet & open
-) {
-	ast::ptr<ast::Type> common;
-	ast::AssertionSet have, need;
-	ast::OpenVarSet newOpen( open );
-
-	// need unify to bind type variables
-	if ( unify( t1, t2, env, have, need, newOpen, common ) ) {
-		ast::CV::Qualifiers q1 = t1->qualifiers, q2 = t2->qualifiers;
-		PRINT(
-			std::cerr << "unify success: " << widenFirst << " " << widenSecond << std::endl;
-		)
-		if ( ( widen.first || q2 <= q1 ) && ( widen.second || q1 <= q2 ) ) {
-			PRINT(
-				std::cerr << "widen okay" << std::endl;
-			)
-			add_qualifiers( common, q1 | q2 );
-			return common;
-		}
-	}
-
-	PRINT(
-		std::cerr << "exact unify failed: " << t1 << " " << t2 << std::endl;
-	)
-	return { nullptr };
-}
-
-} // namespace
-
-ast::ptr< ast::Type > commonType(
-	const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2,
-	ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
-	const ast::OpenVarSet & open, WidenMode widen
-) {
-	unsigned depth1 = type1->referenceDepth();
-	unsigned depth2 = type2->referenceDepth();
-
-	if ( depth1 != depth2 ) {  // implies depth1 > 0 || depth2 > 0
-		PRINT(
-			std::cerr << "reference depth diff: " << (depth1-depth2) << std::endl;
-		)
-		ast::ptr< ast::Type > result;
-		const ast::ReferenceType * ref1 = type1.as< ast::ReferenceType >();
-		const ast::ReferenceType * ref2 = type2.as< ast::ReferenceType >();
-
-		if ( depth1 > depth2 ) {
-			assert( ref1 );
-			result = handleReference( ref1->base, type2, widen, env, open );
-		} else {  // Implies depth1 < depth2
-			assert( ref2 );
-			result = handleReference( type1, ref2->base, widen, env, open );
-		}
-
-		if ( result && ref1 ) {
-			// formal is reference, so result should be reference
-			PRINT(
-				std::cerr << "formal is reference; result should be reference" << std::endl;
-			)
-			result = new ast::ReferenceType{ result, ref1->qualifiers };
-		}
-
-		PRINT(
-			std::cerr << "common type of reference [" << type1 << "] and [" << type2 << "] is "
-			"[" << result << "]" << std::endl;
-		)
-		return result;
-	}
-	// otherwise both are reference types of the same depth and this is handled by the visitor
-	return ast::Pass<CommonType>::read( type1.get(),
-		type2, widen, env, open, need, have );
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/CommonType.cpp
===================================================================
--- src/ResolvExpr/CommonType.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/CommonType.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,795 @@
+//
+// 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.
+//
+// CommonType.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 06:59:27 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Thu Feb 14 17:10:10 2019
+// Update Count     : 24
+//
+
+#include "CommonType.hpp"
+
+#include <cassert>                       // for strict_dynamic_cast
+#include <map>                           // for _Rb_tree_const_iterator
+#include <utility>                       // for pair
+
+#include "AST/Decl.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+#include "Unify.hpp"                     // for unifyExact, WidenMode
+#include "Typeops.hpp"                   // for isFtype
+#include "Tuples/Tuples.hpp"
+
+// #define DEBUG
+#ifdef DEBUG
+#define PRINT(x) x
+#else
+#define PRINT(x)
+#endif
+
+namespace ResolvExpr {
+
+namespace {
+
+	// GENERATED START, DO NOT EDIT
+	// GENERATED BY BasicTypes-gen.cpp
+	#define BT ast::BasicKind::
+	static const ast::BasicKind commonTypes[BT NUMBER_OF_BASIC_TYPES][BT NUMBER_OF_BASIC_TYPES] = { // nearest common ancestor
+		/*		                        B                       C                      SC                      UC                      SI                     SUI
+				                        I                      UI                      LI                     LUI                     LLI                    LLUI
+				                       IB                     UIB                     _FH                     _FH                      _F                     _FC
+				                        F                      FC                     _FX                    _FXC                      FD                    _FDC
+				                        D                      DC                    F80X                   _FDXC                     F80                     _FB
+				                    _FLDC                      FB                      LD                     LDC                    _FBX                  _FLDXC
+				 */
+				  {
+		/*      B */                BT Bool,                BT Char,          BT SignedChar,        BT UnsignedChar,      BT ShortSignedInt,    BT ShortUnsignedInt,
+				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*      C */                BT Char,                BT Char,          BT SignedChar,        BT UnsignedChar,      BT ShortSignedInt,    BT ShortUnsignedInt,
+				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     SC */          BT SignedChar,          BT SignedChar,          BT SignedChar,        BT UnsignedChar,      BT ShortSignedInt,    BT ShortUnsignedInt,
+				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     UC */        BT UnsignedChar,        BT UnsignedChar,        BT UnsignedChar,        BT UnsignedChar,      BT ShortSignedInt,    BT ShortUnsignedInt,
+				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     SI */      BT ShortSignedInt,      BT ShortSignedInt,      BT ShortSignedInt,      BT ShortSignedInt,      BT ShortSignedInt,    BT ShortUnsignedInt,
+				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    SUI */    BT ShortUnsignedInt,    BT ShortUnsignedInt,    BT ShortUnsignedInt,    BT ShortUnsignedInt,    BT ShortUnsignedInt,    BT ShortUnsignedInt,
+				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*      I */           BT SignedInt,           BT SignedInt,           BT SignedInt,           BT SignedInt,           BT SignedInt,           BT SignedInt,
+				             BT SignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     UI */         BT UnsignedInt,         BT UnsignedInt,         BT UnsignedInt,         BT UnsignedInt,         BT UnsignedInt,         BT UnsignedInt,
+				           BT UnsignedInt,         BT UnsignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     LI */       BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,
+				         BT LongSignedInt,       BT LongSignedInt,       BT LongSignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    LUI */     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,
+				       BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,     BT LongUnsignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    LLI */   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,
+				     BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt,   BT LongLongSignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*   LLUI */ BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt,
+				   BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt, BT LongLongUnsignedInt,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     IB */        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,
+				          BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,        BT SignedInt128,
+				          BT SignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    UIB */      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,
+				        BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,      BT UnsignedInt128,
+				        BT UnsignedInt128,      BT UnsignedInt128,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    _FH */            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,
+				              BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,            BT uFloat16,
+				              BT uFloat16,            BT uFloat16,            BT uFloat16,     BT uFloat16Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    _FH */     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,
+				       BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,
+				       BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat16Complex,     BT uFloat32Complex,     BT uFloat32Complex,
+				          BT FloatComplex,        BT FloatComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,     BT uFloat64Complex,     BT uFloat64Complex,
+				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     _F */            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,
+				              BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,            BT uFloat32,
+				              BT uFloat32,            BT uFloat32,            BT uFloat32,     BT uFloat32Complex,            BT uFloat32,     BT uFloat32Complex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    _FC */     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,
+				       BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,
+				       BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,     BT uFloat32Complex,
+				          BT FloatComplex,        BT FloatComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,     BT uFloat64Complex,     BT uFloat64Complex,
+				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+				  {
+		/*      F */               BT Float,               BT Float,               BT Float,               BT Float,               BT Float,               BT Float,
+				                 BT Float,               BT Float,               BT Float,               BT Float,               BT Float,               BT Float,
+				                 BT Float,               BT Float,               BT Float,        BT FloatComplex,               BT Float,        BT FloatComplex,
+				                 BT Float,        BT FloatComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     FC */        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,
+				          BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,
+				          BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,        BT FloatComplex,
+				          BT FloatComplex,        BT FloatComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,     BT uFloat64Complex,     BT uFloat64Complex,
+				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    _FX */           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,
+				             BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,           BT uFloat32x,
+				             BT uFloat32x,           BT uFloat32x,           BT uFloat32x,    BT uFloat32xComplex,           BT uFloat32x,    BT uFloat32xComplex,
+				             BT uFloat32x,    BT uFloat32xComplex,           BT uFloat32x,    BT uFloat32xComplex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*   _FXC */    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,
+				      BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,
+				      BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,
+				      BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,    BT uFloat32xComplex,     BT uFloat64Complex,     BT uFloat64Complex,
+				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     FD */            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,
+				              BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,            BT uFloat64,
+				              BT uFloat64,            BT uFloat64,            BT uFloat64,     BT uFloat64Complex,            BT uFloat64,     BT uFloat64Complex,
+				              BT uFloat64,     BT uFloat64Complex,            BT uFloat64,     BT uFloat64Complex,            BT uFloat64,     BT uFloat64Complex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*   _FDC */     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,
+				       BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,
+				       BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,
+				       BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,     BT uFloat64Complex,
+				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+				  {
+		/*      D */              BT Double,              BT Double,              BT Double,              BT Double,              BT Double,              BT Double,
+				                BT Double,              BT Double,              BT Double,              BT Double,              BT Double,              BT Double,
+				                BT Double,              BT Double,              BT Double,       BT DoubleComplex,              BT Double,       BT DoubleComplex,
+				                BT Double,       BT DoubleComplex,              BT Double,       BT DoubleComplex,              BT Double,       BT DoubleComplex,
+				                BT Double,       BT DoubleComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     DC */       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,
+				         BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,
+				         BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,
+				         BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,       BT DoubleComplex,
+				         BT DoubleComplex,       BT DoubleComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+				  {
+		/*   F80X */           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,
+				             BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,           BT uFloat64x,
+				             BT uFloat64x,           BT uFloat64x,           BT uFloat64x,    BT uFloat64xComplex,           BT uFloat64x,    BT uFloat64xComplex,
+				             BT uFloat64x,    BT uFloat64xComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uFloat64x,    BT uFloat64xComplex,
+				             BT uFloat64x,    BT uFloat64xComplex,           BT uFloat64x,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*  _FDXC */    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,
+				      BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,
+				      BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,
+				      BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,
+				      BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat64xComplex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    F80 */           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,
+				             BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,           BT uuFloat80,
+				             BT uuFloat80,           BT uuFloat80,           BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,    BT uFloat64xComplex,
+				             BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,    BT uFloat64xComplex,
+				             BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,    BT uFloat64xComplex,           BT uuFloat80,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    _FB */           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,
+				             BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,           BT uFloat128,
+				             BT uFloat128,           BT uFloat128,           BT uFloat128,    BT uFloat128Complex,           BT uFloat128,    BT uFloat128Complex,
+				             BT uFloat128,    BT uFloat128Complex,           BT uFloat128,    BT uFloat128Complex,           BT uFloat128,    BT uFloat128Complex,
+				             BT uFloat128,    BT uFloat128Complex,           BT uFloat128,    BT uFloat128Complex,           BT uFloat128,           BT uFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*  _FLDC */    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,    BT uFloat128Complex,
+				      BT uFloat128Complex,    BT uFloat128Complex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     FB */          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,
+				            BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,          BT uuFloat128,
+				            BT uuFloat128,          BT uuFloat128,          BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,    BT uFloat128Complex,
+				            BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,    BT uFloat128Complex,
+				            BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,    BT uFloat128Complex,          BT uuFloat128,          BT uuFloat128,
+				      BT uFloat128Complex,          BT uuFloat128,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*     LD */          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,
+				            BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,          BT LongDouble,
+				            BT LongDouble,          BT LongDouble,          BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,   BT LongDoubleComplex,
+				            BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,   BT LongDoubleComplex,
+				            BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,   BT LongDoubleComplex,          BT LongDouble,          BT LongDouble,
+				     BT LongDoubleComplex,          BT LongDouble,          BT LongDouble,   BT LongDoubleComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/*    LDC */   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
+				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
+				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
+				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
+				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,
+				     BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT LongDoubleComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+				  {
+		/*   _FBX */          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,
+				            BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,          BT uFloat128x,
+				            BT uFloat128x,          BT uFloat128x,          BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				            BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				            BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,          BT uFloat128x,
+				     BT uFloat128xComplex,          BT uFloat128x,          BT uFloat128x,   BT uFloat128xComplex,          BT uFloat128x,   BT uFloat128xComplex,
+				  },
+				  {
+		/* _FLDXC */   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				     BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,   BT uFloat128xComplex,
+				  },
+	}; // commonTypes
+	#undef BT
+	// GENERATED END
+	static_assert(
+		sizeof(commonTypes)/sizeof(commonTypes[0][0]) == ast::BasicKind::NUMBER_OF_BASIC_TYPES * ast::BasicKind::NUMBER_OF_BASIC_TYPES,
+		"Each basic type kind should have a corresponding row in the combined type matrix"
+	);
+
+class CommonType final : public ast::WithShortCircuiting {
+	const ast::Type * type2;
+	WidenMode widen;
+	ast::TypeEnvironment & tenv;
+	const ast::OpenVarSet & open;
+	ast::AssertionSet & need;
+	ast::AssertionSet & have;
+public:
+	static size_t traceId;
+	ast::ptr< ast::Type > result;
+
+	CommonType(
+		const ast::Type * t2, WidenMode w,
+		ast::TypeEnvironment & env, const ast::OpenVarSet & o,
+		ast::AssertionSet & need, ast::AssertionSet & have )
+	: type2( t2 ), widen( w ), tenv( env ), open( o ), need (need), have (have) ,result() {}
+
+	void previsit( const ast::Node * ) { visit_children = false; }
+
+	void postvisit( const ast::VoidType * ) {}
+
+	void postvisit( const ast::BasicType * basic ) {
+		if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) {
+			ast::BasicKind kind;
+			if (basic->kind != basic2->kind && !widen.first && !widen.second) return;
+			else if (!widen.first) kind = basic->kind; // widen.second
+			else if (!widen.second) kind = basic2->kind;
+			else kind = commonTypes[ basic->kind ][ basic2->kind ];
+			// xxx - what does qualifiers even do here??
+			if ( (basic->qualifiers >= basic2->qualifiers || widen.first)
+				&& (basic->qualifiers <= basic2->qualifiers || widen.second) ) {
+				result = new ast::BasicType{ kind, basic->qualifiers | basic2->qualifiers };
+			}
+		} else if (
+			dynamic_cast< const ast::ZeroType * >( type2 )
+			|| dynamic_cast< const ast::OneType * >( type2 )
+		) {
+			if (widen.second) {
+				result = new ast::BasicType{ basic->kind, basic->qualifiers | type2->qualifiers };
+			}
+		} else if ( const ast::EnumInstType * enumInst = dynamic_cast< const ast::EnumInstType * >( type2 ) ) {
+			const ast::EnumDecl* enumDecl = enumInst->base;
+			if ( !enumDecl->base ) {
+				ast::BasicKind kind = commonTypes[ basic->kind ][ ast::BasicKind::SignedInt ];
+				if (
+					( ( kind == basic->kind && basic->qualifiers >= type2->qualifiers )
+						|| widen.first )
+					&& ( ( kind != basic->kind && basic->qualifiers <= type2->qualifiers )
+						|| widen.second )
+				) {
+					result = new ast::BasicType{ kind, basic->qualifiers | type2->qualifiers };
+				}
+			}
+		} else if ( auto type2AsAttr = dynamic_cast< const ast::EnumAttrType * >( type2 ) ) {
+			if ( type2AsAttr->attr == ast::EnumAttribute::Posn ) {
+				ast::BasicKind kind = commonTypes[ basic->kind ][ ast::BasicKind::SignedInt ];
+				if (
+					( ( kind == basic->kind && basic->qualifiers >= type2->qualifiers )
+						|| widen.first )
+					&& ( ( kind != basic->kind && basic->qualifiers <= type2->qualifiers )
+						|| widen.second )
+				) {
+					result = new ast::BasicType{ kind, basic->qualifiers | type2->qualifiers };
+				}
+			}
+		}
+	}
+
+private:
+	template< typename Pointer >
+	void getCommonWithVoidPointer( const Pointer * voidPtr, const Pointer * oPtr ) {
+		const ast::Type * base = oPtr->base;
+		if ( auto var = dynamic_cast< const ast::TypeInstType * >( base ) ) {
+			auto entry = open.find( *var );
+			if ( entry != open.end() ) {
+				ast::AssertionSet need, have;
+				if ( ! tenv.bindVar(
+					var, voidPtr->base, entry->second, need, have, open, widen )
+				) return;
+			}
+		}
+		result = voidPtr;
+		add_qualifiers( result, oPtr->qualifiers );
+	}
+
+public:
+	void postvisit( const ast::PointerType * pointer ) {
+		if ( auto pointer2 = dynamic_cast< const ast::PointerType * >( type2 ) ) {
+			if (
+				widen.first
+				&& pointer2->base.as< ast::VoidType >()
+				&& ! ast::isFtype( pointer->base )
+			) {
+				getCommonWithVoidPointer( pointer2, pointer );
+			} else if (
+				widen.second
+				&& pointer->base.as< ast::VoidType >()
+				&& ! ast::isFtype( pointer2->base )
+			) {
+				getCommonWithVoidPointer( pointer, pointer2 );
+			} else if (
+				( pointer->base->qualifiers >= pointer2->base->qualifiers || widen.first )
+				&& ( pointer->base->qualifiers <= pointer2->base->qualifiers || widen.second )
+			) {
+				ast::CV::Qualifiers q1 = pointer->base->qualifiers;
+				ast::CV::Qualifiers q2 = pointer2->base->qualifiers;
+
+				// force t{1,2} to be cloned if their qualifiers must be stripped, so that
+				// pointer{,2}->base are unchanged
+				ast::ptr< ast::Type > t1{ pointer->base }, t2{ pointer2->base };
+				reset_qualifiers( t1 );
+				reset_qualifiers( t2 );
+
+				ast::OpenVarSet newOpen{ open };
+				if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden() ) ) {
+					result = pointer;
+					if ( q1.val != q2.val ) {
+						// reset result->base->qualifiers to be union of two base qualifiers
+						strict_dynamic_cast< ast::PointerType * >(
+							result.get_and_mutate()
+						)->base.get_and_mutate()->qualifiers = q1 | q2;
+					}
+				} else if ( isFtype (t1) && isFtype (t2) ) {
+					auto f1 = t1.as<ast::FunctionType>();
+					if (!f1) return;
+					auto f2 = t2.strict_as<ast::FunctionType>();
+
+					assertf(f1->returns.size() <= 1, "Function return should not be a list");
+					assertf(f2->returns.size() <= 1, "Function return should not be a list");
+
+					if (
+						( f1->params.size() != f2->params.size() || f1->returns.size() != f2->returns.size() )
+						&& ! f1->isTtype()
+						&& ! f2->isTtype()
+					) return;
+
+					auto params1 = flattenList( f1->params, tenv );
+					auto params2 = flattenList( f2->params, tenv );
+
+					auto crnt1 = params1.begin();
+					auto crnt2 = params2.begin();
+					auto end1 = params1.end();
+					auto end2 = params2.end();
+
+					while (crnt1 != end1 && crnt2 != end2 ) {
+						const ast::Type * arg1 = *crnt1;
+						const ast::Type * arg2 = *crnt2;
+
+						bool isTuple1 = Tuples::isTtype( t1 );
+						bool isTuple2 = Tuples::isTtype( t2 );
+
+						// assumes here that ttype *must* be last parameter
+						if ( isTuple1 && ! isTuple2 ) {
+							// combine remainder of list2, then unify
+							if (unifyExact(
+								arg1, tupleFromTypes( crnt2, end2 ), tenv, need, have, open,
+								noWiden() )) {
+									break;
+							} else return;
+						} else if ( ! isTuple1 && isTuple2 ) {
+							// combine remainder of list1, then unify
+							if (unifyExact(
+								tupleFromTypes( crnt1, end1 ), arg2, tenv, need, have, open,
+								noWiden() )) {
+									break;
+							} else return;
+						}
+
+						// allow qualifiers of pointer and reference base to become more specific
+						if (auto ref1 = dynamic_cast<const ast::ReferenceType *> (arg1)) {
+							if (auto ref2 = dynamic_cast<const ast::ReferenceType *> (arg2)) {
+								ast::ptr<ast::Type> base1 = ref1->base;
+								ast::ptr<ast::Type> base2 = ref2->base;
+
+								// xxx - assume LHS is always the target type
+
+								if ( ! ((widen.second && ref2->qualifiers.is_mutex)
+								|| (ref1->qualifiers.is_mutex == ref2->qualifiers.is_mutex ))) return;
+
+								if ( (widen.second && base1->qualifiers <= base2->qualifiers ) || (base2->qualifiers == base1->qualifiers) ) {
+
+									reset_qualifiers(base1);
+									reset_qualifiers(base2);
+
+									if ( !unifyExact(
+										base1, base2, tenv, need, have, open, noWiden() )
+									) return;
+								}
+							} else return;
+						} else if (auto ptr1 = dynamic_cast<const ast::PointerType *> (arg1)) {
+							if (auto ptr2 = dynamic_cast<const ast::PointerType *> (arg2)) {
+								ast::ptr<ast::Type> base1 = ptr1->base;
+								ast::ptr<ast::Type> base2 = ptr2->base;
+
+								// xxx - assume LHS is always the target type
+								// a function accepting const can always be called by non-const arg
+
+								if ( (widen.second && base1->qualifiers <= base2->qualifiers ) || (base2->qualifiers == base1->qualifiers) ) {
+
+									reset_qualifiers(base1);
+									reset_qualifiers(base2);
+
+									if ( ! unifyExact(
+										base1, base2, tenv, need, have, open, noWiden() )
+									) return;
+								}
+							} else return;
+						} else if (! unifyExact(
+								arg1, arg2, tenv, need, have, open, noWiden() )) {
+							return;
+						}
+						++crnt1; ++crnt2;
+					}
+					if ( crnt1 != end1 ) {
+						// try unifying empty tuple with ttype
+						const ast::Type * t1 = *crnt1;
+						if (! Tuples::isTtype( t1 ) ) return;
+						if (! unifyExact(
+							t1, tupleFromTypes( crnt2, end2 ), tenv, need, have, open,
+							noWiden() )) return;
+					} else if ( crnt2 != end2 ) {
+						// try unifying empty tuple with ttype
+						const ast::Type * t2 = *crnt2;
+						if ( !Tuples::isTtype( t2 ) ) return;
+						if ( !unifyExact(
+							tupleFromTypes( crnt1, end1 ), t2, tenv, need, have, open,
+							noWiden() )) return;
+					}
+					if ((f1->returns.size() == 0 && f2->returns.size() == 0)
+					  || (f1->returns.size() == 1 && f2->returns.size() == 1 && unifyExact(f1->returns[0], f2->returns[0], tenv, need, have, open, noWiden()))) {
+						result = pointer;
+
+						for (auto & assn : f1->assertions) {
+							auto i = need.find(assn);
+							if (i != need.end()) i->second.isUsed = true;
+							auto j = have.find(assn);
+							if (j != have.end()) j->second.isUsed = true;
+						}
+
+						for (auto & assn : f2->assertions) {
+							auto i = need.find(assn);
+							if (i != need.end()) i->second.isUsed = true;
+							auto j = have.find(assn);
+							if (j != have.end()) j->second.isUsed = true;
+						}
+					}
+				} // if ftype
+			}
+		} else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
+			result = pointer;
+			add_qualifiers( result, type2->qualifiers );
+		}
+	}
+
+	void postvisit( const ast::ArrayType * ) {}
+
+	void postvisit( const ast::ReferenceType * ref ) {
+		if ( auto ref2 = dynamic_cast< const ast::ReferenceType * >( type2 ) ) {
+			if (
+				widen.first && ref2->base.as< ast::VoidType >() && ! ast::isFtype( ref->base )
+			) {
+				getCommonWithVoidPointer( ref2, ref );
+			} else if (
+				widen.second && ref->base.as< ast::VoidType>() && ! ast::isFtype( ref2->base )
+			) {
+				getCommonWithVoidPointer( ref, ref2 );
+			} else if (
+				( ref->base->qualifiers >= ref2->base->qualifiers || widen.first )
+				&& ( ref->base->qualifiers <= ref2->base->qualifiers || widen.second )
+			) {
+				ast::CV::Qualifiers q1 = ref->base->qualifiers, q2 = ref2->base->qualifiers;
+
+				// force t{1,2} to be cloned if their qualifiers must be stripped, so that
+				// ref{,2}->base are unchanged
+				ast::ptr< ast::Type > t1{ ref->base }, t2{ ref2->base };
+				reset_qualifiers( t1 );
+				reset_qualifiers( t2 );
+
+				ast::OpenVarSet newOpen{ open };
+				if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden() ) ) {
+					result = ref;
+					if ( q1.val != q2.val ) {
+						// reset result->base->qualifiers to be union of two base qualifiers
+						strict_dynamic_cast< ast::ReferenceType * >(
+							result.get_and_mutate()
+						)->base.get_and_mutate()->qualifiers = q1 | q2;
+					}
+				}
+			}
+		} else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
+			result = ref;
+			add_qualifiers( result, type2->qualifiers );
+		} else {
+			if (!dynamic_cast<const ast::EnumInstType *>(type2))
+				result = commonType( type2, ref, tenv, need, have, open, widen );
+		}
+	}
+
+	void postvisit( const ast::FunctionType * ) {}
+
+	void postvisit( const ast::StructInstType * ) {}
+
+	void postvisit( const ast::UnionInstType * ) {}
+
+	void postvisit( const ast::EnumInstType * enumInst ) {
+		if ( enumInst->base && !enumInst->base->isTyped ) {
+			auto basicType = new ast::BasicType( ast::BasicKind::UnsignedInt );
+			result = commonType( basicType, type2, tenv, need, have, open, widen);
+		}
+	}
+
+	void postvisit( const ast::EnumAttrType * ) {}
+
+	void postvisit( const ast::TraitInstType * ) {}
+
+	void postvisit( const ast::TypeInstType * ) {}
+
+	void postvisit( const ast::TupleType * ) {}
+
+	void postvisit( const ast::VarArgsType * ) {}
+
+	void postvisit( const ast::ZeroType * zero ) {
+		if ( !widen.first ) return;
+		if ( dynamic_cast< const ast::BasicType * >( type2 )
+				|| dynamic_cast< const ast::PointerType * >( type2 ) ) {
+			if ( widen.second || zero->qualifiers <= type2->qualifiers ) {
+				result = type2;
+				add_qualifiers( result, zero->qualifiers );
+			}
+		} else if ( widen.second && dynamic_cast< const ast::OneType * >( type2 ) ) {
+			result = new ast::BasicType{
+				ast::BasicKind::SignedInt, zero->qualifiers | type2->qualifiers };
+		} else if ( const ast::EnumInstType * enumInst = dynamic_cast< const ast::EnumInstType * >( type2 ) ) {
+			const ast::EnumDecl * enumDecl = enumInst->base;
+			if ( !enumDecl->base ) {
+				if ( widen.second || zero->qualifiers <= type2->qualifiers ) {
+					result = type2;
+					add_qualifiers( result, zero->qualifiers );
+				}
+			}
+		}
+	}
+
+	void postvisit( const ast::OneType * one ) {
+		if ( !widen.first ) return;
+		if ( dynamic_cast< const ast::BasicType * >( type2 ) ) {
+			if ( widen.second || one->qualifiers <= type2->qualifiers ) {
+				result = type2;
+				add_qualifiers( result, one->qualifiers );
+			}
+		} else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
+			result = new ast::BasicType{
+				ast::BasicKind::SignedInt, one->qualifiers | type2->qualifiers };
+		} else if ( const ast::EnumInstType * enumInst = dynamic_cast< const ast::EnumInstType * >( type2 ) ) {
+			const ast::EnumDecl * enumDecl = enumInst->base;
+			if ( !enumDecl->base ) {
+				if ( widen.second || one->qualifiers <= type2->qualifiers ) {
+					result = type2;
+					add_qualifiers( result, one->qualifiers );
+				}
+			}
+		}
+	}
+};
+
+// size_t CommonType::traceId = Stats::Heap::new_stacktrace_id("CommonType");
+
+ast::ptr< ast::Type > handleReference(
+	const ast::ptr< ast::Type > & t1, const ast::ptr< ast::Type > & t2, WidenMode widen,
+	ast::TypeEnvironment & env,
+	const ast::OpenVarSet & open
+) {
+	ast::ptr<ast::Type> common;
+	ast::AssertionSet have, need;
+	ast::OpenVarSet newOpen( open );
+
+	// need unify to bind type variables
+	if ( unify( t1, t2, env, have, need, newOpen, common ) ) {
+		ast::CV::Qualifiers q1 = t1->qualifiers, q2 = t2->qualifiers;
+		PRINT(
+			std::cerr << "unify success: " << widenFirst << " " << widenSecond << std::endl;
+		)
+		if ( ( widen.first || q2 <= q1 ) && ( widen.second || q1 <= q2 ) ) {
+			PRINT(
+				std::cerr << "widen okay" << std::endl;
+			)
+			add_qualifiers( common, q1 | q2 );
+			return common;
+		}
+	}
+
+	PRINT(
+		std::cerr << "exact unify failed: " << t1 << " " << t2 << std::endl;
+	)
+	return { nullptr };
+}
+
+} // namespace
+
+ast::ptr< ast::Type > commonType(
+	const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2,
+	ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
+	const ast::OpenVarSet & open, WidenMode widen
+) {
+	unsigned depth1 = type1->referenceDepth();
+	unsigned depth2 = type2->referenceDepth();
+
+	if ( depth1 != depth2 ) {  // implies depth1 > 0 || depth2 > 0
+		PRINT(
+			std::cerr << "reference depth diff: " << (depth1-depth2) << std::endl;
+		)
+		ast::ptr< ast::Type > result;
+		const ast::ReferenceType * ref1 = type1.as< ast::ReferenceType >();
+		const ast::ReferenceType * ref2 = type2.as< ast::ReferenceType >();
+
+		if ( depth1 > depth2 ) {
+			assert( ref1 );
+			result = handleReference( ref1->base, type2, widen, env, open );
+		} else {  // Implies depth1 < depth2
+			assert( ref2 );
+			result = handleReference( type1, ref2->base, widen, env, open );
+		}
+
+		if ( result && ref1 ) {
+			// formal is reference, so result should be reference
+			PRINT(
+				std::cerr << "formal is reference; result should be reference" << std::endl;
+			)
+			result = new ast::ReferenceType{ result, ref1->qualifiers };
+		}
+
+		PRINT(
+			std::cerr << "common type of reference [" << type1 << "] and [" << type2 << "] is "
+			"[" << result << "]" << std::endl;
+		)
+		return result;
+	}
+	// otherwise both are reference types of the same depth and this is handled by the visitor
+	return ast::Pass<CommonType>::read( type1.get(),
+		type2, widen, env, open, need, have );
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/CommonType.hpp
===================================================================
--- src/ResolvExpr/CommonType.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ResolvExpr/CommonType.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -18,5 +18,5 @@
 #include "AST/Fwd.hpp"
 #include "AST/TypeEnvironment.hpp"  // for AssertionSet, OpenVarSet
-#include "WidenMode.h"              // for WidenMode
+#include "WidenMode.hpp"            // for WidenMode
 
 namespace ResolvExpr {
Index: src/ResolvExpr/ConversionCost.cc
===================================================================
--- src/ResolvExpr/ConversionCost.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,517 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ConversionCost.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 07:06:19 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jul 29 16:11:00 2020
-// Update Count     : 28
-//
-
-#include "ConversionCost.h"
-
-#include <cassert>                       // for assert
-#include <list>                          // for list, list<>::const_iterator
-#include <string>                        // for operator==, string
-
-#include "ResolvExpr/Cost.h"             // for Cost
-#include "ResolvExpr/Unify.h"            // for typesCompatibleIgnoreQualifiers
-#include "ResolvExpr/PtrsAssignable.hpp" // for ptrsAssignable
-
-namespace ResolvExpr {
-
-#if 0
-#define PRINT(x) x
-#else
-#define PRINT(x)
-#endif
-
-namespace {
-
-	// GENERATED START, DO NOT EDIT
-	// GENERATED BY BasicTypes-gen.cpp
-	/* EXTENDED INTEGRAL RANK HIERARCHY (root to leaves)
-	                         _Bool
-	char                signed char         unsigned char
-	          signed short int         unsigned short int
-	          signed int               unsigned int
-	          signed long int          unsigned long int
-	          signed long long int     unsigned long long int
-	          __int128                 unsigned __int128
-	          _Float16                 _Float16 _Complex
-	          _Float32                 _Float32 _Complex
-	          float                    float _Complex
-	          _Float32x                _Float32x _Complex
-	          _Float64                 _Float64 _Complex
-	          double                   double _Complex
-	          _Float64x                _Float64x _Complex
-	                     __float80
-	          _Float128                _Float128 _Complex
-	                    __float128
-	          long double              long double _Complex
-	          _Float128x               _Float128x _Complex
-	*/
-	// GENERATED END
-
-	// GENERATED START, DO NOT EDIT
-	// GENERATED BY BasicTypes-gen.cpp
-	static const int costMatrix[ast::BasicKind::NUMBER_OF_BASIC_TYPES][ast::BasicKind::NUMBER_OF_BASIC_TYPES] = { // path length from root to node
-		/*               B    C   SC   UC   SI  SUI    I   UI   LI  LUI  LLI LLUI   IB  UIB  _FH  _FH   _F  _FC    F   FC  _FX _FXC   FD _FDC    D   DC F80X_FDXC  F80  _FB_FLDC   FB   LD  LDC _FBX_FLDXC */
-		/*      B */ {   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  14,  15,  15,  16,  17,  16,  18,  17, },
-		/*      C */ {  -1,   0,   1,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  14,  15,  16,  15,  17,  16, },
-		/*     SC */ {  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  14,  15,  16,  15,  17,  16, },
-		/*     UC */ {  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  14,  15,  16,  15,  17,  16, },
-		/*     SI */ {  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  15,  14,  16,  15, },
-		/*    SUI */ {  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  15,  14,  16,  15, },
-		/*      I */ {  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  14,  13,  15,  14, },
-		/*     UI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  14,  13,  15,  14, },
-		/*     LI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  13,  12,  14,  13, },
-		/*    LUI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  13,  12,  14,  13, },
-		/*    LLI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  12,  11,  13,  12, },
-		/*   LLUI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  12,  11,  13,  12, },
-		/*     IB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  11,  10,  12,  11, },
-		/*    UIB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  11,  10,  12,  11, },
-		/*    _FH */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,  10,   9,  11,  10, },
-		/*    _FH */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,   3,  -1,   4,  -1,   5,  -1,   6,  -1,  -1,   7,  -1,  -1,   8,  -1,   9, },
-		/*     _F */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   9,   8,  10,   9, },
-		/*    _FC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,   3,  -1,   4,  -1,   5,  -1,  -1,   6,  -1,  -1,   7,  -1,   8, },
-		/*      F */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   8,   7,   9,   8, },
-		/*     FC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,   3,  -1,   4,  -1,  -1,   5,  -1,  -1,   6,  -1,   7, },
-		/*    _FX */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   7,   6,   8,   7, },
-		/*   _FXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,   3,  -1,  -1,   4,  -1,  -1,   5,  -1,   6, },
-		/*     FD */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   6,   5,   7,   6, },
-		/*   _FDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,  -1,   3,  -1,  -1,   4,  -1,   5, },
-		/*      D */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   5,   4,   6,   5, },
-		/*     DC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,  -1,   2,  -1,  -1,   3,  -1,   4, },
-		/*   F80X */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   4,   3,   5,   4, },
-		/*  _FDXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,  -1,   1,  -1,  -1,   2,  -1,   3, },
-		/*    F80 */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,   0,   1,   2,   2,   3,   3,   4,   4, },
-		/*    _FB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3, },
-		/*  _FLDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,  -1,   1,  -1,   2, },
-		/*     FB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,   0,   1,   2,   2,   3, },
-		/*     LD */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2, },
-		/*    LDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1, },
-		/*   _FBX */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1, },
-		/* _FLDXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0, },
-	}; // costMatrix
-	static const int maxIntCost = 15;
-	// GENERATED END
-	static_assert(
-		sizeof(costMatrix)/sizeof(costMatrix[0][0]) == ast::BasicKind::NUMBER_OF_BASIC_TYPES * ast::BasicKind::NUMBER_OF_BASIC_TYPES,
-		"Missing row in the cost matrix"
-	);
-
-	// GENERATED START, DO NOT EDIT
-	// GENERATED BY BasicTypes-gen.cpp
-	static const int signMatrix[ast::BasicKind::NUMBER_OF_BASIC_TYPES][ast::BasicKind::NUMBER_OF_BASIC_TYPES] = { // number of sign changes in safe conversion
-		/*               B    C   SC   UC   SI  SUI    I   UI   LI  LUI  LLI LLUI   IB  UIB  _FH  _FH   _F  _FC    F   FC  _FX _FXC   FD _FDC    D   DC F80X_FDXC  F80  _FB_FLDC   FB   LD  LDC _FBX_FLDXC */
-		/*      B */ {   0,   0,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*      C */ {  -1,   0,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*     SC */ {  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*     UC */ {  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*     SI */ {  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*    SUI */ {  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*      I */ {  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*     UI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*     LI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*    LUI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*    LLI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*   LLUI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*     IB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*    UIB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*    _FH */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*    _FH */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
-		/*     _F */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*    _FC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
-		/*      F */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*     FC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
-		/*    _FX */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*   _FXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
-		/*     FD */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*   _FDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
-		/*      D */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*     DC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
-		/*   F80X */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*  _FDXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
-		/*    F80 */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
-		/*    _FB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0, },
-		/*  _FLDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
-		/*     FB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0, },
-		/*     LD */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0, },
-		/*    LDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0, },
-		/*   _FBX */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0, },
-		/* _FLDXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0, },
-	}; // signMatrix
-	// GENERATED END
-	static_assert(
-		sizeof(signMatrix)/sizeof(signMatrix[0][0]) == ast::BasicKind::NUMBER_OF_BASIC_TYPES * ast::BasicKind::NUMBER_OF_BASIC_TYPES,
-		"Missing row in the sign matrix"
-	);
-
-	int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2,
-			const ast::SymbolTable &, const ast::TypeEnvironment & env ) {
-		return ptrsAssignable( t1, t2, env );
-	}
-}
-
-Cost conversionCost(
-	const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
-	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
-) {
-	if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( const ast::EqvClass * eqv = env.lookup( *inst ) ) {
-			if ( eqv->bound ) {
-				return conversionCost(src, eqv->bound, srcIsLvalue, symtab, env );
-			} else {
-				return Cost::infinity;
-			}
-		} else if ( const ast::NamedTypeDecl * named = symtab.lookupType( inst->name ) ) {
-			const ast::TypeDecl * type = dynamic_cast< const ast::TypeDecl * >( named );
-			assertf( type, "Unexpected typedef." );
-			if ( type->base ) {
-				return conversionCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe;
-			}
-		}
-	}
-	if ( typesCompatibleIgnoreQualifiers( src, dst, env ) ) {
-		return Cost::zero;
-	} else if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
-		return Cost::safe;
-	} else if ( const ast::ReferenceType * refType =
-			 dynamic_cast< const ast::ReferenceType * >( dst ) ) {
-		return convertToReferenceCost( src, refType, srcIsLvalue, symtab, env, localPtrsAssignable );
-	} else {
-		return ast::Pass<ConversionCost>::read( src, dst, srcIsLvalue, symtab, env, conversionCost );
-	}
-}
-
-static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
-		int diff, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
-		PtrsCalculation func ) {
-	if ( 0 < diff ) {
-		Cost cost = convertToReferenceCost(
-			strict_dynamic_cast< const ast::ReferenceType * >( src )->base, dst,
-			srcIsLvalue, (diff - 1), symtab, env, func );
-		cost.incReference();
-		return cost;
-	} else if ( diff < -1 ) {
-		Cost cost = convertToReferenceCost(
-			src, strict_dynamic_cast< const ast::ReferenceType * >( dst )->base,
-			srcIsLvalue, (diff + 1), symtab, env, func );
-		cost.incReference();
-		return cost;
-	} else if ( 0 == diff ) {
-		const ast::ReferenceType * srcAsRef = dynamic_cast< const ast::ReferenceType * >( src );
-		const ast::ReferenceType * dstAsRef = dynamic_cast< const ast::ReferenceType * >( dst );
-		if ( srcAsRef && dstAsRef ) {
-			ast::CV::Qualifiers tq1 = srcAsRef->base->qualifiers;
-			ast::CV::Qualifiers tq2 = dstAsRef->base->qualifiers;
-			if ( tq1 <= tq2 && typesCompatibleIgnoreQualifiers(
-					srcAsRef->base, dstAsRef->base, env ) ) {
-				if ( tq1 == tq2 ) {
-					return Cost::zero;
-				} else {
-					return Cost::safe;
-				}
-			} else {
-				int assignResult = func( srcAsRef->base, dstAsRef->base, symtab, env );
-				if ( 0 < assignResult ) {
-					return Cost::safe;
-				} else if ( assignResult < 0 ) {
-					return Cost::unsafe;
-				}
-			}
-		} else {
-			return ast::Pass<ConversionCost>::read( src, dst, srcIsLvalue, symtab, env, conversionCost );
-		}
-	} else {
-		assert( -1 == diff );
-		const ast::ReferenceType * dstAsRef = dynamic_cast< const ast::ReferenceType * >( dst );
-		assert( dstAsRef );
-		if ( typesCompatibleIgnoreQualifiers( src, dstAsRef->base, env ) ) {
-			if ( srcIsLvalue ) {
-				if ( src->qualifiers == dstAsRef->base->qualifiers ) {
-					return Cost::reference;
-				} else if ( src->qualifiers < dstAsRef->base->qualifiers ) {
-					return Cost::safe;
-				} else {
-					return Cost::unsafe;
-				}
-			} else if ( dstAsRef->base->is_const() ) {
-				return Cost::safe;
-			} else {
-				return Cost::unsafe;
-			}
-		}
-	}
-	return Cost::infinity;
-}
-
-Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dst,
-		bool srcIsLvalue, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
-		PtrsCalculation func ) {
-	int sdepth = src->referenceDepth(), ddepth = dst->referenceDepth();
-	return convertToReferenceCost( src, dst, srcIsLvalue, sdepth - ddepth, symtab, env, func );
-}
-
-void ConversionCost::postvisit( const ast::VoidType * voidType ) {
-	(void)voidType;
-	cost = Cost::infinity;
-}
-
-void ConversionCost::conversionCostFromBasicToBasic( const ast::BasicType * src, const ast::BasicType* dest ) {
-	int tableResult = costMatrix[ src->kind ][ dest->kind ];
-	if ( tableResult == -1 ) {
-		cost = Cost::unsafe;
-	} else {
-		cost = Cost::zero;
-		cost.incSafe( tableResult );
-		cost.incSign( signMatrix[ src->kind ][ dest->kind ] );
-	}
-}
-
-void ConversionCost::postvisit( const ast::BasicType * basicType ) {
-	if ( const ast::BasicType * dstAsBasic = dynamic_cast< const ast::BasicType * >( dst ) ) {
-		conversionCostFromBasicToBasic( basicType, dstAsBasic );
-	} else if ( dynamic_cast< const ast::EnumAttrType *>(dst) ) {
-		static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicKind::SignedInt ) };
-		cost = costCalc( basicType, integer, srcIsLvalue, symtab, env );
-	} else if ( auto dstAsEnumInst = dynamic_cast< const ast::EnumInstType * >( dst ) ) {
-		if ( dstAsEnumInst->base && !dstAsEnumInst->base->isTyped ) {
-			cost = Cost::unsafe;
-		}
-	}
-}
-
-void ConversionCost::postvisit( const ast::PointerType * pointerType ) {
-	if ( const ast::PointerType * dstAsPtr = dynamic_cast< const ast::PointerType * >( dst ) ) {
-		ast::CV::Qualifiers tq1 = pointerType->base->qualifiers;
-		ast::CV::Qualifiers tq2 = dstAsPtr->base->qualifiers;
-		if ( tq1 <= tq2 && typesCompatibleIgnoreQualifiers(
-				pointerType->base, dstAsPtr->base, env ) ) {
-			if ( tq1 == tq2 ) {
-				cost = Cost::zero;
-			} else {
-				cost = Cost::safe;
-			}
-		}
-		/*
-		else if ( const ast::FunctionType * dstFunc = dstAsPtr->base.as<ast::FunctionType>()) {
-			if (const ast::FunctionType * srcFunc = pointerType->base.as<ast::FunctionType>()) {
-				if (dstFunc->params.empty() && dstFunc->isVarArgs ) {
-					cost = Cost::unsafe; // assign any function to variadic fptr
-				}
-			}
-			else {
-				ast::AssertionSet need, have; // unused
-				ast::OpenVarSet open;
-				env.extractOpenVars(open);
-				ast::TypeEnvironment tenv = env;
-				if ( unify(dstAsPtr->base, pointerType->base, tenv, need, have, open, symtab) ) {
-					cost = Cost::safe;
-				}
-			}
-			// else infinity
-		}
-		*/
-		else {
-			int assignResult = ptrsAssignable( pointerType->base, dstAsPtr->base, env );
-			if ( 0 < assignResult && tq1 <= tq2 ) {
-				if ( tq1 == tq2 ) {
-					cost = Cost::safe;
-				} else {
-					cost = Cost::safe + Cost::safe;
-				}
-			} else if ( assignResult < 0 ) {
-				cost = Cost::unsafe;
-			} // else Cost::infinity
-		}
-	}
-}
-
-void ConversionCost::postvisit( const ast::ArrayType * arrayType ) {
-	(void)arrayType;
-}
-
-void ConversionCost::postvisit( const ast::ReferenceType * refType ) {
-	assert( nullptr == dynamic_cast< const ast::ReferenceType * >( dst ) );
-
-	cost = costCalc( refType->base, dst, srcIsLvalue, symtab, env );
-
-	// xxx - should qualifiers be considered in pass-by-value?
-	/*
-	if ( refType->base->qualifiers == dst->qualifiers ) {
-		cost.incReference();
-	} else if ( refType->base->qualifiers < dst->qualifiers ) {
-		cost.incSafe();
-	} else {
-		cost.incUnsafe();
-	}
-	*/
-	cost.incReference();
-}
-
-void ConversionCost::postvisit( const ast::FunctionType * functionType ) {
-	(void)functionType;
-}
-
-void ConversionCost::postvisit( const ast::EnumInstType * inst ) {
-	if ( auto dstAsInst = dynamic_cast<const ast::EnumInstType *>( dst ) ) {
-		if (inst->base && dstAsInst->base) {
-			if (inst->base->name == dstAsInst->base->name) {
-				cost = Cost::zero;
-				return;
-			}
-		}
-		return;
-	}
-	static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicKind::SignedInt ) };
-	cost = costCalc( integer, dst, srcIsLvalue, symtab, env );
-	if ( cost < Cost::unsafe ) {
-		cost.incSafe();
-	}
-}
-
-void ConversionCost::postvisit( const ast::EnumAttrType * src ) {
-	auto dstAsEnumAttrType = dynamic_cast<const ast::EnumAttrType *>(dst);
-	assert( src->attr != ast::EnumAttribute::Label );
-	if ( src->attr == ast::EnumAttribute::Value ) {
-		if ( dstAsEnumAttrType && dstAsEnumAttrType->attr == ast::EnumAttribute::Value) {
-			cost = costCalc( src->instance, dstAsEnumAttrType->instance, srcIsLvalue, symtab, env );
-		} else {
-			auto baseType = src->instance->base->base;
-			cost = costCalc( baseType, dst, srcIsLvalue, symtab, env );
-			if ( cost < Cost::infinity ) {
-				cost.incUnsafe();
-			}
-		}
-	} else { // ast::EnumAttribute::Posn
-		if ( auto dstBase = dynamic_cast<const ast::EnumInstType *>( dst ) ) {
-			cost = costCalc( src->instance, dstBase, srcIsLvalue, symtab, env );
-			if ( cost < Cost::unsafe ) cost.incSafe();
-		} else {
-			static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicKind::SignedInt ) };
-			cost = costCalc( integer, dst, srcIsLvalue, symtab, env );
-			if ( cost < Cost::unsafe ) {
-				cost.incSafe();
-			}
-		}
-	}
-}
-
-void ConversionCost::postvisit( const ast::TraitInstType * traitInstType ) {
-	(void)traitInstType;
-}
-
-void ConversionCost::postvisit( const ast::TypeInstType * typeInstType ) {
-	if ( const ast::EqvClass * eqv = env.lookup( *typeInstType ) ) {
-		cost = costCalc( eqv->bound, dst, srcIsLvalue, symtab, env );
-	} else if ( const ast::TypeInstType * dstAsInst =
-			dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( *typeInstType == *dstAsInst ) {
-			cost = Cost::zero;
-		}
-	} else if ( const ast::NamedTypeDecl * namedType = symtab.lookupType( typeInstType->name ) ) {
-		const ast::TypeDecl * type = dynamic_cast< const ast::TypeDecl * >( namedType );
-		assertf( type, "Unexpected typedef.");
-		if ( type->base ) {
-			cost = costCalc( type->base, dst, srcIsLvalue, symtab, env ) + Cost::safe;
-		}
-	}
-}
-
-void ConversionCost::postvisit( const ast::TupleType * tupleType ) {
-	Cost c = Cost::zero;
-	if ( const ast::TupleType * dstAsTuple = dynamic_cast< const ast::TupleType * >( dst ) ) {
-		auto srcIt = tupleType->types.begin();
-		auto dstIt = dstAsTuple->types.begin();
-		auto srcEnd = tupleType->types.end();
-		auto dstEnd = dstAsTuple->types.end();
-		while ( srcIt != srcEnd && dstIt != dstEnd ) {
-			Cost newCost = costCalc( * srcIt++, * dstIt++, srcIsLvalue, symtab, env );
-			if ( newCost == Cost::infinity ) {
-				return;
-			}
-			c += newCost;
-		}
-		if ( dstIt != dstEnd ) {
-			cost = Cost::infinity;
-		} else {
-			cost = c;
-		}
-	}
-}
-
-void ConversionCost::postvisit( const ast::VarArgsType * varArgsType ) {
-	(void)varArgsType;
-	if ( dynamic_cast< const ast::VarArgsType * >( dst ) ) {
-		cost = Cost::zero;
-	}
-}
-
-void ConversionCost::postvisit( const ast::ZeroType * zeroType ) {
-	(void)zeroType;
-	if ( dynamic_cast< const ast::ZeroType * >( dst ) ) {
-		cost = Cost::zero;
-	} else if ( const ast::BasicType * dstAsBasic =
-			dynamic_cast< const ast::BasicType * >( dst ) ) {
-		int tableResult = costMatrix[ ast::BasicKind::SignedInt ][ dstAsBasic->kind ];
-		if ( -1 == tableResult ) {
-			cost = Cost::unsafe;
-		} else {
-			cost = Cost::zero;
-			cost.incSafe( tableResult + 1 );
-			cost.incSign( signMatrix[ ast::BasicKind::SignedInt ][ dstAsBasic->kind ] );
-		}
-		// this has the effect of letting any expr such as x+0, x+1 to be typed
-		// the same as x, instead of at least int. are we willing to sacrifice this little
-		// bit of coherence with C?
-		// TODO: currently this does not work when no zero/one overloads exist. Find a fix for it.
-		// cost = Cost::zero;
-	} else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {
-		cost = Cost::zero;
-		// +1 for zero_t ->, +1 for disambiguation
-		cost.incSafe( maxIntCost + 2 );
-		// assuming 0p is supposed to be used for pointers?
-	} else if ( auto dstAsEnumInst = dynamic_cast< const ast::EnumInstType * >( dst ) ) {
-		if ( dstAsEnumInst->base && !dstAsEnumInst->base->isTyped ) {
-			cost = Cost::unsafe;
-		}
-	}
-}
-
-void ConversionCost::postvisit( const ast::OneType * oneType ) {
-	(void)oneType;
-	if ( dynamic_cast< const ast::OneType * >( dst ) ) {
-		cost = Cost::zero;
-	} else if ( const ast::BasicType * dstAsBasic =
-			dynamic_cast< const ast::BasicType * >( dst ) ) {
-		int tableResult = costMatrix[ ast::BasicKind::SignedInt ][ dstAsBasic->kind ];
-		if ( -1 == tableResult ) {
-			cost = Cost::unsafe;
-		} else {
-			cost = Cost::zero;
-			cost.incSafe( tableResult + 1 );
-			cost.incSign( signMatrix[ ast::BasicKind::SignedInt ][ dstAsBasic->kind ] );
-		}
-	} else if ( auto dstAsEnumInst = dynamic_cast< const ast::EnumInstType * >( dst ) ) {
-		if ( dstAsEnumInst->base && !dstAsEnumInst->base->isTyped ) {
-			cost = Cost::unsafe;
-		}
-	}
-}
-
-// size_t ConversionCost::traceId = Stats::Heap::new_stacktrace_id("ConversionCost");
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/ConversionCost.cpp
===================================================================
--- src/ResolvExpr/ConversionCost.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/ConversionCost.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,517 @@
+//
+// 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.
+//
+// ConversionCost.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 07:06:19 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Jul 29 16:11:00 2020
+// Update Count     : 28
+//
+
+#include "ConversionCost.hpp"
+
+#include <cassert>                       // for assert
+#include <list>                          // for list, list<>::const_iterator
+#include <string>                        // for operator==, string
+
+#include "ResolvExpr/Cost.hpp"           // for Cost
+#include "ResolvExpr/Unify.hpp"          // for typesCompatibleIgnoreQualifiers
+#include "ResolvExpr/PtrsAssignable.hpp" // for ptrsAssignable
+
+namespace ResolvExpr {
+
+#if 0
+#define PRINT(x) x
+#else
+#define PRINT(x)
+#endif
+
+namespace {
+
+	// GENERATED START, DO NOT EDIT
+	// GENERATED BY BasicTypes-gen.cpp
+	/* EXTENDED INTEGRAL RANK HIERARCHY (root to leaves)
+	                         _Bool
+	char                signed char         unsigned char
+	          signed short int         unsigned short int
+	          signed int               unsigned int
+	          signed long int          unsigned long int
+	          signed long long int     unsigned long long int
+	          __int128                 unsigned __int128
+	          _Float16                 _Float16 _Complex
+	          _Float32                 _Float32 _Complex
+	          float                    float _Complex
+	          _Float32x                _Float32x _Complex
+	          _Float64                 _Float64 _Complex
+	          double                   double _Complex
+	          _Float64x                _Float64x _Complex
+	                     __float80
+	          _Float128                _Float128 _Complex
+	                    __float128
+	          long double              long double _Complex
+	          _Float128x               _Float128x _Complex
+	*/
+	// GENERATED END
+
+	// GENERATED START, DO NOT EDIT
+	// GENERATED BY BasicTypes-gen.cpp
+	static const int costMatrix[ast::BasicKind::NUMBER_OF_BASIC_TYPES][ast::BasicKind::NUMBER_OF_BASIC_TYPES] = { // path length from root to node
+		/*               B    C   SC   UC   SI  SUI    I   UI   LI  LUI  LLI LLUI   IB  UIB  _FH  _FH   _F  _FC    F   FC  _FX _FXC   FD _FDC    D   DC F80X_FDXC  F80  _FB_FLDC   FB   LD  LDC _FBX_FLDXC */
+		/*      B */ {   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  14,  15,  15,  16,  17,  16,  18,  17, },
+		/*      C */ {  -1,   0,   1,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  14,  15,  16,  15,  17,  16, },
+		/*     SC */ {  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  14,  15,  16,  15,  17,  16, },
+		/*     UC */ {  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  14,  15,  16,  15,  17,  16, },
+		/*     SI */ {  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  15,  14,  16,  15, },
+		/*    SUI */ {  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  13,  14,  15,  14,  16,  15, },
+		/*      I */ {  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  14,  13,  15,  14, },
+		/*     UI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  12,  13,  14,  13,  15,  14, },
+		/*     LI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  13,  12,  14,  13, },
+		/*    LUI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  11,  12,  13,  12,  14,  13, },
+		/*    LLI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  12,  11,  13,  12, },
+		/*   LLUI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  10,  11,  12,  11,  13,  12, },
+		/*     IB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  11,  10,  12,  11, },
+		/*    UIB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,   9,  10,  11,  10,  12,  11, },
+		/*    _FH */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   8,   9,  10,   9,  11,  10, },
+		/*    _FH */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,   3,  -1,   4,  -1,   5,  -1,   6,  -1,  -1,   7,  -1,  -1,   8,  -1,   9, },
+		/*     _F */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   7,   8,   9,   8,  10,   9, },
+		/*    _FC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,   3,  -1,   4,  -1,   5,  -1,  -1,   6,  -1,  -1,   7,  -1,   8, },
+		/*      F */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   6,   7,   8,   7,   9,   8, },
+		/*     FC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,   3,  -1,   4,  -1,  -1,   5,  -1,  -1,   6,  -1,   7, },
+		/*    _FX */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   5,   6,   7,   6,   8,   7, },
+		/*   _FXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,   3,  -1,  -1,   4,  -1,  -1,   5,  -1,   6, },
+		/*     FD */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   4,   5,   6,   5,   7,   6, },
+		/*   _FDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,   2,  -1,  -1,   3,  -1,  -1,   4,  -1,   5, },
+		/*      D */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3,   4,   5,   4,   6,   5, },
+		/*     DC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1,  -1,  -1,   2,  -1,  -1,   3,  -1,   4, },
+		/*   F80X */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   4,   3,   5,   4, },
+		/*  _FDXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,  -1,   1,  -1,  -1,   2,  -1,   3, },
+		/*    F80 */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,   0,   1,   2,   2,   3,   3,   4,   4, },
+		/*    _FB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2,   2,   3,   3, },
+		/*  _FLDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,  -1,   1,  -1,   2, },
+		/*     FB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,   0,   1,   2,   2,   3, },
+		/*     LD */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   1,   2, },
+		/*    LDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   1, },
+		/*   _FBX */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1, },
+		/* _FLDXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0, },
+	}; // costMatrix
+	static const int maxIntCost = 15;
+	// GENERATED END
+	static_assert(
+		sizeof(costMatrix)/sizeof(costMatrix[0][0]) == ast::BasicKind::NUMBER_OF_BASIC_TYPES * ast::BasicKind::NUMBER_OF_BASIC_TYPES,
+		"Missing row in the cost matrix"
+	);
+
+	// GENERATED START, DO NOT EDIT
+	// GENERATED BY BasicTypes-gen.cpp
+	static const int signMatrix[ast::BasicKind::NUMBER_OF_BASIC_TYPES][ast::BasicKind::NUMBER_OF_BASIC_TYPES] = { // number of sign changes in safe conversion
+		/*               B    C   SC   UC   SI  SUI    I   UI   LI  LUI  LLI LLUI   IB  UIB  _FH  _FH   _F  _FC    F   FC  _FX _FXC   FD _FDC    D   DC F80X_FDXC  F80  _FB_FLDC   FB   LD  LDC _FBX_FLDXC */
+		/*      B */ {   0,   0,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*      C */ {  -1,   0,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*     SC */ {  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*     UC */ {  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*     SI */ {  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*    SUI */ {  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*      I */ {  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*     UI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*     LI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*    LUI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*    LLI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*   LLUI */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*     IB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*    UIB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*    _FH */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*    _FH */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
+		/*     _F */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*    _FC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
+		/*      F */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*     FC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
+		/*    _FX */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*   _FXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
+		/*     FD */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*   _FDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
+		/*      D */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*     DC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
+		/*   F80X */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*  _FDXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
+		/*    F80 */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0,   0,   0, },
+		/*    _FB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0,   0, },
+		/*  _FLDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,  -1,   0,  -1,   0, },
+		/*     FB */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0,   0,   0, },
+		/*     LD */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0,   0,   0, },
+		/*    LDC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,  -1,   0, },
+		/*   _FBX */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0,   0, },
+		/* _FLDXC */ {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   0, },
+	}; // signMatrix
+	// GENERATED END
+	static_assert(
+		sizeof(signMatrix)/sizeof(signMatrix[0][0]) == ast::BasicKind::NUMBER_OF_BASIC_TYPES * ast::BasicKind::NUMBER_OF_BASIC_TYPES,
+		"Missing row in the sign matrix"
+	);
+
+	int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2,
+			const ast::SymbolTable &, const ast::TypeEnvironment & env ) {
+		return ptrsAssignable( t1, t2, env );
+	}
+}
+
+Cost conversionCost(
+	const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
+) {
+	if ( const ast::TypeInstType * inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
+		if ( const ast::EqvClass * eqv = env.lookup( *inst ) ) {
+			if ( eqv->bound ) {
+				return conversionCost(src, eqv->bound, srcIsLvalue, symtab, env );
+			} else {
+				return Cost::infinity;
+			}
+		} else if ( const ast::NamedTypeDecl * named = symtab.lookupType( inst->name ) ) {
+			const ast::TypeDecl * type = dynamic_cast< const ast::TypeDecl * >( named );
+			assertf( type, "Unexpected typedef." );
+			if ( type->base ) {
+				return conversionCost( src, type->base, srcIsLvalue, symtab, env ) + Cost::safe;
+			}
+		}
+	}
+	if ( typesCompatibleIgnoreQualifiers( src, dst, env ) ) {
+		return Cost::zero;
+	} else if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
+		return Cost::safe;
+	} else if ( const ast::ReferenceType * refType =
+			 dynamic_cast< const ast::ReferenceType * >( dst ) ) {
+		return convertToReferenceCost( src, refType, srcIsLvalue, symtab, env, localPtrsAssignable );
+	} else {
+		return ast::Pass<ConversionCost>::read( src, dst, srcIsLvalue, symtab, env, conversionCost );
+	}
+}
+
+static Cost convertToReferenceCost( const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+		int diff, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
+		PtrsCalculation func ) {
+	if ( 0 < diff ) {
+		Cost cost = convertToReferenceCost(
+			strict_dynamic_cast< const ast::ReferenceType * >( src )->base, dst,
+			srcIsLvalue, (diff - 1), symtab, env, func );
+		cost.incReference();
+		return cost;
+	} else if ( diff < -1 ) {
+		Cost cost = convertToReferenceCost(
+			src, strict_dynamic_cast< const ast::ReferenceType * >( dst )->base,
+			srcIsLvalue, (diff + 1), symtab, env, func );
+		cost.incReference();
+		return cost;
+	} else if ( 0 == diff ) {
+		const ast::ReferenceType * srcAsRef = dynamic_cast< const ast::ReferenceType * >( src );
+		const ast::ReferenceType * dstAsRef = dynamic_cast< const ast::ReferenceType * >( dst );
+		if ( srcAsRef && dstAsRef ) {
+			ast::CV::Qualifiers tq1 = srcAsRef->base->qualifiers;
+			ast::CV::Qualifiers tq2 = dstAsRef->base->qualifiers;
+			if ( tq1 <= tq2 && typesCompatibleIgnoreQualifiers(
+					srcAsRef->base, dstAsRef->base, env ) ) {
+				if ( tq1 == tq2 ) {
+					return Cost::zero;
+				} else {
+					return Cost::safe;
+				}
+			} else {
+				int assignResult = func( srcAsRef->base, dstAsRef->base, symtab, env );
+				if ( 0 < assignResult ) {
+					return Cost::safe;
+				} else if ( assignResult < 0 ) {
+					return Cost::unsafe;
+				}
+			}
+		} else {
+			return ast::Pass<ConversionCost>::read( src, dst, srcIsLvalue, symtab, env, conversionCost );
+		}
+	} else {
+		assert( -1 == diff );
+		const ast::ReferenceType * dstAsRef = dynamic_cast< const ast::ReferenceType * >( dst );
+		assert( dstAsRef );
+		if ( typesCompatibleIgnoreQualifiers( src, dstAsRef->base, env ) ) {
+			if ( srcIsLvalue ) {
+				if ( src->qualifiers == dstAsRef->base->qualifiers ) {
+					return Cost::reference;
+				} else if ( src->qualifiers < dstAsRef->base->qualifiers ) {
+					return Cost::safe;
+				} else {
+					return Cost::unsafe;
+				}
+			} else if ( dstAsRef->base->is_const() ) {
+				return Cost::safe;
+			} else {
+				return Cost::unsafe;
+			}
+		}
+	}
+	return Cost::infinity;
+}
+
+Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dst,
+		bool srcIsLvalue, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env,
+		PtrsCalculation func ) {
+	int sdepth = src->referenceDepth(), ddepth = dst->referenceDepth();
+	return convertToReferenceCost( src, dst, srcIsLvalue, sdepth - ddepth, symtab, env, func );
+}
+
+void ConversionCost::postvisit( const ast::VoidType * voidType ) {
+	(void)voidType;
+	cost = Cost::infinity;
+}
+
+void ConversionCost::conversionCostFromBasicToBasic( const ast::BasicType * src, const ast::BasicType* dest ) {
+	int tableResult = costMatrix[ src->kind ][ dest->kind ];
+	if ( tableResult == -1 ) {
+		cost = Cost::unsafe;
+	} else {
+		cost = Cost::zero;
+		cost.incSafe( tableResult );
+		cost.incSign( signMatrix[ src->kind ][ dest->kind ] );
+	}
+}
+
+void ConversionCost::postvisit( const ast::BasicType * basicType ) {
+	if ( const ast::BasicType * dstAsBasic = dynamic_cast< const ast::BasicType * >( dst ) ) {
+		conversionCostFromBasicToBasic( basicType, dstAsBasic );
+	} else if ( dynamic_cast< const ast::EnumAttrType *>(dst) ) {
+		static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicKind::SignedInt ) };
+		cost = costCalc( basicType, integer, srcIsLvalue, symtab, env );
+	} else if ( auto dstAsEnumInst = dynamic_cast< const ast::EnumInstType * >( dst ) ) {
+		if ( dstAsEnumInst->base && !dstAsEnumInst->base->isTyped ) {
+			cost = Cost::unsafe;
+		}
+	}
+}
+
+void ConversionCost::postvisit( const ast::PointerType * pointerType ) {
+	if ( const ast::PointerType * dstAsPtr = dynamic_cast< const ast::PointerType * >( dst ) ) {
+		ast::CV::Qualifiers tq1 = pointerType->base->qualifiers;
+		ast::CV::Qualifiers tq2 = dstAsPtr->base->qualifiers;
+		if ( tq1 <= tq2 && typesCompatibleIgnoreQualifiers(
+				pointerType->base, dstAsPtr->base, env ) ) {
+			if ( tq1 == tq2 ) {
+				cost = Cost::zero;
+			} else {
+				cost = Cost::safe;
+			}
+		}
+		/*
+		else if ( const ast::FunctionType * dstFunc = dstAsPtr->base.as<ast::FunctionType>()) {
+			if (const ast::FunctionType * srcFunc = pointerType->base.as<ast::FunctionType>()) {
+				if (dstFunc->params.empty() && dstFunc->isVarArgs ) {
+					cost = Cost::unsafe; // assign any function to variadic fptr
+				}
+			}
+			else {
+				ast::AssertionSet need, have; // unused
+				ast::OpenVarSet open;
+				env.extractOpenVars(open);
+				ast::TypeEnvironment tenv = env;
+				if ( unify(dstAsPtr->base, pointerType->base, tenv, need, have, open, symtab) ) {
+					cost = Cost::safe;
+				}
+			}
+			// else infinity
+		}
+		*/
+		else {
+			int assignResult = ptrsAssignable( pointerType->base, dstAsPtr->base, env );
+			if ( 0 < assignResult && tq1 <= tq2 ) {
+				if ( tq1 == tq2 ) {
+					cost = Cost::safe;
+				} else {
+					cost = Cost::safe + Cost::safe;
+				}
+			} else if ( assignResult < 0 ) {
+				cost = Cost::unsafe;
+			} // else Cost::infinity
+		}
+	}
+}
+
+void ConversionCost::postvisit( const ast::ArrayType * arrayType ) {
+	(void)arrayType;
+}
+
+void ConversionCost::postvisit( const ast::ReferenceType * refType ) {
+	assert( nullptr == dynamic_cast< const ast::ReferenceType * >( dst ) );
+
+	cost = costCalc( refType->base, dst, srcIsLvalue, symtab, env );
+
+	// xxx - should qualifiers be considered in pass-by-value?
+	/*
+	if ( refType->base->qualifiers == dst->qualifiers ) {
+		cost.incReference();
+	} else if ( refType->base->qualifiers < dst->qualifiers ) {
+		cost.incSafe();
+	} else {
+		cost.incUnsafe();
+	}
+	*/
+	cost.incReference();
+}
+
+void ConversionCost::postvisit( const ast::FunctionType * functionType ) {
+	(void)functionType;
+}
+
+void ConversionCost::postvisit( const ast::EnumInstType * inst ) {
+	if ( auto dstAsInst = dynamic_cast<const ast::EnumInstType *>( dst ) ) {
+		if (inst->base && dstAsInst->base) {
+			if (inst->base->name == dstAsInst->base->name) {
+				cost = Cost::zero;
+				return;
+			}
+		}
+		return;
+	}
+	static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicKind::SignedInt ) };
+	cost = costCalc( integer, dst, srcIsLvalue, symtab, env );
+	if ( cost < Cost::unsafe ) {
+		cost.incSafe();
+	}
+}
+
+void ConversionCost::postvisit( const ast::EnumAttrType * src ) {
+	auto dstAsEnumAttrType = dynamic_cast<const ast::EnumAttrType *>(dst);
+	assert( src->attr != ast::EnumAttribute::Label );
+	if ( src->attr == ast::EnumAttribute::Value ) {
+		if ( dstAsEnumAttrType && dstAsEnumAttrType->attr == ast::EnumAttribute::Value) {
+			cost = costCalc( src->instance, dstAsEnumAttrType->instance, srcIsLvalue, symtab, env );
+		} else {
+			auto baseType = src->instance->base->base;
+			cost = costCalc( baseType, dst, srcIsLvalue, symtab, env );
+			if ( cost < Cost::infinity ) {
+				cost.incUnsafe();
+			}
+		}
+	} else { // ast::EnumAttribute::Posn
+		if ( auto dstBase = dynamic_cast<const ast::EnumInstType *>( dst ) ) {
+			cost = costCalc( src->instance, dstBase, srcIsLvalue, symtab, env );
+			if ( cost < Cost::unsafe ) cost.incSafe();
+		} else {
+			static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicKind::SignedInt ) };
+			cost = costCalc( integer, dst, srcIsLvalue, symtab, env );
+			if ( cost < Cost::unsafe ) {
+				cost.incSafe();
+			}
+		}
+	}
+}
+
+void ConversionCost::postvisit( const ast::TraitInstType * traitInstType ) {
+	(void)traitInstType;
+}
+
+void ConversionCost::postvisit( const ast::TypeInstType * typeInstType ) {
+	if ( const ast::EqvClass * eqv = env.lookup( *typeInstType ) ) {
+		cost = costCalc( eqv->bound, dst, srcIsLvalue, symtab, env );
+	} else if ( const ast::TypeInstType * dstAsInst =
+			dynamic_cast< const ast::TypeInstType * >( dst ) ) {
+		if ( *typeInstType == *dstAsInst ) {
+			cost = Cost::zero;
+		}
+	} else if ( const ast::NamedTypeDecl * namedType = symtab.lookupType( typeInstType->name ) ) {
+		const ast::TypeDecl * type = dynamic_cast< const ast::TypeDecl * >( namedType );
+		assertf( type, "Unexpected typedef.");
+		if ( type->base ) {
+			cost = costCalc( type->base, dst, srcIsLvalue, symtab, env ) + Cost::safe;
+		}
+	}
+}
+
+void ConversionCost::postvisit( const ast::TupleType * tupleType ) {
+	Cost c = Cost::zero;
+	if ( const ast::TupleType * dstAsTuple = dynamic_cast< const ast::TupleType * >( dst ) ) {
+		auto srcIt = tupleType->types.begin();
+		auto dstIt = dstAsTuple->types.begin();
+		auto srcEnd = tupleType->types.end();
+		auto dstEnd = dstAsTuple->types.end();
+		while ( srcIt != srcEnd && dstIt != dstEnd ) {
+			Cost newCost = costCalc( * srcIt++, * dstIt++, srcIsLvalue, symtab, env );
+			if ( newCost == Cost::infinity ) {
+				return;
+			}
+			c += newCost;
+		}
+		if ( dstIt != dstEnd ) {
+			cost = Cost::infinity;
+		} else {
+			cost = c;
+		}
+	}
+}
+
+void ConversionCost::postvisit( const ast::VarArgsType * varArgsType ) {
+	(void)varArgsType;
+	if ( dynamic_cast< const ast::VarArgsType * >( dst ) ) {
+		cost = Cost::zero;
+	}
+}
+
+void ConversionCost::postvisit( const ast::ZeroType * zeroType ) {
+	(void)zeroType;
+	if ( dynamic_cast< const ast::ZeroType * >( dst ) ) {
+		cost = Cost::zero;
+	} else if ( const ast::BasicType * dstAsBasic =
+			dynamic_cast< const ast::BasicType * >( dst ) ) {
+		int tableResult = costMatrix[ ast::BasicKind::SignedInt ][ dstAsBasic->kind ];
+		if ( -1 == tableResult ) {
+			cost = Cost::unsafe;
+		} else {
+			cost = Cost::zero;
+			cost.incSafe( tableResult + 1 );
+			cost.incSign( signMatrix[ ast::BasicKind::SignedInt ][ dstAsBasic->kind ] );
+		}
+		// this has the effect of letting any expr such as x+0, x+1 to be typed
+		// the same as x, instead of at least int. are we willing to sacrifice this little
+		// bit of coherence with C?
+		// TODO: currently this does not work when no zero/one overloads exist. Find a fix for it.
+		// cost = Cost::zero;
+	} else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {
+		cost = Cost::zero;
+		// +1 for zero_t ->, +1 for disambiguation
+		cost.incSafe( maxIntCost + 2 );
+		// assuming 0p is supposed to be used for pointers?
+	} else if ( auto dstAsEnumInst = dynamic_cast< const ast::EnumInstType * >( dst ) ) {
+		if ( dstAsEnumInst->base && !dstAsEnumInst->base->isTyped ) {
+			cost = Cost::unsafe;
+		}
+	}
+}
+
+void ConversionCost::postvisit( const ast::OneType * oneType ) {
+	(void)oneType;
+	if ( dynamic_cast< const ast::OneType * >( dst ) ) {
+		cost = Cost::zero;
+	} else if ( const ast::BasicType * dstAsBasic =
+			dynamic_cast< const ast::BasicType * >( dst ) ) {
+		int tableResult = costMatrix[ ast::BasicKind::SignedInt ][ dstAsBasic->kind ];
+		if ( -1 == tableResult ) {
+			cost = Cost::unsafe;
+		} else {
+			cost = Cost::zero;
+			cost.incSafe( tableResult + 1 );
+			cost.incSign( signMatrix[ ast::BasicKind::SignedInt ][ dstAsBasic->kind ] );
+		}
+	} else if ( auto dstAsEnumInst = dynamic_cast< const ast::EnumInstType * >( dst ) ) {
+		if ( dstAsEnumInst->base && !dstAsEnumInst->base->isTyped ) {
+			cost = Cost::unsafe;
+		}
+	}
+}
+
+// size_t ConversionCost::traceId = Stats::Heap::new_stacktrace_id("ConversionCost");
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/ConversionCost.h
===================================================================
--- src/ResolvExpr/ConversionCost.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,86 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ConversionCost.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 09:37:28 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jul 29 16:12:00 2020
-// Update Count     : 7
-//
-
-#pragma once
-
-#include <functional>         // for function
-
-#include "Cost.h"             // for Cost
-
-#include "AST/Fwd.hpp"
-#include "AST/Pass.hpp"       // for WithShortCircuiting
-
-namespace ResolvExpr {
-
-// Some function pointer types, differ in return type.
-using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *, bool,
-	const ast::SymbolTable &, const ast::TypeEnvironment &)>;
-using PtrsCalculation = std::function<int(const ast::Type *, const ast::Type *,
-	const ast::SymbolTable &, const ast::TypeEnvironment &)>;
-
-Cost conversionCost(
-	const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
-	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env );
-
-Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dest,
-	bool srcIsLvalue, const ast::SymbolTable & indexer, const ast::TypeEnvironment & env,
-	PtrsCalculation func );
-
-class ConversionCost : public ast::WithShortCircuiting {
-protected:
-	const ast::Type * dst;
-	bool srcIsLvalue;
-	const ast::SymbolTable & symtab;
-	const ast::TypeEnvironment & env;
-	CostCalculation costCalc;
-public:
-	static size_t traceId;
-	Cost cost;
-	Cost result() { return cost; }
-
-	ConversionCost( const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab,
-			const ast::TypeEnvironment & env, CostCalculation costCalc ) :
-		dst( dst ), srcIsLvalue( srcIsLvalue ), symtab( symtab ), env( env ),
-		costCalc( costCalc ), cost( Cost::infinity )
-	{}
-
-	void previsit( const ast::Node * ) { visit_children = false; }
-
-	void postvisit( const ast::VoidType * voidType );
-	void postvisit( const ast::BasicType * basicType );
-	void postvisit( const ast::PointerType * pointerType );
-	void postvisit( const ast::ArrayType * arrayType );
-	void postvisit( const ast::ReferenceType * refType );
-	void postvisit( const ast::FunctionType * functionType );
-	void postvisit( const ast::EnumInstType * enumInstType );
-	void postvisit( const ast::TraitInstType * traitInstType );
-	void postvisit( const ast::TypeInstType * typeInstType );
-	void postvisit( const ast::TupleType * tupleType );
-	void postvisit( const ast::VarArgsType * varArgsType );
-	void postvisit( const ast::ZeroType * zeroType );
-	void postvisit( const ast::OneType * oneType );
-	void postvisit( const ast::EnumAttrType * posType );
-private:
-	// refactor for code resue
-	void conversionCostFromBasicToBasic( const ast::BasicType * src, const ast::BasicType* dest );
-};
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/ConversionCost.hpp
===================================================================
--- src/ResolvExpr/ConversionCost.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/ConversionCost.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,86 @@
+//
+// 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.
+//
+// ConversionCost.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 09:37:28 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Jul 29 16:12:00 2020
+// Update Count     : 7
+//
+
+#pragma once
+
+#include <functional>         // for function
+
+#include "Cost.hpp"           // for Cost
+
+#include "AST/Fwd.hpp"
+#include "AST/Pass.hpp"       // for WithShortCircuiting
+
+namespace ResolvExpr {
+
+// Some function pointer types, differ in return type.
+using CostCalculation = std::function<Cost(const ast::Type *, const ast::Type *, bool,
+	const ast::SymbolTable &, const ast::TypeEnvironment &)>;
+using PtrsCalculation = std::function<int(const ast::Type *, const ast::Type *,
+	const ast::SymbolTable &, const ast::TypeEnvironment &)>;
+
+Cost conversionCost(
+	const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,
+	const ast::SymbolTable & symtab, const ast::TypeEnvironment & env );
+
+Cost convertToReferenceCost( const ast::Type * src, const ast::ReferenceType * dest,
+	bool srcIsLvalue, const ast::SymbolTable & indexer, const ast::TypeEnvironment & env,
+	PtrsCalculation func );
+
+class ConversionCost : public ast::WithShortCircuiting {
+protected:
+	const ast::Type * dst;
+	bool srcIsLvalue;
+	const ast::SymbolTable & symtab;
+	const ast::TypeEnvironment & env;
+	CostCalculation costCalc;
+public:
+	static size_t traceId;
+	Cost cost;
+	Cost result() { return cost; }
+
+	ConversionCost( const ast::Type * dst, bool srcIsLvalue, const ast::SymbolTable & symtab,
+			const ast::TypeEnvironment & env, CostCalculation costCalc ) :
+		dst( dst ), srcIsLvalue( srcIsLvalue ), symtab( symtab ), env( env ),
+		costCalc( costCalc ), cost( Cost::infinity )
+	{}
+
+	void previsit( const ast::Node * ) { visit_children = false; }
+
+	void postvisit( const ast::VoidType * voidType );
+	void postvisit( const ast::BasicType * basicType );
+	void postvisit( const ast::PointerType * pointerType );
+	void postvisit( const ast::ArrayType * arrayType );
+	void postvisit( const ast::ReferenceType * refType );
+	void postvisit( const ast::FunctionType * functionType );
+	void postvisit( const ast::EnumInstType * enumInstType );
+	void postvisit( const ast::TraitInstType * traitInstType );
+	void postvisit( const ast::TypeInstType * typeInstType );
+	void postvisit( const ast::TupleType * tupleType );
+	void postvisit( const ast::VarArgsType * varArgsType );
+	void postvisit( const ast::ZeroType * zeroType );
+	void postvisit( const ast::OneType * oneType );
+	void postvisit( const ast::EnumAttrType * posType );
+private:
+	// refactor for code resue
+	void conversionCostFromBasicToBasic( const ast::BasicType * src, const ast::BasicType* dest );
+};
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/Cost.h
===================================================================
--- src/ResolvExpr/Cost.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,181 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Cost.h --
-//
-// Author           : Peter Buhr and Aaron Moss
-// Created On       : Sun May 17 09:39:50 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri May  3 17:15:41 2024
-// Update Count     : 64
-//
-
-#pragma once
-
-#include <iostream>
-#include <cassert>
-#include <climits>
-#include <cstdint>
-
-namespace ResolvExpr {
-
-// To maximize performance and space, the 7 resolution costs are packed into a single 64-bit word. However, the
-// specialization cost is a negative value so a correction is needed is a few places.
-
-class Cost {
-	union {
-		struct {
-		#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-			// Little-endian => first value is low priority and last is high priority.
-			unsigned char padding;					///< unused
-			unsigned char referenceCost;			///< reference conversions
-			unsigned char specCost;					///< Polymorphic type specializations (type assertions), negative cost
-			unsigned char varCost;					///< Count of polymorphic type variables
-			unsigned char signCost;					///< Count of safe sign conversions
-			unsigned char safeCost;					///< Safe (widening) conversions
-			unsigned char polyCost;					///< Count of parameters and return values bound to some poly type
-			unsigned char unsafeCost;				///< Unsafe (narrowing) conversions
-		#else
-			#error Cost BIG_ENDIAN unsupported
-		#endif
-		} v;
-		uint64_t all;
-	};
-	static const unsigned char correctb = 0xff;		// byte correction for negative spec cost
-	static const uint64_t correctw = 0x00'00'00'00'00'ff'00'00; //' word correction for negative spec cost
-  public:
-	// Compiler adjusts constants for correct endian.
-	enum : uint64_t {
-		zero      = 0x00'00'00'00'00'ff'00'00,
-		infinity  = 0xff'ff'ff'ff'ff'00'ff'ff,
-		unsafe    = 0x01'00'00'00'00'ff'00'00,
-		poly      = 0x00'01'00'00'00'ff'00'00,
-		safe      = 0x00'00'01'00'00'ff'00'00,
-		sign      = 0x00'00'00'01'00'ff'00'00,
-		var       = 0x00'00'00'00'01'ff'00'00,
-		spec      = 0x00'00'00'00'00'fe'00'00,
-		reference = 0x00'00'00'00'00'ff'01'00,
-	}; //'
-
-	Cost( uint64_t all ) { Cost::all = all; }
-	Cost( int unsafeCost, int polyCost, int safeCost, int signCost, int varCost, int specCost, int referenceCost ) {
-		// Assume little-endian => first value is low priority and last is high priority.
-		v = {
-		#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-			(unsigned char)0,						// padding
-			(unsigned char)referenceCost,			// low priority
-			(unsigned char)(specCost + correctb),	// correct for signedness
-			(unsigned char)varCost,
-			(unsigned char)signCost,
-			(unsigned char)safeCost,
-			(unsigned char)polyCost,
-			(unsigned char)unsafeCost, 				// high priority
-		#else
-			#error Cost BIG_ENDIAN unsupported
-		#endif
-		};
-	}
-
-	int get_unsafeCost() const { return v.unsafeCost; }
-	int get_polyCost() const { return v.polyCost; }
-	int get_safeCost() const { return v.safeCost; }
-	int get_signCost() const { return v.signCost; }
-	int get_varCost() const { return v.varCost; }
-	int get_specCost() const { return -(correctb - v.specCost); }
-	int get_referenceCost() const { return v.referenceCost; }
-
-	friend bool operator==( const Cost, const Cost );
-	friend bool operator!=( const Cost lhs, const Cost rhs );
-	// returns negative for *this < rhs, 0 for *this == rhs, positive for *this > rhs
-	int compare( const Cost rhs ) const {
-		if ( all == infinity ) return 1;
-		if ( rhs.all == infinity ) return -1;
-		return all > rhs.all ? 1 : all == rhs.all ? 0 : -1;
-	}
-	friend bool operator<( const Cost lhs, const Cost rhs );
-
-	friend Cost operator+( const Cost lhs, const Cost rhs );
-
-	Cost operator+=( const Cost rhs ) {
-		if ( all == infinity ) return *this;
-		if ( rhs.all == infinity ) {
-			all = infinity;
-			return *this;
-		}
-		all += rhs.all - correctw;					// correct for negative spec cost
-		return *this;
-	}
-
-	Cost incUnsafe( int inc = 1 ) {
-		if ( all != infinity ) { assert( v.unsafeCost + inc <= UCHAR_MAX ); v.unsafeCost += inc; }
-		return *this;
-	}
-
-	Cost incPoly( int inc = 1 ) {
-		if ( all != infinity ) { assert( v.polyCost + inc <= UCHAR_MAX ); v.polyCost += inc; }
-		return *this;
-	}
-
-	Cost incSafe( int inc = 1 ) {
-		if ( all != infinity ) { assert( v.safeCost + inc <= UCHAR_MAX ); v.safeCost += inc; }
-		return *this;
-	}
-
-	Cost incSign( int inc = 1 ) {
-		if ( all != infinity ) { assert( v.signCost + inc <= UCHAR_MAX ); v.signCost += inc; }
-		return *this;
-	}
-
-	Cost incVar( int inc = 1 ) {
-		if ( all != infinity ) { assert( v.varCost + inc <= UCHAR_MAX ); v.varCost += inc; }
-		return *this;
-	}
-
-	Cost decSpec( int dec = 1 ) {
-		if ( all != infinity ) { assert( v.specCost - dec >= 0 ); v.specCost -= dec; }
-		return *this;
-	}
-
-	Cost incReference( int inc = 1 ) {
-		if ( all != infinity ) { assert( v.referenceCost + inc <= UCHAR_MAX ); v.referenceCost += inc; }
-		return *this;
-	}
-
-	friend std::ostream & operator<<( std::ostream & os, const Cost cost );
-};
-
-inline bool operator==( const Cost lhs, const Cost rhs ) {
-	return lhs.all == rhs.all;
-}
-
-inline bool operator!=( const Cost lhs, const Cost rhs ) {
-	return !( lhs.all == rhs.all );
-}
-
-inline bool operator<( const Cost lhs, const Cost rhs ) {
-	if ( lhs.all == Cost::infinity ) return false;
-	if ( rhs.all == Cost::infinity ) return true;
-	return lhs.all < rhs.all;
-}
-
-inline Cost operator+( const Cost lhs, const Cost rhs ) {
-	if ( lhs.all == Cost::infinity || rhs.all == Cost::infinity ) return Cost{ Cost::infinity };
-	return Cost{ lhs.all + rhs.all - Cost::correctw }; // correct for negative spec cost
-}
-
-inline std::ostream & operator<<( std::ostream & os, const Cost cost ) {
-	return os << "( " << cost.get_unsafeCost() << ", " << cost.get_polyCost() << ", " << cost.get_safeCost()
-			  << ", " << cost.get_signCost() << ", " << cost.get_varCost() << ", " << cost.get_specCost()
-			  << ", " << cost.get_referenceCost() << " )";
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/Cost.hpp
===================================================================
--- src/ResolvExpr/Cost.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/Cost.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,181 @@
+//
+// 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.
+//
+// Cost.h --
+//
+// Author           : Peter Buhr and Aaron Moss
+// Created On       : Sun May 17 09:39:50 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri May  3 17:15:41 2024
+// Update Count     : 64
+//
+
+#pragma once
+
+#include <iostream>
+#include <cassert>
+#include <climits>
+#include <cstdint>
+
+namespace ResolvExpr {
+
+// To maximize performance and space, the 7 resolution costs are packed into a single 64-bit word. However, the
+// specialization cost is a negative value so a correction is needed is a few places.
+
+class Cost {
+	union {
+		struct {
+		#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+			// Little-endian => first value is low priority and last is high priority.
+			unsigned char padding;					///< unused
+			unsigned char referenceCost;			///< reference conversions
+			unsigned char specCost;					///< Polymorphic type specializations (type assertions), negative cost
+			unsigned char varCost;					///< Count of polymorphic type variables
+			unsigned char signCost;					///< Count of safe sign conversions
+			unsigned char safeCost;					///< Safe (widening) conversions
+			unsigned char polyCost;					///< Count of parameters and return values bound to some poly type
+			unsigned char unsafeCost;				///< Unsafe (narrowing) conversions
+		#else
+			#error Cost BIG_ENDIAN unsupported
+		#endif
+		} v;
+		uint64_t all;
+	};
+	static const unsigned char correctb = 0xff;		// byte correction for negative spec cost
+	static const uint64_t correctw = 0x00'00'00'00'00'ff'00'00; //' word correction for negative spec cost
+  public:
+	// Compiler adjusts constants for correct endian.
+	enum : uint64_t {
+		zero      = 0x00'00'00'00'00'ff'00'00,
+		infinity  = 0xff'ff'ff'ff'ff'00'ff'ff,
+		unsafe    = 0x01'00'00'00'00'ff'00'00,
+		poly      = 0x00'01'00'00'00'ff'00'00,
+		safe      = 0x00'00'01'00'00'ff'00'00,
+		sign      = 0x00'00'00'01'00'ff'00'00,
+		var       = 0x00'00'00'00'01'ff'00'00,
+		spec      = 0x00'00'00'00'00'fe'00'00,
+		reference = 0x00'00'00'00'00'ff'01'00,
+	}; //'
+
+	Cost( uint64_t all ) { Cost::all = all; }
+	Cost( int unsafeCost, int polyCost, int safeCost, int signCost, int varCost, int specCost, int referenceCost ) {
+		// Assume little-endian => first value is low priority and last is high priority.
+		v = {
+		#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+			(unsigned char)0,						// padding
+			(unsigned char)referenceCost,			// low priority
+			(unsigned char)(specCost + correctb),	// correct for signedness
+			(unsigned char)varCost,
+			(unsigned char)signCost,
+			(unsigned char)safeCost,
+			(unsigned char)polyCost,
+			(unsigned char)unsafeCost, 				// high priority
+		#else
+			#error Cost BIG_ENDIAN unsupported
+		#endif
+		};
+	}
+
+	int get_unsafeCost() const { return v.unsafeCost; }
+	int get_polyCost() const { return v.polyCost; }
+	int get_safeCost() const { return v.safeCost; }
+	int get_signCost() const { return v.signCost; }
+	int get_varCost() const { return v.varCost; }
+	int get_specCost() const { return -(correctb - v.specCost); }
+	int get_referenceCost() const { return v.referenceCost; }
+
+	friend bool operator==( const Cost, const Cost );
+	friend bool operator!=( const Cost lhs, const Cost rhs );
+	// returns negative for *this < rhs, 0 for *this == rhs, positive for *this > rhs
+	int compare( const Cost rhs ) const {
+		if ( all == infinity ) return 1;
+		if ( rhs.all == infinity ) return -1;
+		return all > rhs.all ? 1 : all == rhs.all ? 0 : -1;
+	}
+	friend bool operator<( const Cost lhs, const Cost rhs );
+
+	friend Cost operator+( const Cost lhs, const Cost rhs );
+
+	Cost operator+=( const Cost rhs ) {
+		if ( all == infinity ) return *this;
+		if ( rhs.all == infinity ) {
+			all = infinity;
+			return *this;
+		}
+		all += rhs.all - correctw;					// correct for negative spec cost
+		return *this;
+	}
+
+	Cost incUnsafe( int inc = 1 ) {
+		if ( all != infinity ) { assert( v.unsafeCost + inc <= UCHAR_MAX ); v.unsafeCost += inc; }
+		return *this;
+	}
+
+	Cost incPoly( int inc = 1 ) {
+		if ( all != infinity ) { assert( v.polyCost + inc <= UCHAR_MAX ); v.polyCost += inc; }
+		return *this;
+	}
+
+	Cost incSafe( int inc = 1 ) {
+		if ( all != infinity ) { assert( v.safeCost + inc <= UCHAR_MAX ); v.safeCost += inc; }
+		return *this;
+	}
+
+	Cost incSign( int inc = 1 ) {
+		if ( all != infinity ) { assert( v.signCost + inc <= UCHAR_MAX ); v.signCost += inc; }
+		return *this;
+	}
+
+	Cost incVar( int inc = 1 ) {
+		if ( all != infinity ) { assert( v.varCost + inc <= UCHAR_MAX ); v.varCost += inc; }
+		return *this;
+	}
+
+	Cost decSpec( int dec = 1 ) {
+		if ( all != infinity ) { assert( v.specCost - dec >= 0 ); v.specCost -= dec; }
+		return *this;
+	}
+
+	Cost incReference( int inc = 1 ) {
+		if ( all != infinity ) { assert( v.referenceCost + inc <= UCHAR_MAX ); v.referenceCost += inc; }
+		return *this;
+	}
+
+	friend std::ostream & operator<<( std::ostream & os, const Cost cost );
+};
+
+inline bool operator==( const Cost lhs, const Cost rhs ) {
+	return lhs.all == rhs.all;
+}
+
+inline bool operator!=( const Cost lhs, const Cost rhs ) {
+	return !( lhs.all == rhs.all );
+}
+
+inline bool operator<( const Cost lhs, const Cost rhs ) {
+	if ( lhs.all == Cost::infinity ) return false;
+	if ( rhs.all == Cost::infinity ) return true;
+	return lhs.all < rhs.all;
+}
+
+inline Cost operator+( const Cost lhs, const Cost rhs ) {
+	if ( lhs.all == Cost::infinity || rhs.all == Cost::infinity ) return Cost{ Cost::infinity };
+	return Cost{ lhs.all + rhs.all - Cost::correctw }; // correct for negative spec cost
+}
+
+inline std::ostream & operator<<( std::ostream & os, const Cost cost ) {
+	return os << "( " << cost.get_unsafeCost() << ", " << cost.get_polyCost() << ", " << cost.get_safeCost()
+			  << ", " << cost.get_signCost() << ", " << cost.get_varCost() << ", " << cost.get_specCost()
+			  << ", " << cost.get_referenceCost() << " )";
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/CurrentObject.cc
===================================================================
--- src/ResolvExpr/CurrentObject.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,610 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// CurrentObject.h --
-//
-// Author           : Rob Schluntz
-// Created On       : Tue Jun 13 15:28:32 2017
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Dec  9 17:49:51 2023
-// Update Count     : 20
-//
-
-#include <stddef.h>                    // for size_t
-#include <cassert>                     // for assertf, assert, safe_dynamic_...
-#include <deque>
-#include <iostream>                    // for ostream, operator<<, basic_ost...
-#include <stack>                       // for stack
-#include <string>                      // for string, operator<<, allocator
-
-#include "AST/Copy.hpp"                // for shallowCopy
-#include "AST/Expr.hpp"                // for InitAlternative
-#include "AST/GenericSubstitution.hpp" // for genericSubstitution
-#include "AST/Init.hpp"                // for Designation
-#include "AST/Node.hpp"                // for readonly
-#include "AST/Print.hpp"               // for readonly
-#include "AST/Type.hpp"
-#include "Common/Eval.h"               // for eval
-#include "Common/Indenter.h"           // for Indenter, operator<<
-#include "Common/SemanticError.h"      // for SemanticError
-#include "Common/utility.h"            // for toString
-#include "CurrentObject.h"
-
-#if 0
-#define PRINT(x) x
-#else
-#define PRINT(x)
-#endif
-
-namespace ast {
-
-/// Iterates members of a type by initializer.
-class MemberIterator {
-public:
-	virtual ~MemberIterator() {}
-
-	/// Internal set position based on iterator ranges.
-	virtual void setPosition(
-		std::deque< ptr< Expr > >::const_iterator it,
-		std::deque< ptr< Expr > >::const_iterator end ) = 0;
-
-	/// Walks the current object using the given designators as a guide.
-	void setPosition( const std::deque< ptr< Expr > > & designators ) {
-		setPosition( designators.begin(), designators.end() );
-	}
-
-	/// Retrieve the list of possible (Type,Designation) pairs for the
-	/// current position in the current object.
-	virtual std::deque< InitAlternative > operator* () const = 0;
-
-	/// True if the iterator is not currently at the end.
-	virtual operator bool() const = 0;
-
-	/// Moves the iterator by one member in the current object.
-	virtual MemberIterator & bigStep() = 0;
-
-	/// Moves the iterator by one member in the current subobject.
-	virtual MemberIterator & smallStep() = 0;
-
-	/// The type of the current object.
-	virtual const Type * getType() = 0;
-
-	/// The type of the current subobject.
-	virtual const Type * getNext() = 0;
-
-	/// Helper for operator*; aggregates must add designator to each init
-	/// alternative, but adding designators in operator* creates duplicates.
-	virtual std::deque< InitAlternative > first() const = 0;
-};
-
-namespace {
-
-/// create a new MemberIterator that traverses a type correctly
-MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type );
-
-/// Iterates "other" types (e.g. basic, pointer) which do not change at list initializer entry
-class SimpleIterator final : public MemberIterator {
-	CodeLocation location;
-	const Type * type = nullptr;
-public:
-	SimpleIterator( const CodeLocation & loc, const Type * t ) : location( loc ), type( t ) {}
-
-	void setPosition(
-		std::deque< ptr< Expr > >::const_iterator begin,
-		std::deque< ptr< Expr > >::const_iterator end
-	) override {
-		if ( begin != end ) {
-			SemanticError( location, "Un-designated initializer given non-empty designator" );
-		}
-	}
-
-	std::deque< InitAlternative > operator* () const override { return first(); }
-
-	operator bool() const override { return type; }
-
-	SimpleIterator & bigStep() override { return smallStep(); }
-	SimpleIterator & smallStep() override {
-		type = nullptr;  // empty on increment because no members
-		return *this;
-	}
-
-	const Type * getType() override { return type; }
-
-	const Type * getNext() override { return type; }
-
-	std::deque< InitAlternative > first() const override {
-		if ( type ) return { InitAlternative{ type, new Designation{ location } } };
-		return {};
-	}
-};
-
-/// Iterates over an indexed type:
-class IndexIterator : public MemberIterator {
-protected:
-	CodeLocation location;
-	size_t index = 0;
-	size_t size = 0;
-	std::unique_ptr<MemberIterator> memberIter;
-public:
-	IndexIterator( const CodeLocation & loc, size_t size ) :
-		location( loc ), size( size )
-	{}
-
-	void setPosition( const Expr * expr ) {
-		// need to permit integer-constant-expressions, including: integer constants,
-		// enumeration constants, character constants, sizeof expressions, alignof expressions,
-		// cast expressions
-
-		auto arg = eval( expr );
-		assertf( arg.hasKnownValue, "Non-evaluable expression made it to IndexIterator" );
-		index = arg.knownValue;
-
-		// if ( auto constExpr = dynamic_cast< const ConstantExpr * >( expr ) ) {
-		// 	try {
-		// 		index = constExpr->intValue();
-		// 	} catch ( SemanticErrorException & ) {
-		// 		SemanticError( expr, "Constant expression of non-integral type in array designator: " );
-		// 	}
-		// } else if ( auto castExpr = dynamic_cast< const CastExpr * >( expr ) ) {
-		// 	setPosition( castExpr->arg );
-		// } else if ( dynamic_cast< const SizeofExpr * >( expr ) || dynamic_cast< const AlignofExpr * >( expr ) ) {
-		// 	index = 0;
-		// } else {
-		// 	assertf( false,	"2 bad designator given to ArrayIterator: %s", toString( expr ).c_str() );
-		// }
-	}
-
-	void setPosition(
-		std::deque<ast::ptr<ast::Expr>>::const_iterator begin,
-		std::deque<ast::ptr<ast::Expr>>::const_iterator end
-	) override {
-		if ( begin == end ) return;
-
-		setPosition( *begin );
-		memberIter->setPosition( ++begin, end );
-	}
-
-	std::deque< InitAlternative > operator* () const override { return first(); }
-
-	operator bool() const override { return index < size; }
-};
-
-/// Iterates over the members of array types:
-class ArrayIterator final : public IndexIterator {
-	const ArrayType * array = nullptr;
-	const Type * base = nullptr;
-
-	size_t getSize( const Expr * expr ) {
-		auto res = eval( expr );
-		if ( !res.hasKnownValue ) {
-			SemanticError( location, "Array designator must be a constant expression %s", toString( expr ).c_str() );
-		}
-		return res.knownValue;
-	}
-
-public:
-	ArrayIterator( const CodeLocation & loc, const ArrayType * at ) :
-			IndexIterator( loc, getSize( at->dimension) ),
-			array( at ), base( at->base ) {
-		PRINT( std::cerr << "Creating array iterator: " << at << std::endl; )
-		memberIter.reset( createMemberIterator( loc, base ) );
-		if ( at->isVarLen ) {
-			SemanticError( location, at, "VLA initialization does not support @=: " );
-		}
-	}
-
-	ArrayIterator & bigStep() override {
-		PRINT( std::cerr << "bigStep in ArrayIterator (" << index << "/" << size << ")" << std::endl; )
-		++index;
-		memberIter.reset( index < size ? createMemberIterator( location, base ) : nullptr );
-		return *this;
-	}
-
-	ArrayIterator & smallStep() override {
-		PRINT( std::cerr << "smallStep in ArrayIterator (" << index << "/" << size << ")" << std::endl; )
-		if ( memberIter ) {
-			PRINT( std::cerr << "has member iter: " << *memberIter << std::endl; )
-			memberIter->smallStep();
-			if ( *memberIter ) {
-				PRINT( std::cerr << "has valid member iter" << std::endl; )
-				return *this;
-			}
-		}
-		return bigStep();
-	}
-
-	const Type * getType() override { return array; }
-
-	const Type * getNext() override { return base; }
-
-	std::deque< InitAlternative > first() const override {
-		PRINT( std::cerr << "first in ArrayIterator (" << index << "/" << size << ")" << std::endl; )
-		if ( memberIter && *memberIter ) {
-			std::deque< InitAlternative > ret = memberIter->first();
-			for ( InitAlternative & alt : ret ) {
-				alt.designation.get_and_mutate()->designators.emplace_front( ConstantExpr::from_ulong( location, index ) );
-			}
-			return ret;
-		}
-		return {};
-	}
-};
-
-class AggregateIterator : public MemberIterator {
-protected:
-	using MemberList = std::vector< ptr< Decl > >;
-
-	CodeLocation location;
-	std::string kind;  // for debug
-	std::string name;
-	const Type * inst;
-	const MemberList & members;
-	MemberList::const_iterator curMember;
-	bool atbegin = true;  // false at first {small,big}Step
-	const Type * curType = nullptr;
-	std::unique_ptr< MemberIterator > memberIter = nullptr;
-	TypeSubstitution sub;
-
-	bool init() {
-		PRINT( std::cerr << "--init()--" << members.size() << std::endl; )
-		if ( curMember != members.end() ) {
-			if ( auto field = curMember->as< ObjectDecl >() ) {
-				PRINT( std::cerr << "incremented to field: " << field << std::endl; )
-				curType = field->get_type();
-				memberIter.reset( createMemberIterator( location, curType ) );
-				return true;
-			}
-		}
-		return false;
-	}
-
-	AggregateIterator(
-		const CodeLocation & loc, const std::string k, const std::string & n, const Type * i,
-		const MemberList & ms )
-	: location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ),
-	  sub( genericSubstitution( i ) ) {
-		PRINT( std::cerr << "Creating " << kind << "(" << name << ")"; )
-		init();
-	}
-
-public:
-	void setPosition(
-		std::deque< ptr< Expr > >::const_iterator begin,
-		std::deque< ptr< Expr > >::const_iterator end
-	) final {
-		if ( begin == end ) return;
-
-		if ( auto varExpr = begin->as< VariableExpr >() ) {
-			for ( curMember = members.begin(); curMember != members.end(); ++curMember ) {
-				if ( *curMember != varExpr->var ) continue;
-
-				++begin;
-
-				memberIter.reset( createMemberIterator( location, varExpr->result ) );
-				curType = varExpr->result;
-				atbegin = curMember == members.begin() && begin == end;
-				memberIter->setPosition( begin, end );
-				return;
-			}
-			assertf( false, "could not find member in %s: %s", kind.c_str(), toString( varExpr ).c_str() );
-		} else {
-			assertf( false, "1 bad designator given to %s: %s", kind.c_str(), toString( *begin ).c_str() );
-		}
-	}
-
-	std::deque< InitAlternative > operator* () const final {
-		if ( memberIter && *memberIter ) {
-			std::deque< InitAlternative > ret = memberIter->first();
-			PRINT( std::cerr << "sub: " << sub << std::endl; )
-			for ( InitAlternative & alt : ret ) {
-				PRINT( std::cerr << "iterating and adding designators" << std::endl; )
-				alt.designation.get_and_mutate()->designators.emplace_front(
-					new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
-				// need to substitute for generic types so that casts are to concrete types
-				alt.type = shallowCopy(alt.type.get());
-				PRINT( std::cerr << "  type is: " << alt.type; )
-				sub.apply( alt.type ); // also apply to designation??
-				PRINT( std::cerr << " ==> " << alt.type << std::endl; )
-			}
-			return ret;
-		}
-		return {};
-	}
-
-	AggregateIterator & smallStep() final {
-		PRINT( std::cerr << "smallStep in " << kind << std::endl; )
-		atbegin = false;
-		if ( memberIter ) {
-			PRINT( std::cerr << "has member iter, incrementing..." << std::endl; )
-			memberIter->smallStep();
-			if ( *memberIter ) {
-				PRINT( std::cerr << "success!" << std::endl; )
-				return *this;
-			}
-		}
-		return bigStep();
-	}
-
-	AggregateIterator & bigStep() override = 0;
-
-	const Type * getType() final { return inst; }
-
-	const Type * getNext() final {
-		bool hasMember = memberIter && *memberIter;
-		return hasMember ? memberIter->getType() : nullptr;
-	}
-
-	std::deque< InitAlternative > first() const final {
-		std::deque< InitAlternative > ret;
-		PRINT( std::cerr << "first " << kind << std::endl; )
-		if ( memberIter && *memberIter ) {
-			PRINT( std::cerr << "adding children" << std::endl; )
-			ret = memberIter->first();
-			for ( InitAlternative & alt : ret ) {
-				PRINT( std::cerr << "iterating and adding designators" << std::endl; )
-				alt.designation.get_and_mutate()->designators.emplace_front(
-					new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
-			}
-		}
-		if ( atbegin ) {
-			// only add self if at the very beginning of the structure
-			PRINT( std::cerr << "adding self" << std::endl; )
-			ret.emplace_front( inst, new Designation{ location } );
-		}
-		return ret;
-	}
-};
-
-class StructIterator final : public AggregateIterator {
-public:
-	StructIterator( const CodeLocation & loc, const StructInstType * inst )
-	: AggregateIterator( loc, "StructIterator", inst->name, inst, inst->base->members ) {}
-
-	operator bool() const override {
-		return curMember != members.end() || (memberIter && *memberIter);
-	}
-
-	StructIterator & bigStep() override {
-		PRINT( std::cerr << "bigStep in " << kind << std::endl; )
-		atbegin = false;
-		memberIter = nullptr;
-		curType = nullptr;
-		while ( curMember != members.end() ) {
-			++curMember;
-			if ( init() ) return *this;
-		}
-		return *this;
-	}
-};
-
-class UnionIterator final : public AggregateIterator {
-public:
-	UnionIterator( const CodeLocation & loc, const UnionInstType * inst )
-	: AggregateIterator( loc, "UnionIterator", inst->name, inst, inst->base->members ) {}
-
-	operator bool() const override { return memberIter && *memberIter; }
-
-	UnionIterator & bigStep() override {
-		// unions only initialize one member
-		PRINT( std::cerr << "bigStep in " << kind << std::endl; )
-		atbegin = false;
-		memberIter = nullptr;
-		curType = nullptr;
-		curMember = members.end();
-		return *this;
-	}
-};
-
-/// Iterates across the positions in a tuple:
-class TupleIterator final : public IndexIterator {
-	ast::TupleType const * const tuple;
-
-	const ast::Type * typeAtIndex() const {
-		assert( index < size );
-		return tuple->types[ index ].get();
-	}
-
-public:
-	TupleIterator( const CodeLocation & loc, const TupleType * type )
-	: IndexIterator( loc, type->size() ), tuple( type ) {
-		PRINT( std::cerr << "Creating tuple iterator: " << type << std::endl; )
-		memberIter.reset( createMemberIterator( loc, typeAtIndex() ) );
-	}
-
-	TupleIterator & bigStep() override {
-		++index;
-		memberIter.reset( index < size ?
-			createMemberIterator( location, typeAtIndex() ) : nullptr );
-		return *this;
-	}
-
-	TupleIterator & smallStep() override {
-		if ( memberIter ) {
-			PRINT( std::cerr << "has member iter: " << *memberIter << std::endl; )
-			memberIter->smallStep();
-			if ( !memberIter ) {
-				PRINT( std::cerr << "has valid member iter" << std::endl; )
-				return *this;
-			}
-		}
-		return bigStep();
-	}
-
-	const ast::Type * getType() override {
-		return tuple;
-	}
-
-	const ast::Type * getNext() override {
-		bool hasMember = memberIter && *memberIter;
-		return hasMember ? memberIter->getType() : nullptr;
-	}
-
-	std::deque< InitAlternative > first() const override {
-		PRINT( std::cerr << "first in TupleIterator (" << index << "/" << size << ")" << std::endl; )
-		if ( memberIter && *memberIter ) {
-			std::deque< InitAlternative > ret = memberIter->first();
-			for ( InitAlternative & alt : ret ) {
-				alt.designation.get_and_mutate()->designators.emplace_front(
-					ConstantExpr::from_ulong( location, index ) );
-			}
-			return ret;
-		}
-		return {};
-	}
-};
-
-MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type ) {
-	if ( auto aggr = dynamic_cast< const BaseInstType * >( type ) ) {
-		if ( auto sit = dynamic_cast< const StructInstType * >( aggr ) ) {
-			assert( sit->base );
-			return new StructIterator{ loc, sit };
-		} else if ( auto uit = dynamic_cast< const UnionInstType * >( aggr ) ) {
-			assert( uit->base );
-			return new UnionIterator{ loc, uit };
-		} else {
-			assertf(
-				dynamic_cast< const EnumInstType * >( type )
-					|| dynamic_cast< const TypeInstType * >( type ),
-				"Encountered unhandled BaseInstType in createMemberIterator: %s",
-					toString( type ).c_str() );
-			return new SimpleIterator{ loc, type };
-		}
-	} else if ( auto at = dynamic_cast< const ArrayType * >( type ) ) {
-		return new ArrayIterator{ loc, at };
-	} else if ( auto tt = dynamic_cast< const TupleType * >( type ) ) {
-		return new TupleIterator{ loc, tt };
-	} else {
-		return new SimpleIterator{ loc, type };
-	}
-}
-
-} // namespace
-
-CurrentObject::CurrentObject( const CodeLocation & loc, const Type * type ) : objStack() {
-	objStack.emplace_back( new SimpleIterator{ loc, type } );
-}
-
-const Designation * CurrentObject::findNext( const Designation * designation ) {
-	using DesignatorChain = std::deque< ptr< Expr > >;
-	PRINT( std::cerr << "___findNext" << std::endl; )
-
-	// find all the d's
-	std::vector< DesignatorChain > desigAlts{ {} }, newDesigAlts;
-	std::deque< const Type * > curTypes{ objStack.back()->getType() }, newTypes;
-	for ( const Expr * expr : designation->designators ) {
-		PRINT( std::cerr << "____untyped: " << expr << std::endl; )
-		auto dit = desigAlts.begin();
-		auto nexpr = dynamic_cast< const NameExpr * >( expr );
-
-		for ( const Type * t : curTypes ) {
-			assert( dit != desigAlts.end() );
-			DesignatorChain & d = *dit;
-			// Name Designation:
-			if ( nexpr ) {
-				PRINT( std::cerr << "____actual: " << t << std::endl; )
-				if ( auto refType = dynamic_cast< const BaseInstType * >( t ) ) {
-					// concatenate identical field names
-					for ( const Decl * mem : refType->lookup( nexpr->name ) ) {
-						if ( auto field = dynamic_cast< const ObjectDecl * >( mem ) ) {
-							PRINT( std::cerr << "____alt: " << field->type << std::endl; )
-							DesignatorChain d2 = d;
-							d2.emplace_back( new VariableExpr{ expr->location, field } );
-							newDesigAlts.emplace_back( std::move( d2 ) );
-							newTypes.emplace_back( field->type );
-						}
-					}
-				}
-
-				++dit;
-			// Index Designation:
-			} else {
-				if ( auto at = dynamic_cast< const ArrayType * >( t ) ) {
-					PRINT( std::cerr << "____alt: " << at->get_base() << std::endl; )
-					d.emplace_back( expr );
-					newDesigAlts.emplace_back( d );
-					newTypes.emplace_back( at->base );
-				}
-			}
-		}
-
-		// reset queue
-		desigAlts = std::move( newDesigAlts );
-		newDesigAlts.clear();
-		curTypes = std::move( newTypes );
-		newTypes.clear();
-		assertf( desigAlts.size() == curTypes.size(), "Designator alternatives (%zu) and current types (%zu) out of sync", desigAlts.size(), curTypes.size() );
-	}
-
-	if ( desigAlts.size() > 1 ) {
-		SemanticError( designation, toString("Too many alternatives (", desigAlts.size(), ") for designation: ") );
-	} else if ( desigAlts.empty() ) {
-		SemanticError( designation, "No reasonable alternatives for designation: " );
-	}
-
-	DesignatorChain & d = desigAlts.back();
-	PRINT( for ( Expression * expr : d ) {
-		std::cerr << "____desig: " << expr << std::endl;
-	} ) // for
-	assertf( ! curTypes.empty(), "empty designator chosen");
-
-	// set new designators
-	assertf( ! objStack.empty(), "empty object stack when setting designation" );
-	Designation * actualDesignation =
-		new Designation{ designation->location, DesignatorChain{d} };
-	objStack.back()->setPosition( d ); // destroys d
-	return actualDesignation;
-}
-
-void CurrentObject::setNext( const Designation * designation ) {
-	PRINT( std::cerr << "____setNext" << designation << std::endl; )
-	assertf( ! objStack.empty(), "obj stack empty in setNext" );
-	objStack.back()->setPosition( designation->designators );
-}
-
-void CurrentObject::increment() {
-	PRINT( std::cerr << "____increment" << std::endl; )
-	if ( objStack.empty() ) return;
-	PRINT( std::cerr << *objStack.back() << std::endl; )
-	objStack.back()->smallStep();
-}
-
-void CurrentObject::enterListInit( const CodeLocation & loc ) {
-	PRINT( std::cerr << "____entering list init" << std::endl; )
-	assertf( ! objStack.empty(), "empty obj stack entering list init" );
-	const ast::Type * type = objStack.back()->getNext();
-	assert( type );
-	objStack.emplace_back( createMemberIterator( loc, type ) );
-}
-
-void CurrentObject::exitListInit() {
-	PRINT( std::cerr << "____exiting list init" << std::endl; )
-	assertf( ! objStack.empty(), "objstack empty" );
-	objStack.pop_back();
-	if ( ! objStack.empty() ) {
-		PRINT( std::cerr << *objStack.back() << std::endl; )
-		objStack.back()->bigStep();
-	}
-}
-
-std::deque< InitAlternative > CurrentObject::getOptions() {
-	PRINT( std::cerr << "____getting current options" << std::endl; )
-	assertf( ! objStack.empty(), "objstack empty in getOptions" );
-	return **objStack.back();
-}
-
-const Type * CurrentObject::getCurrentType() {
-	PRINT( std::cerr << "____getting current type" << std::endl; )
-	assertf( ! objStack.empty(), "objstack empty in getCurrentType" );
-	return objStack.back()->getNext();
-}
-
-} // namespace ast
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/CurrentObject.cpp
===================================================================
--- src/ResolvExpr/CurrentObject.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/CurrentObject.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,611 @@
+//
+// 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.
+//
+// CurrentObject.cpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Tue Jun 13 15:28:32 2017
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Dec  9 17:49:51 2023
+// Update Count     : 20
+//
+
+#include "CurrentObject.hpp"
+
+#include <stddef.h>                    // for size_t
+#include <cassert>                     // for assertf, assert, safe_dynamic_...
+#include <deque>
+#include <iostream>                    // for ostream, operator<<, basic_ost...
+#include <stack>                       // for stack
+#include <string>                      // for string, operator<<, allocator
+
+#include "AST/Copy.hpp"                // for shallowCopy
+#include "AST/Expr.hpp"                // for InitAlternative
+#include "AST/GenericSubstitution.hpp" // for genericSubstitution
+#include "AST/Init.hpp"                // for Designation
+#include "AST/Node.hpp"                // for readonly
+#include "AST/Print.hpp"               // for readonly
+#include "AST/Type.hpp"
+#include "Common/Eval.hpp"             // for eval
+#include "Common/Indenter.hpp"         // for Indenter, operator<<
+#include "Common/SemanticError.hpp"    // for SemanticError
+#include "Common/Utility.hpp"          // for toString
+
+#if 0
+#define PRINT(x) x
+#else
+#define PRINT(x)
+#endif
+
+namespace ast {
+
+/// Iterates members of a type by initializer.
+class MemberIterator {
+public:
+	virtual ~MemberIterator() {}
+
+	/// Internal set position based on iterator ranges.
+	virtual void setPosition(
+		std::deque< ptr< Expr > >::const_iterator it,
+		std::deque< ptr< Expr > >::const_iterator end ) = 0;
+
+	/// Walks the current object using the given designators as a guide.
+	void setPosition( const std::deque< ptr< Expr > > & designators ) {
+		setPosition( designators.begin(), designators.end() );
+	}
+
+	/// Retrieve the list of possible (Type,Designation) pairs for the
+	/// current position in the current object.
+	virtual std::deque< InitAlternative > operator* () const = 0;
+
+	/// True if the iterator is not currently at the end.
+	virtual operator bool() const = 0;
+
+	/// Moves the iterator by one member in the current object.
+	virtual MemberIterator & bigStep() = 0;
+
+	/// Moves the iterator by one member in the current subobject.
+	virtual MemberIterator & smallStep() = 0;
+
+	/// The type of the current object.
+	virtual const Type * getType() = 0;
+
+	/// The type of the current subobject.
+	virtual const Type * getNext() = 0;
+
+	/// Helper for operator*; aggregates must add designator to each init
+	/// alternative, but adding designators in operator* creates duplicates.
+	virtual std::deque< InitAlternative > first() const = 0;
+};
+
+namespace {
+
+/// create a new MemberIterator that traverses a type correctly
+MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type );
+
+/// Iterates "other" types (e.g. basic, pointer) which do not change at list initializer entry
+class SimpleIterator final : public MemberIterator {
+	CodeLocation location;
+	const Type * type = nullptr;
+public:
+	SimpleIterator( const CodeLocation & loc, const Type * t ) : location( loc ), type( t ) {}
+
+	void setPosition(
+		std::deque< ptr< Expr > >::const_iterator begin,
+		std::deque< ptr< Expr > >::const_iterator end
+	) override {
+		if ( begin != end ) {
+			SemanticError( location, "Un-designated initializer given non-empty designator" );
+		}
+	}
+
+	std::deque< InitAlternative > operator* () const override { return first(); }
+
+	operator bool() const override { return type; }
+
+	SimpleIterator & bigStep() override { return smallStep(); }
+	SimpleIterator & smallStep() override {
+		type = nullptr;  // empty on increment because no members
+		return *this;
+	}
+
+	const Type * getType() override { return type; }
+
+	const Type * getNext() override { return type; }
+
+	std::deque< InitAlternative > first() const override {
+		if ( type ) return { InitAlternative{ type, new Designation{ location } } };
+		return {};
+	}
+};
+
+/// Iterates over an indexed type:
+class IndexIterator : public MemberIterator {
+protected:
+	CodeLocation location;
+	size_t index = 0;
+	size_t size = 0;
+	std::unique_ptr<MemberIterator> memberIter;
+public:
+	IndexIterator( const CodeLocation & loc, size_t size ) :
+		location( loc ), size( size )
+	{}
+
+	void setPosition( const Expr * expr ) {
+		// need to permit integer-constant-expressions, including: integer constants,
+		// enumeration constants, character constants, sizeof expressions, alignof expressions,
+		// cast expressions
+
+		auto arg = eval( expr );
+		assertf( arg.hasKnownValue, "Non-evaluable expression made it to IndexIterator" );
+		index = arg.knownValue;
+
+		// if ( auto constExpr = dynamic_cast< const ConstantExpr * >( expr ) ) {
+		// 	try {
+		// 		index = constExpr->intValue();
+		// 	} catch ( SemanticErrorException & ) {
+		// 		SemanticError( expr, "Constant expression of non-integral type in array designator: " );
+		// 	}
+		// } else if ( auto castExpr = dynamic_cast< const CastExpr * >( expr ) ) {
+		// 	setPosition( castExpr->arg );
+		// } else if ( dynamic_cast< const SizeofExpr * >( expr ) || dynamic_cast< const AlignofExpr * >( expr ) ) {
+		// 	index = 0;
+		// } else {
+		// 	assertf( false,	"2 bad designator given to ArrayIterator: %s", toString( expr ).c_str() );
+		// }
+	}
+
+	void setPosition(
+		std::deque<ast::ptr<ast::Expr>>::const_iterator begin,
+		std::deque<ast::ptr<ast::Expr>>::const_iterator end
+	) override {
+		if ( begin == end ) return;
+
+		setPosition( *begin );
+		memberIter->setPosition( ++begin, end );
+	}
+
+	std::deque< InitAlternative > operator* () const override { return first(); }
+
+	operator bool() const override { return index < size; }
+};
+
+/// Iterates over the members of array types:
+class ArrayIterator final : public IndexIterator {
+	const ArrayType * array = nullptr;
+	const Type * base = nullptr;
+
+	size_t getSize( const Expr * expr ) {
+		auto res = eval( expr );
+		if ( !res.hasKnownValue ) {
+			SemanticError( location, "Array designator must be a constant expression %s", toString( expr ).c_str() );
+		}
+		return res.knownValue;
+	}
+
+public:
+	ArrayIterator( const CodeLocation & loc, const ArrayType * at ) :
+			IndexIterator( loc, getSize( at->dimension) ),
+			array( at ), base( at->base ) {
+		PRINT( std::cerr << "Creating array iterator: " << at << std::endl; )
+		memberIter.reset( createMemberIterator( loc, base ) );
+		if ( at->isVarLen ) {
+			SemanticError( location, at, "VLA initialization does not support @=: " );
+		}
+	}
+
+	ArrayIterator & bigStep() override {
+		PRINT( std::cerr << "bigStep in ArrayIterator (" << index << "/" << size << ")" << std::endl; )
+		++index;
+		memberIter.reset( index < size ? createMemberIterator( location, base ) : nullptr );
+		return *this;
+	}
+
+	ArrayIterator & smallStep() override {
+		PRINT( std::cerr << "smallStep in ArrayIterator (" << index << "/" << size << ")" << std::endl; )
+		if ( memberIter ) {
+			PRINT( std::cerr << "has member iter: " << *memberIter << std::endl; )
+			memberIter->smallStep();
+			if ( *memberIter ) {
+				PRINT( std::cerr << "has valid member iter" << std::endl; )
+				return *this;
+			}
+		}
+		return bigStep();
+	}
+
+	const Type * getType() override { return array; }
+
+	const Type * getNext() override { return base; }
+
+	std::deque< InitAlternative > first() const override {
+		PRINT( std::cerr << "first in ArrayIterator (" << index << "/" << size << ")" << std::endl; )
+		if ( memberIter && *memberIter ) {
+			std::deque< InitAlternative > ret = memberIter->first();
+			for ( InitAlternative & alt : ret ) {
+				alt.designation.get_and_mutate()->designators.emplace_front( ConstantExpr::from_ulong( location, index ) );
+			}
+			return ret;
+		}
+		return {};
+	}
+};
+
+class AggregateIterator : public MemberIterator {
+protected:
+	using MemberList = std::vector< ptr< Decl > >;
+
+	CodeLocation location;
+	std::string kind;  // for debug
+	std::string name;
+	const Type * inst;
+	const MemberList & members;
+	MemberList::const_iterator curMember;
+	bool atbegin = true;  // false at first {small,big}Step
+	const Type * curType = nullptr;
+	std::unique_ptr< MemberIterator > memberIter = nullptr;
+	TypeSubstitution sub;
+
+	bool init() {
+		PRINT( std::cerr << "--init()--" << members.size() << std::endl; )
+		if ( curMember != members.end() ) {
+			if ( auto field = curMember->as< ObjectDecl >() ) {
+				PRINT( std::cerr << "incremented to field: " << field << std::endl; )
+				curType = field->get_type();
+				memberIter.reset( createMemberIterator( location, curType ) );
+				return true;
+			}
+		}
+		return false;
+	}
+
+	AggregateIterator(
+		const CodeLocation & loc, const std::string k, const std::string & n, const Type * i,
+		const MemberList & ms )
+	: location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ),
+	  sub( genericSubstitution( i ) ) {
+		PRINT( std::cerr << "Creating " << kind << "(" << name << ")"; )
+		init();
+	}
+
+public:
+	void setPosition(
+		std::deque< ptr< Expr > >::const_iterator begin,
+		std::deque< ptr< Expr > >::const_iterator end
+	) final {
+		if ( begin == end ) return;
+
+		if ( auto varExpr = begin->as< VariableExpr >() ) {
+			for ( curMember = members.begin(); curMember != members.end(); ++curMember ) {
+				if ( *curMember != varExpr->var ) continue;
+
+				++begin;
+
+				memberIter.reset( createMemberIterator( location, varExpr->result ) );
+				curType = varExpr->result;
+				atbegin = curMember == members.begin() && begin == end;
+				memberIter->setPosition( begin, end );
+				return;
+			}
+			assertf( false, "could not find member in %s: %s", kind.c_str(), toString( varExpr ).c_str() );
+		} else {
+			assertf( false, "1 bad designator given to %s: %s", kind.c_str(), toString( *begin ).c_str() );
+		}
+	}
+
+	std::deque< InitAlternative > operator* () const final {
+		if ( memberIter && *memberIter ) {
+			std::deque< InitAlternative > ret = memberIter->first();
+			PRINT( std::cerr << "sub: " << sub << std::endl; )
+			for ( InitAlternative & alt : ret ) {
+				PRINT( std::cerr << "iterating and adding designators" << std::endl; )
+				alt.designation.get_and_mutate()->designators.emplace_front(
+					new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
+				// need to substitute for generic types so that casts are to concrete types
+				alt.type = shallowCopy(alt.type.get());
+				PRINT( std::cerr << "  type is: " << alt.type; )
+				sub.apply( alt.type ); // also apply to designation??
+				PRINT( std::cerr << " ==> " << alt.type << std::endl; )
+			}
+			return ret;
+		}
+		return {};
+	}
+
+	AggregateIterator & smallStep() final {
+		PRINT( std::cerr << "smallStep in " << kind << std::endl; )
+		atbegin = false;
+		if ( memberIter ) {
+			PRINT( std::cerr << "has member iter, incrementing..." << std::endl; )
+			memberIter->smallStep();
+			if ( *memberIter ) {
+				PRINT( std::cerr << "success!" << std::endl; )
+				return *this;
+			}
+		}
+		return bigStep();
+	}
+
+	AggregateIterator & bigStep() override = 0;
+
+	const Type * getType() final { return inst; }
+
+	const Type * getNext() final {
+		bool hasMember = memberIter && *memberIter;
+		return hasMember ? memberIter->getType() : nullptr;
+	}
+
+	std::deque< InitAlternative > first() const final {
+		std::deque< InitAlternative > ret;
+		PRINT( std::cerr << "first " << kind << std::endl; )
+		if ( memberIter && *memberIter ) {
+			PRINT( std::cerr << "adding children" << std::endl; )
+			ret = memberIter->first();
+			for ( InitAlternative & alt : ret ) {
+				PRINT( std::cerr << "iterating and adding designators" << std::endl; )
+				alt.designation.get_and_mutate()->designators.emplace_front(
+					new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
+			}
+		}
+		if ( atbegin ) {
+			// only add self if at the very beginning of the structure
+			PRINT( std::cerr << "adding self" << std::endl; )
+			ret.emplace_front( inst, new Designation{ location } );
+		}
+		return ret;
+	}
+};
+
+class StructIterator final : public AggregateIterator {
+public:
+	StructIterator( const CodeLocation & loc, const StructInstType * inst )
+	: AggregateIterator( loc, "StructIterator", inst->name, inst, inst->base->members ) {}
+
+	operator bool() const override {
+		return curMember != members.end() || (memberIter && *memberIter);
+	}
+
+	StructIterator & bigStep() override {
+		PRINT( std::cerr << "bigStep in " << kind << std::endl; )
+		atbegin = false;
+		memberIter = nullptr;
+		curType = nullptr;
+		while ( curMember != members.end() ) {
+			++curMember;
+			if ( init() ) return *this;
+		}
+		return *this;
+	}
+};
+
+class UnionIterator final : public AggregateIterator {
+public:
+	UnionIterator( const CodeLocation & loc, const UnionInstType * inst )
+	: AggregateIterator( loc, "UnionIterator", inst->name, inst, inst->base->members ) {}
+
+	operator bool() const override { return memberIter && *memberIter; }
+
+	UnionIterator & bigStep() override {
+		// unions only initialize one member
+		PRINT( std::cerr << "bigStep in " << kind << std::endl; )
+		atbegin = false;
+		memberIter = nullptr;
+		curType = nullptr;
+		curMember = members.end();
+		return *this;
+	}
+};
+
+/// Iterates across the positions in a tuple:
+class TupleIterator final : public IndexIterator {
+	ast::TupleType const * const tuple;
+
+	const ast::Type * typeAtIndex() const {
+		assert( index < size );
+		return tuple->types[ index ].get();
+	}
+
+public:
+	TupleIterator( const CodeLocation & loc, const TupleType * type )
+	: IndexIterator( loc, type->size() ), tuple( type ) {
+		PRINT( std::cerr << "Creating tuple iterator: " << type << std::endl; )
+		memberIter.reset( createMemberIterator( loc, typeAtIndex() ) );
+	}
+
+	TupleIterator & bigStep() override {
+		++index;
+		memberIter.reset( index < size ?
+			createMemberIterator( location, typeAtIndex() ) : nullptr );
+		return *this;
+	}
+
+	TupleIterator & smallStep() override {
+		if ( memberIter ) {
+			PRINT( std::cerr << "has member iter: " << *memberIter << std::endl; )
+			memberIter->smallStep();
+			if ( !memberIter ) {
+				PRINT( std::cerr << "has valid member iter" << std::endl; )
+				return *this;
+			}
+		}
+		return bigStep();
+	}
+
+	const ast::Type * getType() override {
+		return tuple;
+	}
+
+	const ast::Type * getNext() override {
+		bool hasMember = memberIter && *memberIter;
+		return hasMember ? memberIter->getType() : nullptr;
+	}
+
+	std::deque< InitAlternative > first() const override {
+		PRINT( std::cerr << "first in TupleIterator (" << index << "/" << size << ")" << std::endl; )
+		if ( memberIter && *memberIter ) {
+			std::deque< InitAlternative > ret = memberIter->first();
+			for ( InitAlternative & alt : ret ) {
+				alt.designation.get_and_mutate()->designators.emplace_front(
+					ConstantExpr::from_ulong( location, index ) );
+			}
+			return ret;
+		}
+		return {};
+	}
+};
+
+MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type ) {
+	if ( auto aggr = dynamic_cast< const BaseInstType * >( type ) ) {
+		if ( auto sit = dynamic_cast< const StructInstType * >( aggr ) ) {
+			assert( sit->base );
+			return new StructIterator{ loc, sit };
+		} else if ( auto uit = dynamic_cast< const UnionInstType * >( aggr ) ) {
+			assert( uit->base );
+			return new UnionIterator{ loc, uit };
+		} else {
+			assertf(
+				dynamic_cast< const EnumInstType * >( type )
+					|| dynamic_cast< const TypeInstType * >( type ),
+				"Encountered unhandled BaseInstType in createMemberIterator: %s",
+					toString( type ).c_str() );
+			return new SimpleIterator{ loc, type };
+		}
+	} else if ( auto at = dynamic_cast< const ArrayType * >( type ) ) {
+		return new ArrayIterator{ loc, at };
+	} else if ( auto tt = dynamic_cast< const TupleType * >( type ) ) {
+		return new TupleIterator{ loc, tt };
+	} else {
+		return new SimpleIterator{ loc, type };
+	}
+}
+
+} // namespace
+
+CurrentObject::CurrentObject( const CodeLocation & loc, const Type * type ) : objStack() {
+	objStack.emplace_back( new SimpleIterator{ loc, type } );
+}
+
+const Designation * CurrentObject::findNext( const Designation * designation ) {
+	using DesignatorChain = std::deque< ptr< Expr > >;
+	PRINT( std::cerr << "___findNext" << std::endl; )
+
+	// find all the d's
+	std::vector< DesignatorChain > desigAlts{ {} }, newDesigAlts;
+	std::deque< const Type * > curTypes{ objStack.back()->getType() }, newTypes;
+	for ( const Expr * expr : designation->designators ) {
+		PRINT( std::cerr << "____untyped: " << expr << std::endl; )
+		auto dit = desigAlts.begin();
+		auto nexpr = dynamic_cast< const NameExpr * >( expr );
+
+		for ( const Type * t : curTypes ) {
+			assert( dit != desigAlts.end() );
+			DesignatorChain & d = *dit;
+			// Name Designation:
+			if ( nexpr ) {
+				PRINT( std::cerr << "____actual: " << t << std::endl; )
+				if ( auto refType = dynamic_cast< const BaseInstType * >( t ) ) {
+					// concatenate identical field names
+					for ( const Decl * mem : refType->lookup( nexpr->name ) ) {
+						if ( auto field = dynamic_cast< const ObjectDecl * >( mem ) ) {
+							PRINT( std::cerr << "____alt: " << field->type << std::endl; )
+							DesignatorChain d2 = d;
+							d2.emplace_back( new VariableExpr{ expr->location, field } );
+							newDesigAlts.emplace_back( std::move( d2 ) );
+							newTypes.emplace_back( field->type );
+						}
+					}
+				}
+
+				++dit;
+			// Index Designation:
+			} else {
+				if ( auto at = dynamic_cast< const ArrayType * >( t ) ) {
+					PRINT( std::cerr << "____alt: " << at->get_base() << std::endl; )
+					d.emplace_back( expr );
+					newDesigAlts.emplace_back( d );
+					newTypes.emplace_back( at->base );
+				}
+			}
+		}
+
+		// reset queue
+		desigAlts = std::move( newDesigAlts );
+		newDesigAlts.clear();
+		curTypes = std::move( newTypes );
+		newTypes.clear();
+		assertf( desigAlts.size() == curTypes.size(), "Designator alternatives (%zu) and current types (%zu) out of sync", desigAlts.size(), curTypes.size() );
+	}
+
+	if ( desigAlts.size() > 1 ) {
+		SemanticError( designation, toString("Too many alternatives (", desigAlts.size(), ") for designation: ") );
+	} else if ( desigAlts.empty() ) {
+		SemanticError( designation, "No reasonable alternatives for designation: " );
+	}
+
+	DesignatorChain & d = desigAlts.back();
+	PRINT( for ( Expression * expr : d ) {
+		std::cerr << "____desig: " << expr << std::endl;
+	} ) // for
+	assertf( ! curTypes.empty(), "empty designator chosen");
+
+	// set new designators
+	assertf( ! objStack.empty(), "empty object stack when setting designation" );
+	Designation * actualDesignation =
+		new Designation{ designation->location, DesignatorChain{d} };
+	objStack.back()->setPosition( d ); // destroys d
+	return actualDesignation;
+}
+
+void CurrentObject::setNext( const Designation * designation ) {
+	PRINT( std::cerr << "____setNext" << designation << std::endl; )
+	assertf( ! objStack.empty(), "obj stack empty in setNext" );
+	objStack.back()->setPosition( designation->designators );
+}
+
+void CurrentObject::increment() {
+	PRINT( std::cerr << "____increment" << std::endl; )
+	if ( objStack.empty() ) return;
+	PRINT( std::cerr << *objStack.back() << std::endl; )
+	objStack.back()->smallStep();
+}
+
+void CurrentObject::enterListInit( const CodeLocation & loc ) {
+	PRINT( std::cerr << "____entering list init" << std::endl; )
+	assertf( ! objStack.empty(), "empty obj stack entering list init" );
+	const ast::Type * type = objStack.back()->getNext();
+	assert( type );
+	objStack.emplace_back( createMemberIterator( loc, type ) );
+}
+
+void CurrentObject::exitListInit() {
+	PRINT( std::cerr << "____exiting list init" << std::endl; )
+	assertf( ! objStack.empty(), "objstack empty" );
+	objStack.pop_back();
+	if ( ! objStack.empty() ) {
+		PRINT( std::cerr << *objStack.back() << std::endl; )
+		objStack.back()->bigStep();
+	}
+}
+
+std::deque< InitAlternative > CurrentObject::getOptions() {
+	PRINT( std::cerr << "____getting current options" << std::endl; )
+	assertf( ! objStack.empty(), "objstack empty in getOptions" );
+	return **objStack.back();
+}
+
+const Type * CurrentObject::getCurrentType() {
+	PRINT( std::cerr << "____getting current type" << std::endl; )
+	assertf( ! objStack.empty(), "objstack empty in getCurrentType" );
+	return objStack.back()->getNext();
+}
+
+} // namespace ast
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/CurrentObject.h
===================================================================
--- src/ResolvExpr/CurrentObject.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,67 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// CurrentObject.h --
-//
-// Author           : Rob Schluntz
-// Created On       : Thu Jun  8 11:07:25 2017
-// Last Modified By : Andrew Beach
-// Last Modified On : Thu Apr  6 16:14:00 2023
-// Update Count     : 4
-//
-
-#pragma once
-
-#include <deque>
-#include <memory> // for unique_ptr
-#include <vector>
-
-#include "AST/Node.hpp"  // for ptr
-#include "Common/CodeLocation.h"
-
-namespace ast {
-
-// AST class types:
-class Designation;
-class Type;
-struct InitAlternative;
-
-/// Iterates members of a type by initializer
-class MemberIterator;
-
-/// Builds initializer lists in resolution
-class CurrentObject final {
-	std::vector<std::shared_ptr<MemberIterator>> objStack;
-
-public:
-	CurrentObject() = default;
-	CurrentObject( const CodeLocation & loc, const Type * type );
-
-	/// Resolves unresolved designation.
-	const Designation * findNext( const Designation * designation );
-	/// Sets current position using the resolved designation.
-	void setNext( const Designation * designation );
-	/// Steps to next sub-object of current object.
-	void increment();
-	/// Sets new current object for the duration of this brace-enclosed intializer-list.
-	void enterListInit( const CodeLocation & loc );
-	/// Restores previous current object.
-	void exitListInit();
-	/// Produces a list of alternatives (Type *, Designation *)
-	/// for the current sub-object's initializer.
-	std::deque< InitAlternative > getOptions();
-	/// Produces the type of the current object but no subobjects.
-	const Type * getCurrentType();
-};
-
-} // namespace ast
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
-
Index: src/ResolvExpr/CurrentObject.hpp
===================================================================
--- src/ResolvExpr/CurrentObject.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/CurrentObject.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,67 @@
+//
+// 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.
+//
+// CurrentObject.hpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Thu Jun  8 11:07:25 2017
+// Last Modified By : Andrew Beach
+// Last Modified On : Thu Apr  6 16:14:00 2023
+// Update Count     : 4
+//
+
+#pragma once
+
+#include <deque>
+#include <memory> // for unique_ptr
+#include <vector>
+
+#include "AST/Node.hpp"  // for ptr
+#include "Common/CodeLocation.hpp"
+
+namespace ast {
+
+// AST class types:
+class Designation;
+class Type;
+struct InitAlternative;
+
+/// Iterates members of a type by initializer
+class MemberIterator;
+
+/// Builds initializer lists in resolution
+class CurrentObject final {
+	std::vector<std::shared_ptr<MemberIterator>> objStack;
+
+public:
+	CurrentObject() = default;
+	CurrentObject( const CodeLocation & loc, const Type * type );
+
+	/// Resolves unresolved designation.
+	const Designation * findNext( const Designation * designation );
+	/// Sets current position using the resolved designation.
+	void setNext( const Designation * designation );
+	/// Steps to next sub-object of current object.
+	void increment();
+	/// Sets new current object for the duration of this brace-enclosed intializer-list.
+	void enterListInit( const CodeLocation & loc );
+	/// Restores previous current object.
+	void exitListInit();
+	/// Produces a list of alternatives (Type *, Designation *)
+	/// for the current sub-object's initializer.
+	std::deque< InitAlternative > getOptions();
+	/// Produces the type of the current object but no subobjects.
+	const Type * getCurrentType();
+};
+
+} // namespace ast
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
+
Index: src/ResolvExpr/ExplodedArg.cpp
===================================================================
--- src/ResolvExpr/ExplodedArg.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ResolvExpr/ExplodedArg.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,5 +16,5 @@
 #include "ExplodedArg.hpp"
 
-#include "Tuples/Explode.h"   // for Tuples::explode
+#include "Tuples/Explode.hpp"   // for Tuples::explode
 
 namespace ResolvExpr {
Index: src/ResolvExpr/ExplodedArg.hpp
===================================================================
--- src/ResolvExpr/ExplodedArg.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ResolvExpr/ExplodedArg.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -19,5 +19,5 @@
 
 #include "Candidate.hpp"            // for Candidate, CandidateList
-#include "Cost.h"                   // for Cost
+#include "Cost.hpp"                 // for Cost
 #include "AST/Expr.hpp"
 #include "AST/Node.hpp"             // for ptr
Index: src/ResolvExpr/FindOpenVars.cc
===================================================================
--- src/ResolvExpr/FindOpenVars.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,95 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FindOpenVars.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 09:42:48 2015
-// Last Modified By : Andrew
-// Last Modified On : Fri Jul 12 14:18:00 2019
-// Update Count     : 4
-//
-
-#include "FindOpenVars.h"
-
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-#include "AST/TypeEnvironment.hpp"
-
-#include <iostream>
-
-namespace ResolvExpr {
-
-namespace {
-
-struct FindOpenVars final : public ast::WithGuards {
-	ast::OpenVarSet & open;
-	ast::OpenVarSet & closed;
-	ast::AssertionSet & need;
-	ast::AssertionSet & have;
-	ast::TypeEnvironment & env;
-	bool nextIsOpen;
-
-	FindOpenVars(
-		ast::OpenVarSet & o, ast::OpenVarSet & c, ast::AssertionSet & n,
-		ast::AssertionSet & h, ast::TypeEnvironment & env, FirstMode firstIsOpen )
-	: open( o ), closed( c ), need( n ), have( h ), env (env), nextIsOpen( firstIsOpen ) {}
-
-	void previsit( const ast::FunctionType * type ) {
-		// mark open/closed variables
-		if ( nextIsOpen ) {
-			// trying to remove this from resolver.
-			// occasionally used in other parts so not deleting right now.
-
-			// insert open variables unbound to environment.
-			env.add(type->forall);
-
-			for ( auto & decl : type->forall ) {
-				open[ *decl ] = ast::TypeData{ decl->base };
-			}
-			for ( auto & assert : type->assertions ) {
-				need[ assert ].isUsed = false;
-			}
-		} else {
-			for ( auto & decl : type->forall ) {
-				closed[ *decl ] = ast::TypeData{ decl->base };
-			}
-			for ( auto & assert : type->assertions ) {
-				have[ assert ].isUsed = false;
-			}
-		}
-
-		// flip open variables for contained function types
-	//	nextIsOpen = ! nextIsOpen;
-	//	GuardAction( [this](){ nextIsOpen = ! nextIsOpen; } );
-		GuardValue( nextIsOpen ) = !nextIsOpen;
-	}
-};
-
-} // namespace
-
-void findOpenVars(
-		const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed,
-		ast::AssertionSet & need, ast::AssertionSet & have, ast::TypeEnvironment & env, FirstMode firstIsOpen ) {
-	ast::Pass< FindOpenVars > finder{ open, closed, need, have, env, firstIsOpen };
-	type->accept( finder );
-
-	if (!closed.empty()) {
-		std::cerr << "closed: ";
-		for (auto& i : closed) {
-			std::cerr << i.first.base->location << ":" << i.first.base->name << ' ';
-		}
-		std::cerr << std::endl;
-	}
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/FindOpenVars.cpp
===================================================================
--- src/ResolvExpr/FindOpenVars.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/FindOpenVars.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,95 @@
+//
+// 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.
+//
+// FindOpenVars.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 09:42:48 2015
+// Last Modified By : Andrew
+// Last Modified On : Fri Jul 12 14:18:00 2019
+// Update Count     : 4
+//
+
+#include "FindOpenVars.hpp"
+
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+#include "AST/TypeEnvironment.hpp"
+
+#include <iostream>
+
+namespace ResolvExpr {
+
+namespace {
+
+struct FindOpenVars final : public ast::WithGuards {
+	ast::OpenVarSet & open;
+	ast::OpenVarSet & closed;
+	ast::AssertionSet & need;
+	ast::AssertionSet & have;
+	ast::TypeEnvironment & env;
+	bool nextIsOpen;
+
+	FindOpenVars(
+		ast::OpenVarSet & o, ast::OpenVarSet & c, ast::AssertionSet & n,
+		ast::AssertionSet & h, ast::TypeEnvironment & env, FirstMode firstIsOpen )
+	: open( o ), closed( c ), need( n ), have( h ), env (env), nextIsOpen( firstIsOpen ) {}
+
+	void previsit( const ast::FunctionType * type ) {
+		// mark open/closed variables
+		if ( nextIsOpen ) {
+			// trying to remove this from resolver.
+			// occasionally used in other parts so not deleting right now.
+
+			// insert open variables unbound to environment.
+			env.add(type->forall);
+
+			for ( auto & decl : type->forall ) {
+				open[ *decl ] = ast::TypeData{ decl->base };
+			}
+			for ( auto & assert : type->assertions ) {
+				need[ assert ].isUsed = false;
+			}
+		} else {
+			for ( auto & decl : type->forall ) {
+				closed[ *decl ] = ast::TypeData{ decl->base };
+			}
+			for ( auto & assert : type->assertions ) {
+				have[ assert ].isUsed = false;
+			}
+		}
+
+		// flip open variables for contained function types
+	//	nextIsOpen = ! nextIsOpen;
+	//	GuardAction( [this](){ nextIsOpen = ! nextIsOpen; } );
+		GuardValue( nextIsOpen ) = !nextIsOpen;
+	}
+};
+
+} // namespace
+
+void findOpenVars(
+		const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed,
+		ast::AssertionSet & need, ast::AssertionSet & have, ast::TypeEnvironment & env, FirstMode firstIsOpen ) {
+	ast::Pass< FindOpenVars > finder{ open, closed, need, have, env, firstIsOpen };
+	type->accept( finder );
+
+	if (!closed.empty()) {
+		std::cerr << "closed: ";
+		for (auto& i : closed) {
+			std::cerr << i.first.base->location << ":" << i.first.base->name << ' ';
+		}
+		std::cerr << std::endl;
+	}
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/FindOpenVars.h
===================================================================
--- src/ResolvExpr/FindOpenVars.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,39 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FindOpenVars.h -- 
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 09:46:04 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:35:18 2017
-// Update Count     : 3
-//
-
-#pragma once
-
-#include "AST/TypeEnvironment.hpp"  // for AssertionSet, OpenVarSet
-
-namespace ast {
-	class Type;
-}
-
-namespace ResolvExpr {
-
-enum FirstMode { FirstClosed, FirstOpen };
-
-// Updates open and closed variables and their associated assertions
-void findOpenVars(
-	const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed,
-	ast::AssertionSet & need, ast::AssertionSet & have, ast::TypeEnvironment & env, FirstMode firstIsOpen );
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/FindOpenVars.hpp
===================================================================
--- src/ResolvExpr/FindOpenVars.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/FindOpenVars.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,39 @@
+//
+// 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.
+//
+// FindOpenVars.h -- 
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 09:46:04 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Jul 22 09:35:18 2017
+// Update Count     : 3
+//
+
+#pragma once
+
+#include "AST/TypeEnvironment.hpp"  // for AssertionSet, OpenVarSet
+
+namespace ast {
+	class Type;
+}
+
+namespace ResolvExpr {
+
+enum FirstMode { FirstClosed, FirstOpen };
+
+// Updates open and closed variables and their associated assertions
+void findOpenVars(
+	const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed,
+	ast::AssertionSet & need, ast::AssertionSet & have, ast::TypeEnvironment & env, FirstMode firstIsOpen );
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/PolyCost.cc
===================================================================
--- src/ResolvExpr/PolyCost.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,63 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// PolyCost.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 09:50:12 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jun 19 10:45:00 2019
-// Update Count     : 4
-//
-
-#include "AST/SymbolTable.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-#include "AST/TypeEnvironment.hpp"
-
-namespace ResolvExpr {
-
-namespace {
-
-class PolyCost {
-	const ast::SymbolTable &symtab;
-public:
-	int result;
-	const ast::TypeEnvironment &env_;
-
-	PolyCost( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env )
-	: symtab( symtab ), result( 0 ), env_( env ) {}
-
-	void previsit( const ast::TypeInstType * type ) {
-		if ( const ast::EqvClass * eqv = env_.lookup( *type ) ; eqv && eqv->bound ) {
-			if ( const ast::TypeInstType * otherType = eqv->bound.as< ast::TypeInstType >() ) {
-				if ( symtab.lookupType( otherType->name ) ) {
-					// Bound to opaque type.
-					result = 1;
-				}
-			} else {
-				// Bound to concrete type.
-				result = 1;
-			}
-		}
-	}
-};
-
-} // namespace
-
-int polyCost(
-	const ast::Type * type, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
-) {
-	return ast::Pass<PolyCost>::read( type, symtab, env );
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/PolyCost.cpp
===================================================================
--- src/ResolvExpr/PolyCost.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/PolyCost.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,63 @@
+//
+// 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.
+//
+// PolyCost.cc --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 09:50:12 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Jun 19 10:45:00 2019
+// Update Count     : 4
+//
+
+#include "AST/SymbolTable.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+#include "AST/TypeEnvironment.hpp"
+
+namespace ResolvExpr {
+
+namespace {
+
+class PolyCost {
+	const ast::SymbolTable &symtab;
+public:
+	int result;
+	const ast::TypeEnvironment &env_;
+
+	PolyCost( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env )
+	: symtab( symtab ), result( 0 ), env_( env ) {}
+
+	void previsit( const ast::TypeInstType * type ) {
+		if ( const ast::EqvClass * eqv = env_.lookup( *type ) ; eqv && eqv->bound ) {
+			if ( const ast::TypeInstType * otherType = eqv->bound.as< ast::TypeInstType >() ) {
+				if ( symtab.lookupType( otherType->name ) ) {
+					// Bound to opaque type.
+					result = 1;
+				}
+			} else {
+				// Bound to concrete type.
+				result = 1;
+			}
+		}
+	}
+};
+
+} // namespace
+
+int polyCost(
+	const ast::Type * type, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env
+) {
+	return ast::Pass<PolyCost>::read( type, symtab, env );
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/PtrsAssignable.cc
===================================================================
--- src/ResolvExpr/PtrsAssignable.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,113 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// PtrsAssignable.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 11:44:11 2015
-// Last Modified By : Andrew
-// Last Modified On : Mon Jun 24 15:29:00 2019
-// Update Count     : 9
-//
-
-#include "PtrsAssignable.hpp"
-
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-#include "AST/TypeEnvironment.hpp"
-
-namespace ResolvExpr {
-
-namespace {
-
-struct PtrsAssignable : public ast::WithShortCircuiting {
-	const ast::Type * dst;
-	const ast::TypeEnvironment & typeEnv;
-	int result;
-
-	PtrsAssignable( const ast::Type * dst, const ast::TypeEnvironment & env ) :
-		dst( dst ), typeEnv( env ), result( 0 ) {}
-
-	void previsit( ast::Type * ) { visit_children = false; }
-
-	void postvisit( const ast::EnumInstType * ) {
-		if ( dynamic_cast< const ast::BasicType * >( dst ) ) {
-			// int * = E *, etc. is safe. This isn't technically correct, as each
-			// enum has one basic type that it is compatible with, an that type can
-			// differ from enum to enum. Without replicating GCC's internal logic,
-			// there is no way to know which type this particular enum is compatible
-			// with, so punt on this for now.
-			result = 1;
-		}
-	}
-	void postvisit( const ast::TypeInstType * inst ) {
-		if ( const ast::EqvClass * eqv = typeEnv.lookup( *inst ) ) {
-			if ( eqv->bound ) {
-				// T * = S * for any S depends on the type bound to T
-				result = ptrsAssignable( eqv->bound, dst, typeEnv );
-			}
-		}
-	}
-};
-
-} // namespace
-
-int ptrsAssignable( const ast::Type * src, const ast::Type * dst,
-		const ast::TypeEnvironment & env ) {
-	if ( const ast::TypeInstType * dstAsInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( const ast::EqvClass * eqv = env.lookup( *dstAsInst ) ) {
-			return ptrsAssignable( src, eqv->bound, env );
-		}
-	}
-	if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
-		return -1;
-	} else {
-		ast::Pass<PtrsAssignable> visitor( dst, env );
-		src->accept( visitor );
-		return visitor.core.result;
-	}
-
-// see ticket #136 (this should be able to replace the visitor).
-#if 0
-	if ( const ast::TypeInstType * dstAsTypeInst =
-			dynamic_cast< const ast::TypeInstType* >( dst ) ) {
-		if ( const ast::EqvClass * eqv = env.lookup( dstAsTypeInst->get_name() ) ) {
-			return ptrsAssignable( src, eqv->type, env );
-		} // if
-	} // if
-	if ( dynamic_cast< VoidType* >( dst ) ) {
-		// void * = T * for any T is unsafe
-		// xxx - this should be safe, but that currently breaks the build
-		return -1;
-	} else if ( dynamic_cast< EnumInstType * >( src ) ) {
-		if ( dynamic_cast< BasicType * >( dst ) ) {
-			// int * = E *, etc. is safe. This isn't technically correct, as each
-			// enum has one basic type that it is compatible with, an that type can
-			// differ from enum to enum. Without replicating GCC's internal logic,
-			// there is no way to know which type this particular enum is compatible
-			// with, so punt on this for now.
-			return 1;
-		}
-	} else if ( const ast::TypeInstType * typeInstType =
-			dynamic_cast< const ast::TypeInstType * >( src ) ) {
-		if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) {
-			if ( eqv->bound ) {
-				// T * = S * for any S depends on the type bound to T
-				return ptrsAssignable( eqv->bound, dst, env );
-			}
-		}
-	}
-	return 0;
-#endif
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/PtrsAssignable.cpp
===================================================================
--- src/ResolvExpr/PtrsAssignable.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/PtrsAssignable.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,113 @@
+//
+// 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.
+//
+// PtrsAssignable.cc --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 11:44:11 2015
+// Last Modified By : Andrew
+// Last Modified On : Mon Jun 24 15:29:00 2019
+// Update Count     : 9
+//
+
+#include "PtrsAssignable.hpp"
+
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+#include "AST/TypeEnvironment.hpp"
+
+namespace ResolvExpr {
+
+namespace {
+
+struct PtrsAssignable : public ast::WithShortCircuiting {
+	const ast::Type * dst;
+	const ast::TypeEnvironment & typeEnv;
+	int result;
+
+	PtrsAssignable( const ast::Type * dst, const ast::TypeEnvironment & env ) :
+		dst( dst ), typeEnv( env ), result( 0 ) {}
+
+	void previsit( ast::Type * ) { visit_children = false; }
+
+	void postvisit( const ast::EnumInstType * ) {
+		if ( dynamic_cast< const ast::BasicType * >( dst ) ) {
+			// int * = E *, etc. is safe. This isn't technically correct, as each
+			// enum has one basic type that it is compatible with, an that type can
+			// differ from enum to enum. Without replicating GCC's internal logic,
+			// there is no way to know which type this particular enum is compatible
+			// with, so punt on this for now.
+			result = 1;
+		}
+	}
+	void postvisit( const ast::TypeInstType * inst ) {
+		if ( const ast::EqvClass * eqv = typeEnv.lookup( *inst ) ) {
+			if ( eqv->bound ) {
+				// T * = S * for any S depends on the type bound to T
+				result = ptrsAssignable( eqv->bound, dst, typeEnv );
+			}
+		}
+	}
+};
+
+} // namespace
+
+int ptrsAssignable( const ast::Type * src, const ast::Type * dst,
+		const ast::TypeEnvironment & env ) {
+	if ( const ast::TypeInstType * dstAsInst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
+		if ( const ast::EqvClass * eqv = env.lookup( *dstAsInst ) ) {
+			return ptrsAssignable( src, eqv->bound, env );
+		}
+	}
+	if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
+		return -1;
+	} else {
+		ast::Pass<PtrsAssignable> visitor( dst, env );
+		src->accept( visitor );
+		return visitor.core.result;
+	}
+
+// see ticket #136 (this should be able to replace the visitor).
+#if 0
+	if ( const ast::TypeInstType * dstAsTypeInst =
+			dynamic_cast< const ast::TypeInstType* >( dst ) ) {
+		if ( const ast::EqvClass * eqv = env.lookup( dstAsTypeInst->get_name() ) ) {
+			return ptrsAssignable( src, eqv->type, env );
+		} // if
+	} // if
+	if ( dynamic_cast< VoidType* >( dst ) ) {
+		// void * = T * for any T is unsafe
+		// xxx - this should be safe, but that currently breaks the build
+		return -1;
+	} else if ( dynamic_cast< EnumInstType * >( src ) ) {
+		if ( dynamic_cast< BasicType * >( dst ) ) {
+			// int * = E *, etc. is safe. This isn't technically correct, as each
+			// enum has one basic type that it is compatible with, an that type can
+			// differ from enum to enum. Without replicating GCC's internal logic,
+			// there is no way to know which type this particular enum is compatible
+			// with, so punt on this for now.
+			return 1;
+		}
+	} else if ( const ast::TypeInstType * typeInstType =
+			dynamic_cast< const ast::TypeInstType * >( src ) ) {
+		if ( const ast::EqvClass * eqv = env.lookup( typeInstType->name ) ) {
+			if ( eqv->bound ) {
+				// T * = S * for any S depends on the type bound to T
+				return ptrsAssignable( eqv->bound, dst, env );
+			}
+		}
+	}
+	return 0;
+#endif
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/PtrsCastable.cc
===================================================================
--- src/ResolvExpr/PtrsCastable.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,161 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// PtrsCastable.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 11:48:00 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Wed Dec 11 21:48:33 2019
-// Update Count     : 9
-//
-
-#include "PtrsCastable.hpp"
-
-#include "AST/Decl.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-#include "AST/TypeEnvironment.hpp"
-#include "ResolvExpr/PtrsAssignable.hpp" // for ptrsAssignable
-
-namespace ResolvExpr {
-
-namespace {
-	// can this type be cast to an object (1 for yes, -1 for no)
-	int objectCast(
-		const ast::Type * src, const ast::TypeEnvironment & env, const ast::SymbolTable & symtab
-	) {
-		if ( dynamic_cast< const ast::FunctionType * >( src ) ) {
-			return -1;
-		} else if ( auto inst = dynamic_cast< const ast::TypeInstType * >( src ) ) {
-			if ( const ast::NamedTypeDecl * named = symtab.lookupType( inst->name ) ) {
-				if ( auto tyDecl = dynamic_cast< const ast::TypeDecl * >( named ) ) {
-					if ( tyDecl->kind == ast::TypeDecl::Ftype ) {
-						return -1;
-					}
-				}
-			} else if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
-				if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
-					return -1;
-				}
-			}
-		}
-
-		return 1;
-	}
-
-	// can this type be cast to a function (inverse of objectCast)
-	int functionCast(
-		const ast::Type * src, const ast::TypeEnvironment & env, const ast::SymbolTable & symtab
-	) {
-		return -1 * objectCast( src, env, symtab );
-	}
-
-	class PtrsCastable : public ast::WithShortCircuiting {
-		const ast::Type * dst;
-		const ast::TypeEnvironment & env;
-		const ast::SymbolTable & symtab;
-	public:
-		int result;
-
-		PtrsCastable(
-			const ast::Type * d, const ast::TypeEnvironment & e, const ast::SymbolTable & syms )
-		: dst( d ), env( e ), symtab( syms ), result( 0 ) {}
-
-		void previsit( const ast::Type * ) { visit_children = false; }
-
-		void postvisit( const ast::VoidType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::BasicType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::PointerType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::ArrayType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::FunctionType * ) {
-			result = functionCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::StructInstType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::UnionInstType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::EnumInstType * ) {
-			if ( dynamic_cast< const ast::EnumInstType * >( dst ) ) {
-				result = 1;
-			} else if ( auto bt = dynamic_cast< const ast::BasicType * >( dst ) ) {
-				if ( bt->kind == ast::BasicKind::SignedInt ) {
-					result = 0;
-				} else {
-					result = 1;
-				}
-			} else {
-				result = objectCast( dst, env, symtab );
-			}
-		}
-
-		void postvisit( const ast::TraitInstType * ) {}
-
-		void postvisit( const ast::TypeInstType * inst ) {
-			// check trait and destination type are both object or both function
-			result = objectCast( inst, env, symtab ) == objectCast( dst, env, symtab ) ? 1 : -1;
-		}
-
-		void postvisit( const ast::TupleType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::VarArgsType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::ZeroType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-		void postvisit( const ast::OneType * ) {
-			result = objectCast( dst, env, symtab );
-		}
-
-	};
-} // anonymous namespace
-
-int ptrsCastable(
-	const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
-	const ast::TypeEnvironment & env
-) {
-	if ( auto inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
-		if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
-			return ptrsAssignable( src, eqvClass->bound, env );
-		}
-	}
-
-	if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
-		return objectCast( src, env, symtab );
-	} else {
-		return ast::Pass<PtrsCastable>::read( src, dst, env, symtab );
-	}
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/PtrsCastable.cpp
===================================================================
--- src/ResolvExpr/PtrsCastable.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/PtrsCastable.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,161 @@
+//
+// 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.
+//
+// PtrsCastable.cc --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 11:48:00 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Dec 11 21:48:33 2019
+// Update Count     : 9
+//
+
+#include "PtrsCastable.hpp"
+
+#include "AST/Decl.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+#include "AST/TypeEnvironment.hpp"
+#include "ResolvExpr/PtrsAssignable.hpp" // for ptrsAssignable
+
+namespace ResolvExpr {
+
+namespace {
+	// can this type be cast to an object (1 for yes, -1 for no)
+	int objectCast(
+		const ast::Type * src, const ast::TypeEnvironment & env, const ast::SymbolTable & symtab
+	) {
+		if ( dynamic_cast< const ast::FunctionType * >( src ) ) {
+			return -1;
+		} else if ( auto inst = dynamic_cast< const ast::TypeInstType * >( src ) ) {
+			if ( const ast::NamedTypeDecl * named = symtab.lookupType( inst->name ) ) {
+				if ( auto tyDecl = dynamic_cast< const ast::TypeDecl * >( named ) ) {
+					if ( tyDecl->kind == ast::TypeDecl::Ftype ) {
+						return -1;
+					}
+				}
+			} else if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
+				if ( eqvClass->data.kind == ast::TypeDecl::Ftype ) {
+					return -1;
+				}
+			}
+		}
+
+		return 1;
+	}
+
+	// can this type be cast to a function (inverse of objectCast)
+	int functionCast(
+		const ast::Type * src, const ast::TypeEnvironment & env, const ast::SymbolTable & symtab
+	) {
+		return -1 * objectCast( src, env, symtab );
+	}
+
+	class PtrsCastable : public ast::WithShortCircuiting {
+		const ast::Type * dst;
+		const ast::TypeEnvironment & env;
+		const ast::SymbolTable & symtab;
+	public:
+		int result;
+
+		PtrsCastable(
+			const ast::Type * d, const ast::TypeEnvironment & e, const ast::SymbolTable & syms )
+		: dst( d ), env( e ), symtab( syms ), result( 0 ) {}
+
+		void previsit( const ast::Type * ) { visit_children = false; }
+
+		void postvisit( const ast::VoidType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::BasicType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::PointerType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::ArrayType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::FunctionType * ) {
+			result = functionCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::StructInstType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::UnionInstType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::EnumInstType * ) {
+			if ( dynamic_cast< const ast::EnumInstType * >( dst ) ) {
+				result = 1;
+			} else if ( auto bt = dynamic_cast< const ast::BasicType * >( dst ) ) {
+				if ( bt->kind == ast::BasicKind::SignedInt ) {
+					result = 0;
+				} else {
+					result = 1;
+				}
+			} else {
+				result = objectCast( dst, env, symtab );
+			}
+		}
+
+		void postvisit( const ast::TraitInstType * ) {}
+
+		void postvisit( const ast::TypeInstType * inst ) {
+			// check trait and destination type are both object or both function
+			result = objectCast( inst, env, symtab ) == objectCast( dst, env, symtab ) ? 1 : -1;
+		}
+
+		void postvisit( const ast::TupleType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::VarArgsType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::ZeroType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+		void postvisit( const ast::OneType * ) {
+			result = objectCast( dst, env, symtab );
+		}
+
+	};
+} // anonymous namespace
+
+int ptrsCastable(
+	const ast::Type * src, const ast::Type * dst, const ast::SymbolTable & symtab,
+	const ast::TypeEnvironment & env
+) {
+	if ( auto inst = dynamic_cast< const ast::TypeInstType * >( dst ) ) {
+		if ( const ast::EqvClass * eqvClass = env.lookup( *inst ) ) {
+			return ptrsAssignable( src, eqvClass->bound, env );
+		}
+	}
+
+	if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
+		return objectCast( src, env, symtab );
+	} else {
+		return ast::Pass<PtrsCastable>::read( src, dst, env, symtab );
+	}
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/RenameVars.cc
===================================================================
--- src/ResolvExpr/RenameVars.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,153 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// RenameVars.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 12:05:18 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Thr Jun 20 17:39:00 2019
-// Update Count     : 8
-//
-
-#include <ext/alloc_traits.h>      // for __alloc_traits<>::value_type
-#include <memory>                  // for allocator_traits<>::value_type
-#include <sstream>                 // for operator<<, basic_ostream, ostring...
-#include <utility>                 // for pair
-
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-#include "Common/ScopedMap.h"
-#include "Common/SemanticError.h"  // for SemanticError
-#include "RenameVars.h"
-
-#include "AST/Copy.hpp"
-
-namespace ResolvExpr {
-
-namespace {
-
-class RenamingData {
-	int level = 0;
-	int resetCount = 0;
-
-	int next_expr_id = 1;
-	int next_usage_id = 1;
-	ScopedMap< std::string, std::string > nameMap;
-	ScopedMap< std::string, ast::TypeEnvKey > idMap;
-public:
-	void reset() {
-		level = 0;
-		++resetCount;
-	}
-
-	void nextUsage() {
-		++next_usage_id;
-	}
-
-	const ast::TypeInstType * rename( const ast::TypeInstType * type ) {
-		auto it = idMap.find( type->name );
-		if ( it == idMap.end() ) return type;
-
-		// Unconditionally mutate because map will *always* have different name.
-		ast::TypeInstType * mut = ast::shallowCopy( type );
-		// Reconcile base node since some copies might have been made.
-		mut->base = it->second.base;
-		mut->formal_usage = it->second.formal_usage;
-		mut->expr_id = it->second.expr_id;
-		return mut;
-	}
-
-	const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) {
-		if ( type->forall.empty() ) return type;
-		idMap.beginScope();
-
-		// Load new names from this forall clause and perform renaming.
-		auto mutType = ast::shallowCopy( type );
-		// assert( type == mutType && "mutated type must be unique from ForallSubstitutor" );
-		for ( auto & td : mutType->forall ) {
-			auto mut = ast::shallowCopy( td.get() );
-			// assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" );
-
-			if ( mode == GEN_EXPR_ID ) {
-				mut->expr_id = next_expr_id;
-				mut->formal_usage = -1;
-				++next_expr_id;
-			} else if ( mode == GEN_USAGE ) {
-				assertf( mut->expr_id, "unfilled expression id in generating candidate type" );
-				mut->formal_usage = next_usage_id;
-			} else {
-				assert(false);
-			}
-			idMap[ td->name ] = ast::TypeEnvKey( *mut );
-
-			td = mut;
-		}
-
-		return mutType;
-	}
-
-	void closeLevel( const ast::FunctionType * type ) {
-		if ( type->forall.empty() ) return;
-		idMap.endScope();
-	}
-};
-
-// Global State:
-RenamingData renaming;
-
-struct RenameVars final : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ {
-	RenameMode mode;
-
-	const ast::FunctionType * previsit( const ast::FunctionType * type ) {
-		return renaming.openLevel( type, mode );
-	}
-
-	/*
-	const ast::StructInstType * previsit( const ast::StructInstType * type ) {
-		return renaming.openLevel( type );
-	}
-	const ast::UnionInstType * previsit( const ast::UnionInstType * type ) {
-		return renaming.openLevel( type );
-	}
-	const ast::TraitInstType * previsit( const ast::TraitInstType * type ) {
-		return renaming.openLevel( type );
-	}
-	*/
-
-	const ast::TypeInstType * previsit( const ast::TypeInstType * type ) {
-		// Do not rename an actual type.
-		if ( mode == GEN_USAGE && !type->formal_usage ) return type;
-		return renaming.rename( type );
-	}
-	void postvisit( const ast::FunctionType * type ) {
-		renaming.closeLevel( type );
-	}
-};
-
-} // namespace
-
-const ast::Type * renameTyVars( const ast::Type * t, RenameMode mode, bool reset ) {
-	ast::Pass<RenameVars> renamer;
-	renamer.core.mode = mode;
-	if (mode == GEN_USAGE && reset) {
-		renaming.nextUsage();
-	}
-	return t->accept( renamer );
-}
-
-void resetTyVarRenaming() {
-	renaming.reset();
-	renaming.nextUsage();
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/RenameVars.cpp
===================================================================
--- src/ResolvExpr/RenameVars.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/RenameVars.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,153 @@
+//
+// 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.
+//
+// RenameVars.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 12:05:18 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Thr Jun 20 17:39:00 2019
+// Update Count     : 8
+//
+
+#include <ext/alloc_traits.h>         // for __alloc_traits<>::value_type
+#include <memory>                     // for allocator_traits<>::value_type
+#include <sstream>                    // for operator<<, basic_ostream, ostr...
+#include <utility>                    // for pair
+
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+#include "Common/ScopedMap.hpp"
+#include "Common/SemanticError.hpp"   // for SemanticError
+#include "RenameVars.hpp"
+
+#include "AST/Copy.hpp"
+
+namespace ResolvExpr {
+
+namespace {
+
+class RenamingData {
+	int level = 0;
+	int resetCount = 0;
+
+	int next_expr_id = 1;
+	int next_usage_id = 1;
+	ScopedMap< std::string, std::string > nameMap;
+	ScopedMap< std::string, ast::TypeEnvKey > idMap;
+public:
+	void reset() {
+		level = 0;
+		++resetCount;
+	}
+
+	void nextUsage() {
+		++next_usage_id;
+	}
+
+	const ast::TypeInstType * rename( const ast::TypeInstType * type ) {
+		auto it = idMap.find( type->name );
+		if ( it == idMap.end() ) return type;
+
+		// Unconditionally mutate because map will *always* have different name.
+		ast::TypeInstType * mut = ast::shallowCopy( type );
+		// Reconcile base node since some copies might have been made.
+		mut->base = it->second.base;
+		mut->formal_usage = it->second.formal_usage;
+		mut->expr_id = it->second.expr_id;
+		return mut;
+	}
+
+	const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) {
+		if ( type->forall.empty() ) return type;
+		idMap.beginScope();
+
+		// Load new names from this forall clause and perform renaming.
+		auto mutType = ast::shallowCopy( type );
+		// assert( type == mutType && "mutated type must be unique from ForallSubstitutor" );
+		for ( auto & td : mutType->forall ) {
+			auto mut = ast::shallowCopy( td.get() );
+			// assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" );
+
+			if ( mode == GEN_EXPR_ID ) {
+				mut->expr_id = next_expr_id;
+				mut->formal_usage = -1;
+				++next_expr_id;
+			} else if ( mode == GEN_USAGE ) {
+				assertf( mut->expr_id, "unfilled expression id in generating candidate type" );
+				mut->formal_usage = next_usage_id;
+			} else {
+				assert(false);
+			}
+			idMap[ td->name ] = ast::TypeEnvKey( *mut );
+
+			td = mut;
+		}
+
+		return mutType;
+	}
+
+	void closeLevel( const ast::FunctionType * type ) {
+		if ( type->forall.empty() ) return;
+		idMap.endScope();
+	}
+};
+
+// Global State:
+RenamingData renaming;
+
+struct RenameVars final : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ {
+	RenameMode mode;
+
+	const ast::FunctionType * previsit( const ast::FunctionType * type ) {
+		return renaming.openLevel( type, mode );
+	}
+
+	/*
+	const ast::StructInstType * previsit( const ast::StructInstType * type ) {
+		return renaming.openLevel( type );
+	}
+	const ast::UnionInstType * previsit( const ast::UnionInstType * type ) {
+		return renaming.openLevel( type );
+	}
+	const ast::TraitInstType * previsit( const ast::TraitInstType * type ) {
+		return renaming.openLevel( type );
+	}
+	*/
+
+	const ast::TypeInstType * previsit( const ast::TypeInstType * type ) {
+		// Do not rename an actual type.
+		if ( mode == GEN_USAGE && !type->formal_usage ) return type;
+		return renaming.rename( type );
+	}
+	void postvisit( const ast::FunctionType * type ) {
+		renaming.closeLevel( type );
+	}
+};
+
+} // namespace
+
+const ast::Type * renameTyVars( const ast::Type * t, RenameMode mode, bool reset ) {
+	ast::Pass<RenameVars> renamer;
+	renamer.core.mode = mode;
+	if (mode == GEN_USAGE && reset) {
+		renaming.nextUsage();
+	}
+	return t->accept( renamer );
+}
+
+void resetTyVarRenaming() {
+	renaming.reset();
+	renaming.nextUsage();
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/RenameVars.h
===================================================================
--- src/ResolvExpr/RenameVars.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,39 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// RenameVars.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 12:10:28 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:33:54 2017
-// Update Count     : 4
-//
-
-#pragma once
-
-namespace ast {
-	class Type;
-}
-
-namespace ResolvExpr {
-
-enum RenameMode {
-	GEN_USAGE, // for type in VariableExpr
-	GEN_EXPR_ID // for type in decl
-};
-const ast::Type * renameTyVars( const ast::Type *, RenameMode mode = GEN_USAGE, bool reset = true );
-
-/// Resets internal state of renamer to avoid overflow.
-void resetTyVarRenaming();
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/RenameVars.hpp
===================================================================
--- src/ResolvExpr/RenameVars.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/RenameVars.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,39 @@
+//
+// 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.
+//
+// RenameVars.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 12:10:28 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Jul 22 09:33:54 2017
+// Update Count     : 4
+//
+
+#pragma once
+
+namespace ast {
+	class Type;
+}
+
+namespace ResolvExpr {
+
+enum RenameMode {
+	GEN_USAGE, // for type in VariableExpr
+	GEN_EXPR_ID // for type in decl
+};
+const ast::Type * renameTyVars( const ast::Type *, RenameMode mode = GEN_USAGE, bool reset = true );
+
+/// Resets internal state of renamer to avoid overflow.
+void resetTyVarRenaming();
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/ResolveTypeof.cc
===================================================================
--- src/ResolvExpr/ResolveTypeof.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,188 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ResolveTypeof.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 12:12:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Mar 16 16:09:00 2022
-// Update Count     : 4
-//
-
-#include "ResolveTypeof.h"
-
-#include <cassert>  // for assert
-
-#include "AST/CVQualifiers.hpp"
-#include "AST/Node.hpp"
-#include "AST/Pass.hpp"
-#include "AST/TranslationUnit.hpp"
-#include "AST/Type.hpp"
-#include "AST/TypeEnvironment.hpp"
-#include "Common/utility.h"       // for copy
-#include "InitTweak/InitTweak.h"  // for isConstExpr
-#include "RenameVars.h"
-#include "Resolver.h"  // for resolveInVoidContext
-#include "SymTab/Mangler.h"
-
-namespace ResolvExpr {
-
-namespace {
-
-struct ResolveTypeof : public ast::WithShortCircuiting {
-	const ResolveContext & context;
-
-	ResolveTypeof( const ResolveContext & context ) : context( context ) {}
-
-	void previsit( const ast::TypeofType * ) { visit_children = false; }
-
-	const ast::Type * postvisit( const ast::TypeofType * typeofType ) {
-		// pass on null expression
-		if ( ! typeofType->expr ) return typeofType;
-
-		ast::ptr< ast::Type > newType;
-		if ( auto tyExpr = typeofType->expr.as< ast::TypeExpr >() ) {
-			// typeof wrapping type
-			newType = tyExpr->type;
-		} else {
-			// typeof wrapping expression
-			ast::TypeEnvironment dummy;
-			ast::ptr< ast::Expr > newExpr =
-				resolveInVoidContext( typeofType->expr, context, dummy );
-			assert( newExpr->result && ! newExpr->result->isVoid() );
-			newType = newExpr->result;
-		}
-
-		// clear qualifiers for base, combine with typeoftype quals regardless
-		if ( typeofType->kind == ast::TypeofType::Basetypeof ) {
-			// replace basetypeof(<enum>) by int
-			if ( newType.as< ast::EnumInstType >() ) {
-				newType = new ast::BasicType(
-					ast::BasicKind::SignedInt, newType->qualifiers, copy(newType->attributes) );
-			}
-			reset_qualifiers(
-				newType,
-				( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers );
-		} else {
-			add_qualifiers( newType, typeofType->qualifiers );
-		}
-
-		return newType.release();
-	}
-};
-
-} // anonymous namespace
-
-const ast::Type * resolveTypeof( const ast::Type * type , const ResolveContext & context ) {
-	ast::Pass< ResolveTypeof > mutator( context );
-	return type->accept( mutator );
-}
-
-struct FixArrayDimension {
-	const ResolveContext & context;
-	FixArrayDimension(const ResolveContext & context) : context( context ) {}
-
-	const ast::ArrayType * previsit (const ast::ArrayType * arrayType) {
-		if (!arrayType->dimension) return arrayType;
-		auto mutType = mutate(arrayType);
-		auto globalSizeType = context.global.sizeType;
-		ast::ptr<ast::Type> sizetype = globalSizeType ? globalSizeType : new ast::BasicType( ast::BasicKind::LongUnsignedInt );
-		mutType->dimension = findSingleExpression(arrayType->dimension, sizetype, context );
-
-		if (InitTweak::isConstExpr(mutType->dimension)) {
-			mutType->isVarLen = ast::LengthFlag::FixedLen;
-		}
-		else {
-			mutType->isVarLen = ast::LengthFlag::VariableLen;
-		}
-		return mutType;
-	}
-};
-
-const ast::Type * fixArrayType( const ast::Type * type, const ResolveContext & context ) {
-	ast::Pass<FixArrayDimension> visitor(context);
-	return type->accept(visitor);
-}
-
-const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ResolveContext & context ) {
-	if ( decl->isTypeFixed ) {
-		return decl;
-	}
-
-	auto mutDecl = mutate(decl);
-	fixObjectInit(decl, context);
-	{
-		auto resolvedType = resolveTypeof(decl->type, context);
-		resolvedType = fixArrayType(resolvedType, context);
-		mutDecl->type = resolvedType;
-	}
-
-	// Do not mangle unnamed variables.
-	if ( !mutDecl->name.empty() ) {
-		mutDecl->mangleName = Mangle::mangle(mutDecl);
-	}
-
-	mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
-	mutDecl->isTypeFixed = true;
-	return mutDecl;
-}
-
-const ast::ObjectDecl *fixObjectInit(
-		const ast::ObjectDecl *decl, const ResolveContext &context) {
-	if ( decl->isTypeFixed ) {
-		return decl;
-	}
-
-	if ( auto listInit = decl->init.as<ast::ListInit>() ) {
-		for ( size_t k = 0; k < listInit->designations.size(); k++ ) {
-			const ast::Designation *des = listInit->designations[k].get();
-			// Desination here
-			ast::Designation * newDesignation = new ast::Designation(des->location);
-			std::deque<ast::ptr<ast::Expr>> newDesignators;
-
-			for ( ast::ptr<ast::Expr> designator : des->designators ) {
-				// Stupid flag variable for development, to be removed
-				if ( const ast::NameExpr * designatorName = designator.as<ast::NameExpr>() ) {
-					auto candidates = context.symtab.lookupId(designatorName->name);
-					// Does not work for the overloading case currently
-					// assert( candidates.size() == 1 );
-					if ( candidates.size() != 1 ) return decl;
-					auto candidate = candidates.at(0);
-					if ( const ast::EnumInstType * enumInst = dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type())) {
-						// determine that is an enumInst, swap it with its const value
-						assert( candidates.size() == 1 );
-						const ast::EnumDecl * baseEnum = enumInst->base;
-						// Need to iterate over all enum value to find the initializer to swap
-						for ( size_t m = 0; m < baseEnum->members.size(); ++m ) {
-							const ast::ObjectDecl * mem = baseEnum->members.at(m).as<const ast::ObjectDecl>();
-							if ( baseEnum->members.at(m)->name == designatorName->name ) {
-								assert( mem );
-								newDesignators.push_back( ast::ConstantExpr::from_int(designator->location, m) );
-								break;
-							}
-						}
-					} else {
-						newDesignators.push_back( des->designators.at(0) );
-					}
-				} else {
-					newDesignators.push_back( des->designators.at(0) );
-				}
-			}
-			newDesignation->designators = newDesignators;
-			listInit = ast::mutate_field_index(listInit, &ast::ListInit::designations, k, newDesignation);
-		}
-	}
-	return decl;
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/ResolveTypeof.cpp
===================================================================
--- src/ResolvExpr/ResolveTypeof.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/ResolveTypeof.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,188 @@
+//
+// 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.
+//
+// ResolveTypeof.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 12:12:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Mar 16 16:09:00 2022
+// Update Count     : 4
+//
+
+#include "ResolveTypeof.hpp"
+
+#include <cassert>                  // for assert
+
+#include "AST/CVQualifiers.hpp"
+#include "AST/Node.hpp"
+#include "AST/Pass.hpp"
+#include "AST/TranslationUnit.hpp"
+#include "AST/Type.hpp"
+#include "AST/TypeEnvironment.hpp"
+#include "Common/Utility.hpp"       // for copy
+#include "InitTweak/InitTweak.hpp"  // for isConstExpr
+#include "RenameVars.hpp"
+#include "Resolver.hpp"             // for resolveInVoidContext
+#include "SymTab/Mangler.hpp"
+
+namespace ResolvExpr {
+
+namespace {
+
+struct ResolveTypeof : public ast::WithShortCircuiting {
+	const ResolveContext & context;
+
+	ResolveTypeof( const ResolveContext & context ) : context( context ) {}
+
+	void previsit( const ast::TypeofType * ) { visit_children = false; }
+
+	const ast::Type * postvisit( const ast::TypeofType * typeofType ) {
+		// pass on null expression
+		if ( ! typeofType->expr ) return typeofType;
+
+		ast::ptr< ast::Type > newType;
+		if ( auto tyExpr = typeofType->expr.as< ast::TypeExpr >() ) {
+			// typeof wrapping type
+			newType = tyExpr->type;
+		} else {
+			// typeof wrapping expression
+			ast::TypeEnvironment dummy;
+			ast::ptr< ast::Expr > newExpr =
+				resolveInVoidContext( typeofType->expr, context, dummy );
+			assert( newExpr->result && ! newExpr->result->isVoid() );
+			newType = newExpr->result;
+		}
+
+		// clear qualifiers for base, combine with typeoftype quals regardless
+		if ( typeofType->kind == ast::TypeofType::Basetypeof ) {
+			// replace basetypeof(<enum>) by int
+			if ( newType.as< ast::EnumInstType >() ) {
+				newType = new ast::BasicType(
+					ast::BasicKind::SignedInt, newType->qualifiers, copy(newType->attributes) );
+			}
+			reset_qualifiers(
+				newType,
+				( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers );
+		} else {
+			add_qualifiers( newType, typeofType->qualifiers );
+		}
+
+		return newType.release();
+	}
+};
+
+} // anonymous namespace
+
+const ast::Type * resolveTypeof( const ast::Type * type , const ResolveContext & context ) {
+	ast::Pass< ResolveTypeof > mutator( context );
+	return type->accept( mutator );
+}
+
+struct FixArrayDimension {
+	const ResolveContext & context;
+	FixArrayDimension(const ResolveContext & context) : context( context ) {}
+
+	const ast::ArrayType * previsit (const ast::ArrayType * arrayType) {
+		if (!arrayType->dimension) return arrayType;
+		auto mutType = mutate(arrayType);
+		auto globalSizeType = context.global.sizeType;
+		ast::ptr<ast::Type> sizetype = globalSizeType ? globalSizeType : new ast::BasicType( ast::BasicKind::LongUnsignedInt );
+		mutType->dimension = findSingleExpression(arrayType->dimension, sizetype, context );
+
+		if (InitTweak::isConstExpr(mutType->dimension)) {
+			mutType->isVarLen = ast::LengthFlag::FixedLen;
+		}
+		else {
+			mutType->isVarLen = ast::LengthFlag::VariableLen;
+		}
+		return mutType;
+	}
+};
+
+const ast::Type * fixArrayType( const ast::Type * type, const ResolveContext & context ) {
+	ast::Pass<FixArrayDimension> visitor(context);
+	return type->accept(visitor);
+}
+
+const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ResolveContext & context ) {
+	if ( decl->isTypeFixed ) {
+		return decl;
+	}
+
+	auto mutDecl = mutate(decl);
+	fixObjectInit(decl, context);
+	{
+		auto resolvedType = resolveTypeof(decl->type, context);
+		resolvedType = fixArrayType(resolvedType, context);
+		mutDecl->type = resolvedType;
+	}
+
+	// Do not mangle unnamed variables.
+	if ( !mutDecl->name.empty() ) {
+		mutDecl->mangleName = Mangle::mangle(mutDecl);
+	}
+
+	mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
+	mutDecl->isTypeFixed = true;
+	return mutDecl;
+}
+
+const ast::ObjectDecl *fixObjectInit(
+		const ast::ObjectDecl *decl, const ResolveContext &context) {
+	if ( decl->isTypeFixed ) {
+		return decl;
+	}
+
+	if ( auto listInit = decl->init.as<ast::ListInit>() ) {
+		for ( size_t k = 0; k < listInit->designations.size(); k++ ) {
+			const ast::Designation *des = listInit->designations[k].get();
+			// Desination here
+			ast::Designation * newDesignation = new ast::Designation(des->location);
+			std::deque<ast::ptr<ast::Expr>> newDesignators;
+
+			for ( ast::ptr<ast::Expr> designator : des->designators ) {
+				// Stupid flag variable for development, to be removed
+				if ( const ast::NameExpr * designatorName = designator.as<ast::NameExpr>() ) {
+					auto candidates = context.symtab.lookupId(designatorName->name);
+					// Does not work for the overloading case currently
+					// assert( candidates.size() == 1 );
+					if ( candidates.size() != 1 ) return decl;
+					auto candidate = candidates.at(0);
+					if ( const ast::EnumInstType * enumInst = dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type())) {
+						// determine that is an enumInst, swap it with its const value
+						assert( candidates.size() == 1 );
+						const ast::EnumDecl * baseEnum = enumInst->base;
+						// Need to iterate over all enum value to find the initializer to swap
+						for ( size_t m = 0; m < baseEnum->members.size(); ++m ) {
+							const ast::ObjectDecl * mem = baseEnum->members.at(m).as<const ast::ObjectDecl>();
+							if ( baseEnum->members.at(m)->name == designatorName->name ) {
+								assert( mem );
+								newDesignators.push_back( ast::ConstantExpr::from_int(designator->location, m) );
+								break;
+							}
+						}
+					} else {
+						newDesignators.push_back( des->designators.at(0) );
+					}
+				} else {
+					newDesignators.push_back( des->designators.at(0) );
+				}
+			}
+			newDesignation->designators = newDesignators;
+			listInit = ast::mutate_field_index(listInit, &ast::ListInit::designations, k, newDesignation);
+		}
+	}
+	return decl;
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/ResolveTypeof.h
===================================================================
--- src/ResolvExpr/ResolveTypeof.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,38 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ResolveTypeof.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 12:14:53 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Mar 16 11:33:00 2022
-// Update Count     : 4
-//
-
-#pragma once
-
-namespace ast {
-	class Type;
-	class ObjectDecl;
-}
-
-namespace ResolvExpr {
-
-struct ResolveContext;
-
-const ast::Type * resolveTypeof( const ast::Type *, const ResolveContext & );
-const ast::Type * fixArrayType( const ast::Type *, const ResolveContext & );
-const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ResolveContext & );
-const ast::ObjectDecl * fixObjectInit( const ast::ObjectDecl * decl , const ResolveContext & );
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/ResolveTypeof.hpp
===================================================================
--- src/ResolvExpr/ResolveTypeof.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/ResolveTypeof.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,38 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// ResolveTypeof.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 12:14:53 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Mar 16 11:33:00 2022
+// Update Count     : 4
+//
+
+#pragma once
+
+namespace ast {
+	class Type;
+	class ObjectDecl;
+}
+
+namespace ResolvExpr {
+
+struct ResolveContext;
+
+const ast::Type * resolveTypeof( const ast::Type *, const ResolveContext & );
+const ast::Type * fixArrayType( const ast::Type *, const ResolveContext & );
+const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ResolveContext & );
+const ast::ObjectDecl * fixObjectInit( const ast::ObjectDecl * decl , const ResolveContext & );
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,1269 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Resolver.cc --
-//
-// Author           : Aaron B. Moss
-// Created On       : Sun May 17 12:17:01 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Thu Dec 14 18:44:43 2023
-// Update Count     : 251
-//
-
-#include <cassert>                       // for strict_dynamic_cast, assert
-#include <memory>                        // for allocator, allocator_traits<...
-#include <tuple>                         // for get
-#include <vector>                        // for vector
-
-#include "Candidate.hpp"
-#include "CandidateFinder.hpp"
-#include "CurrentObject.h"               // for CurrentObject
-#include "RenameVars.h"                  // for RenameVars, global_renamer
-#include "Resolver.h"
-#include "ResolveTypeof.h"
-#include "ResolveMode.hpp"               // for ResolveMode
-#include "typeops.h"                     // for extractResultType
-#include "Unify.h"                       // for unify
-#include "CompilationState.hpp"
-#include "AST/Decl.hpp"
-#include "AST/Init.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Print.hpp"
-#include "AST/SymbolTable.hpp"
-#include "AST/Type.hpp"
-#include "Common/Eval.h"                 // for eval
-#include "Common/Iterate.hpp"            // for group_iterate
-#include "Common/SemanticError.h"        // for SemanticError
-#include "Common/Stats/ResolveTime.h"    // for ResolveTime::start(), ResolveTime::stop()
-#include "Common/ToString.hpp"           // for toCString
-#include "Common/UniqueName.h"           // for UniqueName
-#include "InitTweak/GenInit.h"
-#include "InitTweak/InitTweak.h"         // for isIntrinsicSingleArgCallStmt
-#include "SymTab/Mangler.h"              // for Mangler
-#include "Tuples/Tuples.h"
-#include "Validate/FindSpecialDecls.h"   // for SizeType
-
-using namespace std;
-
-namespace ResolvExpr {
-
-namespace {
-	/// Finds deleted expressions in an expression tree
-	struct DeleteFinder final : public ast::WithShortCircuiting, public ast::WithVisitorRef<DeleteFinder> {
-		const ast::DeletedExpr * result = nullptr;
-
-		void previsit( const ast::DeletedExpr * expr ) {
-			if ( result ) { visit_children = false; }
-			else { result = expr; }
-		}
-
-		void previsit( const ast::Expr * expr ) {
-			if ( result ) { visit_children = false; }
-			if (expr->inferred.hasParams()) {
-				for (auto & imp : expr->inferred.inferParams() ) {
-					imp.second.expr->accept(*visitor);
-				}
-			}
-		}
-	};
-
-	struct ResolveDesignators final : public ast::WithShortCircuiting {
-		ResolveContext& context;
-		bool result = false;
-
-		ResolveDesignators( ResolveContext& _context ): context(_context) {};
-
-		void previsit( const ast::Node * ) {
-			// short circuit if we already know there are designations
-			if ( result ) visit_children = false;
-		}
-
-		void previsit( const ast::Designation * des ) {
-			if ( result ) visit_children = false;
-			else if ( ! des->designators.empty() ) {
-				if ( (des->designators.size() == 1) ) {
-					const ast::Expr * designator = des->designators.at(0);
-					if ( const ast::NameExpr * designatorName = dynamic_cast<const ast::NameExpr *>(designator) ) {
-						auto candidates = context.symtab.lookupId(designatorName->name);
-						for ( auto candidate : candidates ) {
-							if ( dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type()) ) {
-								result = true;
-								break;
-							}
-						}
-					}
-				}
-				visit_children = false;
-			}
-		}
-	};
-} // anonymous namespace
-
-/// Check if this expression is or includes a deleted expression
-const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) {
-	return ast::Pass<DeleteFinder>::read( expr );
-}
-
-namespace {
-	/// always-accept candidate filter
-	bool anyCandidate( const Candidate & ) { return true; }
-
-	/// Calls the CandidateFinder and finds the single best candidate
-	CandidateRef findUnfinishedKindExpression(
-		const ast::Expr * untyped, const ResolveContext & context, const std::string & kind,
-		std::function<bool(const Candidate &)> pred = anyCandidate, ResolveMode mode = {}
-	) {
-		if ( ! untyped ) return nullptr;
-
-		// xxx - this isn't thread-safe, but should work until we parallelize the resolver
-		static unsigned recursion_level = 0;
-
-		++recursion_level;
-		ast::TypeEnvironment env;
-		CandidateFinder finder( context, env );
-		finder.allowVoid = true;
-		finder.find( untyped, recursion_level == 1 ? mode.atTopLevel() : mode );
-		--recursion_level;
-
-		// produce a filtered list of candidates
-		CandidateList candidates;
-		for ( auto & cand : finder.candidates ) {
-			if ( pred( *cand ) ) { candidates.emplace_back( cand ); }
-		}
-
-		// produce invalid error if no candidates
-		if ( candidates.empty() ) {
-			SemanticError( untyped,
-				toString( "No reasonable alternatives for ", kind, (kind != "" ? " " : ""),
-				"expression: ") );
-		}
-
-		// search for cheapest candidate
-		CandidateList winners;
-		bool seen_undeleted = false;
-		for ( CandidateRef & cand : candidates ) {
-			int c = winners.empty() ? -1 : cand->cost.compare( winners.front()->cost );
-
-			if ( c > 0 ) continue;  // skip more expensive than winner
-
-			if ( c < 0 ) {
-				// reset on new cheapest
-				seen_undeleted = ! findDeletedExpr( cand->expr );
-				winners.clear();
-			} else /* if ( c == 0 ) */ {
-				if ( findDeletedExpr( cand->expr ) ) {
-					// skip deleted expression if already seen one equivalent-cost not
-					if ( seen_undeleted ) continue;
-				} else if ( ! seen_undeleted ) {
-					// replace list of equivalent-cost deleted expressions with one non-deleted
-					winners.clear();
-					seen_undeleted = true;
-				}
-			}
-
-			winners.emplace_back( std::move( cand ) );
-		}
-
-		// promote candidate.cvtCost to .cost
-		// promoteCvtCost( winners );
-
-		// produce ambiguous errors, if applicable
-		if ( winners.size() != 1 ) {
-			std::ostringstream stream;
-			stream << "Cannot choose between " << winners.size() << " alternatives for "
-				<< kind << (kind != "" ? " " : "") << "expression\n";
-			ast::print( stream, untyped );
-			stream << " Alternatives are:\n";
-			print( stream, winners, 1 );
-			SemanticError( untyped->location, stream.str() );
-		}
-
-		// single selected choice
-		CandidateRef & choice = winners.front();
-
-		// fail on only expression deleted
-		if ( ! seen_undeleted ) {
-			SemanticError( untyped->location, choice->expr.get(), "Unique best alternative "
-			"includes deleted identifier in " );
-		}
-
-		return std::move( choice );
-	}
-
-	/// Strips extraneous casts out of an expression
-	struct StripCasts final {
-		const ast::Expr * postvisit( const ast::CastExpr * castExpr ) {
-			if (
-				castExpr->isGenerated == ast::GeneratedCast
-				&& typesCompatible( castExpr->arg->result, castExpr->result )
-			) {
-				// generated cast is the same type as its argument, remove it after keeping env
-				return ast::mutate_field(
-					castExpr->arg.get(), &ast::Expr::env, castExpr->env );
-			}
-			return castExpr;
-		}
-
-		static void strip( ast::ptr< ast::Expr > & expr ) {
-			ast::Pass< StripCasts > stripper;
-			expr = expr->accept( stripper );
-		}
-	};
-
-	/// Swaps argument into expression pointer, saving original environment
-	void swap_and_save_env( ast::ptr< ast::Expr > & expr, const ast::Expr * newExpr ) {
-		ast::ptr< ast::TypeSubstitution > env = expr->env;
-		expr.set_and_mutate( newExpr )->env = env;
-	}
-
-	/// Removes cast to type of argument (unlike StripCasts, also handles non-generated casts)
-	void removeExtraneousCast( ast::ptr<ast::Expr> & expr ) {
-		if ( const ast::CastExpr * castExpr = expr.as< ast::CastExpr >() ) {
-			if ( typesCompatible( castExpr->arg->result, castExpr->result ) ) {
-				// cast is to the same type as its argument, remove it
-				swap_and_save_env( expr, castExpr->arg );
-			}
-		}
-	}
-
-} // anonymous namespace
-
-/// Establish post-resolver invariants for expressions
-void finishExpr(
-	ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env,
-	const ast::TypeSubstitution * oldenv = nullptr
-) {
-	// set up new type substitution for expression
-	ast::ptr< ast::TypeSubstitution > newenv =
-		 oldenv ? oldenv : new ast::TypeSubstitution{};
-	env.writeToSubstitution( *newenv.get_and_mutate() );
-	expr.get_and_mutate()->env = std::move( newenv );
-	// remove unncecessary casts
-	StripCasts::strip( expr );
-}
-
-ast::ptr< ast::Expr > resolveInVoidContext(
-	const ast::Expr * expr, const ResolveContext & context,
-	ast::TypeEnvironment & env
-) {
-	assertf( expr, "expected a non-null expression" );
-
-	// set up and resolve expression cast to void
-	ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr };
-	CandidateRef choice = findUnfinishedKindExpression(
-		untyped, context, "", anyCandidate, ResolveMode::withAdjustment() );
-
-	// a cast expression has either 0 or 1 interpretations (by language rules);
-	// if 0, an exception has already been thrown, and this code will not run
-	const ast::CastExpr * castExpr = choice->expr.strict_as< ast::CastExpr >();
-	env = std::move( choice->env );
-
-	return castExpr->arg;
-}
-
-/// Resolve `untyped` to the expression whose candidate is the best match for a `void`
-/// context.
-ast::ptr< ast::Expr > findVoidExpression(
-	const ast::Expr * untyped, const ResolveContext & context
-) {
-	ast::TypeEnvironment env;
-	ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, context, env );
-	finishExpr( newExpr, env, untyped->env );
-	return newExpr;
-}
-
-namespace {
-	/// resolve `untyped` to the expression whose candidate satisfies `pred` with the
-	/// lowest cost, returning the resolved version
-	ast::ptr< ast::Expr > findKindExpression(
-		const ast::Expr * untyped, const ResolveContext & context,
-		std::function<bool(const Candidate &)> pred = anyCandidate,
-		const std::string & kind = "", ResolveMode mode = {}
-	) {
-		if ( ! untyped ) return {};
-		CandidateRef choice =
-			findUnfinishedKindExpression( untyped, context, kind, pred, mode );
-		ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env );
-		return std::move( choice->expr );
-	}
-
-	/// Resolve `untyped` to the single expression whose candidate is the best match
-	ast::ptr< ast::Expr > findSingleExpression(
-		const ast::Expr * untyped, const ResolveContext & context
-	) {
-		Stats::ResolveTime::start( untyped );
-		auto res = findKindExpression( untyped, context );
-		Stats::ResolveTime::stop();
-		return res;
-	}
-} // anonymous namespace
-
-ast::ptr< ast::Expr > findSingleExpression(
-	const ast::Expr * untyped, const ast::Type * type,
-	const ResolveContext & context
-) {
-	assert( untyped && type );
-	ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type };
-	ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, context );
-	removeExtraneousCast( newExpr );
-	return newExpr;
-}
-
-namespace {
-	bool structOrUnion( const Candidate & i ) {
-		const ast::Type * t = i.expr->result->stripReferences();
-		return dynamic_cast< const ast::StructInstType * >( t ) || dynamic_cast< const ast::UnionInstType * >( t );
-	}
-	/// Predicate for "Candidate has integral type"
-	bool hasIntegralType( const Candidate & i ) {
-		const ast::Type * type = i.expr->result;
-
-		if ( auto bt = dynamic_cast< const ast::BasicType * >( type ) ) {
-			return bt->isInteger();
-		} else if (
-			dynamic_cast< const ast::EnumInstType * >( type )
-			|| dynamic_cast< const ast::ZeroType * >( type )
-			|| dynamic_cast< const ast::OneType * >( type )
-		) {
-			return true;
-		} else return false;
-	}
-
-	/// Resolve `untyped` as an integral expression, returning the resolved version
-	ast::ptr< ast::Expr > findIntegralExpression(
-		const ast::Expr * untyped, const ResolveContext & context
-	) {
-		return findKindExpression( untyped, context, hasIntegralType, "condition" );
-	}
-
-	ast::ptr< ast::Expr > findCondExpression(
-		const ast::Expr * untyped, const ResolveContext & context
-	) {
-		if ( nullptr == untyped ) return untyped;
-		ast::ptr<ast::Expr> condExpr = createCondExpr( untyped );
-		return findIntegralExpression( condExpr, context );
-	}
-
-	/// check if a type is a character type
-	bool isCharType( const ast::Type * t ) {
-		if ( auto bt = dynamic_cast< const ast::BasicType * >( t ) ) {
-			return bt->kind == ast::BasicKind::Char
-				|| bt->kind == ast::BasicKind::SignedChar
-				|| bt->kind == ast::BasicKind::UnsignedChar;
-		}
-		return false;
-	}
-
-	/// Advance a type itertor to the next mutex parameter
-	template<typename Iter>
-	inline bool nextMutex( Iter & it, const Iter & end ) {
-		while ( it != end && ! (*it)->is_mutex() ) { ++it; }
-		return it != end;
-	}
-} // anonymous namespace
-
-class Resolver final
-: public ast::WithSymbolTable, public ast::WithGuards,
-  public ast::WithVisitorRef<Resolver>, public ast::WithShortCircuiting,
-  public ast::WithStmtsToAdd<> {
-
-	ast::ptr< ast::Type > functionReturn = nullptr;
-	ast::CurrentObject currentObject;
-	// for work previously in GenInit
-	static InitTweak::ManagedTypes managedTypes;
-	ResolveContext context;
-
-	bool inEnumDecl = false;
-
-public:
-	static size_t traceId;
-	Resolver( const ast::TranslationGlobal & global ) :
-		ast::WithSymbolTable(ast::SymbolTable::ErrorDetection::ValidateOnAdd),
-		context{ symtab, global } {}
-	Resolver( const ResolveContext & context ) :
-		ast::WithSymbolTable{ context.symtab },
-		context{ symtab, context.global } {}
-
-	const ast::FunctionDecl * previsit( const ast::FunctionDecl * );
-	const ast::FunctionDecl * postvisit( const ast::FunctionDecl * );
-	const ast::ObjectDecl * previsit( const ast::ObjectDecl * );
-	void previsit( const ast::AggregateDecl * );
-	void previsit( const ast::StructDecl * );
-	void previsit( const ast::EnumDecl * );
-	const ast::StaticAssertDecl * previsit( const ast::StaticAssertDecl * );
-
-	const ast::ArrayType * previsit( const ast::ArrayType * );
-	const ast::PointerType * previsit( const ast::PointerType * );
-
-	const ast::ExprStmt *        previsit( const ast::ExprStmt * );
-	const ast::AsmExpr *         previsit( const ast::AsmExpr * );
-	const ast::AsmStmt *         previsit( const ast::AsmStmt * );
-	const ast::IfStmt *          previsit( const ast::IfStmt * );
-	const ast::WhileDoStmt *     previsit( const ast::WhileDoStmt * );
-	const ast::ForStmt *         previsit( const ast::ForStmt * );
-	const ast::SwitchStmt *      previsit( const ast::SwitchStmt * );
-	const ast::CaseClause *      previsit( const ast::CaseClause * );
-	const ast::BranchStmt *      previsit( const ast::BranchStmt * );
-	const ast::ReturnStmt *      previsit( const ast::ReturnStmt * );
-	const ast::ThrowStmt *       previsit( const ast::ThrowStmt * );
-	const ast::CatchClause *     previsit( const ast::CatchClause * );
-	const ast::CatchClause *     postvisit( const ast::CatchClause * );
-	const ast::WaitForStmt *     previsit( const ast::WaitForStmt * );
-	const ast::WithStmt *        previsit( const ast::WithStmt * );
-
-	const ast::SingleInit *      previsit( const ast::SingleInit * );
-	const ast::ListInit *        previsit( const ast::ListInit * );
-	const ast::ConstructorInit * previsit( const ast::ConstructorInit * );
-
-	void resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd);
-	bool shouldGenCtorInit( const ast::ObjectDecl * ) const;
-
-	void beginScope() { managedTypes.beginScope(); }
-	void endScope() { managedTypes.endScope(); }
-	bool on_error(ast::ptr<ast::Decl> & decl);
-};
-// size_t Resolver::traceId = Stats::Heap::new_stacktrace_id("Resolver");
-
-InitTweak::ManagedTypes Resolver::managedTypes;
-
-void resolve( ast::TranslationUnit& translationUnit ) {
-	ast::Pass< Resolver >::run( translationUnit, translationUnit.global );
-}
-
-ast::ptr< ast::Init > resolveCtorInit(
-	const ast::ConstructorInit * ctorInit, const ResolveContext & context
-) {
-	assert( ctorInit );
-	ast::Pass< Resolver > resolver( context );
-	return ctorInit->accept( resolver );
-}
-
-const ast::Expr * resolveStmtExpr(
-	const ast::StmtExpr * stmtExpr, const ResolveContext & context
-) {
-	assert( stmtExpr );
-	ast::Pass< Resolver > resolver( context );
-	auto ret = mutate(stmtExpr->accept(resolver));
-	strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult();
-	return ret;
-}
-
-namespace {
-	const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ResolveContext & context) {
-		std::string name = attr->normalizedName();
-		if (name == "constructor" || name == "destructor") {
-			if (attr->params.size() == 1) {
-				auto arg = attr->params.front();
-				auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicKind::LongLongSignedInt ), context );
-				auto result = eval(arg);
-
-				auto mutAttr = mutate(attr);
-				mutAttr->params.front() = resolved;
-				if (! result.hasKnownValue) {
-					SemanticWarning(loc, Warning::GccAttributes,
-						toCString( name, " priorities must be integers from 0 to 65535 inclusive: ", arg ) );
-				}
-				else {
-					auto priority = result.knownValue;
-					if (priority < 101) {
-						SemanticWarning(loc, Warning::GccAttributes,
-							toCString( name, " priorities from 0 to 100 are reserved for the implementation" ) );
-					} else if (priority < 201 && ! buildingLibrary()) {
-						SemanticWarning(loc, Warning::GccAttributes,
-							toCString( name, " priorities from 101 to 200 are reserved for the implementation" ) );
-					}
-				}
-				return mutAttr;
-			} else if (attr->params.size() > 1) {
-				SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) );
-			} else {
-				SemanticWarning(loc, Warning::GccAttributes, toCString( "too few arguments to ", name, " attribute" ) );
-			}
-		}
-		return attr;
-	}
-}
-
-const ast::FunctionDecl * Resolver::previsit( const ast::FunctionDecl * functionDecl ) {
-	GuardValue( functionReturn );
-
-	assert (functionDecl->unique());
-	if (!functionDecl->has_body() && !functionDecl->withExprs.empty()) {
-		SemanticError(functionDecl->location, functionDecl, "Function without body has with declarations");
-	}
-
-	if (!functionDecl->isTypeFixed) {
-		auto mutDecl = mutate(functionDecl);
-		auto mutType = mutDecl->type.get_and_mutate();
-
-		for (auto & attr: mutDecl->attributes) {
-			attr = handleAttribute(mutDecl->location, attr, context );
-		}
-
-		// handle assertions
-
-		symtab.enterScope();
-		mutType->forall.clear();
-		mutType->assertions.clear();
-		for (auto & typeParam : mutDecl->type_params) {
-			symtab.addType(typeParam);
-			mutType->forall.emplace_back(new ast::TypeInstType(typeParam));
-		}
-		for (auto & asst : mutDecl->assertions) {
-			asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), context);
-			symtab.addId(asst);
-			mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst));
-		}
-
-		// temporarily adds params to symbol table.
-		// actual scoping rules for params and withexprs differ - see Pass::visit(FunctionDecl)
-
-		std::vector<ast::ptr<ast::Type>> paramTypes;
-		std::vector<ast::ptr<ast::Type>> returnTypes;
-
-		for (auto & param : mutDecl->params) {
-			param = fixObjectType(param.strict_as<ast::ObjectDecl>(), context);
-			symtab.addId(param);
-			paramTypes.emplace_back(param->get_type());
-		}
-		for (auto & ret : mutDecl->returns) {
-			ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), context);
-			returnTypes.emplace_back(ret->get_type());
-		}
-		// since function type in decl is just a view of param types, need to update that as well
-		mutType->params = std::move(paramTypes);
-		mutType->returns = std::move(returnTypes);
-
-		auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID));
-
-		std::list<ast::ptr<ast::Stmt>> newStmts;
-		resolveWithExprs (mutDecl->withExprs, newStmts);
-
-		if (mutDecl->stmts) {
-			auto mutStmt = mutDecl->stmts.get_and_mutate();
-			mutStmt->kids.splice(mutStmt->kids.begin(), std::move(newStmts));
-			mutDecl->stmts = mutStmt;
-		}
-
-		symtab.leaveScope();
-
-		mutDecl->type = renamedType;
-		mutDecl->mangleName = Mangle::mangle(mutDecl);
-		mutDecl->isTypeFixed = true;
-		functionDecl = mutDecl;
-	}
-	managedTypes.handleDWT(functionDecl);
-
-	functionReturn = extractResultType( functionDecl->type );
-	return functionDecl;
-}
-
-const ast::FunctionDecl * Resolver::postvisit( const ast::FunctionDecl * functionDecl ) {
-	// default value expressions have an environment which shouldn't be there and trips up
-	// later passes.
-	assert( functionDecl->unique() );
-	ast::FunctionType * mutType = mutate( functionDecl->type.get() );
-
-	for ( unsigned i = 0 ; i < mutType->params.size() ; ++i ) {
-		if ( const ast::ObjectDecl * obj = mutType->params[i].as< ast::ObjectDecl >() ) {
-			if ( const ast::SingleInit * init = obj->init.as< ast::SingleInit >() ) {
-				if ( init->value->env == nullptr ) continue;
-				// clone initializer minus the initializer environment
-				auto mutParam = mutate( mutType->params[i].strict_as< ast::ObjectDecl >() );
-				auto mutInit = mutate( mutParam->init.strict_as< ast::SingleInit >() );
-				auto mutValue = mutate( mutInit->value.get() );
-
-				mutValue->env = nullptr;
-				mutInit->value = mutValue;
-				mutParam->init = mutInit;
-				mutType->params[i] = mutParam;
-
-				assert( ! mutType->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env);
-			}
-		}
-	}
-	mutate_field(functionDecl, &ast::FunctionDecl::type, mutType);
-	return functionDecl;
-}
-
-bool Resolver::shouldGenCtorInit( ast::ObjectDecl const * decl ) const {
-	// If we shouldn't try to construct it, then don't.
-	if ( !InitTweak::tryConstruct( decl ) ) return false;
-	// Otherwise, if it is a managed type, we may construct it.
-	if ( managedTypes.isManaged( decl ) ) return true;
-	// Skip construction if it is trivial at compile-time.
-	if ( InitTweak::isConstExpr( decl->init ) ) return false;
-	// Skip construction for local declarations.
-	return ( !isInFunction() || decl->storage.is_static );
-}
-
-const ast::ObjectDecl * Resolver::previsit( const ast::ObjectDecl * objectDecl ) {
-	// To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()],
-	// class-variable `initContext` is changed multiple times because the LHS is analyzed
-	// twice. The second analysis changes `initContext` because a function type can contain
-	// object declarations in the return and parameter types. Therefore each value of
-	// `initContext` is retained so the type on the first analysis is preserved and used for
-	// selecting the RHS.
-	GuardValue( currentObject );
-
-	if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) {
-		// enumerator initializers should not use the enum type to initialize, since the
-		// enum type is still incomplete at this point. Use `int` instead.
-
-		if ( auto enumBase = dynamic_cast< const ast::EnumInstType * >
-			( objectDecl->get_type() )->base->base ) {
-			objectDecl = fixObjectType( objectDecl, context );
-			currentObject = ast::CurrentObject{
-				objectDecl->location,
-				enumBase
-			};
-		} else {
-			objectDecl = fixObjectType( objectDecl, context );
-			currentObject = ast::CurrentObject{
-				objectDecl->location, new ast::BasicType{ ast::BasicKind::SignedInt } };
-		}
-	} else {
-		if ( !objectDecl->isTypeFixed ) {
-			auto newDecl = fixObjectType(objectDecl, context);
-			auto mutDecl = mutate(newDecl);
-
-			// generate CtorInit wrapper when necessary.
-			// in certain cases, fixObjectType is called before reaching
-			// this object in visitor pass, thus disabling CtorInit codegen.
-			// this happens on aggregate members and function parameters.
-			if ( shouldGenCtorInit( mutDecl ) ) {
-				// constructed objects cannot be designated
-				if ( InitTweak::isDesignated( mutDecl->init ) ) {
-					ast::Pass<ResolveDesignators> res( context );
-					maybe_accept( mutDecl->init.get(), res );
-					if ( !res.core.result ) {
-						SemanticError( mutDecl, "Cannot include designations in the initializer for a managed Object.\n"
-									   "If this is really what you want, initialize with @=." );
-					}
-				}
-				// constructed objects should not have initializers nested too deeply
-				if ( ! InitTweak::checkInitDepth( mutDecl ) ) SemanticError( mutDecl, "Managed object's initializer is too deep " );
-
-				mutDecl->init = InitTweak::genCtorInit( mutDecl->location, mutDecl );
-			}
-
-			objectDecl = mutDecl;
-		}
-		currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() };
-	}
-
-	return objectDecl;
-}
-
-void Resolver::previsit( const ast::AggregateDecl * _aggDecl ) {
-	auto aggDecl = mutate(_aggDecl);
-	assertf(aggDecl == _aggDecl, "type declarations must be unique");
-
-	for (auto & member: aggDecl->members) {
-		// nested type decls are hoisted already. no need to do anything
-		if (auto obj = member.as<ast::ObjectDecl>()) {
-			member = fixObjectType(obj, context);
-		}
-	}
-}
-
-void Resolver::previsit( const ast::StructDecl * structDecl ) {
-	previsit(static_cast<const ast::AggregateDecl *>(structDecl));
-	managedTypes.handleStruct(structDecl);
-}
-
-void Resolver::previsit( const ast::EnumDecl * ) {
-	// in case we decide to allow nested enums
-	GuardValue( inEnumDecl );
-	inEnumDecl = true;
-	// don't need to fix types for enum fields
-}
-
-const ast::StaticAssertDecl * Resolver::previsit(
-	const ast::StaticAssertDecl * assertDecl
-) {
-	return ast::mutate_field(
-		assertDecl, &ast::StaticAssertDecl::cond,
-		findIntegralExpression( assertDecl->cond, context ) );
-}
-
-template< typename PtrType >
-const PtrType * handlePtrType( const PtrType * type, const ResolveContext & context ) {
-	if ( type->dimension ) {
-		const ast::Type * sizeType = context.global.sizeType.get();
-		ast::ptr< ast::Expr > dimension = findSingleExpression( type->dimension, sizeType, context );
-		assertf(dimension->env->empty(), "array dimension expr has nonempty env");
-		dimension.get_and_mutate()->env = nullptr;
-		ast::mutate_field( type, &PtrType::dimension, dimension );
-	}
-	return type;
-}
-
-const ast::ArrayType * Resolver::previsit( const ast::ArrayType * at ) {
-	return handlePtrType( at, context );
-}
-
-const ast::PointerType * Resolver::previsit( const ast::PointerType * pt ) {
-	return handlePtrType( pt, context );
-}
-
-const ast::ExprStmt * Resolver::previsit( const ast::ExprStmt * exprStmt ) {
-	visit_children = false;
-	assertf( exprStmt->expr, "ExprStmt has null expression in resolver" );
-
-	return ast::mutate_field(
-		exprStmt, &ast::ExprStmt::expr, findVoidExpression( exprStmt->expr, context ) );
-}
-
-const ast::AsmExpr * Resolver::previsit( const ast::AsmExpr * asmExpr ) {
-	visit_children = false;
-
-	asmExpr = ast::mutate_field(
-		asmExpr, &ast::AsmExpr::operand, findVoidExpression( asmExpr->operand, context ) );
-
-	return asmExpr;
-}
-
-const ast::AsmStmt * Resolver::previsit( const ast::AsmStmt * asmStmt ) {
-	visitor->maybe_accept( asmStmt, &ast::AsmStmt::input );
-	visitor->maybe_accept( asmStmt, &ast::AsmStmt::output );
-	visit_children = false;
-	return asmStmt;
-}
-
-const ast::IfStmt * Resolver::previsit( const ast::IfStmt * ifStmt ) {
-	return ast::mutate_field(
-		ifStmt, &ast::IfStmt::cond, findCondExpression( ifStmt->cond, context ) );
-}
-
-const ast::WhileDoStmt * Resolver::previsit( const ast::WhileDoStmt * whileDoStmt ) {
-	return ast::mutate_field(
-		whileDoStmt, &ast::WhileDoStmt::cond, findCondExpression( whileDoStmt->cond, context ) );
-}
-
-const ast::ForStmt * Resolver::previsit( const ast::ForStmt * forStmt ) {
-	if ( forStmt->cond ) {
-		forStmt = ast::mutate_field(
-			forStmt, &ast::ForStmt::cond, findCondExpression( forStmt->cond, context ) );
-	}
-
-	if ( forStmt->inc ) {
-		forStmt = ast::mutate_field(
-			forStmt, &ast::ForStmt::inc, findVoidExpression( forStmt->inc, context ) );
-	}
-
-	return forStmt;
-}
-
-const ast::SwitchStmt * Resolver::previsit( const ast::SwitchStmt * switchStmt ) {
-	GuardValue( currentObject );
-	switchStmt = ast::mutate_field(
-		switchStmt, &ast::SwitchStmt::cond,
-		findIntegralExpression( switchStmt->cond, context ) );
-	currentObject = ast::CurrentObject{ switchStmt->location, switchStmt->cond->result };
-	return switchStmt;
-}
-
-const ast::CaseClause * Resolver::previsit( const ast::CaseClause * caseStmt ) {
-	if ( caseStmt->cond ) {
-		std::deque< ast::InitAlternative > initAlts = currentObject.getOptions();
-		assertf( initAlts.size() == 1, "SwitchStmt did not correctly resolve an integral "
-			"expression." );
-
-		ast::ptr< ast::Expr > untyped =
-			new ast::CastExpr{ caseStmt->location, caseStmt->cond, initAlts.front().type };
-		ast::ptr< ast::Expr > newExpr = findSingleExpression( untyped, context );
-
-		// case condition cannot have a cast in C, so it must be removed here, regardless of
-		// whether it would perform a conversion.
-		if ( const ast::CastExpr * castExpr = newExpr.as< ast::CastExpr >() ) {
-			swap_and_save_env( newExpr, castExpr->arg );
-		}
-
-		caseStmt = ast::mutate_field( caseStmt, &ast::CaseClause::cond, newExpr );
-	}
-	return caseStmt;
-}
-
-const ast::BranchStmt * Resolver::previsit( const ast::BranchStmt * branchStmt ) {
-	visit_children = false;
-	// must resolve the argument of a computed goto
-	if ( branchStmt->kind == ast::BranchStmt::Goto && branchStmt->computedTarget ) {
-		// computed goto argument is void*
-		ast::ptr< ast::Type > target = new ast::PointerType{ new ast::VoidType{} };
-		branchStmt = ast::mutate_field(
-			branchStmt, &ast::BranchStmt::computedTarget,
-			findSingleExpression( branchStmt->computedTarget, target, context ) );
-	}
-	return branchStmt;
-}
-
-const ast::ReturnStmt * Resolver::previsit( const ast::ReturnStmt * returnStmt ) {
-	visit_children = false;
-	if ( returnStmt->expr ) {
-		returnStmt = ast::mutate_field(
-			returnStmt, &ast::ReturnStmt::expr,
-			findSingleExpression( returnStmt->expr, functionReturn, context ) );
-	}
-	return returnStmt;
-}
-
-const ast::ThrowStmt * Resolver::previsit( const ast::ThrowStmt * throwStmt ) {
-	visit_children = false;
-	if ( throwStmt->expr ) {
-		const ast::StructDecl * exceptionDecl =
-			symtab.lookupStruct( "__cfaehm_base_exception_t" );
-		assert( exceptionDecl );
-		ast::ptr< ast::Type > exceptType =
-			new ast::PointerType{ new ast::StructInstType{ exceptionDecl } };
-		throwStmt = ast::mutate_field(
-			throwStmt, &ast::ThrowStmt::expr,
-			findSingleExpression( throwStmt->expr, exceptType, context ) );
-	}
-	return throwStmt;
-}
-
-const ast::CatchClause * Resolver::previsit( const ast::CatchClause * catchClause ) {
-	// Until we are very sure this invarent (ifs that move between passes have then)
-	// holds, check it. This allows a check for when to decode the mangling.
-	if ( auto ifStmt = catchClause->body.as<ast::IfStmt>() ) {
-		assert( ifStmt->then );
-	}
-	// Encode the catchStmt so the condition can see the declaration.
-	if ( catchClause->cond ) {
-		ast::CatchClause * clause = mutate( catchClause );
-		clause->body = new ast::IfStmt( clause->location, clause->cond, nullptr, clause->body );
-		clause->cond = nullptr;
-		return clause;
-	}
-	return catchClause;
-}
-
-const ast::CatchClause * Resolver::postvisit( const ast::CatchClause * catchClause ) {
-	// Decode the catchStmt so everything is stored properly.
-	const ast::IfStmt * ifStmt = catchClause->body.as<ast::IfStmt>();
-	if ( nullptr != ifStmt && nullptr == ifStmt->then ) {
-		assert( ifStmt->cond );
-		assert( ifStmt->else_ );
-		ast::CatchClause * clause = ast::mutate( catchClause );
-		clause->cond = ifStmt->cond;
-		clause->body = ifStmt->else_;
-		// ifStmt should be implicately deleted here.
-		return clause;
-	}
-	return catchClause;
-}
-
-const ast::WaitForStmt * Resolver::previsit( const ast::WaitForStmt * stmt ) {
-	visit_children = false;
-
-	// Resolve all clauses first
-	for ( unsigned i = 0; i < stmt->clauses.size(); ++i ) {
-		const ast::WaitForClause & clause = *stmt->clauses[i];
-
-		ast::TypeEnvironment env;
-		CandidateFinder funcFinder( context, env );
-
-		// Find all candidates for a function in canonical form
-		funcFinder.find( clause.target, ResolveMode::withAdjustment() );
-
-		if ( funcFinder.candidates.empty() ) {
-			stringstream ss;
-			ss << "Use of undeclared indentifier '";
-			ss << clause.target.strict_as< ast::NameExpr >()->name;
-			ss << "' in call to waitfor";
-			SemanticError( stmt->location, ss.str() );
-		}
-
-		if ( clause.target_args.empty() ) {
-			SemanticError( stmt->location,
-				"Waitfor clause must have at least one mutex parameter");
-		}
-
-		// Find all alternatives for all arguments in canonical form
-		std::vector< CandidateFinder > argFinders =
-			funcFinder.findSubExprs( clause.target_args );
-
-		// List all combinations of arguments
-		std::vector< CandidateList > possibilities;
-		combos( argFinders.begin(), argFinders.end(), back_inserter( possibilities ) );
-
-		// For every possible function:
-		// * try matching the arguments to the parameters, not the other way around because
-		//   more arguments than parameters
-		CandidateList funcCandidates;
-		std::vector< CandidateList > argsCandidates;
-		SemanticErrorException errors;
-		for ( CandidateRef & func : funcFinder.candidates ) {
-			try {
-				auto pointerType = dynamic_cast< const ast::PointerType * >(
-					func->expr->result->stripReferences() );
-				if ( ! pointerType ) {
-					SemanticError( stmt->location, func->expr->result.get(),
-						"candidate not viable: not a pointer type\n" );
-				}
-
-				auto funcType = pointerType->base.as< ast::FunctionType >();
-				if ( ! funcType ) {
-					SemanticError( stmt->location, func->expr->result.get(),
-						"candidate not viable: not a function type\n" );
-				}
-
-				{
-					auto param    = funcType->params.begin();
-					auto paramEnd = funcType->params.end();
-
-					if( ! nextMutex( param, paramEnd ) ) {
-						SemanticError( stmt->location, funcType,
-							"candidate function not viable: no mutex parameters\n");
-					}
-				}
-
-				CandidateRef func2{ new Candidate{ *func } };
-				// strip reference from function
-				func2->expr = referenceToRvalueConversion( func->expr, func2->cost );
-
-				// Each argument must be matched with a parameter of the current candidate
-				for ( auto & argsList : possibilities ) {
-					try {
-						// Declare data structures needed for resolution
-						ast::OpenVarSet open;
-						ast::AssertionSet need, have;
-						ast::TypeEnvironment resultEnv{ func->env };
-						// Add all type variables as open so that those not used in the
-						// parameter list are still considered open
-						resultEnv.add( funcType->forall );
-
-						// load type variables from arguments into one shared space
-						for ( auto & arg : argsList ) {
-							resultEnv.simpleCombine( arg->env );
-						}
-
-						// Make sure we don't widen any existing bindings
-						resultEnv.forbidWidening();
-
-						// Find any unbound type variables
-						resultEnv.extractOpenVars( open );
-
-						auto param = funcType->params.begin();
-						auto paramEnd = funcType->params.end();
-
-						unsigned n_mutex_param = 0;
-
-						// For every argument of its set, check if it matches one of the
-						// parameters. The order is important
-						for ( auto & arg : argsList ) {
-							// Ignore non-mutex arguments
-							if ( ! nextMutex( param, paramEnd ) ) {
-								// We ran out of parameters but still have arguments.
-								// This function doesn't match
-								SemanticError( stmt->location, funcType,
-									toString("candidate function not viable: too many mutex "
-									"arguments, expected ", n_mutex_param, "\n" ) );
-							}
-
-							++n_mutex_param;
-
-							// Check if the argument matches the parameter type in the current scope.
-							// ast::ptr< ast::Type > paramType = (*param)->get_type();
-
-							if (
-								! unify(
-									arg->expr->result, *param, resultEnv, need, have, open )
-							) {
-								// Type doesn't match
-								stringstream ss;
-								ss << "candidate function not viable: no known conversion "
-									"from '";
-								ast::print( ss, *param );
-								ss << "' to '";
-								ast::print( ss, arg->expr->result );
-								ss << "' with env '";
-								ast::print( ss, resultEnv );
-								ss << "'\n";
-								SemanticError( stmt->location, funcType, ss.str() );
-							}
-
-							++param;
-						}
-
-						// All arguments match!
-
-						// Check if parameters are missing
-						if ( nextMutex( param, paramEnd ) ) {
-							do {
-								++n_mutex_param;
-								++param;
-							} while ( nextMutex( param, paramEnd ) );
-
-							// We ran out of arguments but still have parameters left; this
-							// function doesn't match
-							SemanticError( stmt->location, funcType,
-								toString( "candidate function not viable: too few mutex "
-								"arguments, expected ", n_mutex_param, "\n" ) );
-						}
-
-						// All parameters match!
-
-						// Finish the expressions to tie in proper environments
-						finishExpr( func2->expr, resultEnv );
-						for ( CandidateRef & arg : argsList ) {
-							finishExpr( arg->expr, resultEnv );
-						}
-
-						// This is a match, store it and save it for later
-						funcCandidates.emplace_back( std::move( func2 ) );
-						argsCandidates.emplace_back( std::move( argsList ) );
-
-					} catch ( SemanticErrorException & e ) {
-						errors.append( e );
-					}
-				}
-			} catch ( SemanticErrorException & e ) {
-				errors.append( e );
-			}
-		}
-
-		// Make sure correct number of arguments
-		if( funcCandidates.empty() ) {
-			SemanticErrorException top( stmt->location,
-				"No alternatives for function in call to waitfor" );
-			top.append( errors );
-			throw top;
-		}
-
-		if( argsCandidates.empty() ) {
-			SemanticErrorException top( stmt->location,
-				"No alternatives for arguments in call to waitfor" );
-			top.append( errors );
-			throw top;
-		}
-
-		if( funcCandidates.size() > 1 ) {
-			SemanticErrorException top( stmt->location,
-				"Ambiguous function in call to waitfor" );
-			top.append( errors );
-			throw top;
-		}
-		if( argsCandidates.size() > 1 ) {
-			SemanticErrorException top( stmt->location,
-				"Ambiguous arguments in call to waitfor" );
-			top.append( errors );
-			throw top;
-		}
-		// TODO: need to use findDeletedExpr to ensure no deleted identifiers are used.
-
-		// build new clause
-		auto clause2 = new ast::WaitForClause( clause.location );
-
-		clause2->target = funcCandidates.front()->expr;
-
-		clause2->target_args.reserve( clause.target_args.size() );
-		const ast::StructDecl * decl_monitor = symtab.lookupStruct( "monitor$" );
-		for ( auto arg : argsCandidates.front() ) {
-			const auto & loc = stmt->location;
-
-			ast::Expr * init = new ast::CastExpr( loc,
-				new ast::UntypedExpr( loc,
-					new ast::NameExpr( loc, "get_monitor" ),
-					{ arg->expr }
-				),
-				new ast::PointerType(
-					new ast::StructInstType(
-						decl_monitor
-					)
-				)
-			);
-
-			clause2->target_args.emplace_back( findSingleExpression( init, context ) );
-		}
-
-		// Resolve the conditions as if it were an IfStmt, statements normally
-		clause2->when_cond = findCondExpression( clause.when_cond, context );
-		clause2->stmt = clause.stmt->accept( *visitor );
-
-		// set results into stmt
-		auto n = mutate( stmt );
-		n->clauses[i] = clause2;
-		stmt = n;
-	}
-
-	if ( stmt->timeout_stmt ) {
-		// resolve the timeout as a size_t, the conditions like IfStmt, and stmts normally
-		ast::ptr< ast::Type > target =
-			new ast::BasicType{ ast::BasicKind::LongLongUnsignedInt };
-		auto timeout_time = findSingleExpression( stmt->timeout_time, target, context );
-		auto timeout_cond = findCondExpression( stmt->timeout_cond, context );
-		auto timeout_stmt = stmt->timeout_stmt->accept( *visitor );
-
-		// set results into stmt
-		auto n = mutate( stmt );
-		n->timeout_time = std::move( timeout_time );
-		n->timeout_cond = std::move( timeout_cond );
-		n->timeout_stmt = std::move( timeout_stmt );
-		stmt = n;
-	}
-
-	if ( stmt->else_stmt ) {
-		// resolve the condition like IfStmt, stmts normally
-		auto else_cond = findCondExpression( stmt->else_cond, context );
-		auto else_stmt = stmt->else_stmt->accept( *visitor );
-
-		// set results into stmt
-		auto n = mutate( stmt );
-		n->else_cond = std::move( else_cond );
-		n->else_stmt = std::move( else_stmt );
-		stmt = n;
-	}
-
-	return stmt;
-}
-
-const ast::WithStmt * Resolver::previsit( const ast::WithStmt * withStmt ) {
-	auto mutStmt = mutate(withStmt);
-	resolveWithExprs(mutStmt->exprs, stmtsToAddBefore);
-	return mutStmt;
-}
-
-void Resolver::resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd) {
-	for (auto & expr : exprs) {
-		// only struct- and union-typed expressions are viable candidates
-		expr = findKindExpression( expr, context, structOrUnion, "with expression" );
-
-		// if with expression might be impure, create a temporary so that it is evaluated once
-		if ( Tuples::maybeImpure( expr ) ) {
-			static UniqueName tmpNamer( "_with_tmp_" );
-			const CodeLocation loc = expr->location;
-			auto tmp = new ast::ObjectDecl(loc, tmpNamer.newName(), expr->result, new ast::SingleInit(loc, expr ) );
-			expr = new ast::VariableExpr( loc, tmp );
-			stmtsToAdd.push_back( new ast::DeclStmt(loc, tmp ) );
-			if ( InitTweak::isConstructable( tmp->type ) ) {
-				// generate ctor/dtor and resolve them
-				tmp->init = InitTweak::genCtorInit( loc, tmp );
-			}
-			// since tmp is freshly created, this should modify tmp in-place
-			tmp->accept( *visitor );
-		} else if (expr->env && expr->env->empty()) {
-			expr = ast::mutate_field(expr.get(), &ast::Expr::env, nullptr);
-		}
-	}
-}
-
-const ast::SingleInit * Resolver::previsit( const ast::SingleInit * singleInit ) {
-	visit_children = false;
-	// resolve initialization using the possibilities as determined by the `currentObject`
-	// cursor.
-	ast::ptr< ast::Expr > untyped = new ast::UntypedInitExpr{
-		singleInit->location, singleInit->value, currentObject.getOptions() };
-	ast::ptr<ast::Expr> newExpr = findSingleExpression( untyped, context );
-	const ast::InitExpr * initExpr = newExpr.strict_as< ast::InitExpr >();
-
-	// move cursor to the object that is actually initialized
-	currentObject.setNext( initExpr->designation );
-
-	// discard InitExpr wrapper and retain relevant pieces.
-	// `initExpr` may have inferred params in the case where the expression specialized a
-	// function pointer, and newExpr may already have inferParams of its own, so a simple
-	// swap is not sufficient
-	ast::Expr::InferUnion inferred = initExpr->inferred;
-	swap_and_save_env( newExpr, initExpr->expr );
-	newExpr.get_and_mutate()->inferred.splice( std::move(inferred) );
-
-	// get the actual object's type (may not exactly match what comes back from the resolver
-	// due to conversions)
-	const ast::Type * initContext = currentObject.getCurrentType();
-
-	removeExtraneousCast( newExpr );
-
-	// check if actual object's type is char[]
-	if ( auto at = dynamic_cast< const ast::ArrayType * >( initContext ) ) {
-		if ( isCharType( at->base ) ) {
-			// check if the resolved type is char*
-			if ( auto pt = newExpr->result.as< ast::PointerType >() ) {
-				if ( isCharType( pt->base ) ) {
-					// strip cast if we're initializing a char[] with a char*
-					// e.g. char x[] = "hello"
-					if ( auto ce = newExpr.as< ast::CastExpr >() ) {
-						swap_and_save_env( newExpr, ce->arg );
-					}
-				}
-			}
-		}
-	}
-
-	// move cursor to next object in preparation for next initializer
-	currentObject.increment();
-
-	// set initializer expression to resolved expression
-	return ast::mutate_field( singleInit, &ast::SingleInit::value, std::move(newExpr) );
-}
-
-const ast::ListInit * Resolver::previsit( const ast::ListInit * listInit ) {
-	// move cursor into brace-enclosed initializer-list
-	currentObject.enterListInit( listInit->location );
-
-	assert( listInit->designations.size() == listInit->initializers.size() );
-	for ( unsigned i = 0; i < listInit->designations.size(); ++i ) {
-		// iterate designations and initializers in pairs, moving the cursor to the current
-		// designated object and resolving the initializer against that object
-		listInit = ast::mutate_field_index(
-			listInit, &ast::ListInit::designations, i,
-			currentObject.findNext( listInit->designations[i] ) );
-		listInit = ast::mutate_field_index(
-			listInit, &ast::ListInit::initializers, i,
-			listInit->initializers[i]->accept( *visitor ) );
-	}
-
-	// move cursor out of brace-enclosed initializer-list
-	currentObject.exitListInit();
-
-	visit_children = false;
-	return listInit;
-}
-
-const ast::ConstructorInit * Resolver::previsit( const ast::ConstructorInit * ctorInit ) {
-	visitor->maybe_accept( ctorInit, &ast::ConstructorInit::ctor );
-	visitor->maybe_accept( ctorInit, &ast::ConstructorInit::dtor );
-
-	// found a constructor - can get rid of C-style initializer
-	// xxx - Rob suggests this field is dead code
-	ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::init, nullptr );
-
-	// intrinsic single-parameter constructors and destructors do nothing. Since this was
-	// implicitly generated, there's no way for it to have side effects, so get rid of it to
-	// clean up generated code
-	if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->ctor ) ) {
-		ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::ctor, nullptr );
-	}
-	if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->dtor ) ) {
-		ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::dtor, nullptr );
-	}
-
-	return ctorInit;
-}
-
-// suppress error on autogen functions and mark invalid autogen as deleted.
-bool Resolver::on_error(ast::ptr<ast::Decl> & decl) {
-	if (auto functionDecl = decl.as<ast::FunctionDecl>()) {
-		// xxx - can intrinsic gen ever fail?
-		if (functionDecl->linkage == ast::Linkage::AutoGen) {
-			auto mutDecl = mutate(functionDecl);
-			mutDecl->isDeleted = true;
-			mutDecl->stmts = nullptr;
-			decl = mutDecl;
-			return false;
-		}
-	}
-	return true;
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/Resolver.cpp
===================================================================
--- src/ResolvExpr/Resolver.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/Resolver.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,1269 @@
+//
+// 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.
+//
+// Resolver.cpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Sun May 17 12:17:01 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Thu Dec 14 18:44:43 2023
+// Update Count     : 251
+//
+
+#include <cassert>                       // for strict_dynamic_cast, assert
+#include <memory>                        // for allocator, allocator_traits<...
+#include <tuple>                         // for get
+#include <vector>                        // for vector
+
+#include "Candidate.hpp"
+#include "CandidateFinder.hpp"
+#include "CurrentObject.hpp"             // for CurrentObject
+#include "RenameVars.hpp"                // for RenameVars, global_renamer
+#include "Resolver.hpp"
+#include "ResolveTypeof.hpp"
+#include "ResolveMode.hpp"               // for ResolveMode
+#include "Typeops.hpp"                   // for extractResultType
+#include "Unify.hpp"                     // for unify
+#include "CompilationState.hpp"
+#include "AST/Decl.hpp"
+#include "AST/Init.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Print.hpp"
+#include "AST/SymbolTable.hpp"
+#include "AST/Type.hpp"
+#include "Common/Eval.hpp"               // for eval
+#include "Common/Iterate.hpp"            // for group_iterate
+#include "Common/SemanticError.hpp"      // for SemanticError
+#include "Common/Stats/ResolveTime.hpp"  // for ResolveTime::start(), ResolveTime::stop()
+#include "Common/ToString.hpp"           // for toCString
+#include "Common/UniqueName.hpp"         // for UniqueName
+#include "InitTweak/GenInit.hpp"
+#include "InitTweak/InitTweak.hpp"       // for isIntrinsicSingleArgCallStmt
+#include "SymTab/Mangler.hpp"            // for Mangler
+#include "Tuples/Tuples.hpp"
+#include "Validate/FindSpecialDecls.hpp" // for SizeType
+
+using namespace std;
+
+namespace ResolvExpr {
+
+namespace {
+	/// Finds deleted expressions in an expression tree
+	struct DeleteFinder final : public ast::WithShortCircuiting, public ast::WithVisitorRef<DeleteFinder> {
+		const ast::DeletedExpr * result = nullptr;
+
+		void previsit( const ast::DeletedExpr * expr ) {
+			if ( result ) { visit_children = false; }
+			else { result = expr; }
+		}
+
+		void previsit( const ast::Expr * expr ) {
+			if ( result ) { visit_children = false; }
+			if (expr->inferred.hasParams()) {
+				for (auto & imp : expr->inferred.inferParams() ) {
+					imp.second.expr->accept(*visitor);
+				}
+			}
+		}
+	};
+
+	struct ResolveDesignators final : public ast::WithShortCircuiting {
+		ResolveContext& context;
+		bool result = false;
+
+		ResolveDesignators( ResolveContext& _context ): context(_context) {};
+
+		void previsit( const ast::Node * ) {
+			// short circuit if we already know there are designations
+			if ( result ) visit_children = false;
+		}
+
+		void previsit( const ast::Designation * des ) {
+			if ( result ) visit_children = false;
+			else if ( ! des->designators.empty() ) {
+				if ( (des->designators.size() == 1) ) {
+					const ast::Expr * designator = des->designators.at(0);
+					if ( const ast::NameExpr * designatorName = dynamic_cast<const ast::NameExpr *>(designator) ) {
+						auto candidates = context.symtab.lookupId(designatorName->name);
+						for ( auto candidate : candidates ) {
+							if ( dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type()) ) {
+								result = true;
+								break;
+							}
+						}
+					}
+				}
+				visit_children = false;
+			}
+		}
+	};
+} // anonymous namespace
+
+/// Check if this expression is or includes a deleted expression
+const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) {
+	return ast::Pass<DeleteFinder>::read( expr );
+}
+
+namespace {
+	/// always-accept candidate filter
+	bool anyCandidate( const Candidate & ) { return true; }
+
+	/// Calls the CandidateFinder and finds the single best candidate
+	CandidateRef findUnfinishedKindExpression(
+		const ast::Expr * untyped, const ResolveContext & context, const std::string & kind,
+		std::function<bool(const Candidate &)> pred = anyCandidate, ResolveMode mode = {}
+	) {
+		if ( ! untyped ) return nullptr;
+
+		// xxx - this isn't thread-safe, but should work until we parallelize the resolver
+		static unsigned recursion_level = 0;
+
+		++recursion_level;
+		ast::TypeEnvironment env;
+		CandidateFinder finder( context, env );
+		finder.allowVoid = true;
+		finder.find( untyped, recursion_level == 1 ? mode.atTopLevel() : mode );
+		--recursion_level;
+
+		// produce a filtered list of candidates
+		CandidateList candidates;
+		for ( auto & cand : finder.candidates ) {
+			if ( pred( *cand ) ) { candidates.emplace_back( cand ); }
+		}
+
+		// produce invalid error if no candidates
+		if ( candidates.empty() ) {
+			SemanticError( untyped,
+				toString( "No reasonable alternatives for ", kind, (kind != "" ? " " : ""),
+				"expression: ") );
+		}
+
+		// search for cheapest candidate
+		CandidateList winners;
+		bool seen_undeleted = false;
+		for ( CandidateRef & cand : candidates ) {
+			int c = winners.empty() ? -1 : cand->cost.compare( winners.front()->cost );
+
+			if ( c > 0 ) continue;  // skip more expensive than winner
+
+			if ( c < 0 ) {
+				// reset on new cheapest
+				seen_undeleted = ! findDeletedExpr( cand->expr );
+				winners.clear();
+			} else /* if ( c == 0 ) */ {
+				if ( findDeletedExpr( cand->expr ) ) {
+					// skip deleted expression if already seen one equivalent-cost not
+					if ( seen_undeleted ) continue;
+				} else if ( ! seen_undeleted ) {
+					// replace list of equivalent-cost deleted expressions with one non-deleted
+					winners.clear();
+					seen_undeleted = true;
+				}
+			}
+
+			winners.emplace_back( std::move( cand ) );
+		}
+
+		// promote candidate.cvtCost to .cost
+		// promoteCvtCost( winners );
+
+		// produce ambiguous errors, if applicable
+		if ( winners.size() != 1 ) {
+			std::ostringstream stream;
+			stream << "Cannot choose between " << winners.size() << " alternatives for "
+				<< kind << (kind != "" ? " " : "") << "expression\n";
+			ast::print( stream, untyped );
+			stream << " Alternatives are:\n";
+			print( stream, winners, 1 );
+			SemanticError( untyped->location, stream.str() );
+		}
+
+		// single selected choice
+		CandidateRef & choice = winners.front();
+
+		// fail on only expression deleted
+		if ( ! seen_undeleted ) {
+			SemanticError( untyped->location, choice->expr.get(), "Unique best alternative "
+			"includes deleted identifier in " );
+		}
+
+		return std::move( choice );
+	}
+
+	/// Strips extraneous casts out of an expression
+	struct StripCasts final {
+		const ast::Expr * postvisit( const ast::CastExpr * castExpr ) {
+			if (
+				castExpr->isGenerated == ast::GeneratedCast
+				&& typesCompatible( castExpr->arg->result, castExpr->result )
+			) {
+				// generated cast is the same type as its argument, remove it after keeping env
+				return ast::mutate_field(
+					castExpr->arg.get(), &ast::Expr::env, castExpr->env );
+			}
+			return castExpr;
+		}
+
+		static void strip( ast::ptr< ast::Expr > & expr ) {
+			ast::Pass< StripCasts > stripper;
+			expr = expr->accept( stripper );
+		}
+	};
+
+	/// Swaps argument into expression pointer, saving original environment
+	void swap_and_save_env( ast::ptr< ast::Expr > & expr, const ast::Expr * newExpr ) {
+		ast::ptr< ast::TypeSubstitution > env = expr->env;
+		expr.set_and_mutate( newExpr )->env = env;
+	}
+
+	/// Removes cast to type of argument (unlike StripCasts, also handles non-generated casts)
+	void removeExtraneousCast( ast::ptr<ast::Expr> & expr ) {
+		if ( const ast::CastExpr * castExpr = expr.as< ast::CastExpr >() ) {
+			if ( typesCompatible( castExpr->arg->result, castExpr->result ) ) {
+				// cast is to the same type as its argument, remove it
+				swap_and_save_env( expr, castExpr->arg );
+			}
+		}
+	}
+
+} // anonymous namespace
+
+/// Establish post-resolver invariants for expressions
+void finishExpr(
+	ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env,
+	const ast::TypeSubstitution * oldenv = nullptr
+) {
+	// set up new type substitution for expression
+	ast::ptr< ast::TypeSubstitution > newenv =
+		 oldenv ? oldenv : new ast::TypeSubstitution{};
+	env.writeToSubstitution( *newenv.get_and_mutate() );
+	expr.get_and_mutate()->env = std::move( newenv );
+	// remove unncecessary casts
+	StripCasts::strip( expr );
+}
+
+ast::ptr< ast::Expr > resolveInVoidContext(
+	const ast::Expr * expr, const ResolveContext & context,
+	ast::TypeEnvironment & env
+) {
+	assertf( expr, "expected a non-null expression" );
+
+	// set up and resolve expression cast to void
+	ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr };
+	CandidateRef choice = findUnfinishedKindExpression(
+		untyped, context, "", anyCandidate, ResolveMode::withAdjustment() );
+
+	// a cast expression has either 0 or 1 interpretations (by language rules);
+	// if 0, an exception has already been thrown, and this code will not run
+	const ast::CastExpr * castExpr = choice->expr.strict_as< ast::CastExpr >();
+	env = std::move( choice->env );
+
+	return castExpr->arg;
+}
+
+/// Resolve `untyped` to the expression whose candidate is the best match for a `void`
+/// context.
+ast::ptr< ast::Expr > findVoidExpression(
+	const ast::Expr * untyped, const ResolveContext & context
+) {
+	ast::TypeEnvironment env;
+	ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, context, env );
+	finishExpr( newExpr, env, untyped->env );
+	return newExpr;
+}
+
+namespace {
+	/// resolve `untyped` to the expression whose candidate satisfies `pred` with the
+	/// lowest cost, returning the resolved version
+	ast::ptr< ast::Expr > findKindExpression(
+		const ast::Expr * untyped, const ResolveContext & context,
+		std::function<bool(const Candidate &)> pred = anyCandidate,
+		const std::string & kind = "", ResolveMode mode = {}
+	) {
+		if ( ! untyped ) return {};
+		CandidateRef choice =
+			findUnfinishedKindExpression( untyped, context, kind, pred, mode );
+		ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env );
+		return std::move( choice->expr );
+	}
+
+	/// Resolve `untyped` to the single expression whose candidate is the best match
+	ast::ptr< ast::Expr > findSingleExpression(
+		const ast::Expr * untyped, const ResolveContext & context
+	) {
+		Stats::ResolveTime::start( untyped );
+		auto res = findKindExpression( untyped, context );
+		Stats::ResolveTime::stop();
+		return res;
+	}
+} // anonymous namespace
+
+ast::ptr< ast::Expr > findSingleExpression(
+	const ast::Expr * untyped, const ast::Type * type,
+	const ResolveContext & context
+) {
+	assert( untyped && type );
+	ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type };
+	ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, context );
+	removeExtraneousCast( newExpr );
+	return newExpr;
+}
+
+namespace {
+	bool structOrUnion( const Candidate & i ) {
+		const ast::Type * t = i.expr->result->stripReferences();
+		return dynamic_cast< const ast::StructInstType * >( t ) || dynamic_cast< const ast::UnionInstType * >( t );
+	}
+	/// Predicate for "Candidate has integral type"
+	bool hasIntegralType( const Candidate & i ) {
+		const ast::Type * type = i.expr->result;
+
+		if ( auto bt = dynamic_cast< const ast::BasicType * >( type ) ) {
+			return bt->isInteger();
+		} else if (
+			dynamic_cast< const ast::EnumInstType * >( type )
+			|| dynamic_cast< const ast::ZeroType * >( type )
+			|| dynamic_cast< const ast::OneType * >( type )
+		) {
+			return true;
+		} else return false;
+	}
+
+	/// Resolve `untyped` as an integral expression, returning the resolved version
+	ast::ptr< ast::Expr > findIntegralExpression(
+		const ast::Expr * untyped, const ResolveContext & context
+	) {
+		return findKindExpression( untyped, context, hasIntegralType, "condition" );
+	}
+
+	ast::ptr< ast::Expr > findCondExpression(
+		const ast::Expr * untyped, const ResolveContext & context
+	) {
+		if ( nullptr == untyped ) return untyped;
+		ast::ptr<ast::Expr> condExpr = createCondExpr( untyped );
+		return findIntegralExpression( condExpr, context );
+	}
+
+	/// check if a type is a character type
+	bool isCharType( const ast::Type * t ) {
+		if ( auto bt = dynamic_cast< const ast::BasicType * >( t ) ) {
+			return bt->kind == ast::BasicKind::Char
+				|| bt->kind == ast::BasicKind::SignedChar
+				|| bt->kind == ast::BasicKind::UnsignedChar;
+		}
+		return false;
+	}
+
+	/// Advance a type itertor to the next mutex parameter
+	template<typename Iter>
+	inline bool nextMutex( Iter & it, const Iter & end ) {
+		while ( it != end && ! (*it)->is_mutex() ) { ++it; }
+		return it != end;
+	}
+} // anonymous namespace
+
+class Resolver final
+: public ast::WithSymbolTable, public ast::WithGuards,
+  public ast::WithVisitorRef<Resolver>, public ast::WithShortCircuiting,
+  public ast::WithStmtsToAdd<> {
+
+	ast::ptr< ast::Type > functionReturn = nullptr;
+	ast::CurrentObject currentObject;
+	// for work previously in GenInit
+	static InitTweak::ManagedTypes managedTypes;
+	ResolveContext context;
+
+	bool inEnumDecl = false;
+
+public:
+	static size_t traceId;
+	Resolver( const ast::TranslationGlobal & global ) :
+		ast::WithSymbolTable(ast::SymbolTable::ErrorDetection::ValidateOnAdd),
+		context{ symtab, global } {}
+	Resolver( const ResolveContext & context ) :
+		ast::WithSymbolTable{ context.symtab },
+		context{ symtab, context.global } {}
+
+	const ast::FunctionDecl * previsit( const ast::FunctionDecl * );
+	const ast::FunctionDecl * postvisit( const ast::FunctionDecl * );
+	const ast::ObjectDecl * previsit( const ast::ObjectDecl * );
+	void previsit( const ast::AggregateDecl * );
+	void previsit( const ast::StructDecl * );
+	void previsit( const ast::EnumDecl * );
+	const ast::StaticAssertDecl * previsit( const ast::StaticAssertDecl * );
+
+	const ast::ArrayType * previsit( const ast::ArrayType * );
+	const ast::PointerType * previsit( const ast::PointerType * );
+
+	const ast::ExprStmt *        previsit( const ast::ExprStmt * );
+	const ast::AsmExpr *         previsit( const ast::AsmExpr * );
+	const ast::AsmStmt *         previsit( const ast::AsmStmt * );
+	const ast::IfStmt *          previsit( const ast::IfStmt * );
+	const ast::WhileDoStmt *     previsit( const ast::WhileDoStmt * );
+	const ast::ForStmt *         previsit( const ast::ForStmt * );
+	const ast::SwitchStmt *      previsit( const ast::SwitchStmt * );
+	const ast::CaseClause *      previsit( const ast::CaseClause * );
+	const ast::BranchStmt *      previsit( const ast::BranchStmt * );
+	const ast::ReturnStmt *      previsit( const ast::ReturnStmt * );
+	const ast::ThrowStmt *       previsit( const ast::ThrowStmt * );
+	const ast::CatchClause *     previsit( const ast::CatchClause * );
+	const ast::CatchClause *     postvisit( const ast::CatchClause * );
+	const ast::WaitForStmt *     previsit( const ast::WaitForStmt * );
+	const ast::WithStmt *        previsit( const ast::WithStmt * );
+
+	const ast::SingleInit *      previsit( const ast::SingleInit * );
+	const ast::ListInit *        previsit( const ast::ListInit * );
+	const ast::ConstructorInit * previsit( const ast::ConstructorInit * );
+
+	void resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd);
+	bool shouldGenCtorInit( const ast::ObjectDecl * ) const;
+
+	void beginScope() { managedTypes.beginScope(); }
+	void endScope() { managedTypes.endScope(); }
+	bool on_error(ast::ptr<ast::Decl> & decl);
+};
+// size_t Resolver::traceId = Stats::Heap::new_stacktrace_id("Resolver");
+
+InitTweak::ManagedTypes Resolver::managedTypes;
+
+void resolve( ast::TranslationUnit& translationUnit ) {
+	ast::Pass< Resolver >::run( translationUnit, translationUnit.global );
+}
+
+ast::ptr< ast::Init > resolveCtorInit(
+	const ast::ConstructorInit * ctorInit, const ResolveContext & context
+) {
+	assert( ctorInit );
+	ast::Pass< Resolver > resolver( context );
+	return ctorInit->accept( resolver );
+}
+
+const ast::Expr * resolveStmtExpr(
+	const ast::StmtExpr * stmtExpr, const ResolveContext & context
+) {
+	assert( stmtExpr );
+	ast::Pass< Resolver > resolver( context );
+	auto ret = mutate(stmtExpr->accept(resolver));
+	strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult();
+	return ret;
+}
+
+namespace {
+	const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ResolveContext & context) {
+		std::string name = attr->normalizedName();
+		if (name == "constructor" || name == "destructor") {
+			if (attr->params.size() == 1) {
+				auto arg = attr->params.front();
+				auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicKind::LongLongSignedInt ), context );
+				auto result = eval(arg);
+
+				auto mutAttr = mutate(attr);
+				mutAttr->params.front() = resolved;
+				if (! result.hasKnownValue) {
+					SemanticWarning(loc, Warning::GccAttributes,
+						toCString( name, " priorities must be integers from 0 to 65535 inclusive: ", arg ) );
+				}
+				else {
+					auto priority = result.knownValue;
+					if (priority < 101) {
+						SemanticWarning(loc, Warning::GccAttributes,
+							toCString( name, " priorities from 0 to 100 are reserved for the implementation" ) );
+					} else if (priority < 201 && ! buildingLibrary()) {
+						SemanticWarning(loc, Warning::GccAttributes,
+							toCString( name, " priorities from 101 to 200 are reserved for the implementation" ) );
+					}
+				}
+				return mutAttr;
+			} else if (attr->params.size() > 1) {
+				SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) );
+			} else {
+				SemanticWarning(loc, Warning::GccAttributes, toCString( "too few arguments to ", name, " attribute" ) );
+			}
+		}
+		return attr;
+	}
+}
+
+const ast::FunctionDecl * Resolver::previsit( const ast::FunctionDecl * functionDecl ) {
+	GuardValue( functionReturn );
+
+	assert (functionDecl->unique());
+	if (!functionDecl->has_body() && !functionDecl->withExprs.empty()) {
+		SemanticError(functionDecl->location, functionDecl, "Function without body has with declarations");
+	}
+
+	if (!functionDecl->isTypeFixed) {
+		auto mutDecl = mutate(functionDecl);
+		auto mutType = mutDecl->type.get_and_mutate();
+
+		for (auto & attr: mutDecl->attributes) {
+			attr = handleAttribute(mutDecl->location, attr, context );
+		}
+
+		// handle assertions
+
+		symtab.enterScope();
+		mutType->forall.clear();
+		mutType->assertions.clear();
+		for (auto & typeParam : mutDecl->type_params) {
+			symtab.addType(typeParam);
+			mutType->forall.emplace_back(new ast::TypeInstType(typeParam));
+		}
+		for (auto & asst : mutDecl->assertions) {
+			asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), context);
+			symtab.addId(asst);
+			mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst));
+		}
+
+		// temporarily adds params to symbol table.
+		// actual scoping rules for params and withexprs differ - see Pass::visit(FunctionDecl)
+
+		std::vector<ast::ptr<ast::Type>> paramTypes;
+		std::vector<ast::ptr<ast::Type>> returnTypes;
+
+		for (auto & param : mutDecl->params) {
+			param = fixObjectType(param.strict_as<ast::ObjectDecl>(), context);
+			symtab.addId(param);
+			paramTypes.emplace_back(param->get_type());
+		}
+		for (auto & ret : mutDecl->returns) {
+			ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), context);
+			returnTypes.emplace_back(ret->get_type());
+		}
+		// since function type in decl is just a view of param types, need to update that as well
+		mutType->params = std::move(paramTypes);
+		mutType->returns = std::move(returnTypes);
+
+		auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID));
+
+		std::list<ast::ptr<ast::Stmt>> newStmts;
+		resolveWithExprs (mutDecl->withExprs, newStmts);
+
+		if (mutDecl->stmts) {
+			auto mutStmt = mutDecl->stmts.get_and_mutate();
+			mutStmt->kids.splice(mutStmt->kids.begin(), std::move(newStmts));
+			mutDecl->stmts = mutStmt;
+		}
+
+		symtab.leaveScope();
+
+		mutDecl->type = renamedType;
+		mutDecl->mangleName = Mangle::mangle(mutDecl);
+		mutDecl->isTypeFixed = true;
+		functionDecl = mutDecl;
+	}
+	managedTypes.handleDWT(functionDecl);
+
+	functionReturn = extractResultType( functionDecl->type );
+	return functionDecl;
+}
+
+const ast::FunctionDecl * Resolver::postvisit( const ast::FunctionDecl * functionDecl ) {
+	// default value expressions have an environment which shouldn't be there and trips up
+	// later passes.
+	assert( functionDecl->unique() );
+	ast::FunctionType * mutType = mutate( functionDecl->type.get() );
+
+	for ( unsigned i = 0 ; i < mutType->params.size() ; ++i ) {
+		if ( const ast::ObjectDecl * obj = mutType->params[i].as< ast::ObjectDecl >() ) {
+			if ( const ast::SingleInit * init = obj->init.as< ast::SingleInit >() ) {
+				if ( init->value->env == nullptr ) continue;
+				// clone initializer minus the initializer environment
+				auto mutParam = mutate( mutType->params[i].strict_as< ast::ObjectDecl >() );
+				auto mutInit = mutate( mutParam->init.strict_as< ast::SingleInit >() );
+				auto mutValue = mutate( mutInit->value.get() );
+
+				mutValue->env = nullptr;
+				mutInit->value = mutValue;
+				mutParam->init = mutInit;
+				mutType->params[i] = mutParam;
+
+				assert( ! mutType->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env);
+			}
+		}
+	}
+	mutate_field(functionDecl, &ast::FunctionDecl::type, mutType);
+	return functionDecl;
+}
+
+bool Resolver::shouldGenCtorInit( ast::ObjectDecl const * decl ) const {
+	// If we shouldn't try to construct it, then don't.
+	if ( !InitTweak::tryConstruct( decl ) ) return false;
+	// Otherwise, if it is a managed type, we may construct it.
+	if ( managedTypes.isManaged( decl ) ) return true;
+	// Skip construction if it is trivial at compile-time.
+	if ( InitTweak::isConstExpr( decl->init ) ) return false;
+	// Skip construction for local declarations.
+	return ( !isInFunction() || decl->storage.is_static );
+}
+
+const ast::ObjectDecl * Resolver::previsit( const ast::ObjectDecl * objectDecl ) {
+	// To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()],
+	// class-variable `initContext` is changed multiple times because the LHS is analyzed
+	// twice. The second analysis changes `initContext` because a function type can contain
+	// object declarations in the return and parameter types. Therefore each value of
+	// `initContext` is retained so the type on the first analysis is preserved and used for
+	// selecting the RHS.
+	GuardValue( currentObject );
+
+	if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) {
+		// enumerator initializers should not use the enum type to initialize, since the
+		// enum type is still incomplete at this point. Use `int` instead.
+
+		if ( auto enumBase = dynamic_cast< const ast::EnumInstType * >
+			( objectDecl->get_type() )->base->base ) {
+			objectDecl = fixObjectType( objectDecl, context );
+			currentObject = ast::CurrentObject{
+				objectDecl->location,
+				enumBase
+			};
+		} else {
+			objectDecl = fixObjectType( objectDecl, context );
+			currentObject = ast::CurrentObject{
+				objectDecl->location, new ast::BasicType{ ast::BasicKind::SignedInt } };
+		}
+	} else {
+		if ( !objectDecl->isTypeFixed ) {
+			auto newDecl = fixObjectType(objectDecl, context);
+			auto mutDecl = mutate(newDecl);
+
+			// generate CtorInit wrapper when necessary.
+			// in certain cases, fixObjectType is called before reaching
+			// this object in visitor pass, thus disabling CtorInit codegen.
+			// this happens on aggregate members and function parameters.
+			if ( shouldGenCtorInit( mutDecl ) ) {
+				// constructed objects cannot be designated
+				if ( InitTweak::isDesignated( mutDecl->init ) ) {
+					ast::Pass<ResolveDesignators> res( context );
+					maybe_accept( mutDecl->init.get(), res );
+					if ( !res.core.result ) {
+						SemanticError( mutDecl, "Cannot include designations in the initializer for a managed Object.\n"
+									   "If this is really what you want, initialize with @=." );
+					}
+				}
+				// constructed objects should not have initializers nested too deeply
+				if ( ! InitTweak::checkInitDepth( mutDecl ) ) SemanticError( mutDecl, "Managed object's initializer is too deep " );
+
+				mutDecl->init = InitTweak::genCtorInit( mutDecl->location, mutDecl );
+			}
+
+			objectDecl = mutDecl;
+		}
+		currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() };
+	}
+
+	return objectDecl;
+}
+
+void Resolver::previsit( const ast::AggregateDecl * _aggDecl ) {
+	auto aggDecl = mutate(_aggDecl);
+	assertf(aggDecl == _aggDecl, "type declarations must be unique");
+
+	for (auto & member: aggDecl->members) {
+		// nested type decls are hoisted already. no need to do anything
+		if (auto obj = member.as<ast::ObjectDecl>()) {
+			member = fixObjectType(obj, context);
+		}
+	}
+}
+
+void Resolver::previsit( const ast::StructDecl * structDecl ) {
+	previsit(static_cast<const ast::AggregateDecl *>(structDecl));
+	managedTypes.handleStruct(structDecl);
+}
+
+void Resolver::previsit( const ast::EnumDecl * ) {
+	// in case we decide to allow nested enums
+	GuardValue( inEnumDecl );
+	inEnumDecl = true;
+	// don't need to fix types for enum fields
+}
+
+const ast::StaticAssertDecl * Resolver::previsit(
+	const ast::StaticAssertDecl * assertDecl
+) {
+	return ast::mutate_field(
+		assertDecl, &ast::StaticAssertDecl::cond,
+		findIntegralExpression( assertDecl->cond, context ) );
+}
+
+template< typename PtrType >
+const PtrType * handlePtrType( const PtrType * type, const ResolveContext & context ) {
+	if ( type->dimension ) {
+		const ast::Type * sizeType = context.global.sizeType.get();
+		ast::ptr< ast::Expr > dimension = findSingleExpression( type->dimension, sizeType, context );
+		assertf(dimension->env->empty(), "array dimension expr has nonempty env");
+		dimension.get_and_mutate()->env = nullptr;
+		ast::mutate_field( type, &PtrType::dimension, dimension );
+	}
+	return type;
+}
+
+const ast::ArrayType * Resolver::previsit( const ast::ArrayType * at ) {
+	return handlePtrType( at, context );
+}
+
+const ast::PointerType * Resolver::previsit( const ast::PointerType * pt ) {
+	return handlePtrType( pt, context );
+}
+
+const ast::ExprStmt * Resolver::previsit( const ast::ExprStmt * exprStmt ) {
+	visit_children = false;
+	assertf( exprStmt->expr, "ExprStmt has null expression in resolver" );
+
+	return ast::mutate_field(
+		exprStmt, &ast::ExprStmt::expr, findVoidExpression( exprStmt->expr, context ) );
+}
+
+const ast::AsmExpr * Resolver::previsit( const ast::AsmExpr * asmExpr ) {
+	visit_children = false;
+
+	asmExpr = ast::mutate_field(
+		asmExpr, &ast::AsmExpr::operand, findVoidExpression( asmExpr->operand, context ) );
+
+	return asmExpr;
+}
+
+const ast::AsmStmt * Resolver::previsit( const ast::AsmStmt * asmStmt ) {
+	visitor->maybe_accept( asmStmt, &ast::AsmStmt::input );
+	visitor->maybe_accept( asmStmt, &ast::AsmStmt::output );
+	visit_children = false;
+	return asmStmt;
+}
+
+const ast::IfStmt * Resolver::previsit( const ast::IfStmt * ifStmt ) {
+	return ast::mutate_field(
+		ifStmt, &ast::IfStmt::cond, findCondExpression( ifStmt->cond, context ) );
+}
+
+const ast::WhileDoStmt * Resolver::previsit( const ast::WhileDoStmt * whileDoStmt ) {
+	return ast::mutate_field(
+		whileDoStmt, &ast::WhileDoStmt::cond, findCondExpression( whileDoStmt->cond, context ) );
+}
+
+const ast::ForStmt * Resolver::previsit( const ast::ForStmt * forStmt ) {
+	if ( forStmt->cond ) {
+		forStmt = ast::mutate_field(
+			forStmt, &ast::ForStmt::cond, findCondExpression( forStmt->cond, context ) );
+	}
+
+	if ( forStmt->inc ) {
+		forStmt = ast::mutate_field(
+			forStmt, &ast::ForStmt::inc, findVoidExpression( forStmt->inc, context ) );
+	}
+
+	return forStmt;
+}
+
+const ast::SwitchStmt * Resolver::previsit( const ast::SwitchStmt * switchStmt ) {
+	GuardValue( currentObject );
+	switchStmt = ast::mutate_field(
+		switchStmt, &ast::SwitchStmt::cond,
+		findIntegralExpression( switchStmt->cond, context ) );
+	currentObject = ast::CurrentObject{ switchStmt->location, switchStmt->cond->result };
+	return switchStmt;
+}
+
+const ast::CaseClause * Resolver::previsit( const ast::CaseClause * caseStmt ) {
+	if ( caseStmt->cond ) {
+		std::deque< ast::InitAlternative > initAlts = currentObject.getOptions();
+		assertf( initAlts.size() == 1, "SwitchStmt did not correctly resolve an integral "
+			"expression." );
+
+		ast::ptr< ast::Expr > untyped =
+			new ast::CastExpr{ caseStmt->location, caseStmt->cond, initAlts.front().type };
+		ast::ptr< ast::Expr > newExpr = findSingleExpression( untyped, context );
+
+		// case condition cannot have a cast in C, so it must be removed here, regardless of
+		// whether it would perform a conversion.
+		if ( const ast::CastExpr * castExpr = newExpr.as< ast::CastExpr >() ) {
+			swap_and_save_env( newExpr, castExpr->arg );
+		}
+
+		caseStmt = ast::mutate_field( caseStmt, &ast::CaseClause::cond, newExpr );
+	}
+	return caseStmt;
+}
+
+const ast::BranchStmt * Resolver::previsit( const ast::BranchStmt * branchStmt ) {
+	visit_children = false;
+	// must resolve the argument of a computed goto
+	if ( branchStmt->kind == ast::BranchStmt::Goto && branchStmt->computedTarget ) {
+		// computed goto argument is void*
+		ast::ptr< ast::Type > target = new ast::PointerType{ new ast::VoidType{} };
+		branchStmt = ast::mutate_field(
+			branchStmt, &ast::BranchStmt::computedTarget,
+			findSingleExpression( branchStmt->computedTarget, target, context ) );
+	}
+	return branchStmt;
+}
+
+const ast::ReturnStmt * Resolver::previsit( const ast::ReturnStmt * returnStmt ) {
+	visit_children = false;
+	if ( returnStmt->expr ) {
+		returnStmt = ast::mutate_field(
+			returnStmt, &ast::ReturnStmt::expr,
+			findSingleExpression( returnStmt->expr, functionReturn, context ) );
+	}
+	return returnStmt;
+}
+
+const ast::ThrowStmt * Resolver::previsit( const ast::ThrowStmt * throwStmt ) {
+	visit_children = false;
+	if ( throwStmt->expr ) {
+		const ast::StructDecl * exceptionDecl =
+			symtab.lookupStruct( "__cfaehm_base_exception_t" );
+		assert( exceptionDecl );
+		ast::ptr< ast::Type > exceptType =
+			new ast::PointerType{ new ast::StructInstType{ exceptionDecl } };
+		throwStmt = ast::mutate_field(
+			throwStmt, &ast::ThrowStmt::expr,
+			findSingleExpression( throwStmt->expr, exceptType, context ) );
+	}
+	return throwStmt;
+}
+
+const ast::CatchClause * Resolver::previsit( const ast::CatchClause * catchClause ) {
+	// Until we are very sure this invarent (ifs that move between passes have then)
+	// holds, check it. This allows a check for when to decode the mangling.
+	if ( auto ifStmt = catchClause->body.as<ast::IfStmt>() ) {
+		assert( ifStmt->then );
+	}
+	// Encode the catchStmt so the condition can see the declaration.
+	if ( catchClause->cond ) {
+		ast::CatchClause * clause = mutate( catchClause );
+		clause->body = new ast::IfStmt( clause->location, clause->cond, nullptr, clause->body );
+		clause->cond = nullptr;
+		return clause;
+	}
+	return catchClause;
+}
+
+const ast::CatchClause * Resolver::postvisit( const ast::CatchClause * catchClause ) {
+	// Decode the catchStmt so everything is stored properly.
+	const ast::IfStmt * ifStmt = catchClause->body.as<ast::IfStmt>();
+	if ( nullptr != ifStmt && nullptr == ifStmt->then ) {
+		assert( ifStmt->cond );
+		assert( ifStmt->else_ );
+		ast::CatchClause * clause = ast::mutate( catchClause );
+		clause->cond = ifStmt->cond;
+		clause->body = ifStmt->else_;
+		// ifStmt should be implicately deleted here.
+		return clause;
+	}
+	return catchClause;
+}
+
+const ast::WaitForStmt * Resolver::previsit( const ast::WaitForStmt * stmt ) {
+	visit_children = false;
+
+	// Resolve all clauses first
+	for ( unsigned i = 0; i < stmt->clauses.size(); ++i ) {
+		const ast::WaitForClause & clause = *stmt->clauses[i];
+
+		ast::TypeEnvironment env;
+		CandidateFinder funcFinder( context, env );
+
+		// Find all candidates for a function in canonical form
+		funcFinder.find( clause.target, ResolveMode::withAdjustment() );
+
+		if ( funcFinder.candidates.empty() ) {
+			stringstream ss;
+			ss << "Use of undeclared indentifier '";
+			ss << clause.target.strict_as< ast::NameExpr >()->name;
+			ss << "' in call to waitfor";
+			SemanticError( stmt->location, ss.str() );
+		}
+
+		if ( clause.target_args.empty() ) {
+			SemanticError( stmt->location,
+				"Waitfor clause must have at least one mutex parameter");
+		}
+
+		// Find all alternatives for all arguments in canonical form
+		std::vector< CandidateFinder > argFinders =
+			funcFinder.findSubExprs( clause.target_args );
+
+		// List all combinations of arguments
+		std::vector< CandidateList > possibilities;
+		combos( argFinders.begin(), argFinders.end(), back_inserter( possibilities ) );
+
+		// For every possible function:
+		// * try matching the arguments to the parameters, not the other way around because
+		//   more arguments than parameters
+		CandidateList funcCandidates;
+		std::vector< CandidateList > argsCandidates;
+		SemanticErrorException errors;
+		for ( CandidateRef & func : funcFinder.candidates ) {
+			try {
+				auto pointerType = dynamic_cast< const ast::PointerType * >(
+					func->expr->result->stripReferences() );
+				if ( ! pointerType ) {
+					SemanticError( stmt->location, func->expr->result.get(),
+						"candidate not viable: not a pointer type\n" );
+				}
+
+				auto funcType = pointerType->base.as< ast::FunctionType >();
+				if ( ! funcType ) {
+					SemanticError( stmt->location, func->expr->result.get(),
+						"candidate not viable: not a function type\n" );
+				}
+
+				{
+					auto param    = funcType->params.begin();
+					auto paramEnd = funcType->params.end();
+
+					if( ! nextMutex( param, paramEnd ) ) {
+						SemanticError( stmt->location, funcType,
+							"candidate function not viable: no mutex parameters\n");
+					}
+				}
+
+				CandidateRef func2{ new Candidate{ *func } };
+				// strip reference from function
+				func2->expr = referenceToRvalueConversion( func->expr, func2->cost );
+
+				// Each argument must be matched with a parameter of the current candidate
+				for ( auto & argsList : possibilities ) {
+					try {
+						// Declare data structures needed for resolution
+						ast::OpenVarSet open;
+						ast::AssertionSet need, have;
+						ast::TypeEnvironment resultEnv{ func->env };
+						// Add all type variables as open so that those not used in the
+						// parameter list are still considered open
+						resultEnv.add( funcType->forall );
+
+						// load type variables from arguments into one shared space
+						for ( auto & arg : argsList ) {
+							resultEnv.simpleCombine( arg->env );
+						}
+
+						// Make sure we don't widen any existing bindings
+						resultEnv.forbidWidening();
+
+						// Find any unbound type variables
+						resultEnv.extractOpenVars( open );
+
+						auto param = funcType->params.begin();
+						auto paramEnd = funcType->params.end();
+
+						unsigned n_mutex_param = 0;
+
+						// For every argument of its set, check if it matches one of the
+						// parameters. The order is important
+						for ( auto & arg : argsList ) {
+							// Ignore non-mutex arguments
+							if ( ! nextMutex( param, paramEnd ) ) {
+								// We ran out of parameters but still have arguments.
+								// This function doesn't match
+								SemanticError( stmt->location, funcType,
+									toString("candidate function not viable: too many mutex "
+									"arguments, expected ", n_mutex_param, "\n" ) );
+							}
+
+							++n_mutex_param;
+
+							// Check if the argument matches the parameter type in the current scope.
+							// ast::ptr< ast::Type > paramType = (*param)->get_type();
+
+							if (
+								! unify(
+									arg->expr->result, *param, resultEnv, need, have, open )
+							) {
+								// Type doesn't match
+								stringstream ss;
+								ss << "candidate function not viable: no known conversion "
+									"from '";
+								ast::print( ss, *param );
+								ss << "' to '";
+								ast::print( ss, arg->expr->result );
+								ss << "' with env '";
+								ast::print( ss, resultEnv );
+								ss << "'\n";
+								SemanticError( stmt->location, funcType, ss.str() );
+							}
+
+							++param;
+						}
+
+						// All arguments match!
+
+						// Check if parameters are missing
+						if ( nextMutex( param, paramEnd ) ) {
+							do {
+								++n_mutex_param;
+								++param;
+							} while ( nextMutex( param, paramEnd ) );
+
+							// We ran out of arguments but still have parameters left; this
+							// function doesn't match
+							SemanticError( stmt->location, funcType,
+								toString( "candidate function not viable: too few mutex "
+								"arguments, expected ", n_mutex_param, "\n" ) );
+						}
+
+						// All parameters match!
+
+						// Finish the expressions to tie in proper environments
+						finishExpr( func2->expr, resultEnv );
+						for ( CandidateRef & arg : argsList ) {
+							finishExpr( arg->expr, resultEnv );
+						}
+
+						// This is a match, store it and save it for later
+						funcCandidates.emplace_back( std::move( func2 ) );
+						argsCandidates.emplace_back( std::move( argsList ) );
+
+					} catch ( SemanticErrorException & e ) {
+						errors.append( e );
+					}
+				}
+			} catch ( SemanticErrorException & e ) {
+				errors.append( e );
+			}
+		}
+
+		// Make sure correct number of arguments
+		if( funcCandidates.empty() ) {
+			SemanticErrorException top( stmt->location,
+				"No alternatives for function in call to waitfor" );
+			top.append( errors );
+			throw top;
+		}
+
+		if( argsCandidates.empty() ) {
+			SemanticErrorException top( stmt->location,
+				"No alternatives for arguments in call to waitfor" );
+			top.append( errors );
+			throw top;
+		}
+
+		if( funcCandidates.size() > 1 ) {
+			SemanticErrorException top( stmt->location,
+				"Ambiguous function in call to waitfor" );
+			top.append( errors );
+			throw top;
+		}
+		if( argsCandidates.size() > 1 ) {
+			SemanticErrorException top( stmt->location,
+				"Ambiguous arguments in call to waitfor" );
+			top.append( errors );
+			throw top;
+		}
+		// TODO: need to use findDeletedExpr to ensure no deleted identifiers are used.
+
+		// build new clause
+		auto clause2 = new ast::WaitForClause( clause.location );
+
+		clause2->target = funcCandidates.front()->expr;
+
+		clause2->target_args.reserve( clause.target_args.size() );
+		const ast::StructDecl * decl_monitor = symtab.lookupStruct( "monitor$" );
+		for ( auto arg : argsCandidates.front() ) {
+			const auto & loc = stmt->location;
+
+			ast::Expr * init = new ast::CastExpr( loc,
+				new ast::UntypedExpr( loc,
+					new ast::NameExpr( loc, "get_monitor" ),
+					{ arg->expr }
+				),
+				new ast::PointerType(
+					new ast::StructInstType(
+						decl_monitor
+					)
+				)
+			);
+
+			clause2->target_args.emplace_back( findSingleExpression( init, context ) );
+		}
+
+		// Resolve the conditions as if it were an IfStmt, statements normally
+		clause2->when_cond = findCondExpression( clause.when_cond, context );
+		clause2->stmt = clause.stmt->accept( *visitor );
+
+		// set results into stmt
+		auto n = mutate( stmt );
+		n->clauses[i] = clause2;
+		stmt = n;
+	}
+
+	if ( stmt->timeout_stmt ) {
+		// resolve the timeout as a size_t, the conditions like IfStmt, and stmts normally
+		ast::ptr< ast::Type > target =
+			new ast::BasicType{ ast::BasicKind::LongLongUnsignedInt };
+		auto timeout_time = findSingleExpression( stmt->timeout_time, target, context );
+		auto timeout_cond = findCondExpression( stmt->timeout_cond, context );
+		auto timeout_stmt = stmt->timeout_stmt->accept( *visitor );
+
+		// set results into stmt
+		auto n = mutate( stmt );
+		n->timeout_time = std::move( timeout_time );
+		n->timeout_cond = std::move( timeout_cond );
+		n->timeout_stmt = std::move( timeout_stmt );
+		stmt = n;
+	}
+
+	if ( stmt->else_stmt ) {
+		// resolve the condition like IfStmt, stmts normally
+		auto else_cond = findCondExpression( stmt->else_cond, context );
+		auto else_stmt = stmt->else_stmt->accept( *visitor );
+
+		// set results into stmt
+		auto n = mutate( stmt );
+		n->else_cond = std::move( else_cond );
+		n->else_stmt = std::move( else_stmt );
+		stmt = n;
+	}
+
+	return stmt;
+}
+
+const ast::WithStmt * Resolver::previsit( const ast::WithStmt * withStmt ) {
+	auto mutStmt = mutate(withStmt);
+	resolveWithExprs(mutStmt->exprs, stmtsToAddBefore);
+	return mutStmt;
+}
+
+void Resolver::resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd) {
+	for (auto & expr : exprs) {
+		// only struct- and union-typed expressions are viable candidates
+		expr = findKindExpression( expr, context, structOrUnion, "with expression" );
+
+		// if with expression might be impure, create a temporary so that it is evaluated once
+		if ( Tuples::maybeImpure( expr ) ) {
+			static UniqueName tmpNamer( "_with_tmp_" );
+			const CodeLocation loc = expr->location;
+			auto tmp = new ast::ObjectDecl(loc, tmpNamer.newName(), expr->result, new ast::SingleInit(loc, expr ) );
+			expr = new ast::VariableExpr( loc, tmp );
+			stmtsToAdd.push_back( new ast::DeclStmt(loc, tmp ) );
+			if ( InitTweak::isConstructable( tmp->type ) ) {
+				// generate ctor/dtor and resolve them
+				tmp->init = InitTweak::genCtorInit( loc, tmp );
+			}
+			// since tmp is freshly created, this should modify tmp in-place
+			tmp->accept( *visitor );
+		} else if (expr->env && expr->env->empty()) {
+			expr = ast::mutate_field(expr.get(), &ast::Expr::env, nullptr);
+		}
+	}
+}
+
+const ast::SingleInit * Resolver::previsit( const ast::SingleInit * singleInit ) {
+	visit_children = false;
+	// resolve initialization using the possibilities as determined by the `currentObject`
+	// cursor.
+	ast::ptr< ast::Expr > untyped = new ast::UntypedInitExpr{
+		singleInit->location, singleInit->value, currentObject.getOptions() };
+	ast::ptr<ast::Expr> newExpr = findSingleExpression( untyped, context );
+	const ast::InitExpr * initExpr = newExpr.strict_as< ast::InitExpr >();
+
+	// move cursor to the object that is actually initialized
+	currentObject.setNext( initExpr->designation );
+
+	// discard InitExpr wrapper and retain relevant pieces.
+	// `initExpr` may have inferred params in the case where the expression specialized a
+	// function pointer, and newExpr may already have inferParams of its own, so a simple
+	// swap is not sufficient
+	ast::Expr::InferUnion inferred = initExpr->inferred;
+	swap_and_save_env( newExpr, initExpr->expr );
+	newExpr.get_and_mutate()->inferred.splice( std::move(inferred) );
+
+	// get the actual object's type (may not exactly match what comes back from the resolver
+	// due to conversions)
+	const ast::Type * initContext = currentObject.getCurrentType();
+
+	removeExtraneousCast( newExpr );
+
+	// check if actual object's type is char[]
+	if ( auto at = dynamic_cast< const ast::ArrayType * >( initContext ) ) {
+		if ( isCharType( at->base ) ) {
+			// check if the resolved type is char*
+			if ( auto pt = newExpr->result.as< ast::PointerType >() ) {
+				if ( isCharType( pt->base ) ) {
+					// strip cast if we're initializing a char[] with a char*
+					// e.g. char x[] = "hello"
+					if ( auto ce = newExpr.as< ast::CastExpr >() ) {
+						swap_and_save_env( newExpr, ce->arg );
+					}
+				}
+			}
+		}
+	}
+
+	// move cursor to next object in preparation for next initializer
+	currentObject.increment();
+
+	// set initializer expression to resolved expression
+	return ast::mutate_field( singleInit, &ast::SingleInit::value, std::move(newExpr) );
+}
+
+const ast::ListInit * Resolver::previsit( const ast::ListInit * listInit ) {
+	// move cursor into brace-enclosed initializer-list
+	currentObject.enterListInit( listInit->location );
+
+	assert( listInit->designations.size() == listInit->initializers.size() );
+	for ( unsigned i = 0; i < listInit->designations.size(); ++i ) {
+		// iterate designations and initializers in pairs, moving the cursor to the current
+		// designated object and resolving the initializer against that object
+		listInit = ast::mutate_field_index(
+			listInit, &ast::ListInit::designations, i,
+			currentObject.findNext( listInit->designations[i] ) );
+		listInit = ast::mutate_field_index(
+			listInit, &ast::ListInit::initializers, i,
+			listInit->initializers[i]->accept( *visitor ) );
+	}
+
+	// move cursor out of brace-enclosed initializer-list
+	currentObject.exitListInit();
+
+	visit_children = false;
+	return listInit;
+}
+
+const ast::ConstructorInit * Resolver::previsit( const ast::ConstructorInit * ctorInit ) {
+	visitor->maybe_accept( ctorInit, &ast::ConstructorInit::ctor );
+	visitor->maybe_accept( ctorInit, &ast::ConstructorInit::dtor );
+
+	// found a constructor - can get rid of C-style initializer
+	// xxx - Rob suggests this field is dead code
+	ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::init, nullptr );
+
+	// intrinsic single-parameter constructors and destructors do nothing. Since this was
+	// implicitly generated, there's no way for it to have side effects, so get rid of it to
+	// clean up generated code
+	if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->ctor ) ) {
+		ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::ctor, nullptr );
+	}
+	if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->dtor ) ) {
+		ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::dtor, nullptr );
+	}
+
+	return ctorInit;
+}
+
+// suppress error on autogen functions and mark invalid autogen as deleted.
+bool Resolver::on_error(ast::ptr<ast::Decl> & decl) {
+	if (auto functionDecl = decl.as<ast::FunctionDecl>()) {
+		// xxx - can intrinsic gen ever fail?
+		if (functionDecl->linkage == ast::Linkage::AutoGen) {
+			auto mutDecl = mutate(functionDecl);
+			mutDecl->isDeleted = true;
+			mutDecl->stmts = nullptr;
+			decl = mutDecl;
+			return false;
+		}
+	}
+	return true;
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/Resolver.h
===================================================================
--- src/ResolvExpr/Resolver.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,69 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Resolver.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 12:18:34 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Mar 16 11:32:00 2022
-// Update Count     : 5
-//
-
-#pragma once
-
-#include "AST/Node.hpp"  // for ptr
-
-namespace ast {
-	class ConstructorInit;
-	class Decl;
-	class DeletedExpr;
-	class Expr;
-	class Init;
-	class StmtExpr;
-	class SymbolTable;
-	class TranslationGlobal;
-	class TranslationUnit;
-	class Type;
-	class TypeEnvironment;
-} // namespace ast
-
-namespace ResolvExpr {
-
-/// Helper Type: Passes around information between various sub-calls.
-struct ResolveContext {
-	const ast::SymbolTable & symtab;
-	const ast::TranslationGlobal & global;
-};
-
-/// Checks types and binds syntactic constructs to typed representations
-void resolve( ast::TranslationUnit& translationUnit );
-/// Searches expr and returns the first DeletedExpr found, otherwise nullptr
-const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr );
-/// Find the expression candidate that is the unique
-/// best match for `untyped` in a `void` context.
-ast::ptr< ast::Expr > resolveInVoidContext(
-	const ast::Expr * expr, const ResolveContext &, ast::TypeEnvironment & env );
-/// Resolve `untyped` to the single expression whose
-/// candidate is the best match for the given type.
-ast::ptr< ast::Expr > findSingleExpression(
-	const ast::Expr * untyped, const ast::Type * type, const ResolveContext & );
-ast::ptr< ast::Expr > findVoidExpression(
-	const ast::Expr * untyped, const ResolveContext & );
-/// Resolves a constructor init expression
-ast::ptr< ast::Init > resolveCtorInit(
-	const ast::ConstructorInit * ctorInit, const ResolveContext & context );
-/// Resolves a statement expression
-const ast::Expr * resolveStmtExpr(
-	const ast::StmtExpr * stmtExpr, const ResolveContext & context );
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/Resolver.hpp
===================================================================
--- src/ResolvExpr/Resolver.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/Resolver.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,69 @@
+//
+// 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.
+//
+// Resolver.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 12:18:34 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Mar 16 11:32:00 2022
+// Update Count     : 5
+//
+
+#pragma once
+
+#include "AST/Node.hpp"  // for ptr
+
+namespace ast {
+	class ConstructorInit;
+	class Decl;
+	class DeletedExpr;
+	class Expr;
+	class Init;
+	class StmtExpr;
+	class SymbolTable;
+	class TranslationGlobal;
+	class TranslationUnit;
+	class Type;
+	class TypeEnvironment;
+} // namespace ast
+
+namespace ResolvExpr {
+
+/// Helper Type: Passes around information between various sub-calls.
+struct ResolveContext {
+	const ast::SymbolTable & symtab;
+	const ast::TranslationGlobal & global;
+};
+
+/// Checks types and binds syntactic constructs to typed representations
+void resolve( ast::TranslationUnit& translationUnit );
+/// Searches expr and returns the first DeletedExpr found, otherwise nullptr
+const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr );
+/// Find the expression candidate that is the unique
+/// best match for `untyped` in a `void` context.
+ast::ptr< ast::Expr > resolveInVoidContext(
+	const ast::Expr * expr, const ResolveContext &, ast::TypeEnvironment & env );
+/// Resolve `untyped` to the single expression whose
+/// candidate is the best match for the given type.
+ast::ptr< ast::Expr > findSingleExpression(
+	const ast::Expr * untyped, const ast::Type * type, const ResolveContext & );
+ast::ptr< ast::Expr > findVoidExpression(
+	const ast::Expr * untyped, const ResolveContext & );
+/// Resolves a constructor init expression
+ast::ptr< ast::Init > resolveCtorInit(
+	const ast::ConstructorInit * ctorInit, const ResolveContext & context );
+/// Resolves a statement expression
+const ast::Expr * resolveStmtExpr(
+	const ast::StmtExpr * stmtExpr, const ResolveContext & context );
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/SatisfyAssertions.cpp
===================================================================
--- src/ResolvExpr/SatisfyAssertions.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ResolvExpr/SatisfyAssertions.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -28,9 +28,9 @@
 #include "CandidateFinder.hpp"
 #include "CommonType.hpp"
-#include "Cost.h"
-#include "RenameVars.h"
+#include "Cost.hpp"
+#include "RenameVars.hpp"
 #include "SpecCost.hpp"
-#include "typeops.h"
-#include "Unify.h"
+#include "Typeops.hpp"
+#include "Unify.hpp"
 #include "AST/Decl.hpp"
 #include "AST/Expr.hpp"
@@ -40,9 +40,9 @@
 #include "AST/SymbolTable.hpp"
 #include "AST/TypeEnvironment.hpp"
-#include "FindOpenVars.h"
-#include "Common/FilterCombos.h"
-#include "Common/Indenter.h"
-#include "GenPoly/GenPoly.h"
-#include "SymTab/Mangler.h"
+#include "FindOpenVars.hpp"
+#include "Common/FilterCombos.hpp"
+#include "Common/Indenter.hpp"
+#include "GenPoly/GenPoly.hpp"
+#include "SymTab/Mangler.hpp"
 
 namespace ResolvExpr {
Index: src/ResolvExpr/SpecCost.cc
===================================================================
--- src/ResolvExpr/SpecCost.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,127 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// SpecCost.cc --
-//
-// Author           : Aaron B. Moss
-// Created On       : Tue Oct 02 15:50:00 2018
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jul  3 11:07:00 2019
-// Update Count     : 3
-//
-
-#include <cassert>
-#include <limits>
-#include <type_traits>
-
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-
-namespace ResolvExpr {
-
-namespace {
-
-const ast::Type * expr_result( const ast::ptr< ast::Expr > & expr ) {
-	return expr->result.get();
-}
-
-const ast::Type * type_deref( const ast::ptr< ast::Type > & type ) {
-	return type.get();
-}
-
-/// The specialization counter inner class.
-class SpecCounter : public ast::WithShortCircuiting, public ast::WithVisitorRef<SpecCounter> {
-	int count = -1;  ///< specialization count (-1 for none)
-
-	// Converts the max value to -1 (none), otherwise increments the value.
-	static int toNoneOrInc( int value ) {
-		assert( 0 <= value );
-		return value < std::numeric_limits<int>::max() ? value + 1 : -1;
-	}
-
-	template<typename T> using MapperT =
-		typename std::add_pointer<ast::Type const *(typename T::value_type const &)>::type;
-
-	// Update the minimum to the new lowest non-none value.
-	template<typename T>
-	void updateMinimumPresent( int & minimum, const T & list, MapperT<T> mapper ) {
-		for ( const auto & node : list ) {
-			count = -1;
-
-			if ( ast::Type const * type = mapper( node ) ) {
-				ast::Type const * newType = type->accept( *visitor );
-				assert( newType == nullptr || newType == type );
-			}
-
-			if ( count != -1 && count < minimum ) minimum = count;
-		}
-	}
-
-	// Returns minimum non-negative count + 1 over type parameters (-1 if none such).
-	template<typename T>
-	int minimumPresent( const T & list, MapperT<T> mapper ) {
-		int minCount = std::numeric_limits<int>::max();
-		updateMinimumPresent( minCount, list, mapper );
-		return toNoneOrInc( minCount );
-	}
-
-public:
-	int result() const { return 0 <= count ? count : 0; }
-
-	// Mark specialization of base type.
-	void postvisit( const ast::PointerType * ) { if ( 0 <= count ) ++count; }
-	void postvisit( const ast::ArrayType * ) { if ( 0 <= count ) ++count; }
-	void postvisit( const ast::ReferenceType * ) { if ( 0 <= count ) ++count; }
-
-	void postvisit( const ast::StructInstType * ) { if ( 0 <= count ) ++count; }
-	void postvisit( const ast::UnionInstType * ) { if ( 0 <= count ) ++count; }
-
-	// Use the minimal specialization value over returns and params.
-	void previsit( const ast::FunctionType * fty ) {
-		int minCount = std::numeric_limits<int>::max();
-		updateMinimumPresent( minCount, fty->params, type_deref );
-		updateMinimumPresent( minCount, fty->returns, type_deref );
-		// Add another level to minCount if set.
-		count = toNoneOrInc( minCount );
-		// We have already visited children.
-		visit_children = false;
-	}
-
-	// Look for polymorphic parameters.
-	void previsit( const ast::StructInstType * sty ) {
-		count = minimumPresent( sty->params, expr_result );
-	}
-
-	// Look for polymorphic parameters.
-	void previsit( const ast::UnionInstType * uty ) {
-		count = minimumPresent( uty->params, expr_result );
-	}
-
-	// Note polymorphic type (which may be specialized).
-	// xxx - maybe account for open/closed type variables
-	void postvisit( const ast::TypeInstType * ) { count = 0; }
-
-	// Use the minimal specialization over elements.
-	// xxx - maybe don't increment, tuple flattening doesn't necessarily specialize
-	void previsit( const ast::TupleType * tty ) {
-		count = minimumPresent( tty->types, type_deref );
-		visit_children = false;
-	}
-};
-
-} // namespace
-
-int specCost( const ast::Type * type ) {
-	return ( nullptr == type ) ? 0 : ast::Pass<SpecCounter>::read( type );
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/SpecCost.cpp
===================================================================
--- src/ResolvExpr/SpecCost.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/SpecCost.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,127 @@
+//
+// 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.
+//
+// SpecCost.cc --
+//
+// Author           : Aaron B. Moss
+// Created On       : Tue Oct 02 15:50:00 2018
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Jul  3 11:07:00 2019
+// Update Count     : 3
+//
+
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+
+namespace ResolvExpr {
+
+namespace {
+
+const ast::Type * expr_result( const ast::ptr< ast::Expr > & expr ) {
+	return expr->result.get();
+}
+
+const ast::Type * type_deref( const ast::ptr< ast::Type > & type ) {
+	return type.get();
+}
+
+/// The specialization counter inner class.
+class SpecCounter : public ast::WithShortCircuiting, public ast::WithVisitorRef<SpecCounter> {
+	int count = -1;  ///< specialization count (-1 for none)
+
+	// Converts the max value to -1 (none), otherwise increments the value.
+	static int toNoneOrInc( int value ) {
+		assert( 0 <= value );
+		return value < std::numeric_limits<int>::max() ? value + 1 : -1;
+	}
+
+	template<typename T> using MapperT =
+		typename std::add_pointer<ast::Type const *(typename T::value_type const &)>::type;
+
+	// Update the minimum to the new lowest non-none value.
+	template<typename T>
+	void updateMinimumPresent( int & minimum, const T & list, MapperT<T> mapper ) {
+		for ( const auto & node : list ) {
+			count = -1;
+
+			if ( ast::Type const * type = mapper( node ) ) {
+				ast::Type const * newType = type->accept( *visitor );
+				assert( newType == nullptr || newType == type );
+			}
+
+			if ( count != -1 && count < minimum ) minimum = count;
+		}
+	}
+
+	// Returns minimum non-negative count + 1 over type parameters (-1 if none such).
+	template<typename T>
+	int minimumPresent( const T & list, MapperT<T> mapper ) {
+		int minCount = std::numeric_limits<int>::max();
+		updateMinimumPresent( minCount, list, mapper );
+		return toNoneOrInc( minCount );
+	}
+
+public:
+	int result() const { return 0 <= count ? count : 0; }
+
+	// Mark specialization of base type.
+	void postvisit( const ast::PointerType * ) { if ( 0 <= count ) ++count; }
+	void postvisit( const ast::ArrayType * ) { if ( 0 <= count ) ++count; }
+	void postvisit( const ast::ReferenceType * ) { if ( 0 <= count ) ++count; }
+
+	void postvisit( const ast::StructInstType * ) { if ( 0 <= count ) ++count; }
+	void postvisit( const ast::UnionInstType * ) { if ( 0 <= count ) ++count; }
+
+	// Use the minimal specialization value over returns and params.
+	void previsit( const ast::FunctionType * fty ) {
+		int minCount = std::numeric_limits<int>::max();
+		updateMinimumPresent( minCount, fty->params, type_deref );
+		updateMinimumPresent( minCount, fty->returns, type_deref );
+		// Add another level to minCount if set.
+		count = toNoneOrInc( minCount );
+		// We have already visited children.
+		visit_children = false;
+	}
+
+	// Look for polymorphic parameters.
+	void previsit( const ast::StructInstType * sty ) {
+		count = minimumPresent( sty->params, expr_result );
+	}
+
+	// Look for polymorphic parameters.
+	void previsit( const ast::UnionInstType * uty ) {
+		count = minimumPresent( uty->params, expr_result );
+	}
+
+	// Note polymorphic type (which may be specialized).
+	// xxx - maybe account for open/closed type variables
+	void postvisit( const ast::TypeInstType * ) { count = 0; }
+
+	// Use the minimal specialization over elements.
+	// xxx - maybe don't increment, tuple flattening doesn't necessarily specialize
+	void previsit( const ast::TupleType * tty ) {
+		count = minimumPresent( tty->types, type_deref );
+		visit_children = false;
+	}
+};
+
+} // namespace
+
+int specCost( const ast::Type * type ) {
+	return ( nullptr == type ) ? 0 : ast::Pass<SpecCounter>::read( type );
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/Typeops.hpp
===================================================================
--- src/ResolvExpr/Typeops.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/Typeops.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,105 @@
+//
+// 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.
+//
+// Typeops.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 07:28:22 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Jan 18 11:54:00 2023
+// Update Count     : 7
+//
+
+#pragma once
+
+#include <vector>
+
+#include "AST/Type.hpp"
+
+namespace ResolvExpr {
+
+class TypeEnvironment;
+
+// combos: takes a list of sets and returns a set of lists representing every possible way of forming a list by
+// picking one element out of each set
+template< typename InputIterator, typename OutputIterator >
+void combos( InputIterator begin, InputIterator end, OutputIterator out ) {
+	typedef typename InputIterator::value_type SetType;
+	typedef typename std::vector< typename SetType::value_type > ListType;
+
+	if ( begin == end )	{
+		*out++ = ListType();
+		return;
+	} // if
+
+	InputIterator current = begin;
+	begin++;
+
+	std::vector< ListType > recursiveResult;
+	combos( begin, end, back_inserter( recursiveResult ) );
+
+	for ( const auto& i : recursiveResult ) for ( const auto& j : *current ) {
+		ListType result;
+		std::back_insert_iterator< ListType > inserter = back_inserter( result );
+		*inserter++ = j;
+		std::copy( i.begin(), i.end(), inserter );
+		*out++ = result;
+	}
+}
+
+/// Flatten tuple type into existing list of types.
+inline void flatten(
+	const ast::Type * type, std::vector< ast::ptr< ast::Type > > & out
+) {
+	if ( auto tupleType = dynamic_cast< const ast::TupleType * >( type ) ) {
+		for ( const ast::Type * t : tupleType->types ) {
+			flatten( t, out );
+		}
+	} else {
+		out.emplace_back( type );
+	}
+}
+
+/// Flatten tuple type into list of types.
+inline std::vector< ast::ptr< ast::Type > > flatten( const ast::Type * type ) {
+	std::vector< ast::ptr< ast::Type > > out;
+	out.reserve( type->size() );
+	flatten( type, out );
+	return out;
+}
+
+template< typename Iter >
+const ast::Type * tupleFromTypes( Iter crnt, Iter end ) {
+	std::vector< ast::ptr< ast::Type > > types;
+	while ( crnt != end ) {
+		// it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure
+		// that this results in a flat tuple
+		flatten( *crnt, types );
+
+		++crnt;
+	}
+
+	return new ast::TupleType( std::move(types) );
+}
+
+inline const ast::Type * tupleFromTypes(
+	const std::vector< ast::ptr< ast::Type > > & tys
+) {
+	return tupleFromTypes( tys.begin(), tys.end() );
+}
+
+} // namespace ResolvExpr
+
+namespace ast {
+	// in TypeEnvironment.cpp
+	bool isFtype( const ast::Type * type );
+} // namespace ast
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,760 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Unify.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 12:27:10 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Dec 13 23:43:05 2019
-// Update Count     : 46
-//
-
-#include "Unify.h"
-
-#include <cassert>                  // for assertf, assert
-#include <iterator>                 // for back_insert_iterator, back_inserter
-#include <map>                      // for _Rb_tree_const_iterator, _Rb_tree_i...
-#include <memory>                   // for unique_ptr
-#include <set>                      // for set
-#include <string>                   // for string, operator==, operator!=, bas...
-#include <utility>                  // for pair, move
-#include <vector>
-
-#include "AST/Copy.hpp"
-#include "AST/Decl.hpp"
-#include "AST/Node.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Print.hpp"
-#include "AST/Type.hpp"
-#include "AST/TypeEnvironment.hpp"
-#include "Common/Eval.h"            // for eval
-#include "CommonType.hpp"           // for commonType
-#include "FindOpenVars.h"           // for findOpenVars
-#include "SpecCost.hpp"             // for SpecCost
-#include "Tuples/Tuples.h"          // for isTtype
-#include "typeops.h"                // for flatten, occurs
-
-namespace ast {
-	class SymbolTable;
-}
-
-// #define DEBUG
-
-namespace ResolvExpr {
-
-bool typesCompatible(
-		const ast::Type * first, const ast::Type * second,
-		const ast::TypeEnvironment & env ) {
-	ast::TypeEnvironment newEnv;
-	ast::OpenVarSet open, closed;
-	ast::AssertionSet need, have;
-
-	ast::ptr<ast::Type> newFirst( first ), newSecond( second );
-	env.apply( newFirst );
-	env.apply( newSecond );
-
-	// findOpenVars( newFirst, open, closed, need, have, FirstClosed );
-	findOpenVars( newSecond, open, closed, need, have, newEnv, FirstOpen );
-
-	return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden() );
-}
-
-bool typesCompatibleIgnoreQualifiers(
-		const ast::Type * first, const ast::Type * second,
-		const ast::TypeEnvironment & env ) {
-	ast::TypeEnvironment newEnv;
-	ast::OpenVarSet open;
-	ast::AssertionSet need, have;
-
-	ast::Type * newFirst  = shallowCopy( first  );
-	ast::Type * newSecond = shallowCopy( second );
-
-	newFirst ->qualifiers = {};
-	newSecond->qualifiers = {};
-	ast::ptr< ast::Type > t1_(newFirst );
-	ast::ptr< ast::Type > t2_(newSecond);
-
-	ast::ptr< ast::Type > subFirst = env.apply(newFirst).node;
-	ast::ptr< ast::Type > subSecond = env.apply(newSecond).node;
-
-	return unifyExact(
-		subFirst,
-		subSecond,
-		newEnv, need, have, open, noWiden() );
-}
-
-namespace {
-	/// Replaces ttype variables with their bound types.
-	/// If this isn't done when satifying ttype assertions, then argument lists can have
-	/// different size and structure when they should be compatible.
-	struct TtypeExpander : public ast::WithShortCircuiting, public ast::PureVisitor {
-		ast::TypeEnvironment & tenv;
-
-		TtypeExpander( ast::TypeEnvironment & env ) : tenv( env ) {}
-
-		const ast::Type * postvisit( const ast::TypeInstType * typeInst ) {
-			if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) {
-				// expand ttype parameter into its actual type
-				if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) {
-					return clz->bound;
-				}
-			}
-			return typeInst;
-		}
-	};
-}
-
-std::vector< ast::ptr< ast::Type > > flattenList(
-	const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env
-) {
-	std::vector< ast::ptr< ast::Type > > dst;
-	dst.reserve( src.size() );
-	for ( const auto & d : src ) {
-		ast::Pass<TtypeExpander> expander( env );
-		// TtypeExpander pass is impure (may mutate nodes in place)
-		// need to make nodes shared to prevent accidental mutation
-		ast::ptr<ast::Type> dc = d->accept(expander);
-		auto types = flatten( dc );
-		for ( ast::ptr< ast::Type > & t : types ) {
-			// outermost const, volatile, _Atomic qualifiers in parameters should not play
-			// a role in the unification of function types, since they do not determine
-			// whether a function is callable.
-			// NOTE: **must** consider at least mutex qualifier, since functions can be
-			// overloaded on outermost mutex and a mutex function has different
-			// requirements than a non-mutex function
-			remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic );
-			dst.emplace_back( t );
-		}
-	}
-	return dst;
-}
-
-// Unification of Expressions
-//
-// Boolean outcome (obvious):  Are they basically spelled the same?
-// Side effect of binding variables (subtle):  if `sizeof(int)` ===_expr `sizeof(T)` then `int` ===_ty `T`
-//
-// Context:  if `float[VAREXPR1]` ===_ty `float[VAREXPR2]` then `VAREXPR1` ===_expr `VAREXPR2`
-// where the VAREXPR are meant as notational metavariables representing the fact that unification always
-// sees distinct ast::VariableExpr objects at these positions
-
-static bool unify( const ast::Expr * e1, const ast::Expr * e2, ast::TypeEnvironment & env,
-	ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
-	WidenMode widen );
-
-class UnifyExpr final : public ast::WithShortCircuiting {
-	const ast::Expr * e2;
-	ast::TypeEnvironment & tenv;
-	ast::AssertionSet & need;
-	ast::AssertionSet & have;
-	const ast::OpenVarSet & open;
-	WidenMode widen;
-public:
-	bool result;
-
-private:
-
-	void tryMatchOnStaticValue( const ast::Expr * e1 ) {
-		Evaluation r1 = eval(e1);
-		Evaluation r2 = eval(e2);
-
-		if ( !r1.hasKnownValue ) return;
-		if ( !r2.hasKnownValue ) return;
-
-		if ( r1.knownValue != r2.knownValue ) return;
-
-		visit_children = false;
-		result = true;
-	}
-
-public:
-
-	void previsit( const ast::Node * ) { assert(false); }
-
-	void previsit( const ast::Expr * e1 ) {
-		tryMatchOnStaticValue( e1 );
-		visit_children = false;
-	}
-
-	void previsit( const ast::CastExpr * e1 ) {
-		tryMatchOnStaticValue( e1 );
-
-		if ( result ) {
-			assert( visit_children == false );
-		} else {
-			assert( visit_children == true );
-			visit_children = false;
-
-			auto e2c = dynamic_cast< const ast::CastExpr * >( e2 );
-			if ( !e2c ) return;
-
-			// inspect casts' target types
-			if ( !unifyExact(
-				e1->result, e2c->result, tenv, need, have, open, widen ) ) return;
-
-			// inspect casts' inner expressions
-			result = unify( e1->arg, e2c->arg, tenv, need, have, open, widen );
-		}
-	}
-
-	void previsit( const ast::VariableExpr * e1 ) {
-		tryMatchOnStaticValue( e1 );
-
-		if ( result ) {
-			assert( visit_children == false );
-		} else {
-			assert( visit_children == true );
-			visit_children = false;
-
-			auto e2v = dynamic_cast< const ast::VariableExpr * >( e2 );
-			if ( !e2v ) return;
-
-			assert(e1->var);
-			assert(e2v->var);
-
-			// conservative: variable exprs match if their declarations are represented by the same C++ AST object
-			result = (e1->var == e2v->var);
-		}
-	}
-
-	void previsit( const ast::SizeofExpr * e1 ) {
-		tryMatchOnStaticValue( e1 );
-
-		if ( result ) {
-			assert( visit_children == false );
-		} else {
-			assert( visit_children == true );
-			visit_children = false;
-
-			auto e2so = dynamic_cast< const ast::SizeofExpr * >( e2 );
-			if ( !e2so ) return;
-
-			assert((e1->type != nullptr) ^ (e1->expr != nullptr));
-			assert((e2so->type != nullptr) ^ (e2so->expr != nullptr));
-			if ( !(e1->type && e2so->type) ) return;
-
-			// expression unification calls type unification (mutual recursion)
-			result = unifyExact( e1->type, e2so->type, tenv, need, have, open, widen );
-		}
-	}
-
-	UnifyExpr( const ast::Expr * e2, ast::TypeEnvironment & env, ast::AssertionSet & need,
-		ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen )
-	: e2( e2 ), tenv(env), need(need), have(have), open(open), widen(widen), result(false) {}
-};
-
-static bool unify( const ast::Expr * e1, const ast::Expr * e2, ast::TypeEnvironment & env,
-	ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
-	WidenMode widen ) {
-	assert( e1 && e2 );
-	return ast::Pass<UnifyExpr>::read( e1, e2, env, need, have, open, widen );
-}
-
-class Unify final : public ast::WithShortCircuiting {
-	const ast::Type * type2;
-	ast::TypeEnvironment & tenv;
-	ast::AssertionSet & need;
-	ast::AssertionSet & have;
-	const ast::OpenVarSet & open;
-	WidenMode widen;
-public:
-	static size_t traceId;
-	bool result;
-
-	Unify(
-		const ast::Type * type2, ast::TypeEnvironment & env, ast::AssertionSet & need,
-		ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen )
-	: type2(type2), tenv(env), need(need), have(have), open(open), widen(widen),
-	result(false) {}
-
-	void previsit( const ast::Node * ) { visit_children = false; }
-
-	void postvisit( const ast::VoidType * vt) {
-		result = dynamic_cast< const ast::VoidType * >( type2 )
-			|| tryToUnifyWithEnumValue(vt, type2, tenv, need, have, open, noWiden());
-		;
-	}
-
-	void postvisit( const ast::BasicType * basic ) {
-		if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) {
-			result = basic->kind == basic2->kind;
-		}
-		result = result || tryToUnifyWithEnumValue(basic, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::PointerType * pointer ) {
-		if ( auto pointer2 = dynamic_cast< const ast::PointerType * >( type2 ) ) {
-			result = unifyExact(
-				pointer->base, pointer2->base, tenv, need, have, open,
-				noWiden());
-		}
-		result = result || tryToUnifyWithEnumValue(pointer, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::ArrayType * array ) {
-		auto array2 = dynamic_cast< const ast::ArrayType * >( type2 );
-		if ( !array2 ) return;
-
-		if ( array->isVarLen != array2->isVarLen ) return;
-		if ( (array->dimension != nullptr) != (array2->dimension != nullptr) ) return;
-
-		if ( array->dimension ) {
-			assert( array2->dimension );
-			// type unification calls expression unification (mutual recursion)
-			if ( !unify(array->dimension, array2->dimension,
-				tenv, need, have, open, widen) ) return;
-		}
-
-		result = unifyExact(
-			array->base, array2->base, tenv, need, have, open, noWiden())
-			|| tryToUnifyWithEnumValue(array, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::ReferenceType * ref ) {
-		if ( auto ref2 = dynamic_cast< const ast::ReferenceType * >( type2 ) ) {
-			result = unifyExact(
-				ref->base, ref2->base, tenv, need, have, open, noWiden());
-		}
-	}
-
-private:
-
-	template< typename Iter >
-	static bool unifyTypeList(
-		Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env,
-		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open
-	) {
-		while ( crnt1 != end1 && crnt2 != end2 ) {
-			const ast::Type * t1 = *crnt1;
-			const ast::Type * t2 = *crnt2;
-			bool isTuple1 = Tuples::isTtype( t1 );
-			bool isTuple2 = Tuples::isTtype( t2 );
-
-			// assumes here that ttype *must* be last parameter
-			if ( isTuple1 && !isTuple2 ) {
-				// combine remainder of list2, then unify
-				return unifyExact(
-					t1, tupleFromTypes( crnt2, end2 ), env, need, have, open,
-					noWiden() );
-			} else if ( !isTuple1 && isTuple2 ) {
-				// combine remainder of list1, then unify
-				return unifyExact(
-					tupleFromTypes( crnt1, end1 ), t2, env, need, have, open,
-					noWiden() );
-			}
-
-			if ( !unifyExact(
-				t1, t2, env, need, have, open, noWiden() )
-			) return false;
-
-			++crnt1; ++crnt2;
-		}
-
-		// May get to the end of one argument list before the other. This is only okay if the
-		// other is a ttype
-		if ( crnt1 != end1 ) {
-			// try unifying empty tuple with ttype
-			const ast::Type * t1 = *crnt1;
-			if ( !Tuples::isTtype( t1 ) ) return false;
-			return unifyExact(
-				t1, tupleFromTypes( crnt2, end2 ), env, need, have, open,
-				noWiden() );
-		} else if ( crnt2 != end2 ) {
-			// try unifying empty tuple with ttype
-			const ast::Type * t2 = *crnt2;
-			if ( !Tuples::isTtype( t2 ) ) return false;
-			return unifyExact(
-				tupleFromTypes( crnt1, end1 ), t2, env, need, have, open,
-				noWiden() );
-		}
-
-		return true;
-	}
-
-	static bool unifyTypeList(
-		const std::vector< ast::ptr< ast::Type > > & list1,
-		const std::vector< ast::ptr< ast::Type > > & list2,
-		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
-		const ast::OpenVarSet & open
-	) {
-		return unifyTypeList(
-			list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open);
-	}
-
-	static void markAssertionSet( ast::AssertionSet & assns, const ast::VariableExpr * assn ) {
-		auto i = assns.find( assn );
-		if ( i != assns.end() ) {
-			i->second.isUsed = true;
-		}
-	}
-
-	/// mark all assertions in `type` used in both `assn1` and `assn2`
-	static void markAssertions(
-		ast::AssertionSet & assn1, ast::AssertionSet & assn2,
-		const ast::FunctionType * type
-	) {
-		for ( auto & assert : type->assertions ) {
-			markAssertionSet( assn1, assert );
-			markAssertionSet( assn2, assert );
-		}
-	}
-
-	bool tryToUnifyWithEnumValue( const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env,
-		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
-		WidenMode widen) {
-		if ( auto attrType2 = dynamic_cast<const ast::EnumAttrType *>(type2)) {
-			if (attrType2->attr == ast::EnumAttribute::Value) {
-				return unifyExact( type1, attrType2->instance->base->base, env, need, have, open,
-					widen);
-			} else if (attrType2->attr == ast::EnumAttribute::Posn) {
-				return unifyExact( type1, attrType2->instance, env, need, have, open, widen );
-			}
-		}
-		return false;
-	}
-
-public:
-	void postvisit( const ast::FunctionType * func ) {
-		auto func2 = dynamic_cast< const ast::FunctionType * >( type2 );
-		if ( !func2 ) return;
-
-		if ( func->isVarArgs != func2->isVarArgs ) return;
-
-		// Flatten the parameter lists for both functions so that tuple structure does not
-		// affect unification. Does not actually mutate function parameters.
-		auto params = flattenList( func->params, tenv );
-		auto params2 = flattenList( func2->params, tenv );
-
-		// sizes don't have to match if ttypes are involved; need to be more precise w.r.t.
-		// where the ttype is to prevent errors
-		if (
-			( params.size() != params2.size() || func->returns.size() != func2->returns.size() )
-			&& !func->isTtype()
-			&& !func2->isTtype()
-		) return;
-
-		if ( !unifyTypeList( params, params2, tenv, need, have, open ) ) return;
-		if ( !unifyTypeList(
-			func->returns, func2->returns, tenv, need, have, open ) ) return;
-
-		markAssertions( have, need, func );
-		markAssertions( have, need, func2 );
-
-		result = true;
-	}
-
-private:
-	// Returns: other, cast as XInstType
-	// Assigns this->result: whether types are compatible (up to generic parameters)
-	template< typename XInstType >
-	const XInstType * handleRefType( const XInstType * inst, const ast::Type * other ) {
-		// check that the other type is compatible and named the same
-		auto otherInst = dynamic_cast< const XInstType * >( other );
-		if ( otherInst && inst->name == otherInst->name ) {
-			this->result = otherInst;
-		}
-		return otherInst;
-	}
-
-	/// Creates a tuple type based on a list of TypeExpr
-	template< typename Iter >
-	static const ast::Type * tupleFromExprs(
-		const ast::TypeExpr * param, Iter & crnt, Iter end, ast::CV::Qualifiers qs
-	) {
-		std::vector< ast::ptr< ast::Type > > types;
-		do {
-			types.emplace_back( param->type );
-
-			++crnt;
-			if ( crnt == end ) break;
-			param = strict_dynamic_cast< const ast::TypeExpr * >( crnt->get() );
-		} while(true);
-
-		return new ast::TupleType( std::move(types), qs );
-	}
-
-	template< typename XInstType >
-	void handleGenericRefType( const XInstType * inst, const ast::Type * other ) {
-		// check that other type is compatible and named the same
-		const XInstType * otherInst = handleRefType( inst, other );
-		if ( !this->result ) return;
-
-		// check that parameters of types unify, if any
-		const std::vector< ast::ptr< ast::Expr > > & params = inst->params;
-		const std::vector< ast::ptr< ast::Expr > > & params2 = otherInst->params;
-
-		auto it = params.begin();
-		auto jt = params2.begin();
-		for ( ; it != params.end() && jt != params2.end(); ++it, ++jt ) {
-			auto param = strict_dynamic_cast< const ast::TypeExpr * >( it->get() );
-			auto param2 = strict_dynamic_cast< const ast::TypeExpr * >( jt->get() );
-
-			ast::ptr< ast::Type > pty = param->type;
-			ast::ptr< ast::Type > pty2 = param2->type;
-
-			bool isTuple = Tuples::isTtype( pty );
-			bool isTuple2 = Tuples::isTtype( pty2 );
-
-			if ( isTuple && isTuple2 ) {
-				++it; ++jt;  // skip ttype parameters before break
-			} else if ( isTuple ) {
-				// bundle remaining params into tuple
-				pty2 = tupleFromExprs( param2, jt, params2.end(), pty->qualifiers );
-				++it;  // skip ttype parameter for break
-			} else if ( isTuple2 ) {
-				// bundle remaining params into tuple
-				pty = tupleFromExprs( param, it, params.end(), pty2->qualifiers );
-				++jt;  // skip ttype parameter for break
-			}
-
-			if ( !unifyExact(
-					pty, pty2, tenv, need, have, open, noWiden() ) ) {
-				result = false;
-				return;
-			}
-
-			// ttype parameter should be last
-			if ( isTuple || isTuple2 ) break;
-		}
-		result = it == params.end() && jt == params2.end();
-	}
-
-public:
-	void postvisit( const ast::StructInstType * aggrType ) {
-		handleGenericRefType( aggrType, type2 );
-		result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::UnionInstType * aggrType ) {
-		handleGenericRefType( aggrType, type2 );
-		result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::EnumInstType * aggrType ) {
-		handleRefType( aggrType, type2 );
-		result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::EnumAttrType * enumAttr ) {
-		// Lazy approach for now
-		if ( auto otherPos = dynamic_cast< const ast::EnumAttrType *>( type2 ) ) {
-			if ( enumAttr->match(otherPos) ) {
-				result = otherPos;
-			}
-		}
-	}
-
-	void postvisit( const ast::TraitInstType * aggrType ) {
-		handleRefType( aggrType, type2 );
-		result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::TypeInstType * typeInst ) {
-		// assert( open.find( *typeInst ) == open.end() );
-		auto otherInst = dynamic_cast< const ast::TypeInstType * >( type2 );
-		if ( otherInst && typeInst->name == otherInst->name ) {
-			this->result = otherInst;
-		}
-		result = result || tryToUnifyWithEnumValue(typeInst, type2, tenv, need, have, open, noWiden());
-	}
-
-private:
-	/// Creates a tuple type based on a list of Type
-	static bool unifyList(
-		const std::vector< ast::ptr< ast::Type > > & list1,
-		const std::vector< ast::ptr< ast::Type > > & list2, ast::TypeEnvironment & env,
-		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open
-	) {
-		auto crnt1 = list1.begin();
-		auto crnt2 = list2.begin();
-		while ( crnt1 != list1.end() && crnt2 != list2.end() ) {
-			const ast::Type * t1 = *crnt1;
-			const ast::Type * t2 = *crnt2;
-			bool isTuple1 = Tuples::isTtype( t1 );
-			bool isTuple2 = Tuples::isTtype( t2 );
-
-			// assumes ttype must be last parameter
-			if ( isTuple1 && !isTuple2 ) {
-				// combine entirety of list2, then unify
-				return unifyExact(
-					t1, tupleFromTypes( list2 ), env, need, have, open,
-					noWiden() );
-			} else if ( !isTuple1 && isTuple2 ) {
-				// combine entirety of list1, then unify
-				return unifyExact(
-					tupleFromTypes( list1 ), t2, env, need, have, open,
-					noWiden() );
-			}
-
-			if ( !unifyExact(
-				t1, t2, env, need, have, open, noWiden() )
-			) return false;
-
-			++crnt1; ++crnt2;
-		}
-
-		if ( crnt1 != list1.end() ) {
-			// try unifying empty tuple type with ttype
-			const ast::Type * t1 = *crnt1;
-			if ( !Tuples::isTtype( t1 ) ) return false;
-			// xxx - this doesn't generate an empty tuple, contrary to comment; both ported
-			// from Rob's code
-			return unifyExact(
-					t1, tupleFromTypes( list2 ), env, need, have, open,
-					noWiden() );
-		} else if ( crnt2 != list2.end() ) {
-			// try unifying empty tuple with ttype
-			const ast::Type * t2 = *crnt2;
-			if ( !Tuples::isTtype( t2 ) ) return false;
-			// xxx - this doesn't generate an empty tuple, contrary to comment; both ported
-			// from Rob's code
-			return unifyExact(
-					tupleFromTypes( list1 ), t2, env, need, have, open,
-					noWiden() );
-		}
-
-		return true;
-	}
-
-public:
-	void postvisit( const ast::TupleType * tuple ) {
-		auto tuple2 = dynamic_cast< const ast::TupleType * >( type2 );
-		if ( ! tuple2 ) return;
-
-		ast::Pass<TtypeExpander> expander{ tenv };
-
-		const ast::Type * flat = tuple->accept( expander );
-		const ast::Type * flat2 = tuple2->accept( expander );
-
-		auto types = flatten( flat );
-		auto types2 = flatten( flat2 );
-
-		result = unifyList( types, types2, tenv, need, have, open )
-			|| tryToUnifyWithEnumValue(tuple, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::VarArgsType * vat) {
-		result = dynamic_cast< const ast::VarArgsType * >( type2 )
-			|| tryToUnifyWithEnumValue(vat, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::ZeroType * zt) {
-		result = dynamic_cast< const ast::ZeroType * >( type2 )
-			|| tryToUnifyWithEnumValue(zt, type2, tenv, need, have, open, noWiden());
-	}
-
-	void postvisit( const ast::OneType * ot) {
-		result = dynamic_cast< const ast::OneType * >( type2 )
-			|| tryToUnifyWithEnumValue(ot, type2, tenv, need, have, open, noWiden());
-	}
-};
-
-// size_t Unify::traceId = Stats::Heap::new_stacktrace_id("Unify");
-
-bool unify(
-		const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
-		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
-		ast::OpenVarSet & open
-) {
-	ast::ptr<ast::Type> common;
-	return unify( type1, type2, env, need, have, open, common );
-}
-
-bool unify(
-		const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
-		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
-		ast::OpenVarSet & open, ast::ptr<ast::Type> & common
-) {
-	ast::OpenVarSet closed;
-	// findOpenVars( type1, open, closed, need, have, FirstClosed );
-	findOpenVars( type2, open, closed, need, have, env, FirstOpen );
-	return unifyInexact(
-		type1, type2, env, need, have, open, WidenMode{ true, true }, common );
-}
-
-bool unifyExact(
-		const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env,
-		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
-		WidenMode widen
-) {
-	if ( type1->qualifiers != type2->qualifiers ) return false;
-
-	auto var1 = dynamic_cast< const ast::TypeInstType * >( type1 );
-	auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 );
-	bool isopen1 = var1 && env.lookup(*var1);
-	bool isopen2 = var2 && env.lookup(*var2);
-
-	if ( isopen1 && isopen2 ) {
-		if ( var1->base->kind != var2->base->kind ) return false;
-		return env.bindVarToVar(
-			var1, var2, ast::TypeData{ var1->base->kind, var1->base->sized||var2->base->sized }, need, have,
-			open, widen );
-	} else if ( isopen1 ) {
-		return env.bindVar( var1, type2, ast::TypeData{var1->base}, need, have, open, widen );
-	} else if ( isopen2 ) {
-		return env.bindVar( var2, type1, ast::TypeData{var2->base}, need, have, open, widen );
-	} else {
-		return ast::Pass<Unify>::read(
-			type1, type2, env, need, have, open, widen );
-	}
-}
-
-bool unifyInexact(
-		const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
-		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
-		const ast::OpenVarSet & open, WidenMode widen,
-		ast::ptr<ast::Type> & common
-) {
-	ast::CV::Qualifiers q1 = type1->qualifiers, q2 = type2->qualifiers;
-
-	// force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and
-	// type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1
-	ast::Type * t1 = shallowCopy(type1.get());
-	ast::Type * t2 = shallowCopy(type2.get());
-	t1->qualifiers = {};
-	t2->qualifiers = {};
-	ast::ptr< ast::Type > t1_(t1);
-	ast::ptr< ast::Type > t2_(t2);
-
-	if ( unifyExact( t1, t2, env, need, have, open, widen ) ) {
-		// if exact unification on unqualified types, try to merge qualifiers
-		if ( q1 == q2 || ( ( q1 > q2 || widen.first ) && ( q2 > q1 || widen.second ) ) ) {
-			t1->qualifiers = q1 | q2;
-			common = t1;
-			return true;
-		} else {
-			return false;
-		}
-	} else if (( common = commonType( t1, t2, env, need, have, open, widen ))) {
-		// no exact unification, but common type
-		auto c = shallowCopy(common.get());
-		c->qualifiers = q1 | q2;
-		common = c;
-		return true;
-	} else {
-		return false;
-	}
-}
-
-ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func ) {
-	if ( func->returns.empty() ) return new ast::VoidType();
-	if ( func->returns.size() == 1 ) return func->returns[0];
-
-	std::vector<ast::ptr<ast::Type>> tys;
-	for ( const auto & decl : func->returns ) {
-		tys.emplace_back( decl );
-	}
-	return new ast::TupleType( std::move(tys) );
-}
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/Unify.cpp
===================================================================
--- src/ResolvExpr/Unify.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/Unify.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,760 @@
+//
+// 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.
+//
+// Unify.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 12:27:10 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Fri Dec 13 23:43:05 2019
+// Update Count     : 46
+//
+
+#include "Unify.hpp"
+
+#include <cassert>                  // for assertf, assert
+#include <iterator>                 // for back_insert_iterator, back_inserter
+#include <map>                      // for _Rb_tree_const_iterator, _Rb_tree_i...
+#include <memory>                   // for unique_ptr
+#include <set>                      // for set
+#include <string>                   // for string, operator==, operator!=, bas...
+#include <utility>                  // for pair, move
+#include <vector>
+
+#include "AST/Copy.hpp"
+#include "AST/Decl.hpp"
+#include "AST/Node.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Print.hpp"
+#include "AST/Type.hpp"
+#include "AST/TypeEnvironment.hpp"
+#include "Common/Eval.hpp"          // for eval
+#include "CommonType.hpp"           // for commonType
+#include "FindOpenVars.hpp"         // for findOpenVars
+#include "SpecCost.hpp"             // for SpecCost
+#include "Tuples/Tuples.hpp"        // for isTtype
+#include "Typeops.hpp"              // for flatten, occurs
+
+namespace ast {
+	class SymbolTable;
+}
+
+// #define DEBUG
+
+namespace ResolvExpr {
+
+bool typesCompatible(
+		const ast::Type * first, const ast::Type * second,
+		const ast::TypeEnvironment & env ) {
+	ast::TypeEnvironment newEnv;
+	ast::OpenVarSet open, closed;
+	ast::AssertionSet need, have;
+
+	ast::ptr<ast::Type> newFirst( first ), newSecond( second );
+	env.apply( newFirst );
+	env.apply( newSecond );
+
+	// findOpenVars( newFirst, open, closed, need, have, FirstClosed );
+	findOpenVars( newSecond, open, closed, need, have, newEnv, FirstOpen );
+
+	return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden() );
+}
+
+bool typesCompatibleIgnoreQualifiers(
+		const ast::Type * first, const ast::Type * second,
+		const ast::TypeEnvironment & env ) {
+	ast::TypeEnvironment newEnv;
+	ast::OpenVarSet open;
+	ast::AssertionSet need, have;
+
+	ast::Type * newFirst  = shallowCopy( first  );
+	ast::Type * newSecond = shallowCopy( second );
+
+	newFirst ->qualifiers = {};
+	newSecond->qualifiers = {};
+	ast::ptr< ast::Type > t1_(newFirst );
+	ast::ptr< ast::Type > t2_(newSecond);
+
+	ast::ptr< ast::Type > subFirst = env.apply(newFirst).node;
+	ast::ptr< ast::Type > subSecond = env.apply(newSecond).node;
+
+	return unifyExact(
+		subFirst,
+		subSecond,
+		newEnv, need, have, open, noWiden() );
+}
+
+namespace {
+	/// Replaces ttype variables with their bound types.
+	/// If this isn't done when satifying ttype assertions, then argument lists can have
+	/// different size and structure when they should be compatible.
+	struct TtypeExpander : public ast::WithShortCircuiting, public ast::PureVisitor {
+		ast::TypeEnvironment & tenv;
+
+		TtypeExpander( ast::TypeEnvironment & env ) : tenv( env ) {}
+
+		const ast::Type * postvisit( const ast::TypeInstType * typeInst ) {
+			if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) {
+				// expand ttype parameter into its actual type
+				if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) {
+					return clz->bound;
+				}
+			}
+			return typeInst;
+		}
+	};
+}
+
+std::vector< ast::ptr< ast::Type > > flattenList(
+	const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env
+) {
+	std::vector< ast::ptr< ast::Type > > dst;
+	dst.reserve( src.size() );
+	for ( const auto & d : src ) {
+		ast::Pass<TtypeExpander> expander( env );
+		// TtypeExpander pass is impure (may mutate nodes in place)
+		// need to make nodes shared to prevent accidental mutation
+		ast::ptr<ast::Type> dc = d->accept(expander);
+		auto types = flatten( dc );
+		for ( ast::ptr< ast::Type > & t : types ) {
+			// outermost const, volatile, _Atomic qualifiers in parameters should not play
+			// a role in the unification of function types, since they do not determine
+			// whether a function is callable.
+			// NOTE: **must** consider at least mutex qualifier, since functions can be
+			// overloaded on outermost mutex and a mutex function has different
+			// requirements than a non-mutex function
+			remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic );
+			dst.emplace_back( t );
+		}
+	}
+	return dst;
+}
+
+// Unification of Expressions
+//
+// Boolean outcome (obvious):  Are they basically spelled the same?
+// Side effect of binding variables (subtle):  if `sizeof(int)` ===_expr `sizeof(T)` then `int` ===_ty `T`
+//
+// Context:  if `float[VAREXPR1]` ===_ty `float[VAREXPR2]` then `VAREXPR1` ===_expr `VAREXPR2`
+// where the VAREXPR are meant as notational metavariables representing the fact that unification always
+// sees distinct ast::VariableExpr objects at these positions
+
+static bool unify( const ast::Expr * e1, const ast::Expr * e2, ast::TypeEnvironment & env,
+	ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
+	WidenMode widen );
+
+class UnifyExpr final : public ast::WithShortCircuiting {
+	const ast::Expr * e2;
+	ast::TypeEnvironment & tenv;
+	ast::AssertionSet & need;
+	ast::AssertionSet & have;
+	const ast::OpenVarSet & open;
+	WidenMode widen;
+public:
+	bool result;
+
+private:
+
+	void tryMatchOnStaticValue( const ast::Expr * e1 ) {
+		Evaluation r1 = eval(e1);
+		Evaluation r2 = eval(e2);
+
+		if ( !r1.hasKnownValue ) return;
+		if ( !r2.hasKnownValue ) return;
+
+		if ( r1.knownValue != r2.knownValue ) return;
+
+		visit_children = false;
+		result = true;
+	}
+
+public:
+
+	void previsit( const ast::Node * ) { assert(false); }
+
+	void previsit( const ast::Expr * e1 ) {
+		tryMatchOnStaticValue( e1 );
+		visit_children = false;
+	}
+
+	void previsit( const ast::CastExpr * e1 ) {
+		tryMatchOnStaticValue( e1 );
+
+		if ( result ) {
+			assert( visit_children == false );
+		} else {
+			assert( visit_children == true );
+			visit_children = false;
+
+			auto e2c = dynamic_cast< const ast::CastExpr * >( e2 );
+			if ( !e2c ) return;
+
+			// inspect casts' target types
+			if ( !unifyExact(
+				e1->result, e2c->result, tenv, need, have, open, widen ) ) return;
+
+			// inspect casts' inner expressions
+			result = unify( e1->arg, e2c->arg, tenv, need, have, open, widen );
+		}
+	}
+
+	void previsit( const ast::VariableExpr * e1 ) {
+		tryMatchOnStaticValue( e1 );
+
+		if ( result ) {
+			assert( visit_children == false );
+		} else {
+			assert( visit_children == true );
+			visit_children = false;
+
+			auto e2v = dynamic_cast< const ast::VariableExpr * >( e2 );
+			if ( !e2v ) return;
+
+			assert(e1->var);
+			assert(e2v->var);
+
+			// conservative: variable exprs match if their declarations are represented by the same C++ AST object
+			result = (e1->var == e2v->var);
+		}
+	}
+
+	void previsit( const ast::SizeofExpr * e1 ) {
+		tryMatchOnStaticValue( e1 );
+
+		if ( result ) {
+			assert( visit_children == false );
+		} else {
+			assert( visit_children == true );
+			visit_children = false;
+
+			auto e2so = dynamic_cast< const ast::SizeofExpr * >( e2 );
+			if ( !e2so ) return;
+
+			assert((e1->type != nullptr) ^ (e1->expr != nullptr));
+			assert((e2so->type != nullptr) ^ (e2so->expr != nullptr));
+			if ( !(e1->type && e2so->type) ) return;
+
+			// expression unification calls type unification (mutual recursion)
+			result = unifyExact( e1->type, e2so->type, tenv, need, have, open, widen );
+		}
+	}
+
+	UnifyExpr( const ast::Expr * e2, ast::TypeEnvironment & env, ast::AssertionSet & need,
+		ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen )
+	: e2( e2 ), tenv(env), need(need), have(have), open(open), widen(widen), result(false) {}
+};
+
+static bool unify( const ast::Expr * e1, const ast::Expr * e2, ast::TypeEnvironment & env,
+	ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
+	WidenMode widen ) {
+	assert( e1 && e2 );
+	return ast::Pass<UnifyExpr>::read( e1, e2, env, need, have, open, widen );
+}
+
+class Unify final : public ast::WithShortCircuiting {
+	const ast::Type * type2;
+	ast::TypeEnvironment & tenv;
+	ast::AssertionSet & need;
+	ast::AssertionSet & have;
+	const ast::OpenVarSet & open;
+	WidenMode widen;
+public:
+	static size_t traceId;
+	bool result;
+
+	Unify(
+		const ast::Type * type2, ast::TypeEnvironment & env, ast::AssertionSet & need,
+		ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen )
+	: type2(type2), tenv(env), need(need), have(have), open(open), widen(widen),
+	result(false) {}
+
+	void previsit( const ast::Node * ) { visit_children = false; }
+
+	void postvisit( const ast::VoidType * vt) {
+		result = dynamic_cast< const ast::VoidType * >( type2 )
+			|| tryToUnifyWithEnumValue(vt, type2, tenv, need, have, open, noWiden());
+		;
+	}
+
+	void postvisit( const ast::BasicType * basic ) {
+		if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) {
+			result = basic->kind == basic2->kind;
+		}
+		result = result || tryToUnifyWithEnumValue(basic, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::PointerType * pointer ) {
+		if ( auto pointer2 = dynamic_cast< const ast::PointerType * >( type2 ) ) {
+			result = unifyExact(
+				pointer->base, pointer2->base, tenv, need, have, open,
+				noWiden());
+		}
+		result = result || tryToUnifyWithEnumValue(pointer, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::ArrayType * array ) {
+		auto array2 = dynamic_cast< const ast::ArrayType * >( type2 );
+		if ( !array2 ) return;
+
+		if ( array->isVarLen != array2->isVarLen ) return;
+		if ( (array->dimension != nullptr) != (array2->dimension != nullptr) ) return;
+
+		if ( array->dimension ) {
+			assert( array2->dimension );
+			// type unification calls expression unification (mutual recursion)
+			if ( !unify(array->dimension, array2->dimension,
+				tenv, need, have, open, widen) ) return;
+		}
+
+		result = unifyExact(
+			array->base, array2->base, tenv, need, have, open, noWiden())
+			|| tryToUnifyWithEnumValue(array, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::ReferenceType * ref ) {
+		if ( auto ref2 = dynamic_cast< const ast::ReferenceType * >( type2 ) ) {
+			result = unifyExact(
+				ref->base, ref2->base, tenv, need, have, open, noWiden());
+		}
+	}
+
+private:
+
+	template< typename Iter >
+	static bool unifyTypeList(
+		Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env,
+		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open
+	) {
+		while ( crnt1 != end1 && crnt2 != end2 ) {
+			const ast::Type * t1 = *crnt1;
+			const ast::Type * t2 = *crnt2;
+			bool isTuple1 = Tuples::isTtype( t1 );
+			bool isTuple2 = Tuples::isTtype( t2 );
+
+			// assumes here that ttype *must* be last parameter
+			if ( isTuple1 && !isTuple2 ) {
+				// combine remainder of list2, then unify
+				return unifyExact(
+					t1, tupleFromTypes( crnt2, end2 ), env, need, have, open,
+					noWiden() );
+			} else if ( !isTuple1 && isTuple2 ) {
+				// combine remainder of list1, then unify
+				return unifyExact(
+					tupleFromTypes( crnt1, end1 ), t2, env, need, have, open,
+					noWiden() );
+			}
+
+			if ( !unifyExact(
+				t1, t2, env, need, have, open, noWiden() )
+			) return false;
+
+			++crnt1; ++crnt2;
+		}
+
+		// May get to the end of one argument list before the other. This is only okay if the
+		// other is a ttype
+		if ( crnt1 != end1 ) {
+			// try unifying empty tuple with ttype
+			const ast::Type * t1 = *crnt1;
+			if ( !Tuples::isTtype( t1 ) ) return false;
+			return unifyExact(
+				t1, tupleFromTypes( crnt2, end2 ), env, need, have, open,
+				noWiden() );
+		} else if ( crnt2 != end2 ) {
+			// try unifying empty tuple with ttype
+			const ast::Type * t2 = *crnt2;
+			if ( !Tuples::isTtype( t2 ) ) return false;
+			return unifyExact(
+				tupleFromTypes( crnt1, end1 ), t2, env, need, have, open,
+				noWiden() );
+		}
+
+		return true;
+	}
+
+	static bool unifyTypeList(
+		const std::vector< ast::ptr< ast::Type > > & list1,
+		const std::vector< ast::ptr< ast::Type > > & list2,
+		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
+		const ast::OpenVarSet & open
+	) {
+		return unifyTypeList(
+			list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open);
+	}
+
+	static void markAssertionSet( ast::AssertionSet & assns, const ast::VariableExpr * assn ) {
+		auto i = assns.find( assn );
+		if ( i != assns.end() ) {
+			i->second.isUsed = true;
+		}
+	}
+
+	/// mark all assertions in `type` used in both `assn1` and `assn2`
+	static void markAssertions(
+		ast::AssertionSet & assn1, ast::AssertionSet & assn2,
+		const ast::FunctionType * type
+	) {
+		for ( auto & assert : type->assertions ) {
+			markAssertionSet( assn1, assert );
+			markAssertionSet( assn2, assert );
+		}
+	}
+
+	bool tryToUnifyWithEnumValue( const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env,
+		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
+		WidenMode widen) {
+		if ( auto attrType2 = dynamic_cast<const ast::EnumAttrType *>(type2)) {
+			if (attrType2->attr == ast::EnumAttribute::Value) {
+				return unifyExact( type1, attrType2->instance->base->base, env, need, have, open,
+					widen);
+			} else if (attrType2->attr == ast::EnumAttribute::Posn) {
+				return unifyExact( type1, attrType2->instance, env, need, have, open, widen );
+			}
+		}
+		return false;
+	}
+
+public:
+	void postvisit( const ast::FunctionType * func ) {
+		auto func2 = dynamic_cast< const ast::FunctionType * >( type2 );
+		if ( !func2 ) return;
+
+		if ( func->isVarArgs != func2->isVarArgs ) return;
+
+		// Flatten the parameter lists for both functions so that tuple structure does not
+		// affect unification. Does not actually mutate function parameters.
+		auto params = flattenList( func->params, tenv );
+		auto params2 = flattenList( func2->params, tenv );
+
+		// sizes don't have to match if ttypes are involved; need to be more precise w.r.t.
+		// where the ttype is to prevent errors
+		if (
+			( params.size() != params2.size() || func->returns.size() != func2->returns.size() )
+			&& !func->isTtype()
+			&& !func2->isTtype()
+		) return;
+
+		if ( !unifyTypeList( params, params2, tenv, need, have, open ) ) return;
+		if ( !unifyTypeList(
+			func->returns, func2->returns, tenv, need, have, open ) ) return;
+
+		markAssertions( have, need, func );
+		markAssertions( have, need, func2 );
+
+		result = true;
+	}
+
+private:
+	// Returns: other, cast as XInstType
+	// Assigns this->result: whether types are compatible (up to generic parameters)
+	template< typename XInstType >
+	const XInstType * handleRefType( const XInstType * inst, const ast::Type * other ) {
+		// check that the other type is compatible and named the same
+		auto otherInst = dynamic_cast< const XInstType * >( other );
+		if ( otherInst && inst->name == otherInst->name ) {
+			this->result = otherInst;
+		}
+		return otherInst;
+	}
+
+	/// Creates a tuple type based on a list of TypeExpr
+	template< typename Iter >
+	static const ast::Type * tupleFromExprs(
+		const ast::TypeExpr * param, Iter & crnt, Iter end, ast::CV::Qualifiers qs
+	) {
+		std::vector< ast::ptr< ast::Type > > types;
+		do {
+			types.emplace_back( param->type );
+
+			++crnt;
+			if ( crnt == end ) break;
+			param = strict_dynamic_cast< const ast::TypeExpr * >( crnt->get() );
+		} while(true);
+
+		return new ast::TupleType( std::move(types), qs );
+	}
+
+	template< typename XInstType >
+	void handleGenericRefType( const XInstType * inst, const ast::Type * other ) {
+		// check that other type is compatible and named the same
+		const XInstType * otherInst = handleRefType( inst, other );
+		if ( !this->result ) return;
+
+		// check that parameters of types unify, if any
+		const std::vector< ast::ptr< ast::Expr > > & params = inst->params;
+		const std::vector< ast::ptr< ast::Expr > > & params2 = otherInst->params;
+
+		auto it = params.begin();
+		auto jt = params2.begin();
+		for ( ; it != params.end() && jt != params2.end(); ++it, ++jt ) {
+			auto param = strict_dynamic_cast< const ast::TypeExpr * >( it->get() );
+			auto param2 = strict_dynamic_cast< const ast::TypeExpr * >( jt->get() );
+
+			ast::ptr< ast::Type > pty = param->type;
+			ast::ptr< ast::Type > pty2 = param2->type;
+
+			bool isTuple = Tuples::isTtype( pty );
+			bool isTuple2 = Tuples::isTtype( pty2 );
+
+			if ( isTuple && isTuple2 ) {
+				++it; ++jt;  // skip ttype parameters before break
+			} else if ( isTuple ) {
+				// bundle remaining params into tuple
+				pty2 = tupleFromExprs( param2, jt, params2.end(), pty->qualifiers );
+				++it;  // skip ttype parameter for break
+			} else if ( isTuple2 ) {
+				// bundle remaining params into tuple
+				pty = tupleFromExprs( param, it, params.end(), pty2->qualifiers );
+				++jt;  // skip ttype parameter for break
+			}
+
+			if ( !unifyExact(
+					pty, pty2, tenv, need, have, open, noWiden() ) ) {
+				result = false;
+				return;
+			}
+
+			// ttype parameter should be last
+			if ( isTuple || isTuple2 ) break;
+		}
+		result = it == params.end() && jt == params2.end();
+	}
+
+public:
+	void postvisit( const ast::StructInstType * aggrType ) {
+		handleGenericRefType( aggrType, type2 );
+		result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::UnionInstType * aggrType ) {
+		handleGenericRefType( aggrType, type2 );
+		result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::EnumInstType * aggrType ) {
+		handleRefType( aggrType, type2 );
+		result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::EnumAttrType * enumAttr ) {
+		// Lazy approach for now
+		if ( auto otherPos = dynamic_cast< const ast::EnumAttrType *>( type2 ) ) {
+			if ( enumAttr->match(otherPos) ) {
+				result = otherPos;
+			}
+		}
+	}
+
+	void postvisit( const ast::TraitInstType * aggrType ) {
+		handleRefType( aggrType, type2 );
+		result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::TypeInstType * typeInst ) {
+		// assert( open.find( *typeInst ) == open.end() );
+		auto otherInst = dynamic_cast< const ast::TypeInstType * >( type2 );
+		if ( otherInst && typeInst->name == otherInst->name ) {
+			this->result = otherInst;
+		}
+		result = result || tryToUnifyWithEnumValue(typeInst, type2, tenv, need, have, open, noWiden());
+	}
+
+private:
+	/// Creates a tuple type based on a list of Type
+	static bool unifyList(
+		const std::vector< ast::ptr< ast::Type > > & list1,
+		const std::vector< ast::ptr< ast::Type > > & list2, ast::TypeEnvironment & env,
+		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open
+	) {
+		auto crnt1 = list1.begin();
+		auto crnt2 = list2.begin();
+		while ( crnt1 != list1.end() && crnt2 != list2.end() ) {
+			const ast::Type * t1 = *crnt1;
+			const ast::Type * t2 = *crnt2;
+			bool isTuple1 = Tuples::isTtype( t1 );
+			bool isTuple2 = Tuples::isTtype( t2 );
+
+			// assumes ttype must be last parameter
+			if ( isTuple1 && !isTuple2 ) {
+				// combine entirety of list2, then unify
+				return unifyExact(
+					t1, tupleFromTypes( list2 ), env, need, have, open,
+					noWiden() );
+			} else if ( !isTuple1 && isTuple2 ) {
+				// combine entirety of list1, then unify
+				return unifyExact(
+					tupleFromTypes( list1 ), t2, env, need, have, open,
+					noWiden() );
+			}
+
+			if ( !unifyExact(
+				t1, t2, env, need, have, open, noWiden() )
+			) return false;
+
+			++crnt1; ++crnt2;
+		}
+
+		if ( crnt1 != list1.end() ) {
+			// try unifying empty tuple type with ttype
+			const ast::Type * t1 = *crnt1;
+			if ( !Tuples::isTtype( t1 ) ) return false;
+			// xxx - this doesn't generate an empty tuple, contrary to comment; both ported
+			// from Rob's code
+			return unifyExact(
+					t1, tupleFromTypes( list2 ), env, need, have, open,
+					noWiden() );
+		} else if ( crnt2 != list2.end() ) {
+			// try unifying empty tuple with ttype
+			const ast::Type * t2 = *crnt2;
+			if ( !Tuples::isTtype( t2 ) ) return false;
+			// xxx - this doesn't generate an empty tuple, contrary to comment; both ported
+			// from Rob's code
+			return unifyExact(
+					tupleFromTypes( list1 ), t2, env, need, have, open,
+					noWiden() );
+		}
+
+		return true;
+	}
+
+public:
+	void postvisit( const ast::TupleType * tuple ) {
+		auto tuple2 = dynamic_cast< const ast::TupleType * >( type2 );
+		if ( ! tuple2 ) return;
+
+		ast::Pass<TtypeExpander> expander{ tenv };
+
+		const ast::Type * flat = tuple->accept( expander );
+		const ast::Type * flat2 = tuple2->accept( expander );
+
+		auto types = flatten( flat );
+		auto types2 = flatten( flat2 );
+
+		result = unifyList( types, types2, tenv, need, have, open )
+			|| tryToUnifyWithEnumValue(tuple, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::VarArgsType * vat) {
+		result = dynamic_cast< const ast::VarArgsType * >( type2 )
+			|| tryToUnifyWithEnumValue(vat, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::ZeroType * zt) {
+		result = dynamic_cast< const ast::ZeroType * >( type2 )
+			|| tryToUnifyWithEnumValue(zt, type2, tenv, need, have, open, noWiden());
+	}
+
+	void postvisit( const ast::OneType * ot) {
+		result = dynamic_cast< const ast::OneType * >( type2 )
+			|| tryToUnifyWithEnumValue(ot, type2, tenv, need, have, open, noWiden());
+	}
+};
+
+// size_t Unify::traceId = Stats::Heap::new_stacktrace_id("Unify");
+
+bool unify(
+		const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
+		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
+		ast::OpenVarSet & open
+) {
+	ast::ptr<ast::Type> common;
+	return unify( type1, type2, env, need, have, open, common );
+}
+
+bool unify(
+		const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
+		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
+		ast::OpenVarSet & open, ast::ptr<ast::Type> & common
+) {
+	ast::OpenVarSet closed;
+	// findOpenVars( type1, open, closed, need, have, FirstClosed );
+	findOpenVars( type2, open, closed, need, have, env, FirstOpen );
+	return unifyInexact(
+		type1, type2, env, need, have, open, WidenMode{ true, true }, common );
+}
+
+bool unifyExact(
+		const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env,
+		ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
+		WidenMode widen
+) {
+	if ( type1->qualifiers != type2->qualifiers ) return false;
+
+	auto var1 = dynamic_cast< const ast::TypeInstType * >( type1 );
+	auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 );
+	bool isopen1 = var1 && env.lookup(*var1);
+	bool isopen2 = var2 && env.lookup(*var2);
+
+	if ( isopen1 && isopen2 ) {
+		if ( var1->base->kind != var2->base->kind ) return false;
+		return env.bindVarToVar(
+			var1, var2, ast::TypeData{ var1->base->kind, var1->base->sized||var2->base->sized }, need, have,
+			open, widen );
+	} else if ( isopen1 ) {
+		return env.bindVar( var1, type2, ast::TypeData{var1->base}, need, have, open, widen );
+	} else if ( isopen2 ) {
+		return env.bindVar( var2, type1, ast::TypeData{var2->base}, need, have, open, widen );
+	} else {
+		return ast::Pass<Unify>::read(
+			type1, type2, env, need, have, open, widen );
+	}
+}
+
+bool unifyInexact(
+		const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
+		ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
+		const ast::OpenVarSet & open, WidenMode widen,
+		ast::ptr<ast::Type> & common
+) {
+	ast::CV::Qualifiers q1 = type1->qualifiers, q2 = type2->qualifiers;
+
+	// force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and
+	// type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1
+	ast::Type * t1 = shallowCopy(type1.get());
+	ast::Type * t2 = shallowCopy(type2.get());
+	t1->qualifiers = {};
+	t2->qualifiers = {};
+	ast::ptr< ast::Type > t1_(t1);
+	ast::ptr< ast::Type > t2_(t2);
+
+	if ( unifyExact( t1, t2, env, need, have, open, widen ) ) {
+		// if exact unification on unqualified types, try to merge qualifiers
+		if ( q1 == q2 || ( ( q1 > q2 || widen.first ) && ( q2 > q1 || widen.second ) ) ) {
+			t1->qualifiers = q1 | q2;
+			common = t1;
+			return true;
+		} else {
+			return false;
+		}
+	} else if (( common = commonType( t1, t2, env, need, have, open, widen ))) {
+		// no exact unification, but common type
+		auto c = shallowCopy(common.get());
+		c->qualifiers = q1 | q2;
+		common = c;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func ) {
+	if ( func->returns.empty() ) return new ast::VoidType();
+	if ( func->returns.size() == 1 ) return func->returns[0];
+
+	std::vector<ast::ptr<ast::Type>> tys;
+	for ( const auto & decl : func->returns ) {
+		tys.emplace_back( decl );
+	}
+	return new ast::TupleType( std::move(tys) );
+}
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/Unify.h
===================================================================
--- src/ResolvExpr/Unify.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,71 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Unify.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 13:09:04 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Jan 17 11:12:00 2023
-// Update Count     : 5
-//
-
-#pragma once
-
-#include "AST/Node.hpp"             // for ptr
-#include "AST/TypeEnvironment.hpp"  // for TypeEnvironment, AssertionSet, OpenVarSet
-#include "WidenMode.h"              // for WidenMode
-
-namespace ast {
-	class SymbolTable;
-	class Type;
-}
-
-namespace ResolvExpr {
-
-bool unify(
-	const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
-	ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
-	ast::OpenVarSet & open );
-
-bool unify(
-	const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
-	ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
-	ast::OpenVarSet & open, ast::ptr<ast::Type> & common );
-
-bool unifyExact(
-	const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env,
-	ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
-	WidenMode widen );
-
-bool unifyInexact(
-	const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
-	ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
-	const ast::OpenVarSet & open, WidenMode widen,
-	ast::ptr<ast::Type> & common );
-
-bool typesCompatible(
-	const ast::Type *, const ast::Type *,
-	const ast::TypeEnvironment & env = {} );
-
-bool typesCompatibleIgnoreQualifiers(
-	const ast::Type *, const ast::Type *,
-	const ast::TypeEnvironment & env = {} );
-
-/// Creates or extracts the type represented by returns in a `FunctionType`.
-ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func );
-
-std::vector<ast::ptr<ast::Type>> flattenList(
-	const std::vector<ast::ptr<ast::Type>> & src, ast::TypeEnvironment & env
-);
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/Unify.hpp
===================================================================
--- src/ResolvExpr/Unify.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/Unify.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,71 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Unify.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 13:09:04 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Jan 17 11:12:00 2023
+// Update Count     : 5
+//
+
+#pragma once
+
+#include "AST/Node.hpp"             // for ptr
+#include "AST/TypeEnvironment.hpp"  // for TypeEnvironment, AssertionSet, OpenVarSet
+#include "WidenMode.hpp"            // for WidenMode
+
+namespace ast {
+	class SymbolTable;
+	class Type;
+}
+
+namespace ResolvExpr {
+
+bool unify(
+	const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
+	ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
+	ast::OpenVarSet & open );
+
+bool unify(
+	const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
+	ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
+	ast::OpenVarSet & open, ast::ptr<ast::Type> & common );
+
+bool unifyExact(
+	const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env,
+	ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open,
+	WidenMode widen );
+
+bool unifyInexact(
+	const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2,
+	ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
+	const ast::OpenVarSet & open, WidenMode widen,
+	ast::ptr<ast::Type> & common );
+
+bool typesCompatible(
+	const ast::Type *, const ast::Type *,
+	const ast::TypeEnvironment & env = {} );
+
+bool typesCompatibleIgnoreQualifiers(
+	const ast::Type *, const ast::Type *,
+	const ast::TypeEnvironment & env = {} );
+
+/// Creates or extracts the type represented by returns in a `FunctionType`.
+ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func );
+
+std::vector<ast::ptr<ast::Type>> flattenList(
+	const std::vector<ast::ptr<ast::Type>> & src, ast::TypeEnvironment & env
+);
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/WidenMode.h
===================================================================
--- src/ResolvExpr/WidenMode.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,52 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// WidenMode.h --
-//
-// Author           : Aaron B. Moss
-// Created On       : Mon Jun 18 11:58:00 2018
-// Last Modified By : Aaron B. Moss
-// Last Modified On : Mon Jun 18 11:58:00 2018
-// Update Count     : 1
-//
-
-#pragma once
-
-namespace ResolvExpr {
-
-struct WidenMode {
-	WidenMode( bool first, bool second ): first( first ), second( second ) {}
-
-	WidenMode &operator|=( const WidenMode &other ) {
-		first |= other.first; second |= other.second; return *this;
-	}
-
-	WidenMode &operator&=( const WidenMode &other ) {
-		first &= other.first; second &= other.second; return *this;
-	}
-
-	WidenMode operator|( const WidenMode &other ) {
-		WidenMode newWM( *this ); newWM |= other; return newWM;
-	}
-
-	WidenMode operator&( const WidenMode &other ) {
-		WidenMode newWM( *this ); newWM &= other; return newWM;
-	}
-
-	operator bool() { return first && second; }
-
-	bool first : 1, second : 1;
-};
-
-static inline WidenMode noWiden() { return { false, false }; }
-
-} // namespace ResolvExpr
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/ResolvExpr/WidenMode.hpp
===================================================================
--- src/ResolvExpr/WidenMode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/ResolvExpr/WidenMode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,52 @@
+//
+// 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.
+//
+// WidenMode.h --
+//
+// Author           : Aaron B. Moss
+// Created On       : Mon Jun 18 11:58:00 2018
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Mon Jun 18 11:58:00 2018
+// Update Count     : 1
+//
+
+#pragma once
+
+namespace ResolvExpr {
+
+struct WidenMode {
+	WidenMode( bool first, bool second ): first( first ), second( second ) {}
+
+	WidenMode &operator|=( const WidenMode &other ) {
+		first |= other.first; second |= other.second; return *this;
+	}
+
+	WidenMode &operator&=( const WidenMode &other ) {
+		first &= other.first; second &= other.second; return *this;
+	}
+
+	WidenMode operator|( const WidenMode &other ) {
+		WidenMode newWM( *this ); newWM |= other; return newWM;
+	}
+
+	WidenMode operator&( const WidenMode &other ) {
+		WidenMode newWM( *this ); newWM &= other; return newWM;
+	}
+
+	operator bool() { return first && second; }
+
+	bool first : 1, second : 1;
+};
+
+static inline WidenMode noWiden() { return { false, false }; }
+
+} // namespace ResolvExpr
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ResolvExpr/module.mk
===================================================================
--- src/ResolvExpr/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/ResolvExpr/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,5 +16,5 @@
 
 SRC_RESOLVEXPR = \
-      ResolvExpr/AdjustExprType.cc \
+      ResolvExpr/AdjustExprType.cpp \
       ResolvExpr/AdjustExprType.hpp \
       ResolvExpr/Candidate.cpp \
@@ -22,38 +22,38 @@
       ResolvExpr/CandidateFinder.hpp \
       ResolvExpr/Candidate.hpp \
-      ResolvExpr/CastCost.cc \
+      ResolvExpr/CastCost.cpp \
       ResolvExpr/CastCost.hpp \
-      ResolvExpr/CommonType.cc \
+      ResolvExpr/CommonType.cpp \
       ResolvExpr/CommonType.hpp \
-      ResolvExpr/ConversionCost.cc \
-      ResolvExpr/ConversionCost.h \
-      ResolvExpr/Cost.h \
-      ResolvExpr/CurrentObject.cc \
-      ResolvExpr/CurrentObject.h \
+      ResolvExpr/ConversionCost.cpp \
+      ResolvExpr/ConversionCost.hpp \
+      ResolvExpr/Cost.hpp \
+      ResolvExpr/CurrentObject.cpp \
+      ResolvExpr/CurrentObject.hpp \
       ResolvExpr/ExplodedArg.cpp \
       ResolvExpr/ExplodedArg.hpp \
-      ResolvExpr/FindOpenVars.cc \
-      ResolvExpr/FindOpenVars.h \
-      ResolvExpr/PolyCost.cc \
+      ResolvExpr/FindOpenVars.cpp \
+      ResolvExpr/FindOpenVars.hpp \
+      ResolvExpr/PolyCost.cpp \
       ResolvExpr/PolyCost.hpp \
-      ResolvExpr/PtrsAssignable.cc \
+      ResolvExpr/PtrsAssignable.cpp \
       ResolvExpr/PtrsAssignable.hpp \
-      ResolvExpr/PtrsCastable.cc \
+      ResolvExpr/PtrsCastable.cpp \
       ResolvExpr/PtrsCastable.hpp \
-      ResolvExpr/RenameVars.cc \
-      ResolvExpr/RenameVars.h \
-      ResolvExpr/Resolver.cc \
-      ResolvExpr/Resolver.h \
-      ResolvExpr/ResolveTypeof.cc \
-      ResolvExpr/ResolveTypeof.h \
+      ResolvExpr/RenameVars.cpp \
+      ResolvExpr/RenameVars.hpp \
+      ResolvExpr/Resolver.cpp \
+      ResolvExpr/Resolver.hpp \
+      ResolvExpr/ResolveTypeof.cpp \
+      ResolvExpr/ResolveTypeof.hpp \
       ResolvExpr/ResolveMode.hpp \
       ResolvExpr/SatisfyAssertions.cpp \
       ResolvExpr/SatisfyAssertions.hpp \
-      ResolvExpr/SpecCost.cc \
+      ResolvExpr/SpecCost.cpp \
       ResolvExpr/SpecCost.hpp \
-      ResolvExpr/typeops.h \
-      ResolvExpr/Unify.cc \
-      ResolvExpr/Unify.h \
-      ResolvExpr/WidenMode.h
+      ResolvExpr/typeops.hpp \
+      ResolvExpr/Unify.cpp \
+      ResolvExpr/Unify.hpp \
+      ResolvExpr/WidenMode.hpp
 
 SRC += $(SRC_RESOLVEXPR) \
Index: src/ResolvExpr/typeops.h
===================================================================
--- src/ResolvExpr/typeops.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,105 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// typeops.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 07:28:22 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jan 18 11:54:00 2023
-// Update Count     : 7
-//
-
-#pragma once
-
-#include <vector>
-
-#include "AST/Type.hpp"
-
-namespace ResolvExpr {
-
-class TypeEnvironment;
-
-// combos: takes a list of sets and returns a set of lists representing every possible way of forming a list by
-// picking one element out of each set
-template< typename InputIterator, typename OutputIterator >
-void combos( InputIterator begin, InputIterator end, OutputIterator out ) {
-	typedef typename InputIterator::value_type SetType;
-	typedef typename std::vector< typename SetType::value_type > ListType;
-
-	if ( begin == end )	{
-		*out++ = ListType();
-		return;
-	} // if
-
-	InputIterator current = begin;
-	begin++;
-
-	std::vector< ListType > recursiveResult;
-	combos( begin, end, back_inserter( recursiveResult ) );
-
-	for ( const auto& i : recursiveResult ) for ( const auto& j : *current ) {
-		ListType result;
-		std::back_insert_iterator< ListType > inserter = back_inserter( result );
-		*inserter++ = j;
-		std::copy( i.begin(), i.end(), inserter );
-		*out++ = result;
-	}
-}
-
-/// Flatten tuple type into existing list of types.
-inline void flatten(
-	const ast::Type * type, std::vector< ast::ptr< ast::Type > > & out
-) {
-	if ( auto tupleType = dynamic_cast< const ast::TupleType * >( type ) ) {
-		for ( const ast::Type * t : tupleType->types ) {
-			flatten( t, out );
-		}
-	} else {
-		out.emplace_back( type );
-	}
-}
-
-/// Flatten tuple type into list of types.
-inline std::vector< ast::ptr< ast::Type > > flatten( const ast::Type * type ) {
-	std::vector< ast::ptr< ast::Type > > out;
-	out.reserve( type->size() );
-	flatten( type, out );
-	return out;
-}
-
-template< typename Iter >
-const ast::Type * tupleFromTypes( Iter crnt, Iter end ) {
-	std::vector< ast::ptr< ast::Type > > types;
-	while ( crnt != end ) {
-		// it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure
-		// that this results in a flat tuple
-		flatten( *crnt, types );
-
-		++crnt;
-	}
-
-	return new ast::TupleType( std::move(types) );
-}
-
-inline const ast::Type * tupleFromTypes(
-	const std::vector< ast::ptr< ast::Type > > & tys
-) {
-	return tupleFromTypes( tys.begin(), tys.end() );
-}
-
-} // namespace ResolvExpr
-
-namespace ast {
-	// in TypeEnvironment.cpp
-	bool isFtype( const ast::Type * type );
-} // namespace ast
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/SymTab/Demangle.cc
===================================================================
--- src/SymTab/Demangle.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,330 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Demangle.cc -- Convert a mangled name into a human readable name.
-//
-// Author           : Rob Schluntz
-// Created On       : Thu Jul 19 12:52:41 2018
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Nov  6 15:59:00 2023
-// Update Count     : 12
-//
-
-#include <algorithm>
-#include <sstream>
-
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-#include "CodeGen/GenType.h"
-#include "CodeGen/OperatorTable.h"
-#include "Common/utility.h"								// isPrefix
-#include "Mangler.h"
-
-#define DEBUG
-#ifdef DEBUG
-#define PRINT(x) x
-#else
-#define PRINT(x) {}
-#endif
-
-namespace Mangle {
-
-namespace {
-
-struct Demangler {
-private:
-	std::string str;
-	size_t index = 0;
-	using Parser = std::function<ast::Type * ( ast::CV::Qualifiers )>;
-	std::vector<std::pair<std::string, Parser>> parsers;
-public:
-	Demangler( const std::string & str );
-
-	bool done() const { return str.size() <= index; }
-	char cur() const { assert( !done() ); return str[index]; }
-	bool expect( char ch ) { return str[index++] == ch; }
-
-	bool isPrefix( const std::string & pref );
-	bool extractNumber( size_t & out );
-	bool extractName( std::string & out );
-	bool stripMangleName( std::string & name );
-
-	ast::Type * parseFunction( ast::CV::Qualifiers tq );
-	ast::Type * parseTuple( ast::CV::Qualifiers tq );
-	ast::Type * parsePointer( ast::CV::Qualifiers tq );
-	ast::Type * parseArray( ast::CV::Qualifiers tq );
-	ast::Type * parseStruct( ast::CV::Qualifiers tq );
-	ast::Type * parseUnion( ast::CV::Qualifiers tq );
-	ast::Type * parseEnum( ast::CV::Qualifiers tq );
-	ast::Type * parseType( ast::CV::Qualifiers tq );
-	ast::Type * parseZero( ast::CV::Qualifiers tq );
-	ast::Type * parseOne( ast::CV::Qualifiers tq );
-
-	ast::Type * parseType();
-	bool parse( std::string & name, ast::Type *& type );
-};
-
-Demangler::Demangler(const std::string & str) : str(str) {
-	for (size_t k = 0; k < ast::BasicType::NUMBER_OF_BASIC_TYPES; ++k) {
-		parsers.emplace_back(Encoding::basicTypes[k], [k]( ast::CV::Qualifiers tq ) {
-			PRINT( std::cerr << "basic type: " << k << std::endl; )
-			return new ast::BasicType( (ast::BasicType::Kind)k, tq );
-		});
-	}
-
-	for (size_t k = 0; k < ast::TypeDecl::NUMBER_OF_KINDS; ++k) {
-		static const std::string typeVariableNames[] = { "DT", "DST", "OT", "FT", "TT", "ALT", };
-		static_assert(
-			sizeof(typeVariableNames)/sizeof(typeVariableNames[0]) == ast::TypeDecl::NUMBER_OF_KINDS,
-			"Each type variable kind should have a demangle name prefix"
-		);
-		parsers.emplace_back(Encoding::typeVariables[k], [k, this]( ast::CV::Qualifiers tq ) -> ast::TypeInstType * {
-			PRINT( std::cerr << "type variable type: " << k << std::endl; )
-			size_t N;
-			if (!extractNumber(N)) return nullptr;
-			return new ast::TypeInstType(
-				toString(typeVariableNames[k], N),
-				(ast::TypeDecl::Kind)k,
-				tq );
-		});
-	}
-
-	parsers.emplace_back(Encoding::void_t, [this]( ast::CV::Qualifiers tq ) { return new ast::VoidType(tq); });
-	parsers.emplace_back(Encoding::function, [this]( ast::CV::Qualifiers tq ) { return parseFunction(tq); });
-	parsers.emplace_back(Encoding::pointer, [this]( ast::CV::Qualifiers tq ) { return parsePointer(tq); });
-	parsers.emplace_back(Encoding::array, [this]( ast::CV::Qualifiers tq ) { return parseArray(tq); });
-	parsers.emplace_back(Encoding::tuple, [this]( ast::CV::Qualifiers tq ) { return parseTuple(tq); });
-	parsers.emplace_back(Encoding::struct_t, [this]( ast::CV::Qualifiers tq ) { return parseStruct(tq); });
-	parsers.emplace_back(Encoding::union_t, [this]( ast::CV::Qualifiers tq ) { return parseUnion(tq); });
-	parsers.emplace_back(Encoding::enum_t, [this]( ast::CV::Qualifiers tq ) { return parseEnum(tq); });
-	parsers.emplace_back(Encoding::type, [this]( ast::CV::Qualifiers tq ) { return parseType(tq); });
-	parsers.emplace_back(Encoding::zero, []( ast::CV::Qualifiers tq ) { return new ast::ZeroType(tq); });
-	parsers.emplace_back(Encoding::one, []( ast::CV::Qualifiers tq ) { return new ast::OneType(tq); });
-}
-
-bool Demangler::extractNumber( size_t & out ) {
-	std::stringstream numss;
-	if ( str.size() <= index ) return false;
-	while ( isdigit( str[index] ) ) {
-		numss << str[index];
-		++index;
-		if ( str.size() == index ) break;
-	}
-	if ( !(numss >> out) ) return false;
-	PRINT( std::cerr << "extractNumber success: " << out << std::endl; )
-	return true;
-}
-
-bool Demangler::extractName( std::string & out ) {
-	size_t len;
-	if ( !extractNumber(len) ) return false;
-	if ( str.size() < index + len ) return false;
-	out = str.substr( index, len );
-	index += len;
-	PRINT( std::cerr << "extractName success: " << out << std::endl; )
-	return true;
-}
-
-bool Demangler::isPrefix( const std::string & pref ) {
-	// Wraps the utility isPrefix function.
-	if ( ::isPrefix( str, pref, index ) ) {
-		index += pref.size();
-		return true;
-	}
-	return false;
-}
-
-// strips __NAME__cfa__TYPE_N, where N is [0-9]+: returns str is a match is found, returns empty string otherwise
-bool Demangler::stripMangleName( std::string & name ) {
-	PRINT( std::cerr << "====== " << str.size() << " " << str << std::endl; )
-	if (str.size() < 2+Encoding::manglePrefix.size()) return false; // +2 for at least _1 suffix
-	if ( !isPrefix(Encoding::manglePrefix) || !isdigit(str.back() ) ) return false;
-
-	if (!extractName(name)) return false;
-
-	// Find bounds for type.
-	PRINT( std::cerr << index << " " << str.size() << std::endl; )
-	PRINT( std::cerr << "[");
-	while (isdigit(str.back())) {
-		PRINT(std::cerr << ".");
-		str.pop_back();
-		if (str.size() <= index) return false;
-	}
-	PRINT( std::cerr << "]" << std::endl );
-	if (str.back() != '_') return false;
-	str.pop_back();
-	PRINT( std::cerr << str.size() << " " << name << " " << str.substr(index) << std::endl; )
-	return index < str.size();
-}
-
-ast::Type * Demangler::parseFunction( ast::CV::Qualifiers tq ) {
-	PRINT( std::cerr << "function..." << std::endl; )
-	if ( done() ) return nullptr;
-	ast::FunctionType * ftype = new ast::FunctionType( ast::FixedArgs, tq );
-	std::unique_ptr<ast::Type> manager( ftype );
-	ast::Type * retVal = parseType();
-	if ( !retVal ) return nullptr;
-	PRINT( std::cerr << "with return type: " << retVal << std::endl; )
-	ftype->returns.emplace_back( retVal );
-	if ( done() || !expect('_') ) return nullptr;
-	while ( !done() ) {
-		PRINT( std::cerr << "got ch: " << cur() << std::endl; )
-		if ( cur() == '_' ) return manager.release();
-		ast::Type * param = parseType();
-		if ( !param ) return nullptr;
-		PRINT( std::cerr << "with parameter : " << param << std::endl; )
-		ftype->params.emplace_back( param );
-	}
-	return nullptr;
-}
-
-ast::Type * Demangler::parseTuple( ast::CV::Qualifiers tq ) {
-	PRINT( std::cerr << "tuple..." << std::endl; )
-	std::vector<ast::ptr<ast::Type>> types;
-	size_t ncomponents;
-	if ( !extractNumber(ncomponents) ) return nullptr;
-	for ( size_t i = 0; i < ncomponents; ++i ) {
-		if ( done() ) return nullptr;
-		PRINT( std::cerr << "got ch: " << cur() << std::endl; )
-		ast::Type * t = parseType();
-		if ( !t ) return nullptr;
-		PRINT( std::cerr << "with type : " << t << std::endl; )
-		types.push_back( t );
-	}
-	return new ast::TupleType( std::move( types ), tq );
-}
-
-ast::Type * Demangler::parsePointer( ast::CV::Qualifiers tq ) {
-	PRINT( std::cerr << "pointer..." << std::endl; )
-	ast::Type * t = parseType();
-	if ( !t ) return nullptr;
-	return new ast::PointerType( t, tq );
-}
-
-ast::Type * Demangler::parseArray( ast::CV::Qualifiers tq ) {
-	PRINT( std::cerr << "array..." << std::endl; )
-	size_t length;
-	if ( !extractNumber(length) ) return nullptr;
-	ast::Type * t = parseType();
-	if ( !t ) return nullptr;
-	return new ast::ArrayType(
-		t,
-		ast::ConstantExpr::from_ulong( CodeLocation(), length ),
-		ast::FixedLen,
-		ast::DynamicDim,
-		tq );
-}
-
-ast::Type * Demangler::parseStruct( ast::CV::Qualifiers tq ) {
-	PRINT( std::cerr << "struct..." << std::endl; )
-	std::string name;
-	if ( !extractName(name) ) return nullptr;
-	return new ast::StructInstType( name, tq );
-}
-
-ast::Type * Demangler::parseUnion( ast::CV::Qualifiers tq ) {
-	PRINT( std::cerr << "union..." << std::endl; )
-	std::string name;
-	if ( !extractName(name) ) return nullptr;
-	return new ast::UnionInstType( name, tq );
-}
-
-ast::Type * Demangler::parseEnum( ast::CV::Qualifiers tq ) {
-	PRINT( std::cerr << "enum..." << std::endl; )
-	std::string name;
-	if ( !extractName(name) ) return nullptr;
-	return new ast::EnumInstType( name, tq );
-}
-
-ast::Type * Demangler::parseType( ast::CV::Qualifiers tq ) {
-	PRINT( std::cerr << "type..." << std::endl; )
-	std::string name;
-	if ( !extractName(name) ) return nullptr;
-	PRINT( std::cerr << "typename..." << name << std::endl; )
-	return new ast::TypeInstType( name, ast::TypeDecl::Dtype, tq );
-}
-
-ast::Type * Demangler::parseType() {
-	if (done()) return nullptr;
-
-	if (isPrefix(Encoding::forall)) {
-		PRINT( std::cerr << "polymorphic with..." << std::endl; )
-		size_t dcount, fcount, vcount, acount;
-		if ( !extractNumber(dcount) ) return nullptr;
-		PRINT( std::cerr << dcount << " dtypes" << std::endl; )
-		if ( !expect('_') ) return nullptr;
-		if ( !extractNumber(fcount) ) return nullptr;
-		PRINT( std::cerr << fcount << " ftypes" << std::endl; )
-		if ( !expect('_')) return nullptr;
-		if ( !extractNumber(vcount)) return nullptr;
-		PRINT( std::cerr << vcount << " ttypes" << std::endl; )
-		if ( !expect('_') ) return nullptr;
-		if ( !extractNumber(acount) ) return nullptr;
-		PRINT( std::cerr << acount << " assertions" << std::endl; )
-		if ( !expect('_') ) return nullptr;
-		for ( size_t i = 0 ; i < acount ; ++i ) {
-			// TODO: need to recursively parse assertions, but for now just return nullptr so that
-			// demangler does not crash if there are assertions
-			return nullptr;
-		}
-		if ( !expect('_') ) return nullptr;
-	}
-
-	ast::CV::Qualifiers tq;
-	while (true) {
-		auto qual = std::find_if(Encoding::qualifiers.begin(), Encoding::qualifiers.end(), [this](decltype(Encoding::qualifiers)::value_type val) {
-			return isPrefix(val.second);
-		});
-		if (qual == Encoding::qualifiers.end()) break;
-		tq |= qual->first;
-	}
-
-	// Find the correct type parser and then apply it.
-	auto iter = std::find_if(parsers.begin(), parsers.end(), [this](std::pair<std::string, Parser> & p) {
-		return isPrefix(p.first);
-	});
-	assertf(iter != parsers.end(), "Unhandled type letter: %c at index: %zd", cur(), index);
-	ast::Type * ret = iter->second(tq);
-	if ( !ret ) return nullptr;
-	return ret;
-}
-
-bool Demangler::parse( std::string & name, ast::Type *& type) {
-	if ( !stripMangleName(name) ) return false;
-	PRINT( std::cerr << "stripped name: " << name << std::endl; )
-	ast::Type * t = parseType();
-	if ( !t ) return false;
-	type = t;
-	return true;
-}
-
-std::string demangle( const std::string & mangleName ) {
-	using namespace CodeGen;
-	Demangler demangler( mangleName );
-	std::string name;
-	ast::Type * type = nullptr;
-	if ( !demangler.parse( name, type ) ) return mangleName;
-	ast::readonly<ast::Type> roType = type;
-	if ( auto info = operatorLookupByOutput( name ) ) name = info->inputName;
-	return genType( type, name, Options( false, false, false, false ) );
-}
-
-} // namespace
-
-} // namespace Mangle
-
-extern "C" {
-	char * cforall_demangle(const char * mangleName, int option __attribute__((unused))) {
-		const std::string & demangleName = Mangle::demangle(mangleName);
-		return strdup(demangleName.c_str());
-	}
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/SymTab/Demangle.cpp
===================================================================
--- src/SymTab/Demangle.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/SymTab/Demangle.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,330 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Demangle.cpp -- Convert a mangled name into a human readable name.
+//
+// Author           : Rob Schluntz
+// Created On       : Thu Jul 19 12:52:41 2018
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Nov  6 15:59:00 2023
+// Update Count     : 12
+//
+
+#include <algorithm>
+#include <sstream>
+
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+#include "CodeGen/GenType.hpp"
+#include "CodeGen/OperatorTable.hpp"
+#include "Common/utility.hpp"								// isPrefix
+#include "Mangler.hpp"
+
+#define DEBUG
+#ifdef DEBUG
+#define PRINT(x) x
+#else
+#define PRINT(x) {}
+#endif
+
+namespace Mangle {
+
+namespace {
+
+struct Demangler {
+private:
+	std::string str;
+	size_t index = 0;
+	using Parser = std::function<ast::Type * ( ast::CV::Qualifiers )>;
+	std::vector<std::pair<std::string, Parser>> parsers;
+public:
+	Demangler( const std::string & str );
+
+	bool done() const { return str.size() <= index; }
+	char cur() const { assert( !done() ); return str[index]; }
+	bool expect( char ch ) { return str[index++] == ch; }
+
+	bool isPrefix( const std::string & pref );
+	bool extractNumber( size_t & out );
+	bool extractName( std::string & out );
+	bool stripMangleName( std::string & name );
+
+	ast::Type * parseFunction( ast::CV::Qualifiers tq );
+	ast::Type * parseTuple( ast::CV::Qualifiers tq );
+	ast::Type * parsePointer( ast::CV::Qualifiers tq );
+	ast::Type * parseArray( ast::CV::Qualifiers tq );
+	ast::Type * parseStruct( ast::CV::Qualifiers tq );
+	ast::Type * parseUnion( ast::CV::Qualifiers tq );
+	ast::Type * parseEnum( ast::CV::Qualifiers tq );
+	ast::Type * parseType( ast::CV::Qualifiers tq );
+	ast::Type * parseZero( ast::CV::Qualifiers tq );
+	ast::Type * parseOne( ast::CV::Qualifiers tq );
+
+	ast::Type * parseType();
+	bool parse( std::string & name, ast::Type *& type );
+};
+
+Demangler::Demangler(const std::string & str) : str(str) {
+	for (size_t k = 0; k < ast::BasicType::NUMBER_OF_BASIC_TYPES; ++k) {
+		parsers.emplace_back(Encoding::basicTypes[k], [k]( ast::CV::Qualifiers tq ) {
+			PRINT( std::cerr << "basic type: " << k << std::endl; )
+			return new ast::BasicType( (ast::BasicType::Kind)k, tq );
+		});
+	}
+
+	for (size_t k = 0; k < ast::TypeDecl::NUMBER_OF_KINDS; ++k) {
+		static const std::string typeVariableNames[] = { "DT", "DST", "OT", "FT", "TT", "ALT", };
+		static_assert(
+			sizeof(typeVariableNames)/sizeof(typeVariableNames[0]) == ast::TypeDecl::NUMBER_OF_KINDS,
+			"Each type variable kind should have a demangle name prefix"
+		);
+		parsers.emplace_back(Encoding::typeVariables[k], [k, this]( ast::CV::Qualifiers tq ) -> ast::TypeInstType * {
+			PRINT( std::cerr << "type variable type: " << k << std::endl; )
+			size_t N;
+			if (!extractNumber(N)) return nullptr;
+			return new ast::TypeInstType(
+				toString(typeVariableNames[k], N),
+				(ast::TypeDecl::Kind)k,
+				tq );
+		});
+	}
+
+	parsers.emplace_back(Encoding::void_t, [this]( ast::CV::Qualifiers tq ) { return new ast::VoidType(tq); });
+	parsers.emplace_back(Encoding::function, [this]( ast::CV::Qualifiers tq ) { return parseFunction(tq); });
+	parsers.emplace_back(Encoding::pointer, [this]( ast::CV::Qualifiers tq ) { return parsePointer(tq); });
+	parsers.emplace_back(Encoding::array, [this]( ast::CV::Qualifiers tq ) { return parseArray(tq); });
+	parsers.emplace_back(Encoding::tuple, [this]( ast::CV::Qualifiers tq ) { return parseTuple(tq); });
+	parsers.emplace_back(Encoding::struct_t, [this]( ast::CV::Qualifiers tq ) { return parseStruct(tq); });
+	parsers.emplace_back(Encoding::union_t, [this]( ast::CV::Qualifiers tq ) { return parseUnion(tq); });
+	parsers.emplace_back(Encoding::enum_t, [this]( ast::CV::Qualifiers tq ) { return parseEnum(tq); });
+	parsers.emplace_back(Encoding::type, [this]( ast::CV::Qualifiers tq ) { return parseType(tq); });
+	parsers.emplace_back(Encoding::zero, []( ast::CV::Qualifiers tq ) { return new ast::ZeroType(tq); });
+	parsers.emplace_back(Encoding::one, []( ast::CV::Qualifiers tq ) { return new ast::OneType(tq); });
+}
+
+bool Demangler::extractNumber( size_t & out ) {
+	std::stringstream numss;
+	if ( str.size() <= index ) return false;
+	while ( isdigit( str[index] ) ) {
+		numss << str[index];
+		++index;
+		if ( str.size() == index ) break;
+	}
+	if ( !(numss >> out) ) return false;
+	PRINT( std::cerr << "extractNumber success: " << out << std::endl; )
+	return true;
+}
+
+bool Demangler::extractName( std::string & out ) {
+	size_t len;
+	if ( !extractNumber(len) ) return false;
+	if ( str.size() < index + len ) return false;
+	out = str.substr( index, len );
+	index += len;
+	PRINT( std::cerr << "extractName success: " << out << std::endl; )
+	return true;
+}
+
+bool Demangler::isPrefix( const std::string & pref ) {
+	// Wraps the utility isPrefix function.
+	if ( ::isPrefix( str, pref, index ) ) {
+		index += pref.size();
+		return true;
+	}
+	return false;
+}
+
+// strips __NAME__cfa__TYPE_N, where N is [0-9]+: returns str is a match is found, returns empty string otherwise
+bool Demangler::stripMangleName( std::string & name ) {
+	PRINT( std::cerr << "====== " << str.size() << " " << str << std::endl; )
+	if (str.size() < 2+Encoding::manglePrefix.size()) return false; // +2 for at least _1 suffix
+	if ( !isPrefix(Encoding::manglePrefix) || !isdigit(str.back() ) ) return false;
+
+	if (!extractName(name)) return false;
+
+	// Find bounds for type.
+	PRINT( std::cerr << index << " " << str.size() << std::endl; )
+	PRINT( std::cerr << "[");
+	while (isdigit(str.back())) {
+		PRINT(std::cerr << ".");
+		str.pop_back();
+		if (str.size() <= index) return false;
+	}
+	PRINT( std::cerr << "]" << std::endl );
+	if (str.back() != '_') return false;
+	str.pop_back();
+	PRINT( std::cerr << str.size() << " " << name << " " << str.substr(index) << std::endl; )
+	return index < str.size();
+}
+
+ast::Type * Demangler::parseFunction( ast::CV::Qualifiers tq ) {
+	PRINT( std::cerr << "function..." << std::endl; )
+	if ( done() ) return nullptr;
+	ast::FunctionType * ftype = new ast::FunctionType( ast::FixedArgs, tq );
+	std::unique_ptr<ast::Type> manager( ftype );
+	ast::Type * retVal = parseType();
+	if ( !retVal ) return nullptr;
+	PRINT( std::cerr << "with return type: " << retVal << std::endl; )
+	ftype->returns.emplace_back( retVal );
+	if ( done() || !expect('_') ) return nullptr;
+	while ( !done() ) {
+		PRINT( std::cerr << "got ch: " << cur() << std::endl; )
+		if ( cur() == '_' ) return manager.release();
+		ast::Type * param = parseType();
+		if ( !param ) return nullptr;
+		PRINT( std::cerr << "with parameter : " << param << std::endl; )
+		ftype->params.emplace_back( param );
+	}
+	return nullptr;
+}
+
+ast::Type * Demangler::parseTuple( ast::CV::Qualifiers tq ) {
+	PRINT( std::cerr << "tuple..." << std::endl; )
+	std::vector<ast::ptr<ast::Type>> types;
+	size_t ncomponents;
+	if ( !extractNumber(ncomponents) ) return nullptr;
+	for ( size_t i = 0; i < ncomponents; ++i ) {
+		if ( done() ) return nullptr;
+		PRINT( std::cerr << "got ch: " << cur() << std::endl; )
+		ast::Type * t = parseType();
+		if ( !t ) return nullptr;
+		PRINT( std::cerr << "with type : " << t << std::endl; )
+		types.push_back( t );
+	}
+	return new ast::TupleType( std::move( types ), tq );
+}
+
+ast::Type * Demangler::parsePointer( ast::CV::Qualifiers tq ) {
+	PRINT( std::cerr << "pointer..." << std::endl; )
+	ast::Type * t = parseType();
+	if ( !t ) return nullptr;
+	return new ast::PointerType( t, tq );
+}
+
+ast::Type * Demangler::parseArray( ast::CV::Qualifiers tq ) {
+	PRINT( std::cerr << "array..." << std::endl; )
+	size_t length;
+	if ( !extractNumber(length) ) return nullptr;
+	ast::Type * t = parseType();
+	if ( !t ) return nullptr;
+	return new ast::ArrayType(
+		t,
+		ast::ConstantExpr::from_ulong( CodeLocation(), length ),
+		ast::FixedLen,
+		ast::DynamicDim,
+		tq );
+}
+
+ast::Type * Demangler::parseStruct( ast::CV::Qualifiers tq ) {
+	PRINT( std::cerr << "struct..." << std::endl; )
+	std::string name;
+	if ( !extractName(name) ) return nullptr;
+	return new ast::StructInstType( name, tq );
+}
+
+ast::Type * Demangler::parseUnion( ast::CV::Qualifiers tq ) {
+	PRINT( std::cerr << "union..." << std::endl; )
+	std::string name;
+	if ( !extractName(name) ) return nullptr;
+	return new ast::UnionInstType( name, tq );
+}
+
+ast::Type * Demangler::parseEnum( ast::CV::Qualifiers tq ) {
+	PRINT( std::cerr << "enum..." << std::endl; )
+	std::string name;
+	if ( !extractName(name) ) return nullptr;
+	return new ast::EnumInstType( name, tq );
+}
+
+ast::Type * Demangler::parseType( ast::CV::Qualifiers tq ) {
+	PRINT( std::cerr << "type..." << std::endl; )
+	std::string name;
+	if ( !extractName(name) ) return nullptr;
+	PRINT( std::cerr << "typename..." << name << std::endl; )
+	return new ast::TypeInstType( name, ast::TypeDecl::Dtype, tq );
+}
+
+ast::Type * Demangler::parseType() {
+	if (done()) return nullptr;
+
+	if (isPrefix(Encoding::forall)) {
+		PRINT( std::cerr << "polymorphic with..." << std::endl; )
+		size_t dcount, fcount, vcount, acount;
+		if ( !extractNumber(dcount) ) return nullptr;
+		PRINT( std::cerr << dcount << " dtypes" << std::endl; )
+		if ( !expect('_') ) return nullptr;
+		if ( !extractNumber(fcount) ) return nullptr;
+		PRINT( std::cerr << fcount << " ftypes" << std::endl; )
+		if ( !expect('_')) return nullptr;
+		if ( !extractNumber(vcount)) return nullptr;
+		PRINT( std::cerr << vcount << " ttypes" << std::endl; )
+		if ( !expect('_') ) return nullptr;
+		if ( !extractNumber(acount) ) return nullptr;
+		PRINT( std::cerr << acount << " assertions" << std::endl; )
+		if ( !expect('_') ) return nullptr;
+		for ( size_t i = 0 ; i < acount ; ++i ) {
+			// TODO: need to recursively parse assertions, but for now just return nullptr so that
+			// demangler does not crash if there are assertions
+			return nullptr;
+		}
+		if ( !expect('_') ) return nullptr;
+	}
+
+	ast::CV::Qualifiers tq;
+	while (true) {
+		auto qual = std::find_if(Encoding::qualifiers.begin(), Encoding::qualifiers.end(), [this](decltype(Encoding::qualifiers)::value_type val) {
+			return isPrefix(val.second);
+		});
+		if (qual == Encoding::qualifiers.end()) break;
+		tq |= qual->first;
+	}
+
+	// Find the correct type parser and then apply it.
+	auto iter = std::find_if(parsers.begin(), parsers.end(), [this](std::pair<std::string, Parser> & p) {
+		return isPrefix(p.first);
+	});
+	assertf(iter != parsers.end(), "Unhandled type letter: %c at index: %zd", cur(), index);
+	ast::Type * ret = iter->second(tq);
+	if ( !ret ) return nullptr;
+	return ret;
+}
+
+bool Demangler::parse( std::string & name, ast::Type *& type) {
+	if ( !stripMangleName(name) ) return false;
+	PRINT( std::cerr << "stripped name: " << name << std::endl; )
+	ast::Type * t = parseType();
+	if ( !t ) return false;
+	type = t;
+	return true;
+}
+
+std::string demangle( const std::string & mangleName ) {
+	using namespace CodeGen;
+	Demangler demangler( mangleName );
+	std::string name;
+	ast::Type * type = nullptr;
+	if ( !demangler.parse( name, type ) ) return mangleName;
+	ast::readonly<ast::Type> roType = type;
+	if ( auto info = operatorLookupByOutput( name ) ) name = info->inputName;
+	return genType( type, name, Options( false, false, false, false ) );
+}
+
+} // namespace
+
+} // namespace Mangle
+
+extern "C" {
+	char * cforall_demangle(const char * mangleName, int option __attribute__((unused))) {
+		const std::string & demangleName = Mangle::demangle(mangleName);
+		return strdup(demangleName.c_str());
+	}
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/SymTab/Demangle.h
===================================================================
--- src/SymTab/Demangle.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,28 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Demangle.h -- Convert a mangled name into a human readable name.
-//
-// Author           : Andrew Beach
-// Created On       : Fri May 13 10:11:00 2022
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Nov  6 15:48:00 2023
-// Update Count     : 1
-//
-
-#pragma once
-
-extern "C" {
-	/// Main interface to the demangler as a utility.
-	/// Caller must free the returned string.
-	char * cforall_demangle(const char *, int);
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/SymTab/Demangle.hpp
===================================================================
--- src/SymTab/Demangle.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/SymTab/Demangle.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,28 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Demangle.hpp -- Convert a mangled name into a human readable name.
+//
+// Author           : Andrew Beach
+// Created On       : Fri May 13 10:11:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Nov  6 15:48:00 2023
+// Update Count     : 1
+//
+
+#pragma once
+
+extern "C" {
+	/// Main interface to the demangler as a utility.
+	/// Caller must free the returned string.
+	char * cforall_demangle(const char *, int);
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/SymTab/FixFunction.cc
===================================================================
--- src/SymTab/FixFunction.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,93 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FixFunction.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 16:19:49 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Jul 12 14:28:00 2022
-// Update Count     : 7
-//
-
-#include "FixFunction.h"
-
-#include <list>                   // for list
-
-#include "AST/Decl.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Type.hpp"
-#include "Common/utility.h"       // for copy
-
-namespace SymTab {
-
-namespace {
-
-struct FixFunction final : public ast::WithShortCircuiting {
-	bool isVoid = false;
-
-	void previsit( const ast::FunctionDecl * ) { visit_children = false; }
-
-	const ast::DeclWithType * postvisit( const ast::FunctionDecl * func ) {
-		// Cannot handle cases with asserions.
-		assert( func->assertions.empty() );
-		return new ast::ObjectDecl{
-			func->location, func->name, new ast::PointerType( func->type ), nullptr,
-			func->storage, func->linkage, nullptr, copy( func->attributes ) };
-	}
-
-	void previsit( const ast::ArrayType * ) { visit_children = false; }
-
-	const ast::Type * postvisit( const ast::ArrayType * array ) {
-		return new ast::PointerType{
-			array->base, array->dimension, array->isVarLen, array->isStatic,
-			array->qualifiers };
-	}
-
-	void previsit( const ast::FunctionType * ) { visit_children = false; }
-
-	const ast::Type * postvisit( const ast::FunctionType * type ) {
-		return new ast::PointerType( type );
-	}
-
-	void previsit( const ast::VoidType * ) { isVoid = true; }
-
-	void previsit( const ast::BasicType * ) { visit_children = false; }
-	void previsit( const ast::PointerType * ) { visit_children = false; }
-	void previsit( const ast::StructInstType * ) { visit_children = false; }
-	void previsit( const ast::UnionInstType * ) { visit_children = false; }
-	void previsit( const ast::EnumInstType * ) { visit_children = false; }
-	void previsit( const ast::TraitInstType * ) { visit_children = false; }
-	void previsit( const ast::TypeInstType * ) { visit_children = false; }
-	void previsit( const ast::TupleType * ) { visit_children = false; }
-	void previsit( const ast::VarArgsType * ) { visit_children = false; }
-	void previsit( const ast::ZeroType * ) { visit_children = false; }
-	void previsit( const ast::OneType * ) { visit_children = false; }
-};
-
-} // anonymous namespace
-
-const ast::DeclWithType * fixFunction( const ast::DeclWithType * dwt, bool & isVoid ) {
-	ast::Pass< FixFunction > fixer;
-	dwt = dwt->accept( fixer );
-	isVoid |= fixer.core.isVoid;
-	return dwt;
-}
-
-const ast::Type * fixFunction( const ast::Type * type, bool & isVoid ) {
-	ast::Pass< FixFunction > fixer;
-	type = type->accept( fixer );
-	isVoid |= fixer.core.isVoid;
-	return type;
-}
-
-} // namespace SymTab
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/SymTab/FixFunction.cpp
===================================================================
--- src/SymTab/FixFunction.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/SymTab/FixFunction.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,93 @@
+//
+// 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.
+//
+// FixFunction.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 16:19:49 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Jul 12 14:28:00 2022
+// Update Count     : 7
+//
+
+#include "FixFunction.hpp"
+
+#include <list>                   // for list
+
+#include "AST/Decl.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Type.hpp"
+#include "Common/Utility.hpp"     // for copy
+
+namespace SymTab {
+
+namespace {
+
+struct FixFunction final : public ast::WithShortCircuiting {
+	bool isVoid = false;
+
+	void previsit( const ast::FunctionDecl * ) { visit_children = false; }
+
+	const ast::DeclWithType * postvisit( const ast::FunctionDecl * func ) {
+		// Cannot handle cases with asserions.
+		assert( func->assertions.empty() );
+		return new ast::ObjectDecl{
+			func->location, func->name, new ast::PointerType( func->type ), nullptr,
+			func->storage, func->linkage, nullptr, copy( func->attributes ) };
+	}
+
+	void previsit( const ast::ArrayType * ) { visit_children = false; }
+
+	const ast::Type * postvisit( const ast::ArrayType * array ) {
+		return new ast::PointerType{
+			array->base, array->dimension, array->isVarLen, array->isStatic,
+			array->qualifiers };
+	}
+
+	void previsit( const ast::FunctionType * ) { visit_children = false; }
+
+	const ast::Type * postvisit( const ast::FunctionType * type ) {
+		return new ast::PointerType( type );
+	}
+
+	void previsit( const ast::VoidType * ) { isVoid = true; }
+
+	void previsit( const ast::BasicType * ) { visit_children = false; }
+	void previsit( const ast::PointerType * ) { visit_children = false; }
+	void previsit( const ast::StructInstType * ) { visit_children = false; }
+	void previsit( const ast::UnionInstType * ) { visit_children = false; }
+	void previsit( const ast::EnumInstType * ) { visit_children = false; }
+	void previsit( const ast::TraitInstType * ) { visit_children = false; }
+	void previsit( const ast::TypeInstType * ) { visit_children = false; }
+	void previsit( const ast::TupleType * ) { visit_children = false; }
+	void previsit( const ast::VarArgsType * ) { visit_children = false; }
+	void previsit( const ast::ZeroType * ) { visit_children = false; }
+	void previsit( const ast::OneType * ) { visit_children = false; }
+};
+
+} // anonymous namespace
+
+const ast::DeclWithType * fixFunction( const ast::DeclWithType * dwt, bool & isVoid ) {
+	ast::Pass< FixFunction > fixer;
+	dwt = dwt->accept( fixer );
+	isVoid |= fixer.core.isVoid;
+	return dwt;
+}
+
+const ast::Type * fixFunction( const ast::Type * type, bool & isVoid ) {
+	ast::Pass< FixFunction > fixer;
+	type = type->accept( fixer );
+	isVoid |= fixer.core.isVoid;
+	return type;
+}
+
+} // namespace SymTab
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/SymTab/FixFunction.h
===================================================================
--- src/SymTab/FixFunction.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,36 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FixFunction.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 17:02:08 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Tue Jul 12 14:19:00 2022
-// Update Count     : 5
-//
-
-#pragma once
-
-namespace ast {
-	class DeclWithType;
-	class Type;
-}
-
-namespace SymTab {
-
-/// Returns declaration with function and array types replaced by equivalent pointer types.
-/// Sets isVoid to true if type is void.
-const ast::DeclWithType * fixFunction( const ast::DeclWithType * dwt, bool & isVoid );
-const ast::Type * fixFunction( const ast::Type * type, bool & isVoid );
-
-} // namespace SymTab
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/SymTab/FixFunction.hpp
===================================================================
--- src/SymTab/FixFunction.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/SymTab/FixFunction.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,36 @@
+//
+// 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.
+//
+// FixFunction.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 17:02:08 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Tue Jul 12 14:19:00 2022
+// Update Count     : 5
+//
+
+#pragma once
+
+namespace ast {
+	class DeclWithType;
+	class Type;
+}
+
+namespace SymTab {
+
+/// Returns declaration with function and array types replaced by equivalent pointer types.
+/// Sets isVoid to true if type is void.
+const ast::DeclWithType * fixFunction( const ast::DeclWithType * dwt, bool & isVoid );
+const ast::Type * fixFunction( const ast::Type * type, bool & isVoid );
+
+} // namespace SymTab
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/SymTab/GenImplicitCall.cpp
===================================================================
--- src/SymTab/GenImplicitCall.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/SymTab/GenImplicitCall.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -23,7 +23,7 @@
 #include "AST/Stmt.hpp"                  // for ExprStmt
 #include "AST/Type.hpp"                  // for ArrayType, BasicType, ...
-#include "CodeGen/OperatorTable.h"       // for isCtorDtor
-#include "Common/UniqueName.h"           // for UniqueName
-#include "Common/utility.h"              // for splice
+#include "CodeGen/OperatorTable.hpp"     // for isCtorDtor
+#include "Common/UniqueName.hpp"         // for UniqueName
+#include "Common/Utility.hpp"            // for splice
 
 namespace SymTab {
Index: src/SymTab/GenImplicitCall.hpp
===================================================================
--- src/SymTab/GenImplicitCall.hpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/SymTab/GenImplicitCall.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,5 +16,5 @@
 #pragma once
 
-#include "InitTweak/InitTweak.h"  // for InitExpander
+#include "InitTweak/InitTweak.hpp"  // for InitExpander
 
 namespace SymTab {
Index: src/SymTab/Mangler.cc
===================================================================
--- src/SymTab/Mangler.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,374 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Mangler.cc --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 21:40:29 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Oct 21 16:18:00 2022
-// Update Count     : 75
-//
-#include "Mangler.h"
-
-#include <algorithm>                     // for copy, transform
-#include <cassert>                       // for assert, assertf
-#include <functional>                    // for const_mem_fun_t, mem_fun
-#include <iterator>                      // for ostream_iterator, back_insert_ite...
-#include <list>                          // for _List_iterator, list, _List_const...
-#include <string>                        // for string, char_traits, operator<<
-
-#include "AST/Pass.hpp"
-#include "CodeGen/OperatorTable.h"       // for OperatorInfo, operatorLookup
-#include "Common/ToString.hpp"           // for toCString
-#include "Common/SemanticError.h"        // for SemanticError
-
-namespace Mangle {
-
-namespace {
-
-/// Mangles names to a unique C identifier.
-struct Mangler : public ast::WithShortCircuiting, public ast::WithVisitorRef<Mangler>, public ast::WithGuards {
-	Mangler( Mangle::Mode mode );
-	Mangler( const Mangler & ) = delete;
-
-	void previsit( const ast::Node * ) { visit_children = false; }
-
-	void postvisit( const ast::ObjectDecl * declaration );
-	void postvisit( const ast::FunctionDecl * declaration );
-	void postvisit( const ast::TypeDecl * declaration );
-
-	void postvisit( const ast::VoidType * voidType );
-	void postvisit( const ast::BasicType * basicType );
-	void postvisit( const ast::PointerType * pointerType );
-	void postvisit( const ast::ArrayType * arrayType );
-	void postvisit( const ast::ReferenceType * refType );
-	void postvisit( const ast::FunctionType * functionType );
-	void postvisit( const ast::StructInstType * aggregateUseType );
-	void postvisit( const ast::UnionInstType * aggregateUseType );
-	void postvisit( const ast::EnumInstType * aggregateUseType );
-	void postvisit( const ast::TypeInstType * aggregateUseType );
-	void postvisit( const ast::TraitInstType * inst );
-	void postvisit( const ast::TupleType * tupleType );
-	void postvisit( const ast::VarArgsType * varArgsType );
-	void postvisit( const ast::ZeroType * zeroType );
-	void postvisit( const ast::OneType * oneType );
-	void postvisit( const ast::QualifiedType * qualType );
-	void postvisit( const ast::EnumAttrType * posType );
-
-	/// The result is the current constructed mangled name.
-	std::string result() const { return mangleName; }
-private:
-	std::string mangleName;         ///< Mangled name being constructed
-	typedef std::map< std::string, std::pair< int, int > > VarMapType;
-	VarMapType varNums;             ///< Map of type variables to indices
-	int nextVarNum;                 ///< Next type variable index
-	bool isTopLevel;                ///< Is the Mangler at the top level
-	bool mangleOverridable;         ///< Specially mangle overridable built-in methods
-	bool typeMode;                  ///< Produce a unique mangled name for a type
-	bool mangleGenericParams;       ///< Include generic parameters in name mangling if true
-	bool inFunctionType = false;    ///< Include type qualifiers if false.
-	bool inQualifiedType = false;   ///< Add start/end delimiters around qualified type
-
-private:
-	Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams,
-		int nextVarNum, const VarMapType& varNums );
-	friend class ast::Pass<Mangler>;
-
-private:
-	void mangleDecl( const ast::DeclWithType *declaration );
-	void mangleRef( const ast::BaseInstType *refType, const std::string & prefix );
-
-	void printQualifiers( const ast::Type *type );
-}; // Mangler
-
-Mangler::Mangler( Mangle::Mode mode )
-	: nextVarNum( 0 ), isTopLevel( true ),
-	mangleOverridable  ( ! mode.no_overrideable   ),
-	typeMode           (   mode.type              ),
-	mangleGenericParams( ! mode.no_generic_params ) {}
-
-Mangler::Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams,
-	int nextVarNum, const VarMapType& varNums )
-	: varNums( varNums ), nextVarNum( nextVarNum ), isTopLevel( false ),
-	mangleOverridable( mangleOverridable ), typeMode( typeMode ),
-	mangleGenericParams( mangleGenericParams ) {}
-
-void Mangler::mangleDecl( const ast::DeclWithType * decl ) {
-	bool wasTopLevel = isTopLevel;
-	if ( isTopLevel ) {
-		varNums.clear();
-		nextVarNum = 0;
-		isTopLevel = false;
-	}
-	mangleName += Encoding::manglePrefix;
-	if ( auto opInfo = CodeGen::operatorLookup( decl->name ) ) {
-		mangleName += std::to_string( opInfo->outputName.size() ) + opInfo->outputName;
-	} else {
-		mangleName += std::to_string( decl->name.size() ) + decl->name;
-	}
-	decl->get_type()->accept( *visitor );
-	if ( mangleOverridable && decl->linkage.is_overrideable ) {
-		// want to be able to override autogenerated and intrinsic routines,
-		// so they need a different name mangling
-		if ( decl->linkage == ast::Linkage::AutoGen ) {
-			mangleName += Encoding::autogen;
-		} else if ( decl->linkage == ast::Linkage::Intrinsic ) {
-			mangleName += Encoding::intrinsic;
-		} else {
-			// if we add another kind of overridable function, this has to change
-			assert( false && "unknown overrideable linkage" );
-		}
-	}
-	isTopLevel = wasTopLevel;
-}
-
-void Mangler::postvisit( const ast::ObjectDecl * decl ) {
-	mangleDecl( decl );
-}
-
-void Mangler::postvisit( const ast::FunctionDecl * decl ) {
-	mangleDecl( decl );
-}
-
-void Mangler::postvisit( const ast::VoidType * voidType ) {
-	printQualifiers( voidType );
-	mangleName += Encoding::void_t;
-}
-
-void Mangler::postvisit( const ast::BasicType * basicType ) {
-	printQualifiers( basicType );
-	assertf( basicType->kind < ast::BasicKind::NUMBER_OF_BASIC_TYPES, "Unhandled basic type: %d", basicType->kind );
-	mangleName += Encoding::basicTypes[ basicType->kind ];
-}
-
-void Mangler::postvisit( const ast::PointerType * pointerType ) {
-	printQualifiers( pointerType );
-	// Mangle void (*f)() and void f() to the same name to prevent overloading on functions and function pointers.
-	if ( !pointerType->base.as<ast::FunctionType>() ) mangleName += Encoding::pointer;
-	maybe_accept( pointerType->base.get(), *visitor );
-}
-
-void Mangler::postvisit( const ast::ArrayType * arrayType ) {
-	// TODO: encode dimension
-	printQualifiers( arrayType );
-	mangleName += Encoding::array + "0";
-	arrayType->base->accept( *visitor );
-}
-
-void Mangler::postvisit( const ast::ReferenceType * refType ) {
-	// Don't print prefix (e.g. 'R') for reference types so that references and non-references do not overload.
-	// Further, do not print the qualifiers for a reference type (but do run printQualifers because of TypeDecls, etc.),
-	// by pretending every reference type is a function parameter.
-	GuardValue( inFunctionType ) = true;
-	printQualifiers( refType );
-	refType->base->accept( *visitor );
-}
-
-void Mangler::postvisit( const ast::FunctionType * functionType ) {
-	printQualifiers( functionType );
-	mangleName += Encoding::function;
-	// Turn on inFunctionType so that printQualifiers does not print most qualifiers for function parameters,
-	// since qualifiers on outermost parameter type do not differentiate function types, e.g.,
-	// void (*)(const int) and void (*)(int) are the same type, but void (*)(const int *) and void (*)(int *) are different.
-	GuardValue( inFunctionType ) = true;
-	if (functionType->returns.empty()) mangleName += Encoding::void_t;
-	else accept_each( functionType->returns, *visitor );
-	mangleName += "_";
-	accept_each( functionType->params, *visitor );
-	mangleName += "_";
-}
-
-void Mangler::mangleRef(
-		const ast::BaseInstType * refType, const std::string & prefix ) {
-	printQualifiers( refType );
-
-	mangleName += prefix + std::to_string( refType->name.length() ) + refType->name;
-
-	if ( mangleGenericParams && ! refType->params.empty() ) {
-		mangleName += "_";
-		for ( const ast::Expr * param : refType->params ) {
-			auto paramType = dynamic_cast< const ast::TypeExpr * >( param );
-			assertf(paramType, "Aggregate parameters should be type expressions: %s", toCString(param));
-			paramType->type->accept( *visitor );
-		}
-		mangleName += "_";
-	}
-}
-
-void Mangler::postvisit( const ast::StructInstType * aggregateUseType ) {
-	mangleRef( aggregateUseType, Encoding::struct_t );
-}
-
-void Mangler::postvisit( const ast::UnionInstType * aggregateUseType ) {
-	mangleRef( aggregateUseType, Encoding::union_t );
-}
-
-void Mangler::postvisit( const ast::EnumInstType * aggregateUseType ) {
-	mangleRef( aggregateUseType, Encoding::enum_t );
-}
-
-void Mangler::postvisit( const ast::TypeInstType * typeInst ) {
-	VarMapType::iterator varNum = varNums.find( typeInst->name );
-	if ( varNum == varNums.end() ) {
-		mangleRef( typeInst, Encoding::type );
-	} else {
-		printQualifiers( typeInst );
-		// Note: Can't use name here, since type variable names do not actually disambiguate a function, e.g.
-		//   forall(dtype T) void f(T);
-		//   forall(dtype S) void f(S);
-		// are equivalent and should mangle the same way. This is accomplished by numbering the type variables when they
-		// are first found and prefixing with the appropriate encoding for the type class.
-		assertf( varNum->second.second < ast::TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", varNum->second.second );
-		mangleName += Encoding::typeVariables[varNum->second.second] + std::to_string( varNum->second.first );
-	}
-}
-
-void Mangler::postvisit( const ast::TraitInstType * inst ) {
-	printQualifiers( inst );
-	mangleName += std::to_string( inst->name.size() ) + inst->name;
-}
-
-void Mangler::postvisit( const ast::TupleType * tupleType ) {
-	printQualifiers( tupleType );
-	mangleName += Encoding::tuple + std::to_string( tupleType->types.size() );
-	accept_each( tupleType->types, *visitor );
-}
-
-void Mangler::postvisit( const ast::VarArgsType * varArgsType ) {
-	printQualifiers( varArgsType );
-	static const std::string vargs = "__builtin_va_list";
-	mangleName += Encoding::type + std::to_string( vargs.size() ) + vargs;
-}
-
-void Mangler::postvisit( const ast::ZeroType * ) {
-	mangleName += Encoding::zero;
-}
-
-void Mangler::postvisit( const ast::OneType * ) {
-	mangleName += Encoding::one;
-}
-
-void Mangler::postvisit( const ast::QualifiedType * qualType ) {
-	bool inqual = inQualifiedType;
-	if ( !inqual ) {
-		// N marks the start of a qualified type.
-		inQualifiedType = true;
-		mangleName += Encoding::qualifiedTypeStart;
-	}
-	qualType->parent->accept( *visitor );
-	qualType->child->accept( *visitor );
-	if ( !inqual ) {
-		// E marks the end of a qualified type.
-		inQualifiedType = false;
-		mangleName += Encoding::qualifiedTypeEnd;
-	}
-}
-
-void Mangler::postvisit( const ast::TypeDecl * decl ) {
-	// TODO: is there any case where mangling a TypeDecl makes sense? If so, this code needs to be
-	// fixed to ensure that two TypeDecls mangle to the same name when they are the same type and vice versa.
-	// Note: The current scheme may already work correctly for this case, I have not thought about this deeply
-	// and the case has not yet come up in practice. Alternatively, if not then this code can be removed
-	// aside from the assert false.
-	assertf(false, "Mangler should not visit typedecl: %s", toCString(decl));
-	assertf( decl->kind < ast::TypeDecl::Kind::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", decl->kind );
-	mangleName += Encoding::typeVariables[ decl->kind ] + std::to_string( decl->name.length() ) + decl->name;
-}
-
-void Mangler::postvisit( const ast::EnumAttrType * enumAttr ) {
-	postvisit( enumAttr->instance );
-	// mangleName += "_pos";
-	switch ( enumAttr->attr )
-	{
-		case ast::EnumAttribute::Label:
-			mangleName += "_label_";
-			break;
-		case ast::EnumAttribute::Posn:
-			mangleName += "_posn_";
-			break;
-		case ast::EnumAttribute::Value:
-			mangleName += "_value_";
-			break;
-	}
-
-}
-
-// For debugging:
-__attribute__((unused)) void printVarMap( const std::map< std::string, std::pair< int, int > > &varMap, std::ostream &os ) {
-	for ( std::map< std::string, std::pair< int, int > >::const_iterator i = varMap.begin(); i != varMap.end(); ++i ) {
-		os << i->first << "(" << i->second.first << "/" << i->second.second << ")" << std::endl;
-	}
-}
-
-void Mangler::printQualifiers( const ast::Type * type ) {
-	// Skip if not including qualifiers:
-	if ( typeMode ) return;
-	auto funcType = dynamic_cast<const ast::FunctionType *>( type );
-	if ( funcType && !funcType->forall.empty() ) {
-		std::list< std::string > assertionNames;
-		int dcount = 0, fcount = 0, vcount = 0, acount = 0;
-		mangleName += Encoding::forall;
-		for ( auto & decl : funcType->forall ) {
-			switch ( decl->kind ) {
-			case ast::TypeDecl::Dtype:
-				dcount++;
-				break;
-			case ast::TypeDecl::Ftype:
-				fcount++;
-				break;
-			case ast::TypeDecl::Ttype:
-				vcount++;
-				break;
-			default:
-				assertf( false, "unimplemented kind for type variable %s", Encoding::typeVariables[decl->kind].c_str() );
-			}
-			varNums[ decl->name ] = std::make_pair( nextVarNum, (int)decl->kind );
-		}
-		for ( auto & assert : funcType->assertions ) {
-			assertionNames.push_back( ast::Pass<Mangler>::read(
-				assert->var.get(),
-				mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums ) );
-			acount++;
-		}
-		mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_";
-		for ( const auto & a : assertionNames ) mangleName += a;
-		mangleName += "_";
-	}
-	if ( !inFunctionType ) {
-		// These qualifiers do not distinguish the outermost type of a function parameter.
-		if ( type->is_const() ) {
-			mangleName += Encoding::qualifiers.at( ast::CV::Const );
-		}
-		if ( type->is_volatile() ) {
-			mangleName += Encoding::qualifiers.at( ast::CV::Volatile );
-		}
-		if ( type->is_atomic() ) {
-			mangleName += Encoding::qualifiers.at( ast::CV::Atomic );
-		}
-	}
-	if ( type->is_mutex() ) {
-		mangleName += Encoding::qualifiers.at( ast::CV::Mutex );
-	}
-	if ( inFunctionType ) {
-		// Turn off inFunctionType so that types can be differentiated for nested qualifiers.
-		GuardValue( inFunctionType ) = false;
-	}
-}
-
-} // namespace
-
-std::string mangle( const ast::Node * decl, Mangle::Mode mode ) {
-	return ast::Pass<Mangler>::read( decl, mode );
-}
-
-} // namespace Mangle
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/SymTab/Mangler.cpp
===================================================================
--- src/SymTab/Mangler.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/SymTab/Mangler.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,374 @@
+//
+// 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.
+//
+// Mangler.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 21:40:29 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Oct 21 16:18:00 2022
+// Update Count     : 75
+//
+#include "Mangler.hpp"
+
+#include <algorithm>                     // for copy, transform
+#include <cassert>                       // for assert, assertf
+#include <functional>                    // for const_mem_fun_t, mem_fun
+#include <iterator>                      // for ostream_iterator, back_insert_ite...
+#include <list>                          // for _List_iterator, list, _List_const...
+#include <string>                        // for string, char_traits, operator<<
+
+#include "AST/Pass.hpp"
+#include "CodeGen/OperatorTable.hpp"     // for OperatorInfo, operatorLookup
+#include "Common/ToString.hpp"           // for toCString
+#include "Common/SemanticError.hpp"      // for SemanticError
+
+namespace Mangle {
+
+namespace {
+
+/// Mangles names to a unique C identifier.
+struct Mangler : public ast::WithShortCircuiting, public ast::WithVisitorRef<Mangler>, public ast::WithGuards {
+	Mangler( Mangle::Mode mode );
+	Mangler( const Mangler & ) = delete;
+
+	void previsit( const ast::Node * ) { visit_children = false; }
+
+	void postvisit( const ast::ObjectDecl * declaration );
+	void postvisit( const ast::FunctionDecl * declaration );
+	void postvisit( const ast::TypeDecl * declaration );
+
+	void postvisit( const ast::VoidType * voidType );
+	void postvisit( const ast::BasicType * basicType );
+	void postvisit( const ast::PointerType * pointerType );
+	void postvisit( const ast::ArrayType * arrayType );
+	void postvisit( const ast::ReferenceType * refType );
+	void postvisit( const ast::FunctionType * functionType );
+	void postvisit( const ast::StructInstType * aggregateUseType );
+	void postvisit( const ast::UnionInstType * aggregateUseType );
+	void postvisit( const ast::EnumInstType * aggregateUseType );
+	void postvisit( const ast::TypeInstType * aggregateUseType );
+	void postvisit( const ast::TraitInstType * inst );
+	void postvisit( const ast::TupleType * tupleType );
+	void postvisit( const ast::VarArgsType * varArgsType );
+	void postvisit( const ast::ZeroType * zeroType );
+	void postvisit( const ast::OneType * oneType );
+	void postvisit( const ast::QualifiedType * qualType );
+	void postvisit( const ast::EnumAttrType * posType );
+
+	/// The result is the current constructed mangled name.
+	std::string result() const { return mangleName; }
+private:
+	std::string mangleName;         ///< Mangled name being constructed
+	typedef std::map< std::string, std::pair< int, int > > VarMapType;
+	VarMapType varNums;             ///< Map of type variables to indices
+	int nextVarNum;                 ///< Next type variable index
+	bool isTopLevel;                ///< Is the Mangler at the top level
+	bool mangleOverridable;         ///< Specially mangle overridable built-in methods
+	bool typeMode;                  ///< Produce a unique mangled name for a type
+	bool mangleGenericParams;       ///< Include generic parameters in name mangling if true
+	bool inFunctionType = false;    ///< Include type qualifiers if false.
+	bool inQualifiedType = false;   ///< Add start/end delimiters around qualified type
+
+private:
+	Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams,
+		int nextVarNum, const VarMapType& varNums );
+	friend class ast::Pass<Mangler>;
+
+private:
+	void mangleDecl( const ast::DeclWithType *declaration );
+	void mangleRef( const ast::BaseInstType *refType, const std::string & prefix );
+
+	void printQualifiers( const ast::Type *type );
+}; // Mangler
+
+Mangler::Mangler( Mangle::Mode mode )
+	: nextVarNum( 0 ), isTopLevel( true ),
+	mangleOverridable  ( ! mode.no_overrideable   ),
+	typeMode           (   mode.type              ),
+	mangleGenericParams( ! mode.no_generic_params ) {}
+
+Mangler::Mangler( bool mangleOverridable, bool typeMode, bool mangleGenericParams,
+	int nextVarNum, const VarMapType& varNums )
+	: varNums( varNums ), nextVarNum( nextVarNum ), isTopLevel( false ),
+	mangleOverridable( mangleOverridable ), typeMode( typeMode ),
+	mangleGenericParams( mangleGenericParams ) {}
+
+void Mangler::mangleDecl( const ast::DeclWithType * decl ) {
+	bool wasTopLevel = isTopLevel;
+	if ( isTopLevel ) {
+		varNums.clear();
+		nextVarNum = 0;
+		isTopLevel = false;
+	}
+	mangleName += Encoding::manglePrefix;
+	if ( auto opInfo = CodeGen::operatorLookup( decl->name ) ) {
+		mangleName += std::to_string( opInfo->outputName.size() ) + opInfo->outputName;
+	} else {
+		mangleName += std::to_string( decl->name.size() ) + decl->name;
+	}
+	decl->get_type()->accept( *visitor );
+	if ( mangleOverridable && decl->linkage.is_overrideable ) {
+		// want to be able to override autogenerated and intrinsic routines,
+		// so they need a different name mangling
+		if ( decl->linkage == ast::Linkage::AutoGen ) {
+			mangleName += Encoding::autogen;
+		} else if ( decl->linkage == ast::Linkage::Intrinsic ) {
+			mangleName += Encoding::intrinsic;
+		} else {
+			// if we add another kind of overridable function, this has to change
+			assert( false && "unknown overrideable linkage" );
+		}
+	}
+	isTopLevel = wasTopLevel;
+}
+
+void Mangler::postvisit( const ast::ObjectDecl * decl ) {
+	mangleDecl( decl );
+}
+
+void Mangler::postvisit( const ast::FunctionDecl * decl ) {
+	mangleDecl( decl );
+}
+
+void Mangler::postvisit( const ast::VoidType * voidType ) {
+	printQualifiers( voidType );
+	mangleName += Encoding::void_t;
+}
+
+void Mangler::postvisit( const ast::BasicType * basicType ) {
+	printQualifiers( basicType );
+	assertf( basicType->kind < ast::BasicKind::NUMBER_OF_BASIC_TYPES, "Unhandled basic type: %d", basicType->kind );
+	mangleName += Encoding::basicTypes[ basicType->kind ];
+}
+
+void Mangler::postvisit( const ast::PointerType * pointerType ) {
+	printQualifiers( pointerType );
+	// Mangle void (*f)() and void f() to the same name to prevent overloading on functions and function pointers.
+	if ( !pointerType->base.as<ast::FunctionType>() ) mangleName += Encoding::pointer;
+	maybe_accept( pointerType->base.get(), *visitor );
+}
+
+void Mangler::postvisit( const ast::ArrayType * arrayType ) {
+	// TODO: encode dimension
+	printQualifiers( arrayType );
+	mangleName += Encoding::array + "0";
+	arrayType->base->accept( *visitor );
+}
+
+void Mangler::postvisit( const ast::ReferenceType * refType ) {
+	// Don't print prefix (e.g. 'R') for reference types so that references and non-references do not overload.
+	// Further, do not print the qualifiers for a reference type (but do run printQualifers because of TypeDecls, etc.),
+	// by pretending every reference type is a function parameter.
+	GuardValue( inFunctionType ) = true;
+	printQualifiers( refType );
+	refType->base->accept( *visitor );
+}
+
+void Mangler::postvisit( const ast::FunctionType * functionType ) {
+	printQualifiers( functionType );
+	mangleName += Encoding::function;
+	// Turn on inFunctionType so that printQualifiers does not print most qualifiers for function parameters,
+	// since qualifiers on outermost parameter type do not differentiate function types, e.g.,
+	// void (*)(const int) and void (*)(int) are the same type, but void (*)(const int *) and void (*)(int *) are different.
+	GuardValue( inFunctionType ) = true;
+	if (functionType->returns.empty()) mangleName += Encoding::void_t;
+	else accept_each( functionType->returns, *visitor );
+	mangleName += "_";
+	accept_each( functionType->params, *visitor );
+	mangleName += "_";
+}
+
+void Mangler::mangleRef(
+		const ast::BaseInstType * refType, const std::string & prefix ) {
+	printQualifiers( refType );
+
+	mangleName += prefix + std::to_string( refType->name.length() ) + refType->name;
+
+	if ( mangleGenericParams && ! refType->params.empty() ) {
+		mangleName += "_";
+		for ( const ast::Expr * param : refType->params ) {
+			auto paramType = dynamic_cast< const ast::TypeExpr * >( param );
+			assertf(paramType, "Aggregate parameters should be type expressions: %s", toCString(param));
+			paramType->type->accept( *visitor );
+		}
+		mangleName += "_";
+	}
+}
+
+void Mangler::postvisit( const ast::StructInstType * aggregateUseType ) {
+	mangleRef( aggregateUseType, Encoding::struct_t );
+}
+
+void Mangler::postvisit( const ast::UnionInstType * aggregateUseType ) {
+	mangleRef( aggregateUseType, Encoding::union_t );
+}
+
+void Mangler::postvisit( const ast::EnumInstType * aggregateUseType ) {
+	mangleRef( aggregateUseType, Encoding::enum_t );
+}
+
+void Mangler::postvisit( const ast::TypeInstType * typeInst ) {
+	VarMapType::iterator varNum = varNums.find( typeInst->name );
+	if ( varNum == varNums.end() ) {
+		mangleRef( typeInst, Encoding::type );
+	} else {
+		printQualifiers( typeInst );
+		// Note: Can't use name here, since type variable names do not actually disambiguate a function, e.g.
+		//   forall(dtype T) void f(T);
+		//   forall(dtype S) void f(S);
+		// are equivalent and should mangle the same way. This is accomplished by numbering the type variables when they
+		// are first found and prefixing with the appropriate encoding for the type class.
+		assertf( varNum->second.second < ast::TypeDecl::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", varNum->second.second );
+		mangleName += Encoding::typeVariables[varNum->second.second] + std::to_string( varNum->second.first );
+	}
+}
+
+void Mangler::postvisit( const ast::TraitInstType * inst ) {
+	printQualifiers( inst );
+	mangleName += std::to_string( inst->name.size() ) + inst->name;
+}
+
+void Mangler::postvisit( const ast::TupleType * tupleType ) {
+	printQualifiers( tupleType );
+	mangleName += Encoding::tuple + std::to_string( tupleType->types.size() );
+	accept_each( tupleType->types, *visitor );
+}
+
+void Mangler::postvisit( const ast::VarArgsType * varArgsType ) {
+	printQualifiers( varArgsType );
+	static const std::string vargs = "__builtin_va_list";
+	mangleName += Encoding::type + std::to_string( vargs.size() ) + vargs;
+}
+
+void Mangler::postvisit( const ast::ZeroType * ) {
+	mangleName += Encoding::zero;
+}
+
+void Mangler::postvisit( const ast::OneType * ) {
+	mangleName += Encoding::one;
+}
+
+void Mangler::postvisit( const ast::QualifiedType * qualType ) {
+	bool inqual = inQualifiedType;
+	if ( !inqual ) {
+		// N marks the start of a qualified type.
+		inQualifiedType = true;
+		mangleName += Encoding::qualifiedTypeStart;
+	}
+	qualType->parent->accept( *visitor );
+	qualType->child->accept( *visitor );
+	if ( !inqual ) {
+		// E marks the end of a qualified type.
+		inQualifiedType = false;
+		mangleName += Encoding::qualifiedTypeEnd;
+	}
+}
+
+void Mangler::postvisit( const ast::TypeDecl * decl ) {
+	// TODO: is there any case where mangling a TypeDecl makes sense? If so, this code needs to be
+	// fixed to ensure that two TypeDecls mangle to the same name when they are the same type and vice versa.
+	// Note: The current scheme may already work correctly for this case, I have not thought about this deeply
+	// and the case has not yet come up in practice. Alternatively, if not then this code can be removed
+	// aside from the assert false.
+	assertf(false, "Mangler should not visit typedecl: %s", toCString(decl));
+	assertf( decl->kind < ast::TypeDecl::Kind::NUMBER_OF_KINDS, "Unhandled type variable kind: %d", decl->kind );
+	mangleName += Encoding::typeVariables[ decl->kind ] + std::to_string( decl->name.length() ) + decl->name;
+}
+
+void Mangler::postvisit( const ast::EnumAttrType * enumAttr ) {
+	postvisit( enumAttr->instance );
+	// mangleName += "_pos";
+	switch ( enumAttr->attr )
+	{
+		case ast::EnumAttribute::Label:
+			mangleName += "_label_";
+			break;
+		case ast::EnumAttribute::Posn:
+			mangleName += "_posn_";
+			break;
+		case ast::EnumAttribute::Value:
+			mangleName += "_value_";
+			break;
+	}
+
+}
+
+// For debugging:
+__attribute__((unused)) void printVarMap( const std::map< std::string, std::pair< int, int > > &varMap, std::ostream &os ) {
+	for ( std::map< std::string, std::pair< int, int > >::const_iterator i = varMap.begin(); i != varMap.end(); ++i ) {
+		os << i->first << "(" << i->second.first << "/" << i->second.second << ")" << std::endl;
+	}
+}
+
+void Mangler::printQualifiers( const ast::Type * type ) {
+	// Skip if not including qualifiers:
+	if ( typeMode ) return;
+	auto funcType = dynamic_cast<const ast::FunctionType *>( type );
+	if ( funcType && !funcType->forall.empty() ) {
+		std::list< std::string > assertionNames;
+		int dcount = 0, fcount = 0, vcount = 0, acount = 0;
+		mangleName += Encoding::forall;
+		for ( auto & decl : funcType->forall ) {
+			switch ( decl->kind ) {
+			case ast::TypeDecl::Dtype:
+				dcount++;
+				break;
+			case ast::TypeDecl::Ftype:
+				fcount++;
+				break;
+			case ast::TypeDecl::Ttype:
+				vcount++;
+				break;
+			default:
+				assertf( false, "unimplemented kind for type variable %s", Encoding::typeVariables[decl->kind].c_str() );
+			}
+			varNums[ decl->name ] = std::make_pair( nextVarNum, (int)decl->kind );
+		}
+		for ( auto & assert : funcType->assertions ) {
+			assertionNames.push_back( ast::Pass<Mangler>::read(
+				assert->var.get(),
+				mangleOverridable, typeMode, mangleGenericParams, nextVarNum, varNums ) );
+			acount++;
+		}
+		mangleName += std::to_string( dcount ) + "_" + std::to_string( fcount ) + "_" + std::to_string( vcount ) + "_" + std::to_string( acount ) + "_";
+		for ( const auto & a : assertionNames ) mangleName += a;
+		mangleName += "_";
+	}
+	if ( !inFunctionType ) {
+		// These qualifiers do not distinguish the outermost type of a function parameter.
+		if ( type->is_const() ) {
+			mangleName += Encoding::qualifiers.at( ast::CV::Const );
+		}
+		if ( type->is_volatile() ) {
+			mangleName += Encoding::qualifiers.at( ast::CV::Volatile );
+		}
+		if ( type->is_atomic() ) {
+			mangleName += Encoding::qualifiers.at( ast::CV::Atomic );
+		}
+	}
+	if ( type->is_mutex() ) {
+		mangleName += Encoding::qualifiers.at( ast::CV::Mutex );
+	}
+	if ( inFunctionType ) {
+		// Turn off inFunctionType so that types can be differentiated for nested qualifiers.
+		GuardValue( inFunctionType ) = false;
+	}
+}
+
+} // namespace
+
+std::string mangle( const ast::Node * decl, Mangle::Mode mode ) {
+	return ast::Pass<Mangler>::read( decl, mode );
+}
+
+} // namespace Mangle
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/SymTab/Mangler.h
===================================================================
--- src/SymTab/Mangler.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,104 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Mangler.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 21:44:03 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Thu Oct 27 11:58:00 2022
-// Update Count     : 16
-//
-
-#pragma once
-
-#include <map>                // for map, map<>::value_compare
-#include <sstream>            // for ostringstream
-#include <string>             // for string
-#include <utility>            // for pair
-
-#include "AST/Bitfield.hpp"
-
-// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
-// The CFA name mangling scheme is based closely on the itanium C++ name mangling scheme, with the following key differences:
-// * Variable names are also mangled to include type information, not just functions
-// * CFA does not have template expansion, so the rules for function specialization do not apply.
-// * CFA instead has to handle type parameters and assertion parameters.
-// * Currently name compression is not implemented.
-
-namespace ast {
-	class Node;
-}
-
-namespace Mangle {
-
-/// Bitflags for mangle Mode:
-enum {
-	NoOverrideable  = 1 << 0,
-	Type            = 1 << 1,
-	NoGenericParams = 1 << 2
-};
-
-/// Bitflag type for mangle Mode:
-struct mangle_flags {
-	union {
-		unsigned int val;
-		struct {
-			bool no_overrideable   : 1;
-			bool type              : 1;
-			bool no_generic_params : 1;
-		};
-	};
-
-	constexpr mangle_flags( unsigned int val ) : val(val) {}
-};
-
-using Mode = bitfield<mangle_flags>;
-
-/// Mangle declaration name.
-std::string mangle( const ast::Node * decl, Mode mode = {} );
-
-/// Most common mangle configuration for types.
-static inline std::string mangleType( const ast::Node * type ) {
-	return mangle( type, { NoOverrideable | Type } );
-}
-
-/// The substrings used in name mangling and demangling.
-namespace Encoding {
-	extern const std::string manglePrefix;
-	extern const std::string basicTypes[];
-	extern const std::map<int, std::string> qualifiers;
-
-	extern const std::string void_t;
-	extern const std::string zero;
-	extern const std::string one;
-
-	extern const std::string function;
-	extern const std::string tuple;
-	extern const std::string pointer;
-	extern const std::string array;
-	extern const std::string qualifiedTypeStart;
-	extern const std::string qualifiedTypeEnd;
-
-	extern const std::string forall;
-	extern const std::string typeVariables[];
-
-	extern const std::string struct_t;
-	extern const std::string union_t;
-	extern const std::string enum_t;
-	extern const std::string type;
-
-	extern const std::string autogen;
-	extern const std::string intrinsic;
-}
-
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/SymTab/Mangler.hpp
===================================================================
--- src/SymTab/Mangler.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/SymTab/Mangler.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,104 @@
+//
+// 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.
+//
+// Mangler.hpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 21:44:03 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Thu Oct 27 11:58:00 2022
+// Update Count     : 16
+//
+
+#pragma once
+
+#include <map>                // for map, map<>::value_compare
+#include <sstream>            // for ostringstream
+#include <string>             // for string
+#include <utility>            // for pair
+
+#include "AST/Bitfield.hpp"
+
+// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
+// The CFA name mangling scheme is based closely on the itanium C++ name mangling scheme, with the following key differences:
+// * Variable names are also mangled to include type information, not just functions
+// * CFA does not have template expansion, so the rules for function specialization do not apply.
+// * CFA instead has to handle type parameters and assertion parameters.
+// * Currently name compression is not implemented.
+
+namespace ast {
+	class Node;
+}
+
+namespace Mangle {
+
+/// Bitflags for mangle Mode:
+enum {
+	NoOverrideable  = 1 << 0,
+	Type            = 1 << 1,
+	NoGenericParams = 1 << 2
+};
+
+/// Bitflag type for mangle Mode:
+struct mangle_flags {
+	union {
+		unsigned int val;
+		struct {
+			bool no_overrideable   : 1;
+			bool type              : 1;
+			bool no_generic_params : 1;
+		};
+	};
+
+	constexpr mangle_flags( unsigned int val ) : val(val) {}
+};
+
+using Mode = bitfield<mangle_flags>;
+
+/// Mangle declaration name.
+std::string mangle( const ast::Node * decl, Mode mode = {} );
+
+/// Most common mangle configuration for types.
+static inline std::string mangleType( const ast::Node * type ) {
+	return mangle( type, { NoOverrideable | Type } );
+}
+
+/// The substrings used in name mangling and demangling.
+namespace Encoding {
+	extern const std::string manglePrefix;
+	extern const std::string basicTypes[];
+	extern const std::map<int, std::string> qualifiers;
+
+	extern const std::string void_t;
+	extern const std::string zero;
+	extern const std::string one;
+
+	extern const std::string function;
+	extern const std::string tuple;
+	extern const std::string pointer;
+	extern const std::string array;
+	extern const std::string qualifiedTypeStart;
+	extern const std::string qualifiedTypeEnd;
+
+	extern const std::string forall;
+	extern const std::string typeVariables[];
+
+	extern const std::string struct_t;
+	extern const std::string union_t;
+	extern const std::string enum_t;
+	extern const std::string type;
+
+	extern const std::string autogen;
+	extern const std::string intrinsic;
+}
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/SymTab/ManglerCommon.cc
===================================================================
--- src/SymTab/ManglerCommon.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,129 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Mangler.h --
-//
-// Author           : Richard C. Bilson
-// Created On       : Sun May 17 21:44:03 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Jan 11 21:23:10 2021
-// Update Count     : 29
-//
-
-#include "Mangler.h"
-
-#include "AST/Decl.hpp"
-#include "AST/Type.hpp"
-
-namespace Mangle {
-
-namespace Encoding {
-
-const std::string manglePrefix = "_X";
-
-// GENERATED START, DO NOT EDIT
-// GENERATED BY BasicTypes-gen.cpp
-// NOTES ON MANGLING:
-// * Itanium spec says that Float80 encodes to "e" (like LongDouble), but the distinct lengths cause resolution problems.
-// * Float128 is supposed to encode to "g", but I wanted it to mangle equal to LongDouble.
-// * Mangling for non-standard complex types is by best guess
-// * _FloatN is supposed to encode as "DF"N"_"; modified for same reason as above.
-// * unused mangling identifiers:
-//   - "z" ellipsis
-//   - "Dd" IEEE 754r 64-bit decimal floating point (borrowed for _Float32x)
-//   - "De" IEEE 754r 128-bit decimal floating point
-//   - "Df" IEEE 754r 32-bit decimal floating point
-//   - "Dh" IEEE 754r 16-bit decimal floating point (borrowed for _Float16)
-//   - "DF"N"_" ISO/IEC TS 18661 N-bit binary floating point (_FloatN)
-//   - "Di" char32_t
-//   - "Ds" char16_t
-const std::string basicTypes[ast::BasicKind::NUMBER_OF_BASIC_TYPES] = {
-	"b",        // _Bool
-	"c",        // char
-	"a",        // signed char
-	"h",        // unsigned char
-	"s",        // signed short int
-	"t",        // unsigned short int
-	"i",        // signed int
-	"j",        // unsigned int
-	"l",        // signed long int
-	"m",        // unsigned long int
-	"x",        // signed long long int
-	"y",        // unsigned long long int
-	"n",        // __int128
-	"o",        // unsigned __int128
-	"DF16_",    // _Float16
-	"CDF16_",   // _Float16 _Complex
-	"DF32_",    // _Float32
-	"CDF32_",   // _Float32 _Complex
-	"f",        // float
-	"Cf",       // float _Complex
-	"DF32x_",   // _Float32x
-	"CDF32x_",  // _Float32x _Complex
-	"DF64_",    // _Float64
-	"CDF64_",   // _Float64 _Complex
-	"d",        // double
-	"Cd",       // double _Complex
-	"DF64x_",   // _Float64x
-	"CDF64x_",  // _Float64x _Complex
-	"Dq",       // __float80
-	"DF128_",   // _Float128
-	"CDF128_",  // _Float128 _Complex
-	"g",        // __float128
-	"e",        // long double
-	"Ce",       // long double _Complex
-	"DF128x_",  // _Float128x
-	"CDF128x_", // _Float128x _Complex
-}; // basicTypes
-// GENERATED END
-static_assert(
-	sizeof(basicTypes) / sizeof(basicTypes[0]) == ast::BasicKind::NUMBER_OF_BASIC_TYPES,
-	"Each basic type kind should have a corresponding mangler letter"
-);
-
-const std::map<int, std::string> qualifiers = {
-	{ ast::CV::Const, "K" },
-	{ ast::CV::Volatile, "V" },
-	{ ast::CV::Atomic, "DA" }, // A is array, so need something unique for atmoic. For now, go with multiletter DA
-	{ ast::CV::Mutex, "X" },
-};
-
-const std::string void_t = "v";
-const std::string zero = "Z";
-const std::string one = "O";
-
-const std::string function = "F";
-const std::string tuple = "T";
-const std::string pointer = "P";
-const std::string array = "A";
-const std::string qualifiedTypeStart = "N";
-const std::string qualifiedTypeEnd = "E";
-
-const std::string forall = "Q";
-const std::string typeVariables[] = {
-	"BD", // dtype
-	"BDS", // dtype + sized
-	"BO", // otype
-	"BF", // ftype
-	"BT", // ttype
-	"BAL", // array length type
-};
-static_assert(
-	sizeof(typeVariables) / sizeof(typeVariables[0]) == ast::TypeDecl::NUMBER_OF_KINDS,
-	"Each type variable kind should have a corresponding mangler prefix"
-);
-
-const std::string struct_t = "S";
-const std::string union_t = "U";
-const std::string enum_t = "M";
-const std::string type = "Y";
-
-const std::string autogen = "autogen__";
-const std::string intrinsic = "intrinsic__";
-
-} // namespace Encoding
-
-} // namespace Mangle
Index: src/SymTab/ManglerCommon.cpp
===================================================================
--- src/SymTab/ManglerCommon.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/SymTab/ManglerCommon.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,129 @@
+//
+// 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.
+//
+// ManglerCommon.cpp --
+//
+// Author           : Richard C. Bilson
+// Created On       : Sun May 17 21:44:03 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Jan 11 21:23:10 2021
+// Update Count     : 29
+//
+
+#include "Mangler.hpp"
+
+#include "AST/Decl.hpp"
+#include "AST/Type.hpp"
+
+namespace Mangle {
+
+namespace Encoding {
+
+const std::string manglePrefix = "_X";
+
+// GENERATED START, DO NOT EDIT
+// GENERATED BY BasicTypes-gen.cpp
+// NOTES ON MANGLING:
+// * Itanium spec says that Float80 encodes to "e" (like LongDouble), but the distinct lengths cause resolution problems.
+// * Float128 is supposed to encode to "g", but I wanted it to mangle equal to LongDouble.
+// * Mangling for non-standard complex types is by best guess
+// * _FloatN is supposed to encode as "DF"N"_"; modified for same reason as above.
+// * unused mangling identifiers:
+//   - "z" ellipsis
+//   - "Dd" IEEE 754r 64-bit decimal floating point (borrowed for _Float32x)
+//   - "De" IEEE 754r 128-bit decimal floating point
+//   - "Df" IEEE 754r 32-bit decimal floating point
+//   - "Dh" IEEE 754r 16-bit decimal floating point (borrowed for _Float16)
+//   - "DF"N"_" ISO/IEC TS 18661 N-bit binary floating point (_FloatN)
+//   - "Di" char32_t
+//   - "Ds" char16_t
+const std::string basicTypes[ast::BasicKind::NUMBER_OF_BASIC_TYPES] = {
+	"b",        // _Bool
+	"c",        // char
+	"a",        // signed char
+	"h",        // unsigned char
+	"s",        // signed short int
+	"t",        // unsigned short int
+	"i",        // signed int
+	"j",        // unsigned int
+	"l",        // signed long int
+	"m",        // unsigned long int
+	"x",        // signed long long int
+	"y",        // unsigned long long int
+	"n",        // __int128
+	"o",        // unsigned __int128
+	"DF16_",    // _Float16
+	"CDF16_",   // _Float16 _Complex
+	"DF32_",    // _Float32
+	"CDF32_",   // _Float32 _Complex
+	"f",        // float
+	"Cf",       // float _Complex
+	"DF32x_",   // _Float32x
+	"CDF32x_",  // _Float32x _Complex
+	"DF64_",    // _Float64
+	"CDF64_",   // _Float64 _Complex
+	"d",        // double
+	"Cd",       // double _Complex
+	"DF64x_",   // _Float64x
+	"CDF64x_",  // _Float64x _Complex
+	"Dq",       // __float80
+	"DF128_",   // _Float128
+	"CDF128_",  // _Float128 _Complex
+	"g",        // __float128
+	"e",        // long double
+	"Ce",       // long double _Complex
+	"DF128x_",  // _Float128x
+	"CDF128x_", // _Float128x _Complex
+}; // basicTypes
+// GENERATED END
+static_assert(
+	sizeof(basicTypes) / sizeof(basicTypes[0]) == ast::BasicKind::NUMBER_OF_BASIC_TYPES,
+	"Each basic type kind should have a corresponding mangler letter"
+);
+
+const std::map<int, std::string> qualifiers = {
+	{ ast::CV::Const, "K" },
+	{ ast::CV::Volatile, "V" },
+	{ ast::CV::Atomic, "DA" }, // A is array, so need something unique for atmoic. For now, go with multiletter DA
+	{ ast::CV::Mutex, "X" },
+};
+
+const std::string void_t = "v";
+const std::string zero = "Z";
+const std::string one = "O";
+
+const std::string function = "F";
+const std::string tuple = "T";
+const std::string pointer = "P";
+const std::string array = "A";
+const std::string qualifiedTypeStart = "N";
+const std::string qualifiedTypeEnd = "E";
+
+const std::string forall = "Q";
+const std::string typeVariables[] = {
+	"BD", // dtype
+	"BDS", // dtype + sized
+	"BO", // otype
+	"BF", // ftype
+	"BT", // ttype
+	"BAL", // array length type
+};
+static_assert(
+	sizeof(typeVariables) / sizeof(typeVariables[0]) == ast::TypeDecl::NUMBER_OF_KINDS,
+	"Each type variable kind should have a corresponding mangler prefix"
+);
+
+const std::string struct_t = "S";
+const std::string union_t = "U";
+const std::string enum_t = "M";
+const std::string type = "Y";
+
+const std::string autogen = "autogen__";
+const std::string intrinsic = "intrinsic__";
+
+} // namespace Encoding
+
+} // namespace Mangle
Index: src/SymTab/demangler.cc
===================================================================
--- src/SymTab/demangler.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,25 +1,0 @@
-#include "Demangle.h"
-#include <iostream>
-#include <fstream>
-
-void demangleAndPrint(const std::string & mangleName) {
-	char * demangleName = cforall_demangle(mangleName.c_str(), 0);
-	std::cout << mangleName << " => " << demangleName << std::endl;
-	free(demangleName);
-}
-
-int main(int argc, char * argv[]) {
-	char const * fileName = (1 < argc) ? argv[1] : "in-demangle.txt";
-	std::ifstream in(fileName);
-
-	std::string line;
-	while (std::getline(in, line)) {
-		if (line.empty()) {
-			std::cout << "=================================" << std::endl;
-		} else if (line[0] == '#') {
-			continue;
-		} else {
-			demangleAndPrint(line);
-		}
-	}
-}
Index: src/SymTab/demangler.cpp
===================================================================
--- src/SymTab/demangler.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/SymTab/demangler.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,25 @@
+#include "Demangle.hpp"
+#include <iostream>
+#include <fstream>
+
+void demangleAndPrint(const std::string & mangleName) {
+	char * demangleName = cforall_demangle(mangleName.c_str(), 0);
+	std::cout << mangleName << " => " << demangleName << std::endl;
+	free(demangleName);
+}
+
+int main(int argc, char * argv[]) {
+	char const * fileName = (1 < argc) ? argv[1] : "in-demangle.txt";
+	std::ifstream in(fileName);
+
+	std::string line;
+	while (std::getline(in, line)) {
+		if (line.empty()) {
+			std::cout << "=================================" << std::endl;
+		} else if (line[0] == '#') {
+			continue;
+		} else {
+			demangleAndPrint(line);
+		}
+	}
+}
Index: src/SymTab/module.mk
===================================================================
--- src/SymTab/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/SymTab/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,15 +16,15 @@
 
 SRC_SYMTAB = \
-	SymTab/FixFunction.cc \
-	SymTab/FixFunction.h \
+	SymTab/FixFunction.cpp \
+	SymTab/FixFunction.hpp \
 	SymTab/GenImplicitCall.cpp \
 	SymTab/GenImplicitCall.hpp \
-	SymTab/Mangler.cc \
-	SymTab/ManglerCommon.cc \
-	SymTab/Mangler.h
+	SymTab/Mangler.cpp \
+	SymTab/ManglerCommon.cpp \
+	SymTab/Mangler.hpp
 
 SRC += $(SRC_SYMTAB)
 
 SRCDEMANGLE += $(SRC_SYMTAB) \
-	SymTab/Demangle.cc \
-	SymTab/Demangle.h
+	SymTab/Demangle.cpp \
+	SymTab/Demangle.hpp
Index: src/Tuples/Explode.cc
===================================================================
--- src/Tuples/Explode.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,106 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Explode.cc --
-//
-// Author           : Rob Schluntz
-// Created On       : Wed Nov 9 13:12:24 2016
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jun 12 16:40:00 2016
-// Update Count     : 3
-//
-
-#include "Explode.h"
-
-#include "AST/Pass.hpp"          // for Pass
-
-namespace Tuples {
-
-namespace {
-
-// Remove one level of reference from a reference type.
-const ast::Type * getReferenceBase( const ast::Type * t ) {
-	if ( const ast::ReferenceType * ref = dynamic_cast< const ast::ReferenceType * >( t ) ) {
-		return ref->base;
-	} else {
-		assertf( false, "getReferenceBase for non-ref: %s", toString( t ).c_str() );
-		return nullptr;
-	}
-}
-
-struct CastExploderCore {
-	bool castAdded = false;
-	bool foundUniqueExpr = false;
-	const ast::Expr * applyCast( const ast::Expr * expr, bool first = true ) {
-		// On tuple push the cast down.
-		if ( const ast::TupleExpr * tupleExpr = dynamic_cast< const ast::TupleExpr * >( expr ) ) {
-			foundUniqueExpr = true;
-			std::vector< ast::ptr< ast::Expr > > exprs;
-			for ( const ast::Expr * expr : tupleExpr->exprs ) {
-				exprs.emplace_back( applyCast( expr, false ) );
-			}
-			if ( first ) {
-				castAdded = true;
-				const ast::Expr * tuple = new ast::TupleExpr{
-					tupleExpr->location, std::move( exprs ) };
-				return new ast::CastExpr{ tuple, new ast::ReferenceType{ tuple->result } };
-			} else {
-				return new ast::TupleExpr( tupleExpr->location, std::move( exprs ) );
-			}
-		}
-		if ( dynamic_cast< const ast::ReferenceType * >( expr->result.get() ) ) {
-			return expr;
-		} else {
-			castAdded = true;
-			return new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } };
-		}
-	}
-
-	const ast::Expr * postvisit( const ast::UniqueExpr * node ) {
-		// move cast into unique expr so that the unique expr has type T& rather than
-		// type T. In particular, this transformation helps with generating the
-		// correct code for reference-cast member tuple expressions, since the result
-		// should now be a tuple of references rather than a reference to a tuple.
-		// Still, this code is a bit awkward, and could use some improvement.
-		const ast::UniqueExpr * newNode = new ast::UniqueExpr( node->location,
-				applyCast( node->expr ), node->id );
-		if ( castAdded ) {
-			// if a cast was added by applyCast, then unique expr now has one more layer of reference
-			// than it had coming into this function. To ensure types still match correctly, need to cast
-			//  to reference base so that outer expressions are still correct.
-			castAdded = false;
-			const ast::Type * newType = getReferenceBase( newNode->result );
-			return new ast::CastExpr{ newNode->location, newNode, newType };
-		}
-		return newNode;
-	}
-
-	const ast::Expr * postvisit( const ast::TupleIndexExpr * tupleExpr ) {
-		// tuple index expr needs to be rebuilt to ensure that the type of the
-		// field is consistent with the type of the tuple expr, since the field
-		// may have changed from type T to T&.
-		return new ast::TupleIndexExpr( tupleExpr->location, tupleExpr->tuple, tupleExpr->index );
-	}
-};
-
-} // namespace
-
-const ast::Expr * distributeReference( const ast::Expr * expr ) {
-	ast::Pass<CastExploderCore> exploder;
-	expr = expr->accept( exploder );
-	if ( ! exploder.core.foundUniqueExpr ) {
-		expr = new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } };
-	}
-	return expr;
-}
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/Explode.cpp
===================================================================
--- src/Tuples/Explode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/Explode.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,106 @@
+//
+// 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.
+//
+// Explode.cpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Wed Nov 9 13:12:24 2016
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Jun 12 16:40:00 2016
+// Update Count     : 3
+//
+
+#include "Explode.hpp"
+
+#include "AST/Pass.hpp"          // for Pass
+
+namespace Tuples {
+
+namespace {
+
+// Remove one level of reference from a reference type.
+const ast::Type * getReferenceBase( const ast::Type * t ) {
+	if ( const ast::ReferenceType * ref = dynamic_cast< const ast::ReferenceType * >( t ) ) {
+		return ref->base;
+	} else {
+		assertf( false, "getReferenceBase for non-ref: %s", toString( t ).c_str() );
+		return nullptr;
+	}
+}
+
+struct CastExploderCore {
+	bool castAdded = false;
+	bool foundUniqueExpr = false;
+	const ast::Expr * applyCast( const ast::Expr * expr, bool first = true ) {
+		// On tuple push the cast down.
+		if ( const ast::TupleExpr * tupleExpr = dynamic_cast< const ast::TupleExpr * >( expr ) ) {
+			foundUniqueExpr = true;
+			std::vector< ast::ptr< ast::Expr > > exprs;
+			for ( const ast::Expr * expr : tupleExpr->exprs ) {
+				exprs.emplace_back( applyCast( expr, false ) );
+			}
+			if ( first ) {
+				castAdded = true;
+				const ast::Expr * tuple = new ast::TupleExpr{
+					tupleExpr->location, std::move( exprs ) };
+				return new ast::CastExpr{ tuple, new ast::ReferenceType{ tuple->result } };
+			} else {
+				return new ast::TupleExpr( tupleExpr->location, std::move( exprs ) );
+			}
+		}
+		if ( dynamic_cast< const ast::ReferenceType * >( expr->result.get() ) ) {
+			return expr;
+		} else {
+			castAdded = true;
+			return new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } };
+		}
+	}
+
+	const ast::Expr * postvisit( const ast::UniqueExpr * node ) {
+		// move cast into unique expr so that the unique expr has type T& rather than
+		// type T. In particular, this transformation helps with generating the
+		// correct code for reference-cast member tuple expressions, since the result
+		// should now be a tuple of references rather than a reference to a tuple.
+		// Still, this code is a bit awkward, and could use some improvement.
+		const ast::UniqueExpr * newNode = new ast::UniqueExpr( node->location,
+				applyCast( node->expr ), node->id );
+		if ( castAdded ) {
+			// if a cast was added by applyCast, then unique expr now has one more layer of reference
+			// than it had coming into this function. To ensure types still match correctly, need to cast
+			//  to reference base so that outer expressions are still correct.
+			castAdded = false;
+			const ast::Type * newType = getReferenceBase( newNode->result );
+			return new ast::CastExpr{ newNode->location, newNode, newType };
+		}
+		return newNode;
+	}
+
+	const ast::Expr * postvisit( const ast::TupleIndexExpr * tupleExpr ) {
+		// tuple index expr needs to be rebuilt to ensure that the type of the
+		// field is consistent with the type of the tuple expr, since the field
+		// may have changed from type T to T&.
+		return new ast::TupleIndexExpr( tupleExpr->location, tupleExpr->tuple, tupleExpr->index );
+	}
+};
+
+} // namespace
+
+const ast::Expr * distributeReference( const ast::Expr * expr ) {
+	ast::Pass<CastExploderCore> exploder;
+	expr = expr->accept( exploder );
+	if ( ! exploder.core.foundUniqueExpr ) {
+		expr = new ast::CastExpr{ expr, new ast::ReferenceType{ expr->result } };
+	}
+	return expr;
+}
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/Explode.h
===================================================================
--- src/Tuples/Explode.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,148 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Explode.h --
-//
-// Author           : Rob Schluntz
-// Created On       : Wed Nov 9 13:12:24 2016
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Jun 17 14:36:00 2019
-// Update Count     : 4
-//
-
-#pragma once
-
-#include <iterator>                     // for back_inserter, back_insert_iterator
-#include <utility>                      // for forward
-
-#include "AST/Expr.hpp"
-#include "ResolvExpr/Candidate.hpp"     // for Candidate, CandidateList
-#include "ResolvExpr/ExplodedArg.hpp"   // for ExplodedArg
-#include "Tuples.h"                     // for maybeImpure
-
-namespace ast {
-	class SymbolTable;
-}
-
-namespace Tuples {
-
-const ast::Expr * distributeReference( const ast::Expr * );
-
-/// Append candidate to an OutputIterator of Candidates.
-template<typename OutputIterator>
-void append( OutputIterator out, const ast::Expr * expr, const ast::TypeEnvironment & env,
-		const ast::OpenVarSet & open, const ast::AssertionList & need,
-		const ResolvExpr::Cost & cost, const ResolvExpr::Cost & cvtCost ) {
-	ast::TypeEnvironment copyEnv = env;
-	ast::OpenVarSet copyOpen = open;
-	ast::AssertionSet set;
-	mergeAssertionSet( set, need );
-	*out++ = std::make_shared<ResolvExpr::Candidate>( expr, std::move( copyEnv ),
-		std::move( copyOpen ), std::move( set ), cost, cvtCost );
-}
-
-/// Append candidate to an ExplodedArg.
-static inline void append( ResolvExpr::ExplodedArg& ea, const ast::Expr * expr,
-		const ast::TypeEnvironment&, const ast::OpenVarSet&,
-		const ast::AssertionList&, const ResolvExpr::Cost&, const ResolvExpr::Cost& ) {
-	// I'm not sure why most of the arguments are unused. But they were in the old version.
-	ea.exprs.emplace_back( expr );
-}
-
-/// Check if the expression is a cast to a reference type, return it if it is.
-static inline const ast::CastExpr * isReferenceCast( const ast::Expr * expr ) {
-	if ( const ast::CastExpr * cast = dynamic_cast< const ast::CastExpr * >( expr ) ) {
-		if ( dynamic_cast< const ast::ReferenceType * >( cast->result.get() ) ) {
-			return cast;
-		}
-	}
-	return nullptr;
-}
-
-/// helper function (indirectely) used by explode
-template< typename Output >
-void explodeRecursive(
-	const ast::CastExpr *, const ResolvExpr::Candidate &,
-	const ast::SymbolTable &, Output &&
-) {
-}
-
-/// helper function used by explode
-template< typename Output >
-void explodeUnique(
-	const ast::ptr< ast::Expr > & expr, const ResolvExpr::Candidate & arg,
-	const ast::SymbolTable & symtab, Output && out, bool isTupleAssign
-) {
-	// Tuple assignment can use a faster method if it is cast. Uses recursive exploding.
-	if ( isTupleAssign ) if ( const ast::CastExpr * castExpr = isReferenceCast( expr ) ) {
-		ResolvExpr::CandidateList candidates;
-		explodeUnique( castExpr->arg, arg, symtab, back_inserter( candidates ), true );
-		for ( ResolvExpr::CandidateRef & cand : candidates ) {
-			// Distribute the reference cast over all components of the candidate.
-			append( std::forward<Output>(out), distributeReference( cand->expr ), cand->env,
-				cand->open, cand->need, cand->cost, cand->cvtCost );
-		}
-		return;
-	}
-	const ast::Type * res = expr->result->stripReferences();
-	if ( const ast::TupleType * tupleType = dynamic_cast< const ast::TupleType * >( res ) ) {
-		if ( const ast::ptr< ast::TupleExpr > & tupleExpr = expr.as< ast::TupleExpr >() ) {
-			// Open the tuple expr and continue on its components.
-			for ( const ast::Expr * expr : tupleExpr->exprs ) {
-				explodeUnique( expr, arg, symtab, std::forward<Output>(out), isTupleAssign );
-			}
-		} else {
-			ast::ptr< ast::Expr > local = expr;
-			// Expressions which may have side effects require a single unique instance.
-			if ( Tuples::maybeImpureIgnoreUnique( local ) ) {
-				local = new ast::UniqueExpr( local->location, local );
-			}
-			// Cast a reference away to a value-type to allow further explosion.
-			if ( local->result.as< ast::ReferenceType >() ) {
-				local = new ast::CastExpr{ local, tupleType };
-			}
-			// Now we have to go across the tuple via indexing.
-			for ( unsigned int i = 0 ; i < tupleType->size() ; ++i ) {
-				ast::TupleIndexExpr * idx = new ast::TupleIndexExpr( local->location, local, i );
-				explodeUnique( idx, arg, symtab, std::forward<Output>(out), isTupleAssign );
-				// TODO: We need more input to figure out the exact lifetimes of these types.
-				// delete idx;
-			}
-		}
-	} else {
-		// For atomic/non-tuple types, no explosion is used.
-		append( std::forward<Output>(out), expr, arg.env, arg.open, arg.need, arg.cost,
-			arg.cvtCost );
-	}
-}
-
-/// expands a tuple-valued candidate into multiple candidates, each with a non-tuple type
-template< typename Output >
-void explode(
-	const ResolvExpr::Candidate & arg, const ast::SymbolTable & symtab, Output && out,
-	bool isTupleAssign = false
-) {
-	explodeUnique( arg.expr, arg, symtab, std::forward< Output >( out ), isTupleAssign );
-}
-
-/// explode list of candidates into flattened list of candidates
-template< typename Output >
-void explode(
-	const ResolvExpr::CandidateList & cands, const ast::SymbolTable & symtab, Output && out,
-	bool isTupleAssign = false
-) {
-	for ( const ResolvExpr::CandidateRef & cand : cands ) {
-		explode( *cand, symtab, std::forward< Output >( out ), isTupleAssign );
-	}
-}
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/Explode.hpp
===================================================================
--- src/Tuples/Explode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/Explode.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,148 @@
+//
+// 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.
+//
+// Explode.hpp --
+//
+// Author           : Rob Schluntz
+// Created On       : Wed Nov 9 13:12:24 2016
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon Jun 17 14:36:00 2019
+// Update Count     : 4
+//
+
+#pragma once
+
+#include <iterator>                     // for back_inserter, back_insert_iterator
+#include <utility>                      // for forward
+
+#include "AST/Expr.hpp"
+#include "ResolvExpr/Candidate.hpp"     // for Candidate, CandidateList
+#include "ResolvExpr/ExplodedArg.hpp"   // for ExplodedArg
+#include "Tuples.hpp"                   // for maybeImpure
+
+namespace ast {
+	class SymbolTable;
+}
+
+namespace Tuples {
+
+const ast::Expr * distributeReference( const ast::Expr * );
+
+/// Append candidate to an OutputIterator of Candidates.
+template<typename OutputIterator>
+void append( OutputIterator out, const ast::Expr * expr, const ast::TypeEnvironment & env,
+		const ast::OpenVarSet & open, const ast::AssertionList & need,
+		const ResolvExpr::Cost & cost, const ResolvExpr::Cost & cvtCost ) {
+	ast::TypeEnvironment copyEnv = env;
+	ast::OpenVarSet copyOpen = open;
+	ast::AssertionSet set;
+	mergeAssertionSet( set, need );
+	*out++ = std::make_shared<ResolvExpr::Candidate>( expr, std::move( copyEnv ),
+		std::move( copyOpen ), std::move( set ), cost, cvtCost );
+}
+
+/// Append candidate to an ExplodedArg.
+static inline void append( ResolvExpr::ExplodedArg& ea, const ast::Expr * expr,
+		const ast::TypeEnvironment&, const ast::OpenVarSet&,
+		const ast::AssertionList&, const ResolvExpr::Cost&, const ResolvExpr::Cost& ) {
+	// I'm not sure why most of the arguments are unused. But they were in the old version.
+	ea.exprs.emplace_back( expr );
+}
+
+/// Check if the expression is a cast to a reference type, return it if it is.
+static inline const ast::CastExpr * isReferenceCast( const ast::Expr * expr ) {
+	if ( const ast::CastExpr * cast = dynamic_cast< const ast::CastExpr * >( expr ) ) {
+		if ( dynamic_cast< const ast::ReferenceType * >( cast->result.get() ) ) {
+			return cast;
+		}
+	}
+	return nullptr;
+}
+
+/// helper function (indirectely) used by explode
+template< typename Output >
+void explodeRecursive(
+	const ast::CastExpr *, const ResolvExpr::Candidate &,
+	const ast::SymbolTable &, Output &&
+) {
+}
+
+/// helper function used by explode
+template< typename Output >
+void explodeUnique(
+	const ast::ptr< ast::Expr > & expr, const ResolvExpr::Candidate & arg,
+	const ast::SymbolTable & symtab, Output && out, bool isTupleAssign
+) {
+	// Tuple assignment can use a faster method if it is cast. Uses recursive exploding.
+	if ( isTupleAssign ) if ( const ast::CastExpr * castExpr = isReferenceCast( expr ) ) {
+		ResolvExpr::CandidateList candidates;
+		explodeUnique( castExpr->arg, arg, symtab, back_inserter( candidates ), true );
+		for ( ResolvExpr::CandidateRef & cand : candidates ) {
+			// Distribute the reference cast over all components of the candidate.
+			append( std::forward<Output>(out), distributeReference( cand->expr ), cand->env,
+				cand->open, cand->need, cand->cost, cand->cvtCost );
+		}
+		return;
+	}
+	const ast::Type * res = expr->result->stripReferences();
+	if ( const ast::TupleType * tupleType = dynamic_cast< const ast::TupleType * >( res ) ) {
+		if ( const ast::ptr< ast::TupleExpr > & tupleExpr = expr.as< ast::TupleExpr >() ) {
+			// Open the tuple expr and continue on its components.
+			for ( const ast::Expr * expr : tupleExpr->exprs ) {
+				explodeUnique( expr, arg, symtab, std::forward<Output>(out), isTupleAssign );
+			}
+		} else {
+			ast::ptr< ast::Expr > local = expr;
+			// Expressions which may have side effects require a single unique instance.
+			if ( Tuples::maybeImpureIgnoreUnique( local ) ) {
+				local = new ast::UniqueExpr( local->location, local );
+			}
+			// Cast a reference away to a value-type to allow further explosion.
+			if ( local->result.as< ast::ReferenceType >() ) {
+				local = new ast::CastExpr{ local, tupleType };
+			}
+			// Now we have to go across the tuple via indexing.
+			for ( unsigned int i = 0 ; i < tupleType->size() ; ++i ) {
+				ast::TupleIndexExpr * idx = new ast::TupleIndexExpr( local->location, local, i );
+				explodeUnique( idx, arg, symtab, std::forward<Output>(out), isTupleAssign );
+				// TODO: We need more input to figure out the exact lifetimes of these types.
+				// delete idx;
+			}
+		}
+	} else {
+		// For atomic/non-tuple types, no explosion is used.
+		append( std::forward<Output>(out), expr, arg.env, arg.open, arg.need, arg.cost,
+			arg.cvtCost );
+	}
+}
+
+/// expands a tuple-valued candidate into multiple candidates, each with a non-tuple type
+template< typename Output >
+void explode(
+	const ResolvExpr::Candidate & arg, const ast::SymbolTable & symtab, Output && out,
+	bool isTupleAssign = false
+) {
+	explodeUnique( arg.expr, arg, symtab, std::forward< Output >( out ), isTupleAssign );
+}
+
+/// explode list of candidates into flattened list of candidates
+template< typename Output >
+void explode(
+	const ResolvExpr::CandidateList & cands, const ast::SymbolTable & symtab, Output && out,
+	bool isTupleAssign = false
+) {
+	for ( const ResolvExpr::CandidateRef & cand : cands ) {
+		explode( *cand, symtab, std::forward< Output >( out ), isTupleAssign );
+	}
+}
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/TupleAssignment.cc
===================================================================
--- src/Tuples/TupleAssignment.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,394 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// TupleAssignment.cc --
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Mar 16 14:06:00 2022
-// Update Count     : 10
-//
-
-#include "Tuples.h"
-
-#include <algorithm>                       // for transform
-#include <cassert>                         // for assert
-#include <iterator>                        // for back_insert_iterator, back...
-#include <list>                            // for _List_const_iterator, _Lis...
-#include <memory>                          // for unique_ptr, allocator_trai...
-#include <string>                          // for string
-#include <vector>
-
-#include "AST/Decl.hpp"
-#include "AST/Init.hpp"
-#include "AST/Pass.hpp"
-#include "AST/Stmt.hpp"
-#include "AST/TypeEnvironment.hpp"
-#include "CodeGen/OperatorTable.h"
-#include "Common/UniqueName.h"             // for UniqueName
-#include "Common/utility.h"                // for splice, zipWith
-#include "Explode.h"                       // for explode
-#include "InitTweak/GenInit.h"             // for genCtorInit
-#include "InitTweak/InitTweak.h"           // for getPointerBase, isAssignment
-#include "ResolvExpr/CandidateFinder.hpp"  // for CandidateFinder
-#include "ResolvExpr/Cost.h"               // for Cost
-#include "ResolvExpr/Resolver.h"           // for resolveCtorInit
-#include "ResolvExpr/typeops.h"            // for combos
-
-#if 0
-#define PRINT(x) x
-#else
-#define PRINT(x)
-#endif
-
-namespace Tuples {
-
-namespace {
-
-/// Checks if `expr` is of tuple type.
-bool isTuple( const ast::Expr * expr ) {
-	if ( !expr ) return false;
-	assert( expr->result );
-	return dynamic_cast< const ast::TupleType * >( expr->result->stripReferences() );
-}
-
-/// Checks if `expr` is of tuple type or a cast to one.
-bool refToTuple( const ast::Expr * expr ) {
-	assert( expr->result );
-	// Check for function returning tuple of reference types.
-	if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
-		return refToTuple( castExpr->arg );
-	} else {
-		return isTuple( expr );
-	}
-}
-
-/// Dispatcher for tuple (multiple and mass) assignment operations.
-class TupleAssignSpotter final {
-	/// Actually finds tuple assignment operations, by subclass.
-	struct Matcher {
-		ResolvExpr::CandidateList lhs, rhs;
-		TupleAssignSpotter & spotter;
-		CodeLocation location;
-		ResolvExpr::Cost baseCost;
-		std::vector< ast::ptr< ast::ObjectDecl > > tmpDecls;
-		ast::TypeEnvironment env;
-		ast::OpenVarSet open;
-		ast::AssertionSet need;
-
-		void combineState( const ResolvExpr::Candidate & cand ) {
-			env.simpleCombine( cand.env );
-			ast::mergeOpenVars( open, cand.open );
-			need.insert( cand.need.begin(), cand.need.end() );
-		}
-
-		Matcher(
-			TupleAssignSpotter & s, const CodeLocation & loc,
-			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
-		: lhs( l ), rhs( r ), spotter( s ), location( loc ),
-		  baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ), tmpDecls(),
-		  env(), open(), need() {
-			for ( auto & cand : lhs ) combineState( *cand );
-			for ( auto & cand : rhs ) combineState( *cand );
-		}
-		virtual ~Matcher() = default;
-
-		virtual std::vector< ast::ptr< ast::Expr > > match() = 0;
-
-		/// Removes environments from subexpressions within statement expressions, which could
-		/// throw off later passes like those in Box which rely on PolyMutator, and adds the
-		/// bindings to the env.
-		struct EnvRemover {
-			/// Environment to hoist ExprStmt environments to.
-			ast::TypeEnvironment & tenv;
-
-			EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {}
-
-			const ast::ExprStmt * previsit( const ast::ExprStmt * stmt ) {
-				if ( stmt->expr->env ) {
-					tenv.add( *stmt->expr->env );
-					ast::ExprStmt * mut = mutate( stmt );
-					mut->expr.get_and_mutate()->env = nullptr;
-					return mut;
-				}
-				return stmt;
-			}
-		};
-
-		ast::ObjectDecl * newObject( UniqueName & namer, const ast::Expr * expr ) {
-			assert( expr->result && !expr->result->isVoid() );
-
-			ast::ObjectDecl * ret = new ast::ObjectDecl(
-				location, namer.newName(), expr->result, new ast::SingleInit( location, expr ),
-				ast::Storage::Classes{}, ast::Linkage::Cforall );
-
-			// If expression type is a reference, just need an initializer, otherwise construct.
-			if ( ! expr->result.as< ast::ReferenceType >() ) {
-				// Resolve ctor/dtor for the new object.
-				ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
-						InitTweak::genCtorInit( location, ret ), spotter.crntFinder.context );
-				// Remove environments from subexpressions of stmtExpr.
-				ast::Pass< EnvRemover > rm( env );
-				ret->init = ctorInit->accept( rm );
-			}
-
-			PRINT( std::cerr << "new object: " << ret << std::endl; )
-			return ret;
-		}
-
-		ast::UntypedExpr * createFunc(
-			const std::string & fname, const ast::ObjectDecl * left,
-			const ast::ObjectDecl * right
-		) {
-			assert( left );
-			std::vector< ast::ptr< ast::Expr > > args;
-			args.emplace_back( new ast::VariableExpr( location, left ) );
-			if ( right ) { args.emplace_back( new ast::VariableExpr( location, right ) ); }
-
-			if ( left->type->referenceDepth() > 1 && CodeGen::isConstructor( fname ) ) {
-				args.front() = new ast::AddressExpr( location, args.front() );
-				if ( right ) { args.back() = new ast::AddressExpr( location, args.back() ); }
-				return new ast::UntypedExpr(
-					location, new ast::NameExpr( location, "?=?" ), std::move( args ) );
-			} else {
-				return new ast::UntypedExpr(
-					location, new ast::NameExpr( location, fname ), std::move( args ) );
-			}
-		}
-	};
-
-	/// Finds mass-assignment operations.
-	struct MassAssignMatcher final : public Matcher {
-		MassAssignMatcher(
-			TupleAssignSpotter & s, const CodeLocation & loc,
-			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
-		: Matcher( s, loc, l, r ) {}
-
-		std::vector< ast::ptr< ast::Expr > > match() override {
-			static UniqueName lhsNamer( "__massassign_L" );
-			static UniqueName rhsNamer( "__massassign_R" );
-			// Empty tuple case falls into this matcher.
-			assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 );
-
-			ast::ptr< ast::ObjectDecl > rtmp =
-				1 == rhs.size() ? newObject( rhsNamer, rhs.front()->expr ) : nullptr;
-
-			std::vector< ast::ptr< ast::Expr > > out;
-			for ( ResolvExpr::CandidateRef & lhsCand : lhs ) {
-				// Create a temporary object for each value in
-				// the LHS and create a call involving the RHS.
-				ast::ptr< ast::ObjectDecl > ltmp = newObject( lhsNamer, lhsCand->expr );
-				out.emplace_back( createFunc( spotter.fname, ltmp, rtmp ) );
-				tmpDecls.emplace_back( std::move( ltmp ) );
-			}
-			if ( rtmp ) tmpDecls.emplace_back( std::move( rtmp ) );
-
-			return out;
-		}
-	};
-
-	/// Finds multiple-assignment operations.
-	struct MultipleAssignMatcher final : public Matcher {
-		MultipleAssignMatcher(
-			TupleAssignSpotter & s, const CodeLocation & loc,
-			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
-		: Matcher( s, loc, l, r ) {}
-
-		std::vector< ast::ptr< ast::Expr > > match() override {
-			static UniqueName lhsNamer( "__multassign_L" );
-			static UniqueName rhsNamer( "__multassign_R" );
-
-			if ( lhs.size() != rhs.size() ) return {};
-
-			// Produce a new temporary object for each value in
-			// the LHS and RHS and pairwise create the calls.
-			std::vector< ast::ptr< ast::ObjectDecl > > ltmp, rtmp;
-
-			std::vector< ast::ptr< ast::Expr > > out;
-			for ( unsigned i = 0; i < lhs.size(); ++i ) {
-				ResolvExpr::CandidateRef & lhsCand = lhs[i];
-				ResolvExpr::CandidateRef & rhsCand = rhs[i];
-
-				// Convert RHS to LHS type minus one reference --
-				// important for case where LHS is && and RHS is lvalue.
-				auto lhsType = lhsCand->expr->result.strict_as< ast::ReferenceType >();
-				rhsCand->expr = new ast::CastExpr( rhsCand->expr, lhsType->base );
-				ast::ptr< ast::ObjectDecl > lobj = newObject( lhsNamer, lhsCand->expr );
-				ast::ptr< ast::ObjectDecl > robj = newObject( rhsNamer, rhsCand->expr );
-				out.emplace_back( createFunc( spotter.fname, lobj, robj ) );
-				ltmp.emplace_back( std::move( lobj ) );
-				rtmp.emplace_back( std::move( robj ) );
-
-				// Resolve the cast expression so that rhsCand return type is bound
-				// by the cast type as needed, and transfer the resulting environment.
-				ResolvExpr::CandidateFinder finder( spotter.crntFinder.context, env );
-				finder.find( rhsCand->expr, ResolvExpr::ResolveMode::withAdjustment() );
-				assert( 1 == finder.candidates.size() );
-				env = std::move( finder.candidates.front()->env );
-			}
-
-			splice( tmpDecls, ltmp );
-			splice( tmpDecls, rtmp );
-
-			return out;
-		}
-	};
-
-	ResolvExpr::CandidateFinder & crntFinder;
-	std::string fname;
-	std::unique_ptr< Matcher > matcher;
-
-public:
-	TupleAssignSpotter( ResolvExpr::CandidateFinder & f )
-	: crntFinder( f ), fname(), matcher() {}
-
-	// Find left- and right-hand-sides for mass or multiple assignment.
-	void spot(
-		const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args
-	) {
-		if ( auto op = expr->func.as< ast::NameExpr >() ) {
-			// Skip non-assignment functions.
-			if ( !CodeGen::isCtorDtorAssign( op->name ) ) return;
-			fname = op->name;
-
-			// Handled by CandidateFinder if applicable (both odd cases).
-			if ( args.empty() || ( 1 == args.size() && CodeGen::isAssignment( fname ) ) ) {
-				return;
-			}
-
-			// Look over all possible left-hand-side.
-			for ( ResolvExpr::CandidateRef & lhsCand : args[0] ) {
-				// Skip non-tuple LHS.
-				if ( !refToTuple( lhsCand->expr ) ) continue;
-
-				// Explode is aware of casts - ensure every LHS
-				// is sent into explode with a reference cast.
-				if ( !lhsCand->expr.as< ast::CastExpr >() ) {
-					lhsCand->expr = new ast::CastExpr(
-						lhsCand->expr, new ast::ReferenceType( lhsCand->expr->result ) );
-				}
-
-				// Explode the LHS so that each field of a tuple-valued expr is assigned.
-				ResolvExpr::CandidateList lhs;
-				explode( *lhsCand, crntFinder.context.symtab, back_inserter(lhs), true );
-				for ( ResolvExpr::CandidateRef & cand : lhs ) {
-					// Each LHS value must be a reference - some come in
-					// with a cast, if not just cast to reference here.
-					if ( !cand->expr->result.as< ast::ReferenceType >() ) {
-						cand->expr = new ast::CastExpr(
-							cand->expr, new ast::ReferenceType( cand->expr->result ) );
-					}
-				}
-
-				if ( 1 == args.size() ) {
-					// Mass default-initialization/destruction.
-					ResolvExpr::CandidateList rhs{};
-					matcher.reset( new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
-					match();
-				} else if ( 2 == args.size() ) {
-					for ( const ResolvExpr::CandidateRef & rhsCand : args[1] ) {
-						ResolvExpr::CandidateList rhs;
-						if ( isTuple( rhsCand->expr ) ) {
-							// Multiple assignment:
-							explode( *rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
-							matcher.reset(
-								new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
-						} else {
-							// Mass assignment:
-							rhs.emplace_back( rhsCand );
-							matcher.reset(
-								new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
-						}
-						match();
-					}
-				} else {
-					// Expand all possible RHS possibilities.
-					std::vector< ResolvExpr::CandidateList > rhsCands;
-					combos(
-						std::next( args.begin(), 1 ), args.end(), back_inserter( rhsCands ) );
-					for ( const ResolvExpr::CandidateList & rhsCand : rhsCands ) {
-						// Multiple assignment:
-						ResolvExpr::CandidateList rhs;
-						explode( rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
-						matcher.reset(
-							new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
-						match();
-					}
-				}
-			}
-		}
-	}
-
-	void match() {
-		assert( matcher );
-
-		std::vector< ast::ptr< ast::Expr > > newAssigns = matcher->match();
-
-		if ( !( matcher->lhs.empty() && matcher->rhs.empty() ) ) {
-			// If both LHS and RHS are empty than this is the empty tuple
-			// case, wherein it's okay for newAssigns to be empty. Otherwise,
-			// return early so that no new candidates are generated.
-			if ( newAssigns.empty() ) return;
-		}
-
-		ResolvExpr::CandidateList crnt;
-		// Now resolve new assignments.
-		for ( const ast::Expr * expr : newAssigns ) {
-			PRINT(
-				std::cerr << "== resolving tuple assign ==" << std::endl;
-				std::cerr << expr << std::endl;
-			)
-
-			ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env );
-			finder.allowVoid = true;
-
-			try {
-				finder.find( expr, ResolvExpr::ResolveMode::withAdjustment() );
-			} catch (...) {
-				// No match is not failure, just that this tuple assignment is invalid.
-				return;
-			}
-
-			ResolvExpr::CandidateList & cands = finder.candidates;
-			assert( 1 == cands.size() );
-			assert( cands.front()->expr );
-			crnt.emplace_back( std::move( cands.front() ) );
-		}
-
-		// extract expressions from the assignment candidates to produce a list of assignments
-		// that together form a sigle candidate
-		std::vector< ast::ptr< ast::Expr > > solved;
-		for ( ResolvExpr::CandidateRef & cand : crnt ) {
-			solved.emplace_back( cand->expr );
-			matcher->combineState( *cand );
-		}
-
-		crntFinder.candidates.emplace_back( std::make_shared< ResolvExpr::Candidate >(
-			new ast::TupleAssignExpr(
-				matcher->location, std::move( solved ), std::move( matcher->tmpDecls ) ),
-			std::move( matcher->env ), std::move( matcher->open ), std::move( matcher->need ),
-			ResolvExpr::sumCost( crnt ) + matcher->baseCost ) );
-	}
-};
-
-} // anonymous namespace
-
-void handleTupleAssignment(
-	ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
-	std::vector< ResolvExpr::CandidateFinder > & args
-) {
-	TupleAssignSpotter spotter( finder );
-	spotter.spot( assign, args );
-}
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/TupleAssignment.cpp
===================================================================
--- src/Tuples/TupleAssignment.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/TupleAssignment.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,394 @@
+//
+// 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.
+//
+// TupleAssignment.cpp --
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Mar 16 14:06:00 2022
+// Update Count     : 10
+//
+
+#include "Tuples.hpp"
+
+#include <algorithm>                       // for transform
+#include <cassert>                         // for assert
+#include <iterator>                        // for back_insert_iterator, back...
+#include <list>                            // for _List_const_iterator, _Lis...
+#include <memory>                          // for unique_ptr, allocator_trai...
+#include <string>                          // for string
+#include <vector>
+
+#include "AST/Decl.hpp"
+#include "AST/Init.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/TypeEnvironment.hpp"
+#include "CodeGen/OperatorTable.hpp"
+#include "Common/UniqueName.hpp"           // for UniqueName
+#include "Common/Utility.hpp"              // for splice, zipWith
+#include "Explode.hpp"                     // for explode
+#include "InitTweak/GenInit.hpp"           // for genCtorInit
+#include "InitTweak/InitTweak.hpp"         // for getPointerBase, isAssignment
+#include "ResolvExpr/CandidateFinder.hpp"  // for CandidateFinder
+#include "ResolvExpr/Cost.hpp"             // for Cost
+#include "ResolvExpr/Resolver.hpp"         // for resolveCtorInit
+#include "ResolvExpr/Typeops.hpp"          // for combos
+
+#if 0
+#define PRINT(x) x
+#else
+#define PRINT(x)
+#endif
+
+namespace Tuples {
+
+namespace {
+
+/// Checks if `expr` is of tuple type.
+bool isTuple( const ast::Expr * expr ) {
+	if ( !expr ) return false;
+	assert( expr->result );
+	return dynamic_cast< const ast::TupleType * >( expr->result->stripReferences() );
+}
+
+/// Checks if `expr` is of tuple type or a cast to one.
+bool refToTuple( const ast::Expr * expr ) {
+	assert( expr->result );
+	// Check for function returning tuple of reference types.
+	if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
+		return refToTuple( castExpr->arg );
+	} else {
+		return isTuple( expr );
+	}
+}
+
+/// Dispatcher for tuple (multiple and mass) assignment operations.
+class TupleAssignSpotter final {
+	/// Actually finds tuple assignment operations, by subclass.
+	struct Matcher {
+		ResolvExpr::CandidateList lhs, rhs;
+		TupleAssignSpotter & spotter;
+		CodeLocation location;
+		ResolvExpr::Cost baseCost;
+		std::vector< ast::ptr< ast::ObjectDecl > > tmpDecls;
+		ast::TypeEnvironment env;
+		ast::OpenVarSet open;
+		ast::AssertionSet need;
+
+		void combineState( const ResolvExpr::Candidate & cand ) {
+			env.simpleCombine( cand.env );
+			ast::mergeOpenVars( open, cand.open );
+			need.insert( cand.need.begin(), cand.need.end() );
+		}
+
+		Matcher(
+			TupleAssignSpotter & s, const CodeLocation & loc,
+			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
+		: lhs( l ), rhs( r ), spotter( s ), location( loc ),
+		  baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ), tmpDecls(),
+		  env(), open(), need() {
+			for ( auto & cand : lhs ) combineState( *cand );
+			for ( auto & cand : rhs ) combineState( *cand );
+		}
+		virtual ~Matcher() = default;
+
+		virtual std::vector< ast::ptr< ast::Expr > > match() = 0;
+
+		/// Removes environments from subexpressions within statement expressions, which could
+		/// throw off later passes like those in Box which rely on PolyMutator, and adds the
+		/// bindings to the env.
+		struct EnvRemover {
+			/// Environment to hoist ExprStmt environments to.
+			ast::TypeEnvironment & tenv;
+
+			EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {}
+
+			const ast::ExprStmt * previsit( const ast::ExprStmt * stmt ) {
+				if ( stmt->expr->env ) {
+					tenv.add( *stmt->expr->env );
+					ast::ExprStmt * mut = mutate( stmt );
+					mut->expr.get_and_mutate()->env = nullptr;
+					return mut;
+				}
+				return stmt;
+			}
+		};
+
+		ast::ObjectDecl * newObject( UniqueName & namer, const ast::Expr * expr ) {
+			assert( expr->result && !expr->result->isVoid() );
+
+			ast::ObjectDecl * ret = new ast::ObjectDecl(
+				location, namer.newName(), expr->result, new ast::SingleInit( location, expr ),
+				ast::Storage::Classes{}, ast::Linkage::Cforall );
+
+			// If expression type is a reference, just need an initializer, otherwise construct.
+			if ( ! expr->result.as< ast::ReferenceType >() ) {
+				// Resolve ctor/dtor for the new object.
+				ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
+						InitTweak::genCtorInit( location, ret ), spotter.crntFinder.context );
+				// Remove environments from subexpressions of stmtExpr.
+				ast::Pass< EnvRemover > rm( env );
+				ret->init = ctorInit->accept( rm );
+			}
+
+			PRINT( std::cerr << "new object: " << ret << std::endl; )
+			return ret;
+		}
+
+		ast::UntypedExpr * createFunc(
+			const std::string & fname, const ast::ObjectDecl * left,
+			const ast::ObjectDecl * right
+		) {
+			assert( left );
+			std::vector< ast::ptr< ast::Expr > > args;
+			args.emplace_back( new ast::VariableExpr( location, left ) );
+			if ( right ) { args.emplace_back( new ast::VariableExpr( location, right ) ); }
+
+			if ( left->type->referenceDepth() > 1 && CodeGen::isConstructor( fname ) ) {
+				args.front() = new ast::AddressExpr( location, args.front() );
+				if ( right ) { args.back() = new ast::AddressExpr( location, args.back() ); }
+				return new ast::UntypedExpr(
+					location, new ast::NameExpr( location, "?=?" ), std::move( args ) );
+			} else {
+				return new ast::UntypedExpr(
+					location, new ast::NameExpr( location, fname ), std::move( args ) );
+			}
+		}
+	};
+
+	/// Finds mass-assignment operations.
+	struct MassAssignMatcher final : public Matcher {
+		MassAssignMatcher(
+			TupleAssignSpotter & s, const CodeLocation & loc,
+			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
+		: Matcher( s, loc, l, r ) {}
+
+		std::vector< ast::ptr< ast::Expr > > match() override {
+			static UniqueName lhsNamer( "__massassign_L" );
+			static UniqueName rhsNamer( "__massassign_R" );
+			// Empty tuple case falls into this matcher.
+			assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 );
+
+			ast::ptr< ast::ObjectDecl > rtmp =
+				1 == rhs.size() ? newObject( rhsNamer, rhs.front()->expr ) : nullptr;
+
+			std::vector< ast::ptr< ast::Expr > > out;
+			for ( ResolvExpr::CandidateRef & lhsCand : lhs ) {
+				// Create a temporary object for each value in
+				// the LHS and create a call involving the RHS.
+				ast::ptr< ast::ObjectDecl > ltmp = newObject( lhsNamer, lhsCand->expr );
+				out.emplace_back( createFunc( spotter.fname, ltmp, rtmp ) );
+				tmpDecls.emplace_back( std::move( ltmp ) );
+			}
+			if ( rtmp ) tmpDecls.emplace_back( std::move( rtmp ) );
+
+			return out;
+		}
+	};
+
+	/// Finds multiple-assignment operations.
+	struct MultipleAssignMatcher final : public Matcher {
+		MultipleAssignMatcher(
+			TupleAssignSpotter & s, const CodeLocation & loc,
+			const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
+		: Matcher( s, loc, l, r ) {}
+
+		std::vector< ast::ptr< ast::Expr > > match() override {
+			static UniqueName lhsNamer( "__multassign_L" );
+			static UniqueName rhsNamer( "__multassign_R" );
+
+			if ( lhs.size() != rhs.size() ) return {};
+
+			// Produce a new temporary object for each value in
+			// the LHS and RHS and pairwise create the calls.
+			std::vector< ast::ptr< ast::ObjectDecl > > ltmp, rtmp;
+
+			std::vector< ast::ptr< ast::Expr > > out;
+			for ( unsigned i = 0; i < lhs.size(); ++i ) {
+				ResolvExpr::CandidateRef & lhsCand = lhs[i];
+				ResolvExpr::CandidateRef & rhsCand = rhs[i];
+
+				// Convert RHS to LHS type minus one reference --
+				// important for case where LHS is && and RHS is lvalue.
+				auto lhsType = lhsCand->expr->result.strict_as< ast::ReferenceType >();
+				rhsCand->expr = new ast::CastExpr( rhsCand->expr, lhsType->base );
+				ast::ptr< ast::ObjectDecl > lobj = newObject( lhsNamer, lhsCand->expr );
+				ast::ptr< ast::ObjectDecl > robj = newObject( rhsNamer, rhsCand->expr );
+				out.emplace_back( createFunc( spotter.fname, lobj, robj ) );
+				ltmp.emplace_back( std::move( lobj ) );
+				rtmp.emplace_back( std::move( robj ) );
+
+				// Resolve the cast expression so that rhsCand return type is bound
+				// by the cast type as needed, and transfer the resulting environment.
+				ResolvExpr::CandidateFinder finder( spotter.crntFinder.context, env );
+				finder.find( rhsCand->expr, ResolvExpr::ResolveMode::withAdjustment() );
+				assert( 1 == finder.candidates.size() );
+				env = std::move( finder.candidates.front()->env );
+			}
+
+			splice( tmpDecls, ltmp );
+			splice( tmpDecls, rtmp );
+
+			return out;
+		}
+	};
+
+	ResolvExpr::CandidateFinder & crntFinder;
+	std::string fname;
+	std::unique_ptr< Matcher > matcher;
+
+public:
+	TupleAssignSpotter( ResolvExpr::CandidateFinder & f )
+	: crntFinder( f ), fname(), matcher() {}
+
+	// Find left- and right-hand-sides for mass or multiple assignment.
+	void spot(
+		const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args
+	) {
+		if ( auto op = expr->func.as< ast::NameExpr >() ) {
+			// Skip non-assignment functions.
+			if ( !CodeGen::isCtorDtorAssign( op->name ) ) return;
+			fname = op->name;
+
+			// Handled by CandidateFinder if applicable (both odd cases).
+			if ( args.empty() || ( 1 == args.size() && CodeGen::isAssignment( fname ) ) ) {
+				return;
+			}
+
+			// Look over all possible left-hand-side.
+			for ( ResolvExpr::CandidateRef & lhsCand : args[0] ) {
+				// Skip non-tuple LHS.
+				if ( !refToTuple( lhsCand->expr ) ) continue;
+
+				// Explode is aware of casts - ensure every LHS
+				// is sent into explode with a reference cast.
+				if ( !lhsCand->expr.as< ast::CastExpr >() ) {
+					lhsCand->expr = new ast::CastExpr(
+						lhsCand->expr, new ast::ReferenceType( lhsCand->expr->result ) );
+				}
+
+				// Explode the LHS so that each field of a tuple-valued expr is assigned.
+				ResolvExpr::CandidateList lhs;
+				explode( *lhsCand, crntFinder.context.symtab, back_inserter(lhs), true );
+				for ( ResolvExpr::CandidateRef & cand : lhs ) {
+					// Each LHS value must be a reference - some come in
+					// with a cast, if not just cast to reference here.
+					if ( !cand->expr->result.as< ast::ReferenceType >() ) {
+						cand->expr = new ast::CastExpr(
+							cand->expr, new ast::ReferenceType( cand->expr->result ) );
+					}
+				}
+
+				if ( 1 == args.size() ) {
+					// Mass default-initialization/destruction.
+					ResolvExpr::CandidateList rhs{};
+					matcher.reset( new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
+					match();
+				} else if ( 2 == args.size() ) {
+					for ( const ResolvExpr::CandidateRef & rhsCand : args[1] ) {
+						ResolvExpr::CandidateList rhs;
+						if ( isTuple( rhsCand->expr ) ) {
+							// Multiple assignment:
+							explode( *rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
+							matcher.reset(
+								new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
+						} else {
+							// Mass assignment:
+							rhs.emplace_back( rhsCand );
+							matcher.reset(
+								new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
+						}
+						match();
+					}
+				} else {
+					// Expand all possible RHS possibilities.
+					std::vector< ResolvExpr::CandidateList > rhsCands;
+					combos(
+						std::next( args.begin(), 1 ), args.end(), back_inserter( rhsCands ) );
+					for ( const ResolvExpr::CandidateList & rhsCand : rhsCands ) {
+						// Multiple assignment:
+						ResolvExpr::CandidateList rhs;
+						explode( rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
+						matcher.reset(
+							new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
+						match();
+					}
+				}
+			}
+		}
+	}
+
+	void match() {
+		assert( matcher );
+
+		std::vector< ast::ptr< ast::Expr > > newAssigns = matcher->match();
+
+		if ( !( matcher->lhs.empty() && matcher->rhs.empty() ) ) {
+			// If both LHS and RHS are empty than this is the empty tuple
+			// case, wherein it's okay for newAssigns to be empty. Otherwise,
+			// return early so that no new candidates are generated.
+			if ( newAssigns.empty() ) return;
+		}
+
+		ResolvExpr::CandidateList crnt;
+		// Now resolve new assignments.
+		for ( const ast::Expr * expr : newAssigns ) {
+			PRINT(
+				std::cerr << "== resolving tuple assign ==" << std::endl;
+				std::cerr << expr << std::endl;
+			)
+
+			ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env );
+			finder.allowVoid = true;
+
+			try {
+				finder.find( expr, ResolvExpr::ResolveMode::withAdjustment() );
+			} catch (...) {
+				// No match is not failure, just that this tuple assignment is invalid.
+				return;
+			}
+
+			ResolvExpr::CandidateList & cands = finder.candidates;
+			assert( 1 == cands.size() );
+			assert( cands.front()->expr );
+			crnt.emplace_back( std::move( cands.front() ) );
+		}
+
+		// extract expressions from the assignment candidates to produce a list of assignments
+		// that together form a sigle candidate
+		std::vector< ast::ptr< ast::Expr > > solved;
+		for ( ResolvExpr::CandidateRef & cand : crnt ) {
+			solved.emplace_back( cand->expr );
+			matcher->combineState( *cand );
+		}
+
+		crntFinder.candidates.emplace_back( std::make_shared< ResolvExpr::Candidate >(
+			new ast::TupleAssignExpr(
+				matcher->location, std::move( solved ), std::move( matcher->tmpDecls ) ),
+			std::move( matcher->env ), std::move( matcher->open ), std::move( matcher->need ),
+			ResolvExpr::sumCost( crnt ) + matcher->baseCost ) );
+	}
+};
+
+} // anonymous namespace
+
+void handleTupleAssignment(
+	ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
+	std::vector< ResolvExpr::CandidateFinder > & args
+) {
+	TupleAssignSpotter spotter( finder );
+	spotter.spot( assign, args );
+}
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/TupleExpansion.cpp
===================================================================
--- src/Tuples/TupleExpansion.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Tuples/TupleExpansion.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,8 +14,8 @@
 //
 
-#include "Tuples.h"
+#include "Tuples.hpp"
 
 #include "AST/Pass.hpp"
-#include "Common/ScopedMap.h"
+#include "Common/ScopedMap.hpp"
 
 namespace Tuples {
Index: src/Tuples/Tuples.cc
===================================================================
--- src/Tuples/Tuples.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,70 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Tuples.cc -- A collection of tuple operations.
-//
-// Author           : Andrew Beach
-// Created On       : Mon Jun 17 14:41:00 2019
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon May 16 16:15:00 2022
-// Update Count     : 2
-//
-
-#include "Tuples.h"
-
-#include "AST/Pass.hpp"
-#include "AST/Inspect.hpp"
-#include "AST/LinkageSpec.hpp"
-#include "InitTweak/InitTweak.h"
-
-namespace Tuples {
-
-namespace {
-
-/// Determines if impurity (read: side-effects) may exist in a piece of code.
-/// Currently gives a very crude approximation, wherein almost any function
-/// call expression means the code may be impure.
-struct ImpurityDetector : public ast::WithShortCircuiting {
-	bool result = false;
-
-	void previsit( ast::ApplicationExpr const * appExpr ) {
-		if ( ast::DeclWithType const * function = ast::getFunction( appExpr ) ) {
-			if ( function->linkage == ast::Linkage::Intrinsic
-					&& ( function->name == "*?" || function->name == "?[?]" ) ) {
-				return;
-			}
-		}
-		result = true; visit_children = false;
-	}
-	void previsit( ast::UntypedExpr const * ) {
-		result = true; visit_children = false;
-	}
-};
-
-struct ImpurityDetectorIgnoreUnique : public ImpurityDetector {
-	using ImpurityDetector::previsit;
-	void previsit( ast::UniqueExpr const * ) {
-		visit_children = false;
-	}
-};
-
-} // namespace
-
-bool maybeImpure( const ast::Expr * expr ) {
-	return ast::Pass<ImpurityDetector>::read( expr );
-}
-
-bool maybeImpureIgnoreUnique( const ast::Expr * expr ) {
-	return ast::Pass<ImpurityDetectorIgnoreUnique>::read( expr );
-}
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/Tuples.cpp
===================================================================
--- src/Tuples/Tuples.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/Tuples.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,70 @@
+//
+// 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.
+//
+// Tuples.cpp -- A collection of tuple operations.
+//
+// Author           : Andrew Beach
+// Created On       : Mon Jun 17 14:41:00 2019
+// Last Modified By : Andrew Beach
+// Last Modified On : Mon May 16 16:15:00 2022
+// Update Count     : 2
+//
+
+#include "Tuples.hpp"
+
+#include "AST/Pass.hpp"
+#include "AST/Inspect.hpp"
+#include "AST/LinkageSpec.hpp"
+#include "InitTweak/InitTweak.hpp"
+
+namespace Tuples {
+
+namespace {
+
+/// Determines if impurity (read: side-effects) may exist in a piece of code.
+/// Currently gives a very crude approximation, wherein almost any function
+/// call expression means the code may be impure.
+struct ImpurityDetector : public ast::WithShortCircuiting {
+	bool result = false;
+
+	void previsit( ast::ApplicationExpr const * appExpr ) {
+		if ( ast::DeclWithType const * function = ast::getFunction( appExpr ) ) {
+			if ( function->linkage == ast::Linkage::Intrinsic
+					&& ( function->name == "*?" || function->name == "?[?]" ) ) {
+				return;
+			}
+		}
+		result = true; visit_children = false;
+	}
+	void previsit( ast::UntypedExpr const * ) {
+		result = true; visit_children = false;
+	}
+};
+
+struct ImpurityDetectorIgnoreUnique : public ImpurityDetector {
+	using ImpurityDetector::previsit;
+	void previsit( ast::UniqueExpr const * ) {
+		visit_children = false;
+	}
+};
+
+} // namespace
+
+bool maybeImpure( const ast::Expr * expr ) {
+	return ast::Pass<ImpurityDetector>::read( expr );
+}
+
+bool maybeImpureIgnoreUnique( const ast::Expr * expr ) {
+	return ast::Pass<ImpurityDetectorIgnoreUnique>::read( expr );
+}
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/Tuples.h
===================================================================
--- src/Tuples/Tuples.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,63 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Tuples.h -- A collection of tuple operations.
-//
-// Author           : Rodolfo G. Esteves
-// Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Henry Xue
-// Last Modified On : Mon Aug 23 15:36:09 2021
-// Update Count     : 19
-//
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include "AST/Fwd.hpp"
-#include "AST/Node.hpp"
-namespace ResolvExpr {
-	class CandidateFinder;
-}
-
-namespace Tuples {
-
-// TupleAssignment.cc
-void handleTupleAssignment(
-	ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
-	std::vector< ResolvExpr::CandidateFinder > & args );
-
-// TupleExpansion.cc
-/// Expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate.
-void expandMemberTuples( ast::TranslationUnit & translationUnit );
-
-/// Replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc.
-void expandTuples( ast::TranslationUnit & translaionUnit );
-
-/// Replaces UniqueExprs with a temporary variable and one call.
-void expandUniqueExpr( ast::TranslationUnit & translationUnit );
-
-/// Returns VoidType if any of the expressions have Voidtype, otherwise TupleType of the Expression result types.
-const ast::Type * makeTupleType( const std::vector<ast::ptr<ast::Expr>> & exprs );
-
-/// Returns a TypeInstType if `type` is a ttype, nullptr otherwise
-const ast::TypeInstType * isTtype( const ast::Type * type );
-
-/// Returns true if the expression may contain side-effects.
-bool maybeImpure( const ast::Expr * expr );
-
-/// Returns true if the expression may contain side-effect,
-/// ignoring the presence of unique expressions.
-bool maybeImpureIgnoreUnique( const ast::Expr * expr );
-
-} // namespace Tuples
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Tuples/Tuples.hpp
===================================================================
--- src/Tuples/Tuples.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Tuples/Tuples.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,63 @@
+//
+// 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.
+//
+// Tuples.hpp -- A collection of tuple operations.
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Henry Xue
+// Last Modified On : Mon Aug 23 15:36:09 2021
+// Update Count     : 19
+//
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "AST/Fwd.hpp"
+#include "AST/Node.hpp"
+namespace ResolvExpr {
+	class CandidateFinder;
+}
+
+namespace Tuples {
+
+// TupleAssignment.cc
+void handleTupleAssignment(
+	ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
+	std::vector< ResolvExpr::CandidateFinder > & args );
+
+// TupleExpansion.cc
+/// Expands z.[a, b.[x, y], c] into [z.a, z.b.x, z.b.y, z.c], inserting UniqueExprs as appropriate.
+void expandMemberTuples( ast::TranslationUnit & translationUnit );
+
+/// Replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc.
+void expandTuples( ast::TranslationUnit & translaionUnit );
+
+/// Replaces UniqueExprs with a temporary variable and one call.
+void expandUniqueExpr( ast::TranslationUnit & translationUnit );
+
+/// Returns VoidType if any of the expressions have Voidtype, otherwise TupleType of the Expression result types.
+const ast::Type * makeTupleType( const std::vector<ast::ptr<ast::Expr>> & exprs );
+
+/// Returns a TypeInstType if `type` is a ttype, nullptr otherwise
+const ast::TypeInstType * isTtype( const ast::Type * type );
+
+/// Returns true if the expression may contain side-effects.
+bool maybeImpure( const ast::Expr * expr );
+
+/// Returns true if the expression may contain side-effect,
+/// ignoring the presence of unique expressions.
+bool maybeImpureIgnoreUnique( const ast::Expr * expr );
+
+} // namespace Tuples
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Tuples/module.mk
===================================================================
--- src/Tuples/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Tuples/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,10 +16,10 @@
 
 SRC_TUPLES = \
-	Tuples/Explode.cc \
-	Tuples/Explode.h \
-	Tuples/TupleAssignment.cc \
+	Tuples/Explode.cpp \
+	Tuples/Explode.hpp \
+	Tuples/TupleAssignment.cpp \
 	Tuples/TupleExpansion.cpp \
-	Tuples/Tuples.cc \
-	Tuples/Tuples.h
+	Tuples/Tuples.cpp \
+	Tuples/Tuples.hpp
 
 SRC += $(SRC_TUPLES)
Index: src/Validate/Autogen.cpp
===================================================================
--- src/Validate/Autogen.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/Autogen.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,11 +16,11 @@
 #include "Autogen.hpp"
 
-#include <algorithm>               // for count_if
-#include <cassert>                 // for strict_dynamic_cast, assert, assertf
-#include <iterator>                // for back_insert_iterator, back_inserter
-#include <list>                    // for list, _List_iterator, list<>::iter...
-#include <set>                     // for set, _Rb_tree_const_iterator
-#include <utility>                 // for pair
-#include <vector>                  // for vector
+#include <algorithm>                   // for count_if
+#include <cassert>                     // for strict_dynamic_cast, assert, a...
+#include <iterator>                    // for back_insert_iterator, back_ins...
+#include <list>                        // for list, _List_iterator, list<>::...
+#include <set>                         // for set, _Rb_tree_const_iterator
+#include <utility>                     // for pair
+#include <vector>                      // for vector
 
 #include "AST/Attribute.hpp"
@@ -34,12 +34,12 @@
 #include "AST/Stmt.hpp"
 #include "AST/SymbolTable.hpp"
-#include "CodeGen/OperatorTable.h" // for isCtorDtor, isCtorDtorAssign
-#include "Common/ScopedMap.h"      // for ScopedMap<>::const_iterator, Scope...
-#include "Common/utility.h"        // for cloneAll, operator+
-#include "GenPoly/ScopedSet.h"     // for ScopedSet, ScopedSet<>::iterator
-#include "InitTweak/GenInit.h"     // for fixReturnStatements
-#include "InitTweak/InitTweak.h"   // for isAssignment, isCopyConstructor
+#include "CodeGen/OperatorTable.hpp"   // for isCtorDtor, isCtorDtorAssign
+#include "Common/ScopedMap.hpp"        // for ScopedMap<>::const_iterator, S...
+#include "Common/Utility.hpp"          // for cloneAll, operator+
+#include "GenPoly/ScopedSet.hpp"       // for ScopedSet, ScopedSet<>::iterator
+#include "InitTweak/GenInit.hpp"       // for fixReturnStatements
+#include "InitTweak/InitTweak.hpp"     // for isAssignment, isCopyConstructor
 #include "SymTab/GenImplicitCall.hpp"  // for genImplicitCall
-#include "SymTab/Mangler.h"        // for Mangler
+#include "SymTab/Mangler.hpp"          // for Mangler
 #include "CompilationState.hpp"
 
Index: src/Validate/CompoundLiteral.cpp
===================================================================
--- src/Validate/CompoundLiteral.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/CompoundLiteral.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,5 +20,5 @@
 #include "AST/Pass.hpp"
 #include "AST/TranslationUnit.hpp"
-#include "Common/UniqueName.h"
+#include "Common/UniqueName.hpp"
 
 namespace Validate {
Index: src/Validate/EliminateTypedef.cpp
===================================================================
--- src/Validate/EliminateTypedef.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/EliminateTypedef.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -21,5 +21,5 @@
 #include "AST/Pass.hpp"
 #include "AST/Stmt.hpp"
-#include "Common/utility.h"
+#include "Common/Utility.hpp"
 
 namespace Validate {
Index: src/Validate/EnumAndPointerDecay.cpp
===================================================================
--- src/Validate/EnumAndPointerDecay.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/EnumAndPointerDecay.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,5 +20,5 @@
 #include "AST/Pass.hpp"
 #include "AST/Type.hpp"
-#include "SymTab/FixFunction.h"
+#include "SymTab/FixFunction.hpp"
 #include "Validate/NoIdSymbolTable.hpp"
 
Index: src/Validate/FindSpecialDecls.cpp
===================================================================
--- src/Validate/FindSpecialDecls.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/FindSpecialDecls.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -14,5 +14,5 @@
 //
 
-#include "Validate/FindSpecialDecls.h"
+#include "Validate/FindSpecialDecls.hpp"
 
 #include "AST/Decl.hpp"
Index: src/Validate/FindSpecialDecls.h
===================================================================
--- src/Validate/FindSpecialDecls.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,34 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// FindSpecialDeclarations.h -- Find special declarations used in the compiler.
-//
-// Author           : Rob Schluntz
-// Created On       : Thu Aug 30 09:49:02 2018
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Nov 10 15:16:00 2021
-// Update Count     : 3
-//
-
-#pragma once
-
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace Validate {
-
-/// Find and remember some of the special declarations that are useful for
-/// generating code, so that they do not have to be discovered multiple times.
-void findGlobalDecls( ast::TranslationUnit & translationUnit );
-
-} // namespace Validate
-
-// Local Variables: //
-// tab-width: 4 //
-// mode: c++ //
-// compile-command: "make install" //
-// End: //
Index: src/Validate/FindSpecialDecls.hpp
===================================================================
--- src/Validate/FindSpecialDecls.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Validate/FindSpecialDecls.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,34 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// FindSpecialDeclarations.hpp -- Find special declarations used in the compiler.
+//
+// Author           : Rob Schluntz
+// Created On       : Thu Aug 30 09:49:02 2018
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Nov 10 15:16:00 2021
+// Update Count     : 3
+//
+
+#pragma once
+
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace Validate {
+
+/// Find and remember some of the special declarations that are useful for
+/// generating code, so that they do not have to be discovered multiple times.
+void findGlobalDecls( ast::TranslationUnit & translationUnit );
+
+} // namespace Validate
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Validate/FixQualifiedTypes.cpp
===================================================================
--- src/Validate/FixQualifiedTypes.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/FixQualifiedTypes.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -21,5 +21,5 @@
 #include "AST/TranslationUnit.hpp"
 #include "Common/ToString.hpp"             // for toString
-#include "SymTab/Mangler.h"                // for Mangler
+#include "SymTab/Mangler.hpp"              // for Mangler
 #include "Validate/NoIdSymbolTable.hpp"
 
Index: src/Validate/FixReturnTypes.cpp
===================================================================
--- src/Validate/FixReturnTypes.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/FixReturnTypes.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,5 +20,5 @@
 #include "AST/Type.hpp"
 #include "CodeGen/CodeGenerator.hpp"
-#include "ResolvExpr/Unify.h"
+#include "ResolvExpr/Unify.hpp"
 
 namespace Validate {
Index: src/Validate/ForallPointerDecay.cpp
===================================================================
--- src/Validate/ForallPointerDecay.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/ForallPointerDecay.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -20,9 +20,9 @@
 #include "AST/DeclReplacer.hpp"
 #include "AST/Pass.hpp"
-#include "CodeGen/OperatorTable.h"
-#include "Common/CodeLocation.h"
+#include "CodeGen/OperatorTable.hpp"
+#include "Common/CodeLocation.hpp"
 #include "Common/ToString.hpp"
-#include "Common/utility.h"
-#include "SymTab/FixFunction.h"
+#include "Common/Utility.hpp"
+#include "SymTab/FixFunction.hpp"
 
 namespace Validate {
Index: src/Validate/ImplementEnumFunc.cpp
===================================================================
--- src/Validate/ImplementEnumFunc.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/ImplementEnumFunc.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -2,6 +2,6 @@
 #include "AST/Pass.hpp"
 #include "AST/TranslationUnit.hpp"
-#include "CodeGen/OperatorTable.h"  // for isCtorDtor, isCtorDtorAssign
-#include "InitTweak/InitTweak.h"    // for isAssignment, isCopyConstructor
+#include "CodeGen/OperatorTable.hpp"  // for isCtorDtor, isCtorDtorAssign
+#include "InitTweak/InitTweak.hpp"    // for isAssignment, isCopyConstructor
 namespace Validate {
 
Index: src/Validate/ReplaceTypedef.cpp
===================================================================
--- src/Validate/ReplaceTypedef.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/ReplaceTypedef.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -18,7 +18,7 @@
 #include "AST/Copy.hpp"
 #include "AST/Pass.hpp"
-#include "Common/ScopedMap.h"
-#include "Common/UniqueName.h"
-#include "ResolvExpr/Unify.h"
+#include "Common/ScopedMap.hpp"
+#include "Common/UniqueName.hpp"
+#include "ResolvExpr/Unify.hpp"
 
 namespace Validate {
Index: src/Validate/VerifyCtorDtorAssign.cpp
===================================================================
--- src/Validate/VerifyCtorDtorAssign.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Validate/VerifyCtorDtorAssign.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -17,5 +17,5 @@
 
 #include "AST/Pass.hpp"
-#include "CodeGen/OperatorTable.h"
+#include "CodeGen/OperatorTable.hpp"
 
 namespace Validate {
Index: src/Virtual/ExpandCasts.cc
===================================================================
--- src/Virtual/ExpandCasts.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,272 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ExpandCasts.cc --
-//
-// Author           : Andrew Beach
-// Created On       : Mon Jul 24 13:59:00 2017
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Nov 27 09:28:20 2023
-// Update Count     : 10
-//
-
-#include "ExpandCasts.h"
-
-#include <cassert>                 // for assert, assertf
-#include <iterator>                // for back_inserter, inserter
-#include <string>                  // for string, allocator, operator==, ope...
-
-#include "AST/Copy.hpp"
-#include "AST/Decl.hpp"
-#include "AST/Expr.hpp"
-#include "AST/Pass.hpp"
-#include "Common/ScopedMap.h"      // for ScopedMap
-#include "Common/SemanticError.h"  // for SemanticError
-#include "SymTab/Mangler.h"        // for mangleType
-
-namespace Virtual {
-
-namespace {
-
-bool is_prefix( const std::string & prefix, const std::string& entire ) {
-	size_t const p_size = prefix.size();
-	return (p_size < entire.size() && prefix == entire.substr(0, p_size));
-}
-
-bool is_type_id_object( const ast::ObjectDecl * decl ) {
-	return is_prefix( "__cfatid_", decl->name );
-}
-
-	// Indented until the new ast code gets added.
-
-	/// Maps virtual table types the instance for that type.
-
-/// Better error locations for generated casts.
-// TODO: Does the improved distribution of code locations make this unneeded?
-CodeLocation castLocation( const ast::VirtualCastExpr * castExpr ) {
-	if ( castExpr->location.isSet() ) {
-		return castExpr->location;
-	} else if ( castExpr->arg->location.isSet() ) {
-		return castExpr->arg->location;
-	} else {
-		return CodeLocation();
-	}
-}
-
-[[noreturn]] void castError( ast::VirtualCastExpr const * castExpr, std::string const & message ) {
-	SemanticError( castLocation( castExpr ), message );
-}
-
-class TypeIdTable final {
-	ScopedMap<std::string, ast::ObjectDecl const *> instances;
-public:
-	void enterScope() { instances.beginScope(); }
-	void leaveScope() { instances.endScope(); }
-
-	// Attempt to insert an instance into the map. If there is a conflict,
-	// returns the previous declaration for error messages.
-	ast::ObjectDecl const * insert( ast::ObjectDecl const * typeIdDecl ) {
-		std::string mangledName = Mangle::mangleType( typeIdDecl->type );
-		ast::ObjectDecl const *& value = instances[ mangledName ];
-		if ( value ) {
-			if ( typeIdDecl->storage.is_extern ) {
-				return nullptr;
-			} else if ( !value->storage.is_extern ) {
-				return value;
-			}
-		}
-		value = typeIdDecl;
-		return nullptr;
-	}
-
-	ast::ObjectDecl const * lookup( ast::Type const * typeIdType ) {
-		std::string mangledName = Mangle::mangleType( typeIdType );
-		auto const it = instances.find( mangledName );
-		return ( instances.end() == it ) ? nullptr : it->second;
-	}
-};
-
-struct ExpandCastsCore final {
-	void previsit( ast::FunctionDecl const * decl );
-	void previsit( ast::StructDecl const * decl );
-	void previsit( ast::ObjectDecl const * decl );
-	ast::Expr const * postvisit( ast::VirtualCastExpr const * expr );
-
-	ast::CastExpr const * cast_to_type_id(
-		ast::Expr const * expr, unsigned int level_of_indirection );
-
-	ast::FunctionDecl const * vcast_decl = nullptr;
-	ast::StructDecl const * info_decl = nullptr;
-
-	TypeIdTable symtab;
-};
-
-void ExpandCastsCore::previsit( ast::FunctionDecl const * decl ) {
-	if ( !vcast_decl && "__cfavir_virtual_cast" == decl->name ) {
-		vcast_decl = decl;
-	}
-}
-
-void ExpandCastsCore::previsit( ast::StructDecl const * decl ) {
-	if ( !info_decl && decl->body && "__cfavir_type_info" == decl->name ) {
-		info_decl = decl;
-	}
-}
-
-void ExpandCastsCore::previsit( ast::ObjectDecl const * decl ) {
-	if ( is_type_id_object( decl ) ) {
-		// Multiple definitions should be fine because of linkonce.
-		symtab.insert( decl );
-	}
-}
-
-/// Get the base type from a pointer or reference.
-ast::Type const * getBaseType( ast::ptr<ast::Type> const & type ) {
-	if ( auto target = type.as<ast::PointerType>() ) {
-		return target->base.get();
-	} else if ( auto target = type.as<ast::ReferenceType>() ) {
-		return target->base.get();
-	} else {
-		return nullptr;
-	}
-}
-
-/// Copy newType, but give the copy the params of the oldType.
-ast::StructInstType * polyCopy(
-		ast::StructInstType const * oldType,
-		ast::StructInstType const * newType ) {
-	assert( oldType->params.size() == newType->params.size() );
-	ast::StructInstType * retType = ast::deepCopy( newType );
-	if ( ! oldType->params.empty() ) {
-		retType->params.clear();
-		for ( auto oldParams : oldType->params ) {
-			retType->params.push_back( ast::deepCopy( oldParams ) );
-		}
-	}
-	return retType;
-}
-
-/// Follow the "head" field of the structure to get the type that is pointed
-/// to by that field.
-ast::StructInstType const * followHeadPointerType(
-		CodeLocation const & errorLocation,
-		ast::StructInstType const * oldType,
-		std::string const & fieldName ) {
-	ast::StructDecl const * oldDecl = oldType->base;
-	assert( oldDecl );
-
-	// Helper function for throwing semantic errors.
-	auto throwError = [&fieldName, &errorLocation, &oldDecl]( std::string const & message ) {
-		SemanticError( errorLocation, "While following head pointer of %s named \"%s\": %s",
-					   oldDecl->name.c_str(), fieldName.c_str(), message.c_str() );
-	};
-
-	if ( oldDecl->members.empty() ) {
-		throwError( "Type has no fields." );
-	}
-	ast::ptr<ast::Decl> const & memberDecl = oldDecl->members.front();
-	assert( memberDecl );
-	ast::ObjectDecl const * fieldDecl = memberDecl.as<ast::ObjectDecl>();
-	assert( fieldDecl );
-	if ( fieldName != fieldDecl->name ) {
-		throwError( "Head field did not have expected name." );
-	}
-
-	ast::ptr<ast::Type> const & fieldType = fieldDecl->type;
-	if ( nullptr == fieldType ) {
-		throwError( "Could not get head field." );
-	}
-	auto ptrType = fieldType.as<ast::PointerType>();
-	if ( nullptr == ptrType ) {
-		throwError( "First field is not a pointer type." );
-	}
-	assert( ptrType->base );
-	auto newType = ptrType->base.as<ast::StructInstType>();
-	if ( nullptr == newType ) {
-		throwError( "First field does not point to a structure type." );
-	}
-
-	return polyCopy( oldType, newType );
-}
-
-/// Get the type-id type from a virtual type.
-ast::StructInstType const * getTypeIdType(
-		CodeLocation const & errorLocation,
-		ast::Type const * type ) {
-	auto typeInst = dynamic_cast<ast::StructInstType const *>( type );
-	if ( nullptr == typeInst ) {
-		return nullptr;
-	}
-	ast::ptr<ast::StructInstType> tableInst =
-		followHeadPointerType( errorLocation, typeInst, "virtual_table" );
-	if ( nullptr == tableInst ) {
-		return nullptr;
-	}
-	ast::StructInstType const * typeIdInst =
-		followHeadPointerType( errorLocation, tableInst, "__cfavir_typeid" );
-	return typeIdInst;
-}
-
-ast::Expr const * ExpandCastsCore::postvisit(
-		ast::VirtualCastExpr const * expr ) {
-	assertf( expr->result, "Virtual cast target not found before expansion." );
-
-	assert( vcast_decl );
-	assert( info_decl );
-
-	ast::Type const * base_type = getBaseType( expr->result );
-	if ( nullptr == base_type ) {
-		castError( expr, "Virtual cast target must be a pointer or reference type." );
-	}
-	ast::StructInstType const * type_id_type =
-			getTypeIdType( castLocation( expr ), base_type );
-	if ( nullptr == type_id_type ) {
-		castError( expr, "Ill formed virtual cast target type." );
-	}
-	ast::ObjectDecl const * type_id = symtab.lookup( type_id_type );
-	if ( nullptr == type_id ) {
-		// I'm trying to give a different error for polymorpic types as
-		// different things can go wrong there.
-		if ( type_id_type->params.empty() ) {
-			castError( expr, "Virtual cast does not target a virtual type." );
-		} else {
-			castError( expr, "Virtual cast does not target a type with a "
-				"type id (possible missing virtual table)." );
-		}
-	}
-
-	return new ast::CastExpr( expr->location,
-		new ast::ApplicationExpr( expr->location,
-			ast::VariableExpr::functionPointer( expr->location, vcast_decl ),
-			{
-				cast_to_type_id(
-					new ast::AddressExpr( expr->location,
-						new ast::VariableExpr( expr->location, type_id ) ),
-					1 ),
-				cast_to_type_id( expr->arg, 2 ),
-			}
-		),
-		ast::deepCopy( expr->result )
-	);
-}
-
-ast::CastExpr const * ExpandCastsCore::cast_to_type_id(
-		ast::Expr const * expr, unsigned int level_of_indirection ) {
-	assert( info_decl );
-	ast::Type * type = new ast::StructInstType( info_decl, ast::CV::Const );
-	for ( unsigned int i = 0 ; i < level_of_indirection ; ++i ) {
-		type = new ast::PointerType( type );
-	}
-	return new ast::CastExpr( expr->location, expr, type );
-}
-
-} // namespace
-
-void expandCasts( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<ExpandCastsCore>::run( translationUnit );
-}
-
-} // namespace Virtual
Index: src/Virtual/ExpandCasts.cpp
===================================================================
--- src/Virtual/ExpandCasts.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Virtual/ExpandCasts.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,272 @@
+//
+// 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.
+//
+// ExpandCasts.cpp --
+//
+// Author           : Andrew Beach
+// Created On       : Mon Jul 24 13:59:00 2017
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Nov 27 09:28:20 2023
+// Update Count     : 10
+//
+
+#include "ExpandCasts.hpp"
+
+#include <cassert>                   // for assert, assertf
+#include <iterator>                  // for back_inserter, inserter
+#include <string>                    // for string, allocator, operator==, o...
+
+#include "AST/Copy.hpp"
+#include "AST/Decl.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
+#include "Common/ScopedMap.hpp"      // for ScopedMap
+#include "Common/SemanticError.hpp"  // for SemanticError
+#include "SymTab/Mangler.hpp"        // for mangleType
+
+namespace Virtual {
+
+namespace {
+
+bool is_prefix( const std::string & prefix, const std::string& entire ) {
+	size_t const p_size = prefix.size();
+	return (p_size < entire.size() && prefix == entire.substr(0, p_size));
+}
+
+bool is_type_id_object( const ast::ObjectDecl * decl ) {
+	return is_prefix( "__cfatid_", decl->name );
+}
+
+	// Indented until the new ast code gets added.
+
+	/// Maps virtual table types the instance for that type.
+
+/// Better error locations for generated casts.
+// TODO: Does the improved distribution of code locations make this unneeded?
+CodeLocation castLocation( const ast::VirtualCastExpr * castExpr ) {
+	if ( castExpr->location.isSet() ) {
+		return castExpr->location;
+	} else if ( castExpr->arg->location.isSet() ) {
+		return castExpr->arg->location;
+	} else {
+		return CodeLocation();
+	}
+}
+
+[[noreturn]] void castError( ast::VirtualCastExpr const * castExpr, std::string const & message ) {
+	SemanticError( castLocation( castExpr ), message );
+}
+
+class TypeIdTable final {
+	ScopedMap<std::string, ast::ObjectDecl const *> instances;
+public:
+	void enterScope() { instances.beginScope(); }
+	void leaveScope() { instances.endScope(); }
+
+	// Attempt to insert an instance into the map. If there is a conflict,
+	// returns the previous declaration for error messages.
+	ast::ObjectDecl const * insert( ast::ObjectDecl const * typeIdDecl ) {
+		std::string mangledName = Mangle::mangleType( typeIdDecl->type );
+		ast::ObjectDecl const *& value = instances[ mangledName ];
+		if ( value ) {
+			if ( typeIdDecl->storage.is_extern ) {
+				return nullptr;
+			} else if ( !value->storage.is_extern ) {
+				return value;
+			}
+		}
+		value = typeIdDecl;
+		return nullptr;
+	}
+
+	ast::ObjectDecl const * lookup( ast::Type const * typeIdType ) {
+		std::string mangledName = Mangle::mangleType( typeIdType );
+		auto const it = instances.find( mangledName );
+		return ( instances.end() == it ) ? nullptr : it->second;
+	}
+};
+
+struct ExpandCastsCore final {
+	void previsit( ast::FunctionDecl const * decl );
+	void previsit( ast::StructDecl const * decl );
+	void previsit( ast::ObjectDecl const * decl );
+	ast::Expr const * postvisit( ast::VirtualCastExpr const * expr );
+
+	ast::CastExpr const * cast_to_type_id(
+		ast::Expr const * expr, unsigned int level_of_indirection );
+
+	ast::FunctionDecl const * vcast_decl = nullptr;
+	ast::StructDecl const * info_decl = nullptr;
+
+	TypeIdTable symtab;
+};
+
+void ExpandCastsCore::previsit( ast::FunctionDecl const * decl ) {
+	if ( !vcast_decl && "__cfavir_virtual_cast" == decl->name ) {
+		vcast_decl = decl;
+	}
+}
+
+void ExpandCastsCore::previsit( ast::StructDecl const * decl ) {
+	if ( !info_decl && decl->body && "__cfavir_type_info" == decl->name ) {
+		info_decl = decl;
+	}
+}
+
+void ExpandCastsCore::previsit( ast::ObjectDecl const * decl ) {
+	if ( is_type_id_object( decl ) ) {
+		// Multiple definitions should be fine because of linkonce.
+		symtab.insert( decl );
+	}
+}
+
+/// Get the base type from a pointer or reference.
+ast::Type const * getBaseType( ast::ptr<ast::Type> const & type ) {
+	if ( auto target = type.as<ast::PointerType>() ) {
+		return target->base.get();
+	} else if ( auto target = type.as<ast::ReferenceType>() ) {
+		return target->base.get();
+	} else {
+		return nullptr;
+	}
+}
+
+/// Copy newType, but give the copy the params of the oldType.
+ast::StructInstType * polyCopy(
+		ast::StructInstType const * oldType,
+		ast::StructInstType const * newType ) {
+	assert( oldType->params.size() == newType->params.size() );
+	ast::StructInstType * retType = ast::deepCopy( newType );
+	if ( ! oldType->params.empty() ) {
+		retType->params.clear();
+		for ( auto oldParams : oldType->params ) {
+			retType->params.push_back( ast::deepCopy( oldParams ) );
+		}
+	}
+	return retType;
+}
+
+/// Follow the "head" field of the structure to get the type that is pointed
+/// to by that field.
+ast::StructInstType const * followHeadPointerType(
+		CodeLocation const & errorLocation,
+		ast::StructInstType const * oldType,
+		std::string const & fieldName ) {
+	ast::StructDecl const * oldDecl = oldType->base;
+	assert( oldDecl );
+
+	// Helper function for throwing semantic errors.
+	auto throwError = [&fieldName, &errorLocation, &oldDecl]( std::string const & message ) {
+		SemanticError( errorLocation, "While following head pointer of %s named \"%s\": %s",
+					   oldDecl->name.c_str(), fieldName.c_str(), message.c_str() );
+	};
+
+	if ( oldDecl->members.empty() ) {
+		throwError( "Type has no fields." );
+	}
+	ast::ptr<ast::Decl> const & memberDecl = oldDecl->members.front();
+	assert( memberDecl );
+	ast::ObjectDecl const * fieldDecl = memberDecl.as<ast::ObjectDecl>();
+	assert( fieldDecl );
+	if ( fieldName != fieldDecl->name ) {
+		throwError( "Head field did not have expected name." );
+	}
+
+	ast::ptr<ast::Type> const & fieldType = fieldDecl->type;
+	if ( nullptr == fieldType ) {
+		throwError( "Could not get head field." );
+	}
+	auto ptrType = fieldType.as<ast::PointerType>();
+	if ( nullptr == ptrType ) {
+		throwError( "First field is not a pointer type." );
+	}
+	assert( ptrType->base );
+	auto newType = ptrType->base.as<ast::StructInstType>();
+	if ( nullptr == newType ) {
+		throwError( "First field does not point to a structure type." );
+	}
+
+	return polyCopy( oldType, newType );
+}
+
+/// Get the type-id type from a virtual type.
+ast::StructInstType const * getTypeIdType(
+		CodeLocation const & errorLocation,
+		ast::Type const * type ) {
+	auto typeInst = dynamic_cast<ast::StructInstType const *>( type );
+	if ( nullptr == typeInst ) {
+		return nullptr;
+	}
+	ast::ptr<ast::StructInstType> tableInst =
+		followHeadPointerType( errorLocation, typeInst, "virtual_table" );
+	if ( nullptr == tableInst ) {
+		return nullptr;
+	}
+	ast::StructInstType const * typeIdInst =
+		followHeadPointerType( errorLocation, tableInst, "__cfavir_typeid" );
+	return typeIdInst;
+}
+
+ast::Expr const * ExpandCastsCore::postvisit(
+		ast::VirtualCastExpr const * expr ) {
+	assertf( expr->result, "Virtual cast target not found before expansion." );
+
+	assert( vcast_decl );
+	assert( info_decl );
+
+	ast::Type const * base_type = getBaseType( expr->result );
+	if ( nullptr == base_type ) {
+		castError( expr, "Virtual cast target must be a pointer or reference type." );
+	}
+	ast::StructInstType const * type_id_type =
+			getTypeIdType( castLocation( expr ), base_type );
+	if ( nullptr == type_id_type ) {
+		castError( expr, "Ill formed virtual cast target type." );
+	}
+	ast::ObjectDecl const * type_id = symtab.lookup( type_id_type );
+	if ( nullptr == type_id ) {
+		// I'm trying to give a different error for polymorpic types as
+		// different things can go wrong there.
+		if ( type_id_type->params.empty() ) {
+			castError( expr, "Virtual cast does not target a virtual type." );
+		} else {
+			castError( expr, "Virtual cast does not target a type with a "
+				"type id (possible missing virtual table)." );
+		}
+	}
+
+	return new ast::CastExpr( expr->location,
+		new ast::ApplicationExpr( expr->location,
+			ast::VariableExpr::functionPointer( expr->location, vcast_decl ),
+			{
+				cast_to_type_id(
+					new ast::AddressExpr( expr->location,
+						new ast::VariableExpr( expr->location, type_id ) ),
+					1 ),
+				cast_to_type_id( expr->arg, 2 ),
+			}
+		),
+		ast::deepCopy( expr->result )
+	);
+}
+
+ast::CastExpr const * ExpandCastsCore::cast_to_type_id(
+		ast::Expr const * expr, unsigned int level_of_indirection ) {
+	assert( info_decl );
+	ast::Type * type = new ast::StructInstType( info_decl, ast::CV::Const );
+	for ( unsigned int i = 0 ; i < level_of_indirection ; ++i ) {
+		type = new ast::PointerType( type );
+	}
+	return new ast::CastExpr( expr->location, expr, type );
+}
+
+} // namespace
+
+void expandCasts( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<ExpandCastsCore>::run( translationUnit );
+}
+
+} // namespace Virtual
Index: src/Virtual/ExpandCasts.h
===================================================================
--- src/Virtual/ExpandCasts.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,32 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// ExpandCasts.h --
-//
-// Author           : Andrew Beach
-// Created On       : Mon Jul 24 13:54:00 2017
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Jul 29 14:40:00 2022
-// Update Count     : 1
-//
-
-#pragma once
-
-#include <list>  // for list
-
-class Declaration;
-namespace ast {
-	class TranslationUnit;
-}
-
-namespace Virtual {
-void expandCasts( std::list< Declaration * > & translationUnit );
-void expandCasts( ast::TranslationUnit & translationUnit );
-// Breaks all virtual cast nodes up into translatable nodes.
-
-// Later this might just set some information so it can happen at CodeGen.
-
-}
Index: src/Virtual/ExpandCasts.hpp
===================================================================
--- src/Virtual/ExpandCasts.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Virtual/ExpandCasts.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+// ExpandCasts.hpp --
+//
+// Author           : Andrew Beach
+// Created On       : Mon Jul 24 13:54:00 2017
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Jul 29 14:40:00 2022
+// Update Count     : 1
+//
+
+#pragma once
+
+#include <list>  // for list
+
+class Declaration;
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace Virtual {
+void expandCasts( std::list< Declaration * > & translationUnit );
+void expandCasts( ast::TranslationUnit & translationUnit );
+// Breaks all virtual cast nodes up into translatable nodes.
+
+// Later this might just set some information so it can happen at CodeGen.
+
+}
Index: src/Virtual/Tables.cc
===================================================================
--- src/Virtual/Tables.cc	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,220 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Tables.cc --
-//
-// Author           : Andrew Beach
-// Created On       : Mon Aug 31 11:11:00 2020
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Mar 11 10:40:00 2022
-// Update Count     : 3
-//
-
-#include "AST/Attribute.hpp"
-#include "AST/Copy.hpp"
-#include "AST/Decl.hpp"
-#include "AST/Expr.hpp"
-#include "AST/Init.hpp"
-#include "AST/Stmt.hpp"
-#include "AST/Type.hpp"
-
-namespace Virtual {
-
-std::string typeIdType( std::string const & type_name ) {
-	return "__cfatid_struct_" + type_name;
-}
-
-std::string typeIdName( std::string const & type_name ) {
-	return "__cfatid_" + type_name;
-}
-
-static std::string typeIdTypeToInstance( std::string const & type_name ) {
-	return typeIdName(type_name.substr(16));
-}
-
-std::string vtableTypeName( std::string const & name ) {
-	return name + "_vtable";
-}
-
-std::string baseTypeName( std::string const & vtable_type_name ) {
-	return vtable_type_name.substr(0, vtable_type_name.size() - 7);
-}
-
-std::string instanceName( std::string const & name ) {
-	return std::string("_") + name + "_instance";
-}
-
-std::string vtableInstanceName( std::string const & name ) {
-	return instanceName( vtableTypeName( name ) );
-}
-
-std::string concurrentDefaultVTableName() {
-	return "_default_vtable";
-}
-
-bool isVTableInstanceName( std::string const & name ) {
-	// There are some delicate length calculations here.
-	return 17 < name.size() && '_' == name[0] &&
-		std::string("_vtable_instance") == name.substr(1, name.size() - 17);
-}
-
-static ast::ObjectDecl * makeVtableDeclaration(
-		CodeLocation const & location, std::string const & name,
-		ast::StructInstType const * type, ast::Init const * init ) {
-	ast::Storage::Classes storage;
-	if ( nullptr == init ) {
-		storage.is_extern = true;
-	}
-	return new ast::ObjectDecl(
-		location,
-		name,
-		type,
-		init,
-		storage,
-		ast::Linkage::Cforall
-	);
-}
-
-ast::ObjectDecl * makeVtableForward(
-		CodeLocation const & location, std::string const & name,
-		ast::StructInstType const * vtableType ) {
-	assert( vtableType );
-	return makeVtableDeclaration( location, name, vtableType, nullptr );
-}
-
-static std::vector<ast::ptr<ast::Init>> buildInits(
-		CodeLocation const & location,
-		//std::string const & name,
-		ast::StructInstType const * vtableType,
-		ast::Type const * objectType ) {
-	ast::StructDecl const * vtableStruct = vtableType->base;
-
-	std::vector<ast::ptr<ast::Init>> inits;
-	inits.reserve( vtableStruct->members.size() );
-
-	// This is designed to run before the resolver.
-	for ( auto field : vtableStruct->members ) {
-		if ( std::string( "parent" ) == field->name ) {
-			// This will not work with polymorphic state.
-			auto oField = field.strict_as<ast::ObjectDecl>();
-			auto fieldType = oField->type.strict_as<ast::PointerType>();
-			auto parentType = fieldType->base.strict_as<ast::StructInstType>();
-			std::string const & parentInstance = instanceName( parentType->name );
-			inits.push_back(
-					new ast::SingleInit( location, new ast::AddressExpr( new ast::NameExpr( location, parentInstance ) ) ) );
-		} else if ( std::string( "__cfavir_typeid" ) == field->name ) {
-			std::string const & baseType = baseTypeName( vtableType->name );
-			std::string const & typeId = typeIdName( baseType );
-			inits.push_back( new ast::SingleInit( location, new ast::AddressExpr( new ast::NameExpr( location, typeId ) ) ) );
-		} else if ( std::string( "size" ) == field->name ) {
-			inits.push_back( new ast::SingleInit( location, new ast::SizeofExpr( location, objectType )
-			) );
-		} else if ( std::string( "align" ) == field->name ) {
-			inits.push_back( new ast::SingleInit( location,
-				new ast::AlignofExpr( location, objectType )
-			) );
-		} else {
-			inits.push_back( new ast::SingleInit( location,
-				new ast::NameExpr( location, field->name )
-			) );
-		}
-		//ast::Expr * expr = buildInitExpr(...);
-		//inits.push_back( new ast::SingleInit( location, expr ) )
-	}
-
-	return inits;
-}
-
-ast::ObjectDecl * makeVtableInstance(
-		CodeLocation const & location,
-		std::string const & name,
-		ast::StructInstType const * vtableType,
-		ast::Type const * objectType,
-		ast::Init const * init ) {
-	assert( vtableType );
-	assert( objectType );
-
-	// Build the initialization.
-	if ( nullptr == init ) {
-		init = new ast::ListInit( location,
-			buildInits( location, vtableType, objectType ) );
-
-	// The provided init should initialize everything except the parent
-	// pointer, the size-of and align-of fields. These should be inserted.
-	} else {
-		// Except this is not yet supported.
-		assert(false);
-	}
-	return makeVtableDeclaration( location, name, vtableType, init );
-}
-
-namespace {
-	std::string const functionName = "get_exception_vtable";
-}
-
-ast::FunctionDecl * makeGetExceptionForward(
-		CodeLocation const & location,
-		ast::Type const * vtableType,
-		ast::Type const * exceptType ) {
-	assert( vtableType );
-	assert( exceptType );
-	return new ast::FunctionDecl(
-		location,
-		functionName,
-		{ new ast::ObjectDecl(
-			location,
-			"__unused",
-			new ast::PointerType( exceptType )
-		) },
-		{ new ast::ObjectDecl(
-			location,
-			"_retvalue",
-			new ast::ReferenceType( vtableType )
-		) },
-		nullptr,
-		ast::Storage::Classes(),
-		ast::Linkage::Cforall,
-		{ new ast::Attribute( "unused" ) }
-	);
-}
-
-ast::FunctionDecl * makeGetExceptionFunction(
-		CodeLocation const & location,
-		ast::ObjectDecl const * vtableInstance, ast::Type const * exceptType ) {
-	assert( vtableInstance );
-	assert( exceptType );
-	ast::FunctionDecl * func = makeGetExceptionForward(
-			location, ast::deepCopy( vtableInstance->type ), exceptType );
-	func->stmts = new ast::CompoundStmt( location, {
-		new ast::ReturnStmt( location, new ast::VariableExpr( location, vtableInstance ) )
-	} );
-	return func;
-}
-
-ast::ObjectDecl * makeTypeIdInstance(
-		CodeLocation const & location,
-		ast::StructInstType const * typeIdType ) {
-	assert( typeIdType );
-	ast::StructInstType * type = ast::mutate( typeIdType );
-	type->set_const( true );
-	std::string const & typeid_name = typeIdTypeToInstance( typeIdType->name );
-	return new ast::ObjectDecl(
-		location,
-		typeid_name,
-		type,
-		new ast::ListInit( location, {
-			new ast::SingleInit( location,
-				new ast::AddressExpr( location,
-					new ast::NameExpr( location, "__cfatid_exception_t" ) ) )
-		} ),
-		ast::Storage::Classes(),
-		ast::Linkage::Cforall,
-		nullptr,
-		{ new ast::Attribute( "cfa_linkonce" ) }
-	);
-}
-
-}
Index: src/Virtual/Tables.cpp
===================================================================
--- src/Virtual/Tables.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Virtual/Tables.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,220 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Tables.cc --
+//
+// Author           : Andrew Beach
+// Created On       : Mon Aug 31 11:11:00 2020
+// Last Modified By : Andrew Beach
+// Last Modified On : Fri Mar 11 10:40:00 2022
+// Update Count     : 3
+//
+
+#include "AST/Attribute.hpp"
+#include "AST/Copy.hpp"
+#include "AST/Decl.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Init.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/Type.hpp"
+
+namespace Virtual {
+
+std::string typeIdType( std::string const & type_name ) {
+	return "__cfatid_struct_" + type_name;
+}
+
+std::string typeIdName( std::string const & type_name ) {
+	return "__cfatid_" + type_name;
+}
+
+static std::string typeIdTypeToInstance( std::string const & type_name ) {
+	return typeIdName(type_name.substr(16));
+}
+
+std::string vtableTypeName( std::string const & name ) {
+	return name + "_vtable";
+}
+
+std::string baseTypeName( std::string const & vtable_type_name ) {
+	return vtable_type_name.substr(0, vtable_type_name.size() - 7);
+}
+
+std::string instanceName( std::string const & name ) {
+	return std::string("_") + name + "_instance";
+}
+
+std::string vtableInstanceName( std::string const & name ) {
+	return instanceName( vtableTypeName( name ) );
+}
+
+std::string concurrentDefaultVTableName() {
+	return "_default_vtable";
+}
+
+bool isVTableInstanceName( std::string const & name ) {
+	// There are some delicate length calculations here.
+	return 17 < name.size() && '_' == name[0] &&
+		std::string("_vtable_instance") == name.substr(1, name.size() - 17);
+}
+
+static ast::ObjectDecl * makeVtableDeclaration(
+		CodeLocation const & location, std::string const & name,
+		ast::StructInstType const * type, ast::Init const * init ) {
+	ast::Storage::Classes storage;
+	if ( nullptr == init ) {
+		storage.is_extern = true;
+	}
+	return new ast::ObjectDecl(
+		location,
+		name,
+		type,
+		init,
+		storage,
+		ast::Linkage::Cforall
+	);
+}
+
+ast::ObjectDecl * makeVtableForward(
+		CodeLocation const & location, std::string const & name,
+		ast::StructInstType const * vtableType ) {
+	assert( vtableType );
+	return makeVtableDeclaration( location, name, vtableType, nullptr );
+}
+
+static std::vector<ast::ptr<ast::Init>> buildInits(
+		CodeLocation const & location,
+		//std::string const & name,
+		ast::StructInstType const * vtableType,
+		ast::Type const * objectType ) {
+	ast::StructDecl const * vtableStruct = vtableType->base;
+
+	std::vector<ast::ptr<ast::Init>> inits;
+	inits.reserve( vtableStruct->members.size() );
+
+	// This is designed to run before the resolver.
+	for ( auto field : vtableStruct->members ) {
+		if ( std::string( "parent" ) == field->name ) {
+			// This will not work with polymorphic state.
+			auto oField = field.strict_as<ast::ObjectDecl>();
+			auto fieldType = oField->type.strict_as<ast::PointerType>();
+			auto parentType = fieldType->base.strict_as<ast::StructInstType>();
+			std::string const & parentInstance = instanceName( parentType->name );
+			inits.push_back(
+					new ast::SingleInit( location, new ast::AddressExpr( new ast::NameExpr( location, parentInstance ) ) ) );
+		} else if ( std::string( "__cfavir_typeid" ) == field->name ) {
+			std::string const & baseType = baseTypeName( vtableType->name );
+			std::string const & typeId = typeIdName( baseType );
+			inits.push_back( new ast::SingleInit( location, new ast::AddressExpr( new ast::NameExpr( location, typeId ) ) ) );
+		} else if ( std::string( "size" ) == field->name ) {
+			inits.push_back( new ast::SingleInit( location, new ast::SizeofExpr( location, objectType )
+			) );
+		} else if ( std::string( "align" ) == field->name ) {
+			inits.push_back( new ast::SingleInit( location,
+				new ast::AlignofExpr( location, objectType )
+			) );
+		} else {
+			inits.push_back( new ast::SingleInit( location,
+				new ast::NameExpr( location, field->name )
+			) );
+		}
+		//ast::Expr * expr = buildInitExpr(...);
+		//inits.push_back( new ast::SingleInit( location, expr ) )
+	}
+
+	return inits;
+}
+
+ast::ObjectDecl * makeVtableInstance(
+		CodeLocation const & location,
+		std::string const & name,
+		ast::StructInstType const * vtableType,
+		ast::Type const * objectType,
+		ast::Init const * init ) {
+	assert( vtableType );
+	assert( objectType );
+
+	// Build the initialization.
+	if ( nullptr == init ) {
+		init = new ast::ListInit( location,
+			buildInits( location, vtableType, objectType ) );
+
+	// The provided init should initialize everything except the parent
+	// pointer, the size-of and align-of fields. These should be inserted.
+	} else {
+		// Except this is not yet supported.
+		assert(false);
+	}
+	return makeVtableDeclaration( location, name, vtableType, init );
+}
+
+namespace {
+	std::string const functionName = "get_exception_vtable";
+}
+
+ast::FunctionDecl * makeGetExceptionForward(
+		CodeLocation const & location,
+		ast::Type const * vtableType,
+		ast::Type const * exceptType ) {
+	assert( vtableType );
+	assert( exceptType );
+	return new ast::FunctionDecl(
+		location,
+		functionName,
+		{ new ast::ObjectDecl(
+			location,
+			"__unused",
+			new ast::PointerType( exceptType )
+		) },
+		{ new ast::ObjectDecl(
+			location,
+			"_retvalue",
+			new ast::ReferenceType( vtableType )
+		) },
+		nullptr,
+		ast::Storage::Classes(),
+		ast::Linkage::Cforall,
+		{ new ast::Attribute( "unused" ) }
+	);
+}
+
+ast::FunctionDecl * makeGetExceptionFunction(
+		CodeLocation const & location,
+		ast::ObjectDecl const * vtableInstance, ast::Type const * exceptType ) {
+	assert( vtableInstance );
+	assert( exceptType );
+	ast::FunctionDecl * func = makeGetExceptionForward(
+			location, ast::deepCopy( vtableInstance->type ), exceptType );
+	func->stmts = new ast::CompoundStmt( location, {
+		new ast::ReturnStmt( location, new ast::VariableExpr( location, vtableInstance ) )
+	} );
+	return func;
+}
+
+ast::ObjectDecl * makeTypeIdInstance(
+		CodeLocation const & location,
+		ast::StructInstType const * typeIdType ) {
+	assert( typeIdType );
+	ast::StructInstType * type = ast::mutate( typeIdType );
+	type->set_const( true );
+	std::string const & typeid_name = typeIdTypeToInstance( typeIdType->name );
+	return new ast::ObjectDecl(
+		location,
+		typeid_name,
+		type,
+		new ast::ListInit( location, {
+			new ast::SingleInit( location,
+				new ast::AddressExpr( location,
+					new ast::NameExpr( location, "__cfatid_exception_t" ) ) )
+		} ),
+		ast::Storage::Classes(),
+		ast::Linkage::Cforall,
+		nullptr,
+		{ new ast::Attribute( "cfa_linkonce" ) }
+	);
+}
+
+}
Index: src/Virtual/Tables.h
===================================================================
--- src/Virtual/Tables.h	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ 	(revision )
@@ -1,71 +1,0 @@
-//
-// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
-//
-// The contents of this file are covered under the licence agreement in the
-// file "LICENCE" distributed with Cforall.
-//
-// Tables.h --
-//
-// Author           : Andrew Beach
-// Created On       : Mon Aug 31 11:07:00 2020
-// Last Modified By : Andrew Beach
-// Last Modified On : Wec Dec  8 16:58:00 2021
-// Update Count     : 3
-//
-
-#include <list>  // for list
-
-#include <string>
-#include "AST/Fwd.hpp"
-
-namespace Virtual {
-
-std::string typeIdType( std::string const & type_name );
-std::string typeIdName( std::string const & type_name );
-std::string vtableTypeName( std::string const & type_name );
-std::string instanceName( std::string const & vtable_name );
-std::string vtableInstanceName( std::string const & type_name );
-std::string concurrentDefaultVTableName();
-bool isVTableInstanceName( std::string const & name );
-
-/* Create a forward declaration of a vtable of the given type.
- * vtableType node is consumed.
- */
-ast::ObjectDecl * makeVtableForward(
-	CodeLocation const & location, std::string const & name,
-	ast::StructInstType const * vtableType );
-
-/* Create an initialized definition of a vtable.
- * vtableType and init (if provided) nodes are consumed.
- */
-ast::ObjectDecl * makeVtableInstance(
-	CodeLocation const & location,
-	std::string const & name,
-	ast::StructInstType const * vtableType,
-	ast::Type const * objectType,
-	ast::Init const * init = nullptr );
-
-// Some special code for how exceptions interact with virtual tables.
-
-/* Create a forward declaration of the exception virtual function
- * linking the vtableType to the exceptType. Both nodes are consumed.
- */
-ast::FunctionDecl * makeGetExceptionForward(
-	CodeLocation const & location,
-	ast::Type const * vtableType,
-	ast::Type const * exceptType );
-
-/* Create the definition of the exception virtual function.
- * exceptType node is consumed.
- */
-ast::FunctionDecl * makeGetExceptionFunction(
-	CodeLocation const & location,
-	ast::ObjectDecl const * vtableInstance, ast::Type const * exceptType );
-
-/* Build an instance of the type-id from the type of the type-id.
- * TODO: Should take the parent type. Currently locked to the exception_t.
- */
-ast::ObjectDecl * makeTypeIdInstance(
-	const CodeLocation & location, ast::StructInstType const * typeIdType );
-
-}
Index: src/Virtual/Tables.hpp
===================================================================
--- src/Virtual/Tables.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
+++ src/Virtual/Tables.hpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -0,0 +1,71 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Tables.hpp --
+//
+// Author           : Andrew Beach
+// Created On       : Mon Aug 31 11:07:00 2020
+// Last Modified By : Andrew Beach
+// Last Modified On : Wec Dec  8 16:58:00 2021
+// Update Count     : 3
+//
+
+#include <list>  // for list
+
+#include <string>
+#include "AST/Fwd.hpp"
+
+namespace Virtual {
+
+std::string typeIdType( std::string const & type_name );
+std::string typeIdName( std::string const & type_name );
+std::string vtableTypeName( std::string const & type_name );
+std::string instanceName( std::string const & vtable_name );
+std::string vtableInstanceName( std::string const & type_name );
+std::string concurrentDefaultVTableName();
+bool isVTableInstanceName( std::string const & name );
+
+/* Create a forward declaration of a vtable of the given type.
+ * vtableType node is consumed.
+ */
+ast::ObjectDecl * makeVtableForward(
+	CodeLocation const & location, std::string const & name,
+	ast::StructInstType const * vtableType );
+
+/* Create an initialized definition of a vtable.
+ * vtableType and init (if provided) nodes are consumed.
+ */
+ast::ObjectDecl * makeVtableInstance(
+	CodeLocation const & location,
+	std::string const & name,
+	ast::StructInstType const * vtableType,
+	ast::Type const * objectType,
+	ast::Init const * init = nullptr );
+
+// Some special code for how exceptions interact with virtual tables.
+
+/* Create a forward declaration of the exception virtual function
+ * linking the vtableType to the exceptType. Both nodes are consumed.
+ */
+ast::FunctionDecl * makeGetExceptionForward(
+	CodeLocation const & location,
+	ast::Type const * vtableType,
+	ast::Type const * exceptType );
+
+/* Create the definition of the exception virtual function.
+ * exceptType node is consumed.
+ */
+ast::FunctionDecl * makeGetExceptionFunction(
+	CodeLocation const & location,
+	ast::ObjectDecl const * vtableInstance, ast::Type const * exceptType );
+
+/* Build an instance of the type-id from the type of the type-id.
+ * TODO: Should take the parent type. Currently locked to the exception_t.
+ */
+ast::ObjectDecl * makeTypeIdInstance(
+	const CodeLocation & location, ast::StructInstType const * typeIdType );
+
+}
Index: src/Virtual/module.mk
===================================================================
--- src/Virtual/module.mk	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/Virtual/module.mk	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -16,8 +16,8 @@
 
 SRC += \
-	Virtual/ExpandCasts.cc \
-	Virtual/ExpandCasts.h \
-	Virtual/Tables.cc \
-	Virtual/Tables.h \
+	Virtual/ExpandCasts.cpp \
+	Virtual/ExpandCasts.hpp \
+	Virtual/Tables.cpp \
+	Virtual/Tables.hpp \
 	Virtual/VirtualDtor.cpp \
 	Virtual/VirtualDtor.hpp
Index: src/main.cpp
===================================================================
--- src/main.cpp	(revision e542b022d671c829add209f1759232750a12ff1e)
+++ src/main.cpp	(revision c92bdcc6ef9bc5c1b005f67d1c9f428bb8bd2b4c)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// main.cc --
+// main.cpp --
 //
 // Author           : Peter Buhr and Rob Schluntz
@@ -35,34 +35,34 @@
 #include "CompilationState.hpp"
 #include "../config.h"                      // for CFA_LIBDIR
-#include "CodeGen/FixMain.h"                // for FixMain
-#include "CodeGen/FixNames.h"               // for fixNames
-#include "CodeGen/Generate.h"               // for generate
-#include "CodeGen/LinkOnce.h"               // for translateLinkOnce
+#include "CodeGen/FixMain.hpp"              // for FixMain
+#include "CodeGen/FixNames.hpp"             // for fixNames
+#include "CodeGen/Generate.hpp"             // for generate
+#include "CodeGen/LinkOnce.hpp"             // for translateLinkOnce
 #include "Common/CodeLocationTools.hpp"     // for forceFillCodeLocations
 #include "Common/DeclStats.hpp"             // for printDeclStats
 #include "Common/ResolvProtoDump.hpp"       // for dumpAsResolverProto
-#include "Common/Stats.h"                   // for Stats
-#include "Common/utility.h"                 // for deleteAll, filter, printAll
+#include "Common/Stats.hpp"                 // for Stats
+#include "Common/Utility.hpp"               // for deleteAll, filter, printAll
 #include "Concurrency/Actors.hpp"           // for implementActors
 #include "Concurrency/Corun.hpp"            // for implementCorun
-#include "Concurrency/Keywords.h"           // for implementMutex, implement...
-#include "Concurrency/Waitfor.h"            // for generateWaitfor
+#include "Concurrency/Keywords.hpp"         // for implementMutex, implement...
+#include "Concurrency/Waitfor.hpp"          // for generateWaitfor
 #include "Concurrency/Waituntil.hpp"        // for generateWaitUntil
-#include "ControlStruct/ExceptDecl.h"       // for translateExcept
-#include "ControlStruct/ExceptTranslate.h"  // for translateThrows, translat...
+#include "ControlStruct/ExceptDecl.hpp"     // for translateExcept
+#include "ControlStruct/ExceptTranslate.hpp"// for translateThrows, translat...
 #include "ControlStruct/FixLabels.hpp"      // for fixLabels
 #include "ControlStruct/HoistControlDecls.hpp" //  hoistControlDecls
-#include "GenPoly/Box.h"                    // for box
-#include "GenPoly/InstantiateGeneric.h"     // for instantiateGeneric
-#include "GenPoly/Lvalue.h"                 // for convertLvalue
-#include "GenPoly/Specialize.h"             // for convertSpecializations
-#include "InitTweak/FixInit.h"              // for fix
-#include "InitTweak/GenInit.h"              // for genInit
+#include "GenPoly/Box.hpp"                  // for box
+#include "GenPoly/InstantiateGeneric.hpp"   // for instantiateGeneric
+#include "GenPoly/Lvalue.hpp"               // for convertLvalue
+#include "GenPoly/Specialize.hpp"           // for convertSpecializations
+#include "InitTweak/FixInit.hpp"            // for fix
+#include "InitTweak/GenInit.hpp"            // for genInit
 #include "MakeLibCfa.hpp"                   // for makeLibCfa
 #include "Parser/RunParser.hpp"             // for buildList, dumpParseTree,...
 #include "ResolvExpr/CandidatePrinter.hpp"  // for printCandidates
 #include "ResolvExpr/EraseWith.hpp"         // for eraseWith
-#include "ResolvExpr/Resolver.h"            // for resolve
-#include "Tuples/Tuples.h"                  // for expandMemberTuples, expan...
+#include "ResolvExpr/Resolver.hpp"          // for resolve
+#include "Tuples/Tuples.hpp"                // for expandMemberTuples, expan...
 #include "Validate/Autogen.hpp"             // for autogenerateRoutines
 #include "Validate/ImplementEnumFunc.hpp"   // for implementEnumFunc
@@ -70,5 +70,5 @@
 #include "Validate/EliminateTypedef.hpp"    // for eliminateTypedef
 #include "Validate/EnumAndPointerDecay.hpp" // for decayEnumsAndPointers
-#include "Validate/FindSpecialDecls.h"      // for findGlobalDecls
+#include "Validate/FindSpecialDecls.hpp"    // for findGlobalDecls
 #include "Validate/FixQualifiedTypes.hpp"   // for fixQualifiedTypes
 #include "Validate/FixReturnTypes.hpp"      // for fixReturnTypes
@@ -83,5 +83,5 @@
 #include "Validate/ReturnCheck.hpp"         // for checkReturnStatements
 #include "Validate/VerifyCtorDtorAssign.hpp" // for verifyCtorDtorAssign
-#include "Virtual/ExpandCasts.h"            // for expandCasts
+#include "Virtual/ExpandCasts.hpp"          // for expandCasts
 #include "Virtual/VirtualDtor.hpp"          // for implementVirtDtors
 
