Index: doc/theses/jiada_liang_MMath/CFAenum.tex
===================================================================
--- doc/theses/jiada_liang_MMath/CFAenum.tex	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ doc/theses/jiada_liang_MMath/CFAenum.tex	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -11,5 +11,5 @@
 
 C already provides @const@-style aliasing using the unnamed enumerator \see{\VRef{s:TypeName}}, even if the name @enum@ is misleading (@const@ would be better).
-Given the existence of this form, it is straightforward to extend it with types other than integers.
+Given the existence of this form, it is straightforward to extend it with types other than @int@.
 \begin{cfa}
 enum E { Size = 20u, PI = 3.14159L, Jack = L"John" };
@@ -21,8 +21,8 @@
 
 
-\section{Enumerator Unscoping}
-\label{s:EnumeratorUnscoping}
-
-In C, unscoped enumerators presents a \newterm{naming problem} when multiple enumeration types appear in the same scope with duplicate enumerator names.
+\section{Enumerator Visibility}
+\label{s:EnumeratorVisibility}
+
+In C, unscoped enumerators present a \newterm{naming problem} when multiple enumeration types appear in the same scope with duplicate enumerator names.
 There is no mechanism in C to resolve these naming conflicts other than renaming one of the duplicates, which may be impossible.
 
@@ -33,5 +33,5 @@
 enum E1 { First, Second, Third, Fourth };
 enum E2 { @Fourth@, @Third@, @Second@, @First@ }; $\C{// same enumerator names}$
-E1 p() { return Third; }				$\C{// correctly resolved duplicate names}$
+E1 p() { return Third; }				$\C{// return}$
 E2 p() { return Fourth; }
 void foo() {
@@ -54,5 +54,5 @@
 enum RGB @!@ { Red, Green, Blue };
 \end{cfa}
-Now the enumerators \emph{must} be qualified with the associated enumeration.
+Now the enumerators \emph{must} be qualified with the associated enumeration type.
 \begin{cfa}
 Week week = @Week.@Mon;
@@ -68,5 +68,5 @@
 }
 \end{cfa}
-As in Section~\ref{s:EnumeratorUnscoping}, opening multiple scoped enumerations in a @with@ can result in duplicate enumeration names, but \CFA implicit type resolution and explicit qualification/casting handles ambiguities.
+As in Section~\ref{s:EnumeratorVisibility}, opening multiple scoped enumerations in a @with@ can result in duplicate enumeration names, but \CFA implicit type resolution and explicit qualification/casting handle ambiguities.
 
 \section{Enumeration Trait}
@@ -344,5 +344,5 @@
 \end{cfa}
 which make @Third == First@ and @Fourth == Second@, causing a compilation error because of duplicase @case@ clauses.
-To better match with programmer intuition, \CFA toggles between value and position semantics depneding on the language context.
+To better match with programmer intuition, \CFA toggles between value and position semantics depending on the language context.
 For conditional clauses and switch statments, \CFA uses the robust position implementation.
 \begin{cfa}
Index: doc/theses/jiada_liang_MMath/background.tex
===================================================================
--- doc/theses/jiada_liang_MMath/background.tex	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ doc/theses/jiada_liang_MMath/background.tex	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -74,9 +74,9 @@
 However, it is restricted to integral values.
 \begin{clang}
-enum { Size = 20, Max = 10, MaxPlus10 = Max + 10, Max10Plus1, Fred = -7 };
+enum { Size = 20, Max = 10, MaxPlus10 = Max + 10, @Max10Plus1@, Fred = -7 };
 \end{clang}
 Here, the aliased constants are: 20, 10, 20, 21, and -7.
 Direct initialization is by a compile-time expression generating a constant value.
-An enumerator without initialization is \newterm{auto-initialized}: from left to right, starting at zero or the next explicitly initialized constant, incrementing by @1@.
+Indirect initialization (without initialization, @Max10Plus1@) is \newterm{auto-initialized}: from left to right, starting at zero or the next explicitly initialized constant, incrementing by @1@.
 Because multiple independent enumerators can be combined, enumerators with the same values can occur.
 The enumerators are rvalues, so assignment is disallowed.
@@ -88,7 +88,7 @@
 \begin{cfa}
 typedef struct /* unnamed */  { ... } S;
-struct /* unnamed */  { ... } x, y, z;			$\C{// questionable}$
+struct /* unnamed */  { ... } x, y, z;	$\C{// questionable}$
 struct S {
-	union /* unnamed */ {						$\C{// unscoped fields}$
+	union /* unnamed */ {				$\C{// unscoped fields}$
 		int i;  double d ;  char ch;
 	};
@@ -107,5 +107,6 @@
 enum Week {
 	Thu@ = 10@, Fri, Sat, Sun,
-	Mon@ = 0@, Tue, Wed@,@ }; // terminating comma
+	Mon@ = 0@, Tue, Wed@,@			$\C{// terminating comma}$
+};
 \end{clang}
 Note, the comma in the enumerator list can be a terminator or a separator, allowing the list to end with a dangling comma.\footnote{
Index: doc/theses/jiada_liang_MMath/intro.tex
===================================================================
--- doc/theses/jiada_liang_MMath/intro.tex	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ doc/theses/jiada_liang_MMath/intro.tex	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -135,4 +135,5 @@
 
 \subsection{Algebraic Data Type}
+\label{s:AlgebraicDataType}
 
 An algebraic data type (ADT)\footnote{ADT is overloaded with abstract data type.} is another language feature often linked with enumeration, where an ADT conjoins an arbitrary type, possibly a \lstinline[language=C++]{class} or @union@, and a named constructor.
Index: doc/theses/jiada_liang_MMath/relatedwork.tex
===================================================================
--- doc/theses/jiada_liang_MMath/relatedwork.tex	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ doc/theses/jiada_liang_MMath/relatedwork.tex	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -18,5 +18,5 @@
 \end{comment}
 
-Enumeration-like features exist in many popular programming languages, both past and present, \eg Pascal~\cite{Pascal}, Ada~\cite{Ada}, \Csharp~\cite{Csharp}, OCaml~\cite{OCaml} \CC, Go~\cite{Go}, Haskell~\cite{Haskell}, Java~\cite{Java}, Modula-3~\cite{Modula-3}, Rust~\cite{Rust}, Swift~\cite{Swift}, Python~\cite{Python}.
+Enumeration-like features exist in many popular programming languages, both past and present, \eg Pascal~\cite{Pascal}, Ada~\cite{Ada}, \Csharp~\cite{Csharp}, OCaml~\cite{OCaml} \CC, Go~\cite{Go}, Haskell~\cite{Haskell}, Java~\cite{Java}, Rust~\cite{Rust}, Swift~\cite{Swift}, Python~\cite{Python}.
 Among theses languages, there are a large set of overlapping features, but each language has its own unique extensions and restrictions.
 
@@ -24,5 +24,5 @@
 \label{s:Pascal}
 
-Classic Pascal has the \lstinline[language=Pascal]{const} aliasing declaration binding a name to a constant literal/expression.
+Classic Pascal introduced the \lstinline[language=Pascal]{const} aliasing declaration binding a name to a constant literal/expression.
 \begin{pascal}
 const one = 0 + 1;   Vowels = set of (A,E,I,O,U);   NULL = NIL;
@@ -62,5 +62,5 @@
 type Traffic_Light is ( @Red@, Yellow, @Green@ );
 \end{ada}
-Like \CFA, Ada uses an advanced type-resolution algorithm, including the left-hand side of assignment, to disambiguate among overloaded identifiers.
+Like \CFA, Ada uses a type-resolution algorithm including the left-hand side of assignmente to disambiguate among overloaded identifiers.
 \VRef[Figure]{f:AdaEnumeration} shows how ambiguity is handled using a cast, \ie \lstinline[language=ada]{RGB'(Red)}.
 
@@ -97,5 +97,5 @@
 Ada provides an alias mechanism, \lstinline[language=ada]{renames}, for aliasing types, which is useful to shorten package identifiers.
 \begin{ada}
-OtherRed : RGB renames Red;
+@OtherRed@ : RGB renames Red;
 \end{ada}
 which suggests a possible \CFA extension to @typedef@.
@@ -160,5 +160,5 @@
 Hence, the ordering of the enumerators is crucial to provide the necessary ranges.
 
-An enumeration type can be used in the Ada \lstinline[language=ada]{case} (all enumerators must appear or a default) or iterating constructs.
+An enumeration type can be used in the Ada \lstinline[language=ada]{case} (all enumerators must appear or a @default@) or iterating constructs.
 \begin{cquote}
 \setlength{\tabcolsep}{15pt}
@@ -241,5 +241,5 @@
 whereas C @const@ declarations without @static@ are marked @R@.
 
-The following \CC non-backwards compatible changes are made \see{\cite[\S~7.2]{ANSI98:C++}}.
+The following \CC non-backwards compatible changes are made \see{\cite[\S~7.2]{ANSI98:c++}}.
 \begin{cquote}
 Change: \CC objects of enumeration type can only be assigned values of the same enumeration type.
@@ -248,5 +248,5 @@
 \begin{c++}
 enum color { red, blue, green };
-color c = 1;			 				$\C{// valid C, invalid C++}$
+color c = 1;			 				$\C{// valid C, invalid c++}$
 \end{c++}
 \textbf{Rationale}: The type-safe nature of \CC. \\
@@ -263,5 +263,5 @@
 enum e { A };
 sizeof(A) == sizeof(int)		 		$\C{// in C}$
-sizeof(A) == sizeof(e)		 			$\C{// in C++}$
+sizeof(A) == sizeof(e)		 			$\C{// in c++}$
 /* and sizeof(int) is not necessary equal to sizeof(e) */
 \end{c++}
@@ -279,5 +279,8 @@
 int i = A;    i = e; 					$\C{// implicit casts to int}$
 \end{c++}
-\CC{11} added a scoped enumeration, \lstinline[language=c++]{enum class} (or \lstinline[language=c++]{enum struct}), where the enumerators are accessed using type qualification.
+\CC{11} added a scoped enumeration, \lstinline[language=c++]{enum class} (or \lstinline[language=c++]{enum struct})\footnote{
+The use of keyword \lstinline[language=c++]{class} is resonable because default visibility is \lstinline[language=c++]{private} (scoped).
+However, default visibility for \lstinline[language=c++]{struct} is \lstinline[language=c++]{public} (unscoped) making it an odd choice.},
+where the enumerators are accessed using type qualification.
 \begin{c++}
 enum class E { A, B, C };
@@ -291,5 +294,5 @@
 E e = A;    e = B;						$\C{// direct access}$
 \end{c++}
-\CC{11} added the ability to explicitly declare the underlying \emph{integral} type for \lstinline[language=c++]{enum class}.
+\CC{11} added the ability to explicitly declare only an underlying \emph{integral} type for \lstinline[language=c++]{enum class}.
 \begin{c++}
 enum class RGB @: long@ { Red, Green, Blue };
@@ -302,7 +305,35 @@
 char ch = rgb::Red;   ch = crgb;		$\C{// error}$
 \end{c++}
-Finally, enumerations can be used in the @switch@ statement but there is no mechanism to iterate through an enumeration.
-An enumeration type cannot declare an array dimension but can be used as a subscript.
-There is no mechanism to subtype or inherit from enumerations.
+An enumeration can be used in the @if@ and @switch@ statements.
+\begin{cquote}
+\setlength{\tabcolsep}{15pt}
+\begin{tabular}{@{}ll@{}}
+\begin{c++}
+if ( @day@ <= Fri )
+	cout << "weekday" << endl;
+
+
+
+
+\end{c++}
+&
+\begin{c++}
+switch ( @day@ ) {
+  case Mon: case Tue: case Wed: case Thu: case Fri:
+	cout << "weekday" << endl; break;
+  case Sat: case Sun:
+	cout << "weekend" << endl; break;
+}
+\end{c++}
+\end{tabular}
+\end{cquote}
+However, there is no mechanism to iterate through an enumeration without an unsafe cast and it does not understand the enumerator values.
+\begin{c++}
+enum Week { Mon, Tue, Wed, Thu = 10, Fri, Sat, Sun };
+for ( Week d = Mon; d <= Sun; d = @(Week)(d + 1)@ ) cout << d << ' ';
+0 1 2 @3 4 5 6 7 8 9@ 10 11 12 13
+\end{c++}
+An enumeration type cannot declare an array dimension but an enumerator can be used as a subscript.
+There is no mechanism to subtype or inherit from an enumeration.
 
 
@@ -311,115 +342,113 @@
 
 % https://www.tutorialsteacher.com/codeeditor?cid=cs-mk8Ojx
-
-\Csharp is a dynamically-typed programming-language with a scoped, integral enumeration-type similar to the C/\CC enumeration.
+% https://learn.microsoft.com/en-us/dotnet/api/system.enum?view=net-8.0
+% https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/enums
+
+\Csharp is a dynamically-typed programming-language with a scoped, integral enumeration similar to \CC \lstinline[language=C++]{enum class}.
 \begin{csharp}
-enum Weekday : byte { Mon, Tue, Wed, Thu@ = 10@, Fri, Sat, Sun@,@ };
+enum Week : @long@ { Mon, Tue, Wed, Thu@ = 10@, Fri, Sat, Sun@,@ } // terminating comma
+enum RGB { Red, Green, Blue }
 \end{csharp}
-The default underlying type is @int@, with auto-incrementing, implicit/explicit initialization, terminator comma, and optional integral typing (default @int@).
-A method cannot be defined in an enumeration type.
-As well, there is an explicit bidirectional conversion between an enumeration and its integral type, and an implicit conversion to the enumerator label in display contexts.
+The default underlying integral type is @int@ (no @char@), with auto-incrementing, implicit/explicit initialization, and terminating comma.
+A method cannot be defined in an enumeration type (extension methods are possible).
+There is an explicit bidirectional conversion between an enumeration and its integral type, and an implicit conversion to the enumerator label in display contexts.
 \begin{csharp}
-int day = (int)Weekday.Fri;		$\C{// day == 10}$
-Weekday weekday = (Weekdays)42;		$\C{// weekday == 42, logically invalid}$
-Console.WriteLine( Weekday.Fri ); $\C{// print Fri}$
-string mon = Weekday.Mon.ToString(); $\C{// mon == "Mon"}$
+int iday = (int)Week.Fri;			$\C{// day == 11}$
+Week day = @(Week)@42;				$\C{// day == 42, unsafe}$
+string mon = Week.Mon.ToString();	$\C{// mon == "Mon"}$
+RGB rgb = RGB.Red;					$\C{// rgb == "Red"}$
+day = @(Week)@rgb;					$\C{// day == "Mon", unsafe}$
+Console.WriteLine( Week.Fri );		$\C{// print label Fri}$
 \end{csharp}
-
-The @Enum.GetValues@ pseudo-method retrieves an array of the enumeration constants for looping over an enumeration type or variable (expensive operation).
+The majority of the integral operators (relational and arithmetic) work with enumerations, except @*@ and @/@.
 \begin{csharp}
-foreach ( Weekday constant in @Enum.GetValues@( typeof(Weekday) ) ) {
-	Console.WriteLine( constant + " " + (int)constant ); // label, position
-}
+day = day++ - 5;					$\C{// unsafe}$
+day = day & day;
 \end{csharp}
 
-The @Flags@ attribute creates a bit-flags enumeration, allowing bitwise operators @&@, @|@, @~@ (complement), @^@ (xor).
+An enumeration can be used in the @if@ and @switch@ statements.
+\begin{cquote}
+\setlength{\tabcolsep}{15pt}
+\begin{tabular}{@{}ll@{}}
 \begin{csharp}
-@[Flags]@ public enum Weekday {
-	None = 0x0, Mon = 0x1, Tue = 0x2, Wed = 0x4,
-	Thu = 0x8, Fri = 0x10, Sat = 0x20, Sun = 0x40,
-	Weekend = @Sat | Sun@,
-	Weekdays = @Mon | Tue | Wed | Thu | Fri@
-}
-Weekday meetings = @Weekday.Mon | Weekday.Wed@; // 0x5
-\end{csharp}
-
-\VRef[Figure]{CsharpFreeVersusClass} shows an enumeration with free routines for manipulation, and embedding the enumeration and operations into an enumeration class.
-The key observation is that an enumeration class is just a structuring mechanism without any additional semantics.
-
-% https://learn.microsoft.com/en-us/dotnet/api/system.enum?view=net-8.0
-
-\begin{figure}
-\centering
-\begin{tabular}{@{}l|l@{}}
-\multicolumn{1}{@{}c|}{non-object oriented} & \multicolumn{1}{c@{}}{object oriented} \\
-\hline
-\begin{csharp}
-public class Program {
-
-	enum Weekday {
-		Mon, Tue, Wed, Thu, Fri, Sat, Sun };
-
-	static bool isWeekday( Weekday wd ) {
-		return wd <= Weekday.Fri;
-	}
-	static bool isWeekend( Weekday wd ) {
-		return Weekday.Sat <= wd;
-	}
-
-
-	public static void Main() {
-		Weekday day = Weekday.Sat;
-
-		Console.WriteLine( isWeekday( day ) );
-		Console.WriteLine( isWeekend( day ) );
-	}
-}
+if ( @day@ <= Week.Fri )
+	Console.WriteLine( "weekday" );
+
+
+
+
+
 \end{csharp}
 &
 \begin{csharp}
-public class Program {
-	public @class@ WeekDay : Enumeration {
-		public enum Day {
-				Mon, Tue, Wed, Thu, Fri, Sat, Sun };
-		public enum Day2 : Day {
-				XXX, YYY };
-		Day day;
-		public bool isWeekday() {
-			return day <= Day.Fri;
-		}
-		public bool isWeekend() {
-			return day > Day.Fri;
-		}
-		public WeekDay( Day d ) { day = d; }
-	}
-	public static void Main() {
-		WeekDay cday = new
-				WeekDay( WeekDay.Day.Sat );
-		Console.WriteLine( cday.isWeekday() );
-		Console.WriteLine( cday.isWeekend() );
-	}
+switch ( @day@ ) {
+  case Week.Mon: case Week.Tue: case Week.Wed:
+  case Week.Thu: case Week.Fri:
+	Console.WriteLine( "weekday" ); break;
+  case Week.Sat: case Week.Sun:
+	Console.WriteLine( "weekend" ); break;
 }
 \end{csharp}
 \end{tabular}
-\caption{\Csharp: Free Routine Versus Class Enumeration}
-\label{CsharpFreeVersusClass}
-\end{figure}
+\end{cquote}
+However, there is no mechanism to iterate through an enumeration without an unsafe cast to increment and positions versus values is not handled.
+\begin{csharp}
+for ( Week d = Mon; d <= Sun; @d += 1@ ) {
+	Console.Write( d + " " );
+}
+Mon Tue Wed @3 4 5 6 7 8 9@ Thu Fri Sat Sun
+\end{csharp}
+The @Enum.GetValues@ pseudo-method retrieves an array of the enumeration constants for looping over an enumeration type or variable (expensive operation).
+\begin{csharp}
+foreach ( Week d in @Enum.GetValues@( typeof(Week) ) ) {
+	Console.WriteLine( d + " " + (int)d + " " ); // label, position
+}
+Mon 0, Tue 1, Wed 2, Thu 10, Fri 11, Sat 12, Sun 13,
+\end{csharp}
+
+An enumeration type cannot declare an array dimension but an enumerator can be used as a subscript.
+There is no mechanism to subtype or inherit from an enumeration.
+
+The @Flags@ attribute creates a bit-flags enumeration, making bitwise operators @&@, @|@, @~@ (complement), @^@ (xor) sensible.
+\begin{csharp}
+@[Flags]@ public enum Week {
+	None = 0x0, Mon = 0x1, Tue = 0x2, Wed = 0x4,
+	Thu = 0x8, Fri = 0x10, Sat = 0x20, Sun = 0x40,
+	Weekdays = @Mon | Tue | Wed | Thu | Fri@ $\C{// Weekdays == 0x1f}$
+	Weekend = @Sat | Sun@,			$\C{// Weekend == 0x60}$
+}
+Week meetings = @Week.Mon | Week.Wed@; $\C{// 0x5}$
+\end{csharp}
 
 
 \section{Golang}
 
-Golang provides pseudo-enumeration similar to classic Pascal \lstinline[language=pascal]{const}, binding a name to a constant literal/expression.
+Golang has a no enumeration.
+It has @const@ aliasing declarations, similar to \CC \see{\VRef{s:C++RelatedWork}}, for basic types with type inferencing and static initialization (constant expression).
 \begin{Go}
-const ( R = 0; G; B )					$\C{// implicit: 0 0 0}$
-const ( Fred = "Fred"; Mary = "Mary"; Jane = "Jane" ) $\C{// explicit: Fred Mary Jane}$
+const R @int@ = 0;  const G @uint@ = 1;  const B = 2; $\C{// explicit typing and type inferencing}$
+const Fred = "Fred";  const Mary = "Mary";  const Jane = "Jane";
+const S = 0;  const T = 0;
+const USA = "USA";  const U = "USA";
+const V = 3.1;  const W = 3.1;
+\end{Go}
+Since these declarations are unmutable variables, they are unscoped and Golang has no overloading.
+
+Golang provides an enumeration-like feature to group together @const@ declaration into a block and introduces a form of auto-initialization.
+\begin{Go}
+const ( R = 0; G; B )					$\C{// implicit initialization: 0 0 0}$
+const ( Fred = "Fred"; Mary = "Mary"; Jane = "Jane" ) $\C{// explicit initialization: Fred Mary Jane}$
 const ( S = 0; T; USA = "USA"; U; V = 3.1; W ) $\C{// type change, implicit/explicit: 0 0 USA USA 3.1 3.1}$
 \end{Go}
-Constant identifiers are unscoped and must be unique (no overloading).
-The first enumerator \emph{must} be explicitly initialized;
-subsequent enumerators can be implicitly or explicitly initialized.
-Implicit initialization is the previous (predecessor) enumerator value.
-
-Auto-incrementing is supported by the keyword \lstinline[language=Go]{iota}, available only in the \lstinline[language=Go]{const} declaration.
-The \lstinline[language=Go]{iota} is a \emph{per \lstinline[language=golang]{const} declaration} integer counter, starting at zero and implicitly incremented by one for each \lstinline[language=golang]{const} identifier (enumerator).
+The first identifier \emph{must} be explicitly initialized;
+subsequent identifiers can be implicitly or explicitly initialized.
+Implicit initialization is the \emph{previous} (predecessor) identifier value.
+
+Each @const@ declaration provides an implicit integer counter starting at zero, called \lstinline[language=Go]{iota}.
+Using \lstinline[language=Go]{iota} outside of a @const@ block always sets the identifier to zero.
+\begin{Go}
+const R = iota;							$\C{// 0}$
+\end{Go}
+Inside a @const@ block, \lstinline[language=Go]{iota} is implicitly incremented for each \lstinline[language=golang]{const} identifier and used to initialize the next uninitialized identifier.
 \begin{Go}
 const ( R = @iota@; G; B )				$\C{// implicit: 0 1 2}$
@@ -430,24 +459,26 @@
 const ( O1 = iota + 1; @_@; O3; @_@; O5 ) // 1, 3, 5 
 \end{Go}
-Auto-incrementing stops after an explicit initialization.
+Auto-initialization reverts from \lstinline[language=Go]{iota} to the previous value after an explicit initialization, but auto-incrementing of \lstinline[language=Go]{iota} continues.
 \begin{Go}
 const ( Mon = iota; Tue; Wed; // 0, 1, 2
-	@Thu = 10@; Fri; Sat; Sun ) // 10, 10, 10, 10
+		@Thu = 10@; Fri; Sat; Sun = itoa ) // 10, 10, 10, 6
 \end{Go}
-Auto-incrementing can be restarted with an expression containing \emph{one} \lstinline[language=Go]{iota}.
+Auto-initialization from \lstinline[language=Go]{iota} is restarted and \lstinline[language=Go]{iota} reinitialized with an expression containing as most \emph{one} \lstinline[language=Go]{iota}.
 \begin{Go}
-const ( V1 = iota; V2; @V3 = 7;@ V4 = @iota@; V5 ) // 0 1 7 3 4
+const ( V1 = iota; V2; @V3 = 7;@ V4 = @iota@ + 1; V5 ) // 0 1 7 4 5
 const ( Mon = iota; Tue; Wed; // 0, 1, 2
-	@Thu = 10;@ Fri = @iota - Wed + Thu - 1@; Sat; Sun ) // 10, 11, 12, 13
+		@Thu = 10;@ Fri = @iota - Wed + Thu - 1@; Sat; Sun ) // 10, 11, 12, 13
 \end{Go}
-Note, \lstinline[language=Go]{iota} is advanced for an explicitly initialized enumerator, like the underscore @_@ identifier.
+Here, @V4@ and @Fri@ restart auto-incrementing from \lstinline[language=Go]{iota} and reset \lstinline[language=Go]{iota} to 4 and 11, respectively, because of the intialization expressions containing \lstinline[language=Go]{iota}.
+Note, because \lstinline[language=Go]{iota} is incremented for an explicitly initialized identifier or @_@,
+at @Fri@ \lstinline[language=Go]{iota} is 4 requiring the minus one to compute the value for @Fri@.
 
 Basic switch and looping are possible.
 \begin{cquote}
-\setlength{\tabcolsep}{15pt}
+\setlength{\tabcolsep}{20pt}
 \begin{tabular}{@{}ll@{}}
 \begin{Go}
-day := Mon;
-switch day {
+day := Mon;	// := $\(\Rightarrow\)$ type inferencing
+switch @day@ {
   case Mon, Tue, Wed, Thu, Fri:
 	fmt.Println( "weekday" );
@@ -459,5 +490,5 @@
 \begin{Go}
 
-for i := Mon; i <= Sun; i += 1 {
+for i := @Mon@; i <= @Sun@; i += 1 {
 	fmt.Println( i )
 }
@@ -470,163 +501,207 @@
 However, the loop prints the values from 0 to 13 because there is no actual enumeration.
 
+A constant variable can be used as an array dimension or a subscript.
+\begin{Go}
+var ar[@Sun@] int
+ar[@Mon@] = 3
+\end{Go}
+
 
 \section{Java}
 
-Every enumeration in Java is an enumeration class.
-For a basic enumeration
+Java provides an enumeration using a specialized class.
+A basic Java enumeration is an opaque enumeration, where the enumerators are constants.
 \begin{Java}
-enum Weekday { Mon, Tue, Wed, Thu, Fri, Sat, Sun };
-Weekday day = Weekday.Sat;
+enum Week {
+	Mon, Tue, Wed, Thu, Fri, Sat, Sun;
+}
+Week day = Week.Sat;
 \end{Java}
-the scoped enumerators are an ordered list of @final@ methods of type integer, ordered left to right starting at 0, increasing by 1.
-The value of an enumeration instance is restricted to the enumeration's enumerators.
-There is an implicit @int@ variable in the enumeration used to store the value of an enumeration instance.
-The position (ordinal) and label are accessible, where the value is the same as the position.
+The enumerators members are scoped and cannot be made \lstinline[language=java]{public}, hence require qualification.
+The value of an enumeration instance is restricted to its enumerators.
+
+The position (ordinal) and label are accessible but there is no value.
 \begin{Java}
-System.out.println( day.!ordinal()! + " " + day.!name()! ); // 5 Sat
+System.out.println( day.!ordinal()! + " " + !day! + " " + day.!name()! );
+5 Sat Sat
 \end{Java}
-There is an inverse function @valueOf@ from string to enumerator.
+Since @day@ has no value, it prints its label (name).
+The member @valueOf@ is the inverse of @name@ converting a string to enumerator.
 \begin{Java}
-day = Weekday.valueOf( "Wed" );
+day = Week.valueOf( "Wed" );
 \end{Java}
-There are no implicit conversions to/from an enumerator and its underlying type.
-Like \Csharp, \VRef[Figure]{f:JavaFreeVersusClass} shows the same example for an enumeration with free routines for manipulation, and embedding the enumeration and operations into an enumeration class.
-
-\begin{figure}
-\centering
-\begin{tabular}{@{}l|l@{}}
-\multicolumn{1}{@{}c|}{non-object oriented} & \multicolumn{1}{c@{}}{object oriented} \\
-\hline
+Extra members can be added to provide specialized operations.
 \begin{Java}
-enum Weekday !{!
-	Mon, Tue, Wed, Thu, Fri, Sat, Sun !}!;
-
-static boolean isWeekday( Weekday wd ) {
-	return wd.ordinal() <= Weekday.Fri.ordinal();
-}
-static boolean isWeekend( Weekday wd ) {
-	return Weekday.Fri.ordinal() < wd.ordinal();
-}
-
-public static void main( String[] args ) {
-	Weekday day = Weekday.Sat;
-	System.out.println( isWeekday( day ) );
-	System.out.println( isWeekend( day ) );
-}
+public boolean isWeekday() { return !ordinal()! <= Fri.ordinal(); }
+public boolean isWeekend() { return Fri.ordinal() < !ordinal()!; }
+\end{Java}
+Notice the unqualified calls to @ordinal@ in the members implying a \lstinline[language=Java]{this} to some implicit implementation variable, likely an @int@.
+
+Enumerator values require an enumeration type (any Java type may be used) and implementation member.
+\begin{Java}
+enum Week {
+	Mon!(1)!, Tue!(2)!, Wed!(3)!, Thu!(4)!, Fri!(5)!, Sat!(6)!, Sun!(7)!; // must appear first
+	private !long! day;					$\C{// enumeration type and implementation member}$
+	private Week( !long! d ) { day = d; } $\C{// enumerator initialization}$
+};
+Week day = Week.Sat;
+\end{Java}
+The position, value, and label are accessible.
+\begin{Java}
+System.out.println( !day.ordinal()! + " " + !day.day! + " " + !day.name()! );
+5 6 Sat
+\end{Java}
+If the implementation member is \lstinline[language=Java]{public}, the enumeration is unsafe, as any value of the underlying type can be assigned to it, \eg @day = 42@.
+The implementation constructor must be private since it is only used internally to initialize the enumerators.
+Initialization occurs at the enumeration-type declaration for each enumerator in the first line.
+
+Enumerations can be used in the @if@ and @switch@ statements but only for equality tests.
+\begin{cquote}
+\setlength{\tabcolsep}{15pt}
+\begin{tabular}{@{}ll@{}}
+\begin{Java}
+if ( !day! == Week.Fri )
+	System.out.println( "Fri" );
+
+
+
+
 \end{Java}
 &
 \begin{Java}
-enum Weekday !{!
-	Mon, Tue, Wed, Thu, Fri, Sat, Sun;
-
-	public boolean isWeekday() {
-		return ordinal() <= Weekday.Fri.ordinal();
-	}
-	public boolean isWeekend() {
-		return Weekday.Fri.ordinal() < ordinal();
-	}
-!}!
-public static void main( String[] args ) {
-	WeekDay day = WeekDay.Sat;
-	System.out.println( day.isWeekday() );
-	System.out.println( day.isWeekend() );
+switch ( !day! ) {
+  case Mon: case Tue: case Wed: case Thu: case Fri:
+	System.out.println( "weekday" );  break;
+  case Sat: case Sun:
+	System.out.println( "weekend" );  break;
 }
 \end{Java}
 \end{tabular}
-\caption{Java: Free Routine Versus Class Enumeration}
-\label{f:JavaFreeVersusClass}
-\end{figure}
-
-To explicitly assign enumerator values and/or use a non-@int@ enumeration type (any Java type may be used), the enumeration must specify an explicit type in the enumeration class and a constructor.
+\end{cquote}
+Notice enumerators in the @switch@ statement do not require qualification.
+
+There are no arithemtic operations on enumerations, so there is no arithmetic way to iterate through an enumeration without making the implementation type \lstinline[language=Java]{public}.
+Like \Csharp, looping over an enumeration is done using method @values@, which returns an array of enumerator values (expensive operation).
 \begin{Java}
-enum Weekday {
-	Mon!(1)!, Tue!(2)!, Wed!(3)!, Thu!(4)!, Fri!(5)!, Sat!(6)!, Sun!(7)!; // must appear first
-	private !long! day;					$\C{// underlying enumeration type}$
-	private Weekday( !long! d ) { day = d; } $\C{// used to initialize enumerators}$
-};
-Weekday day = Weekday.Sat;
-\end{Java}
-If an enumerator initialization is a runtime expression, the expression is executed once at the point the enumeration is declaraed.
-
-The position, value, and label are accessible.
-\begin{Java}
-System.out.println( !day.ordinal()! + " " + !day.day! + " " + !day.name()! );  // 5 6 Sat
-\end{Java}
-The constructor is private so only initialization or assignment can be used to set an enumeration, which ensures only corresponding enumerator values are allowed.
-
-An enumeration can appear in a @switch@ statement, but no ranges.
-\begin{Java}
-switch ( day ) {
-  case Mon: case Tue: case Wed: case Thu: case Fri:
-	System.out.println( "weekday" );
-	break;
-  case Sat: case Sun:
-	System.out.println( "weekend" );
-	break;
-}
-\end{Java}
-Like \Csharp, looping over an enumeration is done using method @values@, which returns the array of enumerator values (expensive operation).
-\begin{Java}
-for ( Weekday iday : Weekday.values() ) {
-	System.out.print( iday.ordinal() + iday.day + " " +  iday.name() + ",  " );
+for ( Week d : Week.values() ) {
+	System.out.print( d.ordinal() + d.day + " " +  d.name() + ",  " );
 }
 0 1 Mon,  1 2 Tue,  2 3 Wed,  3 4 Thu,  4 5 Fri,  5 6 Sat,  6 7 Sun,  
 \end{Java}
 
-As well, Java provides an @EnumSet@ where the underlying type is an efficient set of bits, one per enumeration \see{\Csharp \lstinline{Flags}, \VRef{s:Csharp}}, providing (logical) operations on groups of enumerators.
+An enumeration type cannot declare an array dimension nor can an enumerator be used as a subscript.
+Enumeration inheritence is disallowed because an enumeration is \lstinline[language=Java]{final}.
+
+Java provides an @EnumSet@ where the underlying type is an efficient set of bits, one per enumeration \see{\Csharp \lstinline{Flags}, \VRef{s:Csharp}}, providing (logical) operations on groups of enumerators.
 There is also a specialized version of @HashMap@ with enumerator keys, which has performance benefits.
 
-Enumeration inheritence is disallowed because an enumeration is @final@.
-
-
-
-\section{Modula-3}
-
-
 
 \section{Rust}
+
 % https://doc.rust-lang.org/reference/items/enumerations.html
 
-Rust provides a scoped enumeration based on variant types.
-% An enumeration, also referred to as an enum, is a simultaneous definition of a nominal enumerated type as well as a set of constructors, that can be used to create or pattern-match values of the corresponding enumerated type.
-An enumeration without constructors is called field-less.
+Rust @enum@ provides two largely independent mechanisms: an ADT and an enumeration.
+When @enum@ is an ADT, pattern matching is used to discriminate among the variant types.
+\begin{cquote}
+\sf\setlength{\tabcolsep}{20pt}
+\begin{tabular}{@{}ll@{}}
 \begin{rust}
-enum Week { Mon, Tues, Wed, Thu, Fri, Sat, Sun@,@ }
-let mut week: Week = Week::Mon;
-week = Week::Fri;
+struct S {
+	i : isize,  j : isize
+}
+enum @ADT@ {
+	I(isize),   // int
+	F(f64),   // float
+	S(S),     // struct
+}
 \end{rust}
-A field-less enumeration with only unit variants is called unit-only.
+&
 \begin{rust}
-enum Week { Mon = 0, Tues = 1, Wed = 2, Thu = 3, Fri = 4, Sat = 5, Sun = 6 }
+let mut s = S{ i : 3, j : 4 };
+let mut adt : ADT;
+adt = ADT::I(3);  adt = ADT::F(3.5);  adt = ADT::S(s); // init examples
+@match@ adt {
+	ADT::I(i) => println!( "{:?}", i ),
+	ADT::F(f) => println!( "{:?}", f ),
+	ADT::S(s) => println!( "{:?} {:?}", s.i, s.j ),
+}
 \end{rust}
-Enum constructors can have either named or unnamed fields:
-\begin{rust}
-enum Animal {
-	Dog( String, f64 ),
-	Cat{ name: String, weight: f64 },
-}
-let mut a: Animal = Animal::Dog("Cocoa".to_string(), 37.2);
-a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 };
-\end{rust}
-Here, @Dog@ is an @enum@ variant, whereas @Cat@ is a struct-like variant.
-
-Each @enum@ type has an implicit integer tag (discriminant), with a unique value for each variant type.
-Like a C enumeration, the tag values for the variant types start at 0 with auto incrementing.
-The tag is re-purposed for enumeration by allowing it to be explicitly set, and auto incrmenting continues from that value.
-\begin{cquote}
-\sf\setlength{\tabcolsep}{3pt}
-\begin{tabular}{rcccccccr}
-@enum@ Week \{	& Mon,	& Tue,	& Wed = 2,	& Thu = 10,	& Fri, 	& Sat = 5,	& Sun	& \};	\\
-\rm tags		& 0		& 1		& 2			& 10		& 11 	& 5			& 6		&		\\
 \end{tabular}
 \end{cquote}
-In general, the tag can only be read as an opaque reference for comparison.
+When the variant types are the unit type, the ADT is still not an enumeration because there is no enumerating \see{\VRef{s:AlgebraicDataType}}.
 \begin{rust}
-if mem::discriminant(&week) == mem::discriminant(&Week::Mon) ...
+enum Week { Mon, Tues, Wed, Thu, Fri, Sat, Sun@,@ } // terminating comma
+let mut week : Week = Week::Mon;
+match week {
+	Week::Mon => println!( "Mon" ),
+	...
+	Week::Sun => println!( "Sun" ),
+}
 \end{rust}
-If the enumeration is unit-only, or field-less with no explicit discriminants and where only unit variants are explicit, then the discriminant is accessible with a numeric cast.
+
+However, Rust allows direct setting of the ADT constructor, which means it is actually a tag.
+\begin{cquote}
+\sf\setlength{\tabcolsep}{15pt}
+\begin{tabular}{@{}ll@{}}
 \begin{rust}
-if week as isize == Week::Mon as isize ...
+enum Week {
+	Mon, Tues, Wed, // start 0
+	Thu @= 10@, Fri,
+	Sat, Sun,
+}
+
 \end{rust}
+&
+\begin{rust}
+#[repr(u8)]
+enum ADT {
+	I(isize) @= 5@,  // ???
+	F(f64) @= 10@,
+	S(S) @= 0@,
+}
+\end{rust}
+\end{tabular}
+\end{cquote}
+Through this integral tag, it is possible to enumerate, and when all tags represent the unit type, it behaves like \CC \lstinline[language=C++]{enum class}.
+When tags represent non-unit types, Rust largely precludes accessing the tag because the semantics become meaningless.
+Hence, the two mechanisms are largely disjoint, and ony the enumeration component is discussed.
+
+In detail, the @enum@ type has an implicit integer tag (discriminant), with a unique value for each variant type.
+Direct initialization is by a compile-time expression generating a constant value.
+Indirect initialization (without initialization, @Fri@/@Sun@) is auto-initialized: from left to right, starting at zero or the next explicitly initialized constant, incrementing by @1@.
+There is an explicit cast from the tag to integer.
+\begin{rust}
+let mut mon : isize = Week::Mon as isize;
+\end{rust}
+An enumeration can be used in the @if@ and \lstinline[language=rust]{match} (@switch@) statements.
+\begin{cquote}
+\setlength{\tabcolsep}{8pt}
+\begin{tabular}{@{}ll@{}}
+\begin{c++}
+if @week as isize@ == Week::Mon as isize {
+	println!( "{:?}", week );
+}
+
+
+\end{c++}
+&
+\begin{c++}
+match @week@ {
+	Week::Mon | Week:: Tue | Week::Wed | Week::Thu
+		| Week::Fri => println!( "weekday" ),
+	Week::Sat | Week:: Sun => println!( "weekend" ),
+}
+\end{c++}
+\end{tabular}
+\end{cquote}
+However, there is no mechanism to iterate through an enumeration without an casting to integral and positions versus values is not handled.
+\begin{c++}
+for d in Week::Mon as isize ..= Week::Sun as isize {
+	print!( "{:?} ", d );
+}
+0 1 2 @3 4 5 6 7 8 9@ 10 11 12 13
+\end{c++}
+An enumeration type cannot declare an array dimension nor as a subscript.
+There is no mechanism to subtype or inherit from an enumeration.
 
 
@@ -2162,7 +2237,7 @@
 A basic variant is a list of nullary datatype constructors, which is like an unscoped, opaque enumeration.
 \begin{ocaml}
-type weekday = Mon | Tue | Wed | Thu | Fri | Sat | Sun
-let day : weekday @= Mon@				$\C{(* bind *)}$
-let take_class( d : weekday ) =
+type week = Mon | Tue | Wed | Thu | Fri | Sat | Sun
+let day : week @= Mon@				$\C{(* bind *)}$
+let take_class( d : week ) =
 	@match@ d with						$\C{(* matching *)}$
 		Mon | Wed -> Printf.printf "CS442\n" |
@@ -2175,8 +2250,8 @@
 The only operations are binding and pattern matching (equality), where the variant name is logically the implementation tag stored in the union for discriminating the value in the object storage.
 After compilation, variant names are mapped to an opague ascending intergral type discriminants, starting from 0.
-Here, function @take_class@ has a @weekday@ parameter, and returns @"CS442"@, if the weekday value is @Mon@ or @Wed@, @"CS343"@, if the value is @Tue@ or @Thu@, and @"Tutorial"@ for @Fri@.
-The ``@_@'' is a wildcard matching any @weekday@ value, so the function returns @"Take a break"@ for values @Sat@ or @Sun@, which are not matched by the previous cases.
+Here, function @take_class@ has a @week@ parameter, and returns @"CS442"@, if the week value is @Mon@ or @Wed@, @"CS343"@, if the value is @Tue@ or @Thu@, and @"Tutorial"@ for @Fri@.
+The ``@_@'' is a wildcard matching any @week@ value, so the function returns @"Take a break"@ for values @Sat@ or @Sun@, which are not matched by the previous cases.
 Since the variant has no type, it has a \newterm{0-arity constructor}, \ie no parameters.
-Because @weekday@ is a union of values @Mon@ to @Sun@, it is a \newterm{union type} in turns of the functional-programming paradigm. 
+Because @week@ is a union of values @Mon@ to @Sun@, it is a \newterm{union type} in turns of the functional-programming paradigm. 
 
 Each variant can have an associated heterogeneous type, with an n-ary constructor for creating a corresponding value.
@@ -2242,5 +2317,5 @@
 term "tag" further.
 
-<<Because weekday is a summation of values Mon to Sun, it is a sum type in
+<<Because week is a summation of values Mon to Sun, it is a sum type in
 turns of the functional-programming paradigm>>
 
@@ -2259,9 +2334,9 @@
 > I've marked 3 places with your name to shows places with enum ordering.
 >
-> type weekday = Mon | Tue | Wed | Thu | Fri | Sat | Sun
-> let day : weekday = Mon
-> let take_class( d : weekday ) =
+> type week = Mon | Tue | Wed | Thu | Fri | Sat | Sun
+> let day : week = Mon
+> let take_class( d : week ) =
 > 	if d <= Fri then				(* Gregor *)
-> 		Printf.printf "weekday\n"
+> 		Printf.printf "week\n"
 > 	else if d >= Sat then			(* Gregor *)
 > 		Printf.printf "weekend\n";
@@ -2474,5 +2549,5 @@
 loop			&		&		&		&		&		&		&		&	  	&		&		&		&		& \CM	\\
 \hline
-array			&		&		&		&		&		&		&		&	  	&		&		& \CM	&		& \CM	\\
+array/subscript	&		&		&		&		&		&		&		&	  	&		&		& \CM	&		& \CM	\\
 \hline
 subtype			&		&		&		&		&		&		&		&	  	&		&		&		&		& \CM	\\
Index: doc/theses/mike_brooks_MMath/background.tex
===================================================================
--- doc/theses/mike_brooks_MMath/background.tex	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ doc/theses/mike_brooks_MMath/background.tex	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -437,5 +437,5 @@
 class seqnode : public uSeqable { ... }
 %]
-A node inheriting from @uSeqable@ can appear in a sequence/collection but a node inherting from @uColable@ can only appear in a collection.
+A node inheriting from @uSeqable@ can appear in a sequence/collection but a node inheriting from @uColable@ can only appear in a collection.
 Along with providing the appropriate link fields, the types @uColable@ and @uSeqable@ also provide one member routine:
 %[
@@ -604,5 +604,5 @@
 supplying the link fields by inheritance makes them implicit and relies on compiler placement, such as the start or end of @req@.
 An example of an explicit attribute is cache alignment of the link fields in conjunction with other @req@ fields, improving locality and/or avoiding false sharing.
-Wrapped reference has no control over the link fields, but the seperate data allows some control;
+Wrapped reference has no control over the link fields, but the separate data allows some control;
 wrapped value has no control over data or links.
 
@@ -690,5 +690,5 @@
 Each group of intrusive links become the links for each separate STL list.
 The upside is the unlimited number of a lists a node can be associated with simultaneously, any number of STL lists can be created dynamically.
-The downside is the dynamic allocation of the link nodes and manging multiple lists.
+The downside is the dynamic allocation of the link nodes and managing multiple lists.
 Note, it might be possible to wrap the multiple lists in another type to hide this implementation issue.
 
@@ -776,23 +776,23 @@
 \section{String}
 
-A string is a logical sequence of symbols, where the form of the symbols can vary significantly: 7/8-bit characters (ASCII/Latin-1), or 2/4/8-byte (UNICODE) characters/symbols or variable length (UTF-8/16/32) characters.
+A string is a sequence of symbols, where the form of a symbol can vary significantly: 7/8-bit characters (ASCII/Latin-1), or 2/4/8-byte (UNICODE) characters/symbols or variable length (UTF-8/16/32) characters.
 A string can be read left-to-right, right-to-left, top-to-bottom, and have stacked elements (Arabic).
 
-An integer character constant is a sequence of one or more multibyte characters enclosed in single-quotes, as in @'x'@.
-A wide character constant is the same, except prefixed by the letter @L@, @u@, or @U@.
-Except for escape sequences, the elements of the sequence are any members of the source character set;
-they are mapped in an implementation-defined manner to members of the execution character set.
-
-A C character-string literal is a sequence of zero or more multibyte characters enclosed in double-quotes, as in @"xyz"@.
-A UTF-8 string literal is the same, except prefixed by @u8@.
-A wide string literal is the same, except prefixed by the letter @L@, @u@, or @U@.
-
-For UTF-8 string literals, the array elements have type @char@, and are initialized with the characters of the multibyte character sequence, as encoded in UTF-8.
-For wide string literals prefixed by the letter @L@, the array elements have type @wchar_t@ and are initialized with the sequence of wide characters corresponding to the multibyte character sequence, as defined by the @mbstowcs@ function with an implementation-defined current locale.
-For wide string literals prefixed by the letter @u@ or @U@, the array elements have type @char16_t@ or @char32_t@, respectively, and are initialized with the sequence of wide characters corresponding to the multibyte character sequence, as defined by successive calls to the @mbrtoc16@, or @mbrtoc32@ function as appropriate for its type, with an implementation-defined current locale.
+A C character constant is an ASCII/Latin-1 character enclosed in single-quotes, \eg @'x'@, @'@\textsterling@'@.
+A wide C character constant is the same, except prefixed by the letter @L@, @u@, or @U@, \eg @u'\u25A0'@ (black square), where the @\u@ identifies a universal character name.
+A character can be formed from an escape sequence, which expresses a non-typable character (@'\n'@), a delimiter character @'\''@, or a raw character @'\x2f'@.
+
+A character sequence is zero or more regular, wide, or escape characters enclosed in double-quotes @"xyz\n"@.
+The kind of characters in the string is denoted by a prefix: UTF-8 characters are prefixed by @u8@, wide characters are prefixed by @L@, @u@, or @U@.
+
+For UTF-8 string literals, the array elements have type @char@ and are initialized with the characters of the multibyte character sequences, \eg @u8"\xe1\x90\x87"@ (Canadian syllabics Y-Cree OO).
+For wide string literals prefixed by the letter @L@, the array elements have type @wchar_t@ and are initialized with the wide characters corresponding of the multibyte character sequence, \eg @L"abc@$\mu$@"@ and read/print using @wsanf@/@wprintf@.
+The value of a wide-character is implementation-defined, usually a UTF-16 character.
+For wide string literals prefixed by the letter @u@ or @U@, the array elements have type @char16_t@ or @char32_t@, respectively, and are initialized with wide characters corresponding to the multibyte character sequence, \eg @u"abc@$\mu$@"@, @U"abc@$\mu$@"@.
+The value of a @"u"@ character is an UTF-16 character;
+the value of a @"U"@ character is an UTF-32 character.
 The value of a string literal containing a multibyte character or escape sequence not represented in the execution character set is implementation-defined.
 
-
-Another bad C design decision is to have null-terminated strings rather than maintaining a separate string length.
+C strings are null-terminated rather than maintaining a separate string length.
 \begin{quote}
 Technically, a string is an array whose elements are single characters.
@@ -800,2 +800,13 @@
 This representation means that there is no real limit to how long a string can be, but programs have to scan one completely to determine its length.
 \end{quote}
+Unfortunately, this design decision is both unsafe and inefficient.
+It is common error in C to forget the space in a character array for the terminator or overwrite the terminator, resulting in array overruns in string operations.
+The need to repeatedly scan an entire string to determine its length can result in significant cost, as it is not possible to cache the length in many cases.
+
+C strings are fixed size because arrays are used for the implementation.
+However, string manipulation commonly results in dynamically-sized temporary and final string values.
+As a result, storage management for C strings is a nightmare, quickly resulting in array overruns and incorrect results.
+
+Collectively, these design decisions make working with strings in C, awkward, time consuming, and very unsafe.
+While there are companion string routines that take the maximum lengths of strings to prevent array overruns, that means the semantics of the operation can fail because strings are truncated.
+Suffice it to say, C is not a go-to language for string applications, which is why \CC introduced the @string@ type.
Index: driver/cfa.cc
===================================================================
--- driver/cfa.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ driver/cfa.cc	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -26,5 +26,5 @@
 #include <sys/stat.h>
 
-#include "Common/SemanticError.h"
+#include "Common/SemanticError.hpp"
 #include "config.h"										// configure info
 
Index: libcfa/src/enum.hfa
===================================================================
--- libcfa/src/enum.hfa	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ libcfa/src/enum.hfa	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -26,11 +26,11 @@
 	forall(E | TypedEnum(T, E)) {
 		// comparison
-		int ?==?(E l, E r);
-		int ?!=?(E l, E r);
-		int ?!=?(E l, zero_t);
-		int ?<?(E l, E r);
-		int ?<=?(E l, E r);
-		int ?>?(E l, E r);
-		int ?>=?(E l, E r);
+		int ?==?(E l, E r);								// true if l and r are same enumerators
+		int ?!=?(E l, E r);								// true if l and r are different enumerators
+		int ?!=?(E l, zero_t);							// true if l is not the first enumerator
+		int ?<?(E l, E r);								// true if l is an enuemerator before r
+		int ?<=?(E l, E r);								// true if l before or the same as r
+		int ?>?(E l, E r);								// true if l is an enuemrator after r
+		int ?>=?(E l, E r);								// true if l after or the same as r
 	}
 }
Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Decl.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Expr.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Inspect.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Label.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/LinkageSpec.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/LinkageSpec.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Node.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/ParseNode.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Pass.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -423,5 +423,5 @@
 }
 
-#include "Common/Stats.h"
+#include "Common/Stats.hpp"
 
 namespace ast {
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Pass.proto.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Print.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Stmt.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/SymbolTable.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/SymbolTable.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Type.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/TypeEnvironment.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/TypeEnvironment.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp
===================================================================
--- src/AST/TypeSubstitution.cpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/TypeSubstitution.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// TypeSubstitution.cc --
+// TypeSubstitution.cpp --
 //
 // Author           : Richard C. Bilson
Index: src/AST/TypeSubstitution.hpp
===================================================================
--- src/AST/TypeSubstitution.hpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/TypeSubstitution.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/AST/Util.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/BasicTypes-gen.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/CodeGen/CodeGenerator.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/CodeGen/CodeGenerator.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/FixMain.cc
===================================================================
--- src/CodeGen/FixMain.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/FixMain.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/FixMain.h
===================================================================
--- src/CodeGen/FixMain.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/FixMain.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/FixNames.cc
===================================================================
--- src/CodeGen/FixNames.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/FixNames.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/FixNames.h
===================================================================
--- src/CodeGen/FixNames.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/FixNames.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/GenType.cc
===================================================================
--- src/CodeGen/GenType.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(revision )
@@ -1,364 +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::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::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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/GenType.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -0,0 +1,365 @@
+//
+// 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::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::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: c/CodeGen/GenType.h
===================================================================
--- src/CodeGen/GenType.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/GenType.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/Generate.cc
===================================================================
--- src/CodeGen/Generate.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/Generate.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/Generate.h
===================================================================
--- src/CodeGen/Generate.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/Generate.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/LinkOnce.cc
===================================================================
--- src/CodeGen/LinkOnce.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/LinkOnce.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/LinkOnce.h
===================================================================
--- src/CodeGen/LinkOnce.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/LinkOnce.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/OperatorTable.cc
===================================================================
--- src/CodeGen/OperatorTable.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/OperatorTable.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/OperatorTable.h
===================================================================
--- src/CodeGen/OperatorTable.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/OperatorTable.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/CodeGen/Options.h
===================================================================
--- src/CodeGen/Options.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/CodeGen/Options.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/CodeGen/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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/FixMain.cpp \
+	CodeGen/FixMain.hpp \
+	CodeGen/FixNames.cpp \
+	CodeGen/FixNames.hpp \
+	CodeGen/Generate.cpp \
+	CodeGen/Generate.hpp \
+	CodeGen/LinkOnce.cpp \
+	CodeGen/LinkOnce.hpp \
+	CodeGen/Options.hpp
 
 SRCDEMANGLE += $(SRC_CODEGEN)
Index: c/Common/Assert.cc
===================================================================
--- src/Common/Assert.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Assert.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp --
+//
+// 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 cassert
+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: c/Common/CodeLocation.h
===================================================================
--- src/Common/CodeLocation.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/CodeLocation.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Common/CodeLocationTools.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -20,5 +20,5 @@
 #include "AST/Pass.hpp"
 #include "AST/TranslationUnit.hpp"
-#include "Common/CodeLocation.h"
+#include "Common/CodeLocation.hpp"
 
 namespace {
Index: c/Common/DebugMalloc.cc
===================================================================
--- src/Common/DebugMalloc.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/DebugMalloc.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Common/DeclStats.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -19,5 +19,5 @@
 #include "AST/Pass.hpp"
 #include "AST/Print.hpp"
-#include "Common/VectorMap.h"
+#include "Common/VectorMap.hpp"
 
 #include <iostream>
Index: c/Common/ErrorObjects.h
===================================================================
--- src/Common/ErrorObjects.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/ErrorObjects.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Eval.cc
===================================================================
--- src/Common/Eval.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Eval.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Eval.h
===================================================================
--- src/Common/Eval.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Eval.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Examine.cc
===================================================================
--- src/Common/Examine.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Examine.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Examine.h
===================================================================
--- src/Common/Examine.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Examine.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/FilterCombos.h
===================================================================
--- src/Common/FilterCombos.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/FilterCombos.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.hpp 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: c/Common/Indenter.cc
===================================================================
--- src/Common/Indenter.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Indenter.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Indenter.h
===================================================================
--- src/Common/Indenter.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Indenter.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/PersistentMap.h
===================================================================
--- src/Common/PersistentMap.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/PersistentMap.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Common/ResolvProtoDump.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -26,5 +26,5 @@
 #include "AST/TranslationUnit.hpp"
 #include "AST/Type.hpp"
-#include "CodeGen/OperatorTable.h"
+#include "CodeGen/OperatorTable.hpp"
 
 namespace {
Index: c/Common/ScopedMap.h
===================================================================
--- src/Common/ScopedMap.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/ScopedMap.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/SemanticError.cc
===================================================================
--- src/Common/SemanticError.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/SemanticError.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/SemanticError.h
===================================================================
--- src/Common/SemanticError.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/SemanticError.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Stats.h
===================================================================
--- src/Common/Stats.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 information, see Stats/Stats.cpp
+
+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: c/Common/Stats/Base.h
===================================================================
--- src/Common/Stats/Base.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/Base.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.hpp --
+//
+// 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: c/Common/Stats/Counter.cc
===================================================================
--- src/Common/Stats/Counter.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/Counter.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Stats/Counter.h
===================================================================
--- src/Common/Stats/Counter.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/Counter.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Stats/Heap.cc
===================================================================
--- src/Common/Stats/Heap.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/Heap.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp --
+//
+// 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.cpp");
+			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.cpp");
+		}
+
+		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: c/Common/Stats/Heap.h
===================================================================
--- src/Common/Stats/Heap.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/Heap.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Stats/ResolveTime.cc
===================================================================
--- src/Common/Stats/ResolveTime.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/ResolveTime.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Stats/ResolveTime.h
===================================================================
--- src/Common/Stats/ResolveTime.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/ResolveTime.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Stats/Stats.cc
===================================================================
--- src/Common/Stats/Stats.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/Stats.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp --
+//
+// 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: c/Common/Stats/Time.cc
===================================================================
--- src/Common/Stats/Time.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/Time.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/Stats/Time.h
===================================================================
--- src/Common/Stats/Time.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Stats/Time.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/UniqueName.cc
===================================================================
--- src/Common/UniqueName.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/UniqueName.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/UniqueName.h
===================================================================
--- src/Common/UniqueName.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/UniqueName.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/Utility.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/VectorMap.h
===================================================================
--- src/Common/VectorMap.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Common/VectorMap.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Common/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Common/utility.h
===================================================================
--- src/Common/utility.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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/CompilationState.cpp
===================================================================
--- src/CompilationState.cpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/CompilationState.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// CompilationState.cc --
+// CompilationState.cpp --
 //
 // Author           : Rob Schluntz
Index: src/Concurrency/Corun.cpp
===================================================================
--- src/Concurrency/Corun.cpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Concurrency/Corun.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Concurrency/Keywords.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Concurrency/Keywords.h
===================================================================
--- src/Concurrency/Keywords.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Concurrency/Keywords.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Concurrency/Waitfor.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Concurrency/Waitfor.h
===================================================================
--- src/Concurrency/Waitfor.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Concurrency/Waitfor.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Concurrency/Waituntil.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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/Waituntil.hpp
===================================================================
--- src/Concurrency/Waituntil.hpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Concurrency/Waituntil.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// Waitfor.h --
+// Waituntil.hpp --
 //
 // Author           : Thierry Delisle
Index: src/Concurrency/module.mk
===================================================================
--- src/Concurrency/module.mk	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Concurrency/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ControlStruct/ExceptDecl.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ControlStruct/ExceptDecl.h
===================================================================
--- src/ControlStruct/ExceptDecl.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ControlStruct/ExceptDecl.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ControlStruct/ExceptTranslate.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -14,5 +14,5 @@
 //
 
-#include "ExceptTranslate.h"
+#include "ExceptTranslate.hpp"
 
 #include "AST/Expr.hpp"
Index: c/ControlStruct/ExceptTranslate.h
===================================================================
--- src/ControlStruct/ExceptTranslate.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ControlStruct/ExceptTranslate.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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/ControlStruct/module.mk
===================================================================
--- src/ControlStruct/module.mk	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ControlStruct/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -17,7 +17,7 @@
 SRC += \
 	ControlStruct/ExceptDecl.cpp \
-	ControlStruct/ExceptDecl.h \
+	ControlStruct/ExceptDecl.hpp \
 	ControlStruct/ExceptTranslate.cpp \
-	ControlStruct/ExceptTranslate.h \
+	ControlStruct/ExceptTranslate.hpp \
 	ControlStruct/FixLabels.cpp \
 	ControlStruct/FixLabels.hpp \
Index: src/GenPoly/Box.cpp
===================================================================
--- src/GenPoly/Box.cpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/GenPoly/Box.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/GenPoly/Box.h
===================================================================
--- src/GenPoly/Box.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/Box.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/GenPoly/ErasableScopedMap.h
===================================================================
--- src/GenPoly/ErasableScopedMap.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/ErasableScopedMap.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.hpp -- 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: c/GenPoly/FindFunction.cc
===================================================================
--- src/GenPoly/FindFunction.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/FindFunction.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/GenPoly/FindFunction.h
===================================================================
--- src/GenPoly/FindFunction.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/FindFunction.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/GenPoly.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp).
+
+	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: c/GenPoly/GenPoly.h
===================================================================
--- src/GenPoly/GenPoly.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/GenPoly.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/GenPoly/InstantiateGeneric.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/GenPoly/InstantiateGeneric.h
===================================================================
--- src/GenPoly/InstantiateGeneric.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/InstantiateGeneric.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/GenPoly/Lvalue.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/GenPoly/Lvalue.h
===================================================================
--- src/GenPoly/Lvalue.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/Lvalue.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.hpp -- 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: c/GenPoly/Lvalue2.cc
===================================================================
--- src/GenPoly/Lvalue2.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/Lvalue2.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp -- 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: c/GenPoly/ScopedSet.h
===================================================================
--- src/GenPoly/ScopedSet.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/ScopedSet.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.hpp -- 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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/GenPoly/ScrubTypeVars.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/GenPoly/ScrubTypeVars.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/GenPoly/Specialize.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/GenPoly/Specialize.h
===================================================================
--- src/GenPoly/Specialize.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/GenPoly/Specialize.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.hpp -- 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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/GenPoly/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/InitTweak/FixGlobalInit.cc
===================================================================
--- src/InitTweak/FixGlobalInit.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/InitTweak/FixGlobalInit.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/InitTweak/FixGlobalInit.h
===================================================================
--- src/InitTweak/FixGlobalInit.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/InitTweak/FixGlobalInit.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/InitTweak/FixInit.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/InitTweak/FixInit.h
===================================================================
--- src/InitTweak/FixInit.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/InitTweak/FixInit.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/InitTweak/GenInit.cc
===================================================================
--- src/InitTweak/GenInit.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/InitTweak/GenInit.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -0,0 +1,366 @@
+//
+// 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 genImplicitCall 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: c/InitTweak/GenInit.h
===================================================================
--- src/InitTweak/GenInit.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/InitTweak/GenInit.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/InitTweak/InitTweak.cc
===================================================================
--- src/InitTweak/InitTweak.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/InitTweak/InitTweak.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/InitTweak/InitTweak.h
===================================================================
--- src/InitTweak/InitTweak.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/InitTweak/InitTweak.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.hpp --
+//
+// 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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/InitTweak/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/MakeLibCfa.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Makefile.am	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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
@@ -73,5 +73,5 @@
 cfa_cpplib_PROGRAMS += $(DEMANGLER)
 EXTRA_PROGRAMS = ../driver/demangler
-___driver_demangler_SOURCES = SymTab/demangler.cc # test driver for the demangler, also useful as a sanity check that libdemangle.a is complete
+___driver_demangler_SOURCES = SymTab/demangler.cpp # test driver for the demangler, also useful as a sanity check that libdemangle.a is complete
 ___driver_demangler_LDADD = libdemangle.a -ldl			# yywrap
 noinst_LIBRARIES = $(LIBDEMANGLE)
Index: c/Parser/DeclarationNode.cc
===================================================================
--- src/Parser/DeclarationNode.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/DeclarationNode.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/DeclarationNode.h
===================================================================
--- src/Parser/DeclarationNode.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/DeclarationNode.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/ExpressionNode.cc
===================================================================
--- src/Parser/ExpressionNode.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/ExpressionNode.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/ExpressionNode.h
===================================================================
--- src/Parser/ExpressionNode.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/ExpressionNode.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/InitializerNode.cc
===================================================================
--- src/Parser/InitializerNode.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/InitializerNode.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/InitializerNode.h
===================================================================
--- src/Parser/InitializerNode.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/InitializerNode.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/ParseNode.cc
===================================================================
--- src/Parser/ParseNode.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/ParseNode.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/ParseNode.h
===================================================================
--- src/Parser/ParseNode.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/ParseNode.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/ParserTypes.h
===================================================================
--- src/Parser/ParserTypes.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/ParserTypes.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/ParserUtility.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Parser/RunParser.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/StatementNode.cc
===================================================================
--- src/Parser/StatementNode.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/StatementNode.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/StatementNode.h
===================================================================
--- src/Parser/StatementNode.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/StatementNode.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/TypeData.cc
===================================================================
--- src/Parser/TypeData.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/TypeData.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/TypeData.h
===================================================================
--- src/Parser/TypeData.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/TypeData.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/TypedefTable.cc
===================================================================
--- src/Parser/TypedefTable.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/TypedefTable.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/TypedefTable.h
===================================================================
--- src/Parser/TypedefTable.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Parser/TypedefTable.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Parser/lex.ll	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Parser/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Parser/parser.yy	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Parser/parserutility.h
===================================================================
--- src/Parser/parserutility.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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: c/ResolvExpr/AdjustExprType.cc
===================================================================
--- src/ResolvExpr/AdjustExprType.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/AdjustExprType.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp --
+//
+// 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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ResolvExpr/Candidate.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ResolvExpr/CandidatePrinter.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -24,5 +24,5 @@
 #include "AST/TranslationUnit.hpp"
 #include "ResolvExpr/CandidateFinder.hpp"
-#include "ResolvExpr/Resolver.h"
+#include "ResolvExpr/Resolver.hpp"
 
 namespace ResolvExpr {
Index: c/ResolvExpr/CastCost.cc
===================================================================
--- src/ResolvExpr/CastCost.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/CastCost.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ResolvExpr/CastCost.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/CommonType.cc
===================================================================
--- src/ResolvExpr/CommonType.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(revision )
@@ -1,781 +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 };
-				}
-			}
-		}
-	}
-
-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::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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/CommonType.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -0,0 +1,781 @@
+//
+// 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 };
+				}
+			}
+		}
+	}
+
+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::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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ResolvExpr/CommonType.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/ConversionCost.cc
===================================================================
--- src/ResolvExpr/ConversionCost.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(revision )
@@ -1,491 +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 ( 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 ( !inst->base->isTyped ) {
-		if ( cost < Cost::unsafe ) {
-			cost.incSafe();
-		}
-		return;
-	}
-	cost.incUnsafe();
-}
-
-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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/ConversionCost.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -0,0 +1,491 @@
+//
+// 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 ( 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 ( !inst->base->isTyped ) {
+		if ( cost < Cost::unsafe ) {
+			cost.incSafe();
+		}
+		return;
+	}
+	cost.incUnsafe();
+}
+
+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: c/ResolvExpr/ConversionCost.h
===================================================================
--- src/ResolvExpr/ConversionCost.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(revision )
@@ -1,85 +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 );
-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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/ConversionCost.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -0,0 +1,85 @@
+//
+// 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 );
+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: c/ResolvExpr/Cost.h
===================================================================
--- src/ResolvExpr/Cost.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/Cost.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/CurrentObject.cc
===================================================================
--- src/ResolvExpr/CurrentObject.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/CurrentObject.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/CurrentObject.h
===================================================================
--- src/ResolvExpr/CurrentObject.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/CurrentObject.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ResolvExpr/ExplodedArg.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ResolvExpr/ExplodedArg.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/FindOpenVars.cc
===================================================================
--- src/ResolvExpr/FindOpenVars.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/FindOpenVars.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/FindOpenVars.h
===================================================================
--- src/ResolvExpr/FindOpenVars.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/FindOpenVars.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/PolyCost.cc
===================================================================
--- src/ResolvExpr/PolyCost.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/PolyCost.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp --
+//
+// 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: c/ResolvExpr/PtrsAssignable.cc
===================================================================
--- src/ResolvExpr/PtrsAssignable.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/PtrsAssignable.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp --
+//
+// 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: c/ResolvExpr/PtrsCastable.cc
===================================================================
--- src/ResolvExpr/PtrsCastable.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/PtrsCastable.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp --
+//
+// 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: c/ResolvExpr/RenameVars.cc
===================================================================
--- src/ResolvExpr/RenameVars.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/RenameVars.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/RenameVars.h
===================================================================
--- src/ResolvExpr/RenameVars.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/RenameVars.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/ResolveTypeof.cc
===================================================================
--- src/ResolvExpr/ResolveTypeof.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/ResolveTypeof.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/ResolveTypeof.h
===================================================================
--- src/ResolvExpr/ResolveTypeof.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/ResolveTypeof.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/Resolver.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/Resolver.h
===================================================================
--- src/ResolvExpr/Resolver.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/Resolver.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ResolvExpr/SatisfyAssertions.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/SpecCost.cc
===================================================================
--- src/ResolvExpr/SpecCost.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/SpecCost.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp --
+//
+// 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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/Typeops.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(revision )
@@ -1,732 +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 );
-		}
-	}
-
-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 );
-	}
-
-	void postvisit( const ast::UnionInstType * aggrType ) {
-		handleGenericRefType( aggrType, type2 );
-	}
-
-	void postvisit( const ast::EnumInstType * aggrType ) {
-		handleRefType( aggrType, type2 );
-	}
-
-	void postvisit( const ast::TraitInstType * aggrType ) {
-		handleRefType( aggrType, type2 );
-	}
-
-	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;
-		}
-	}
-
-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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/Unify.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -0,0 +1,732 @@
+//
+// 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 );
+		}
+	}
+
+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 );
+	}
+
+	void postvisit( const ast::UnionInstType * aggrType ) {
+		handleGenericRefType( aggrType, type2 );
+	}
+
+	void postvisit( const ast::EnumInstType * aggrType ) {
+		handleRefType( aggrType, type2 );
+	}
+
+	void postvisit( const ast::TraitInstType * aggrType ) {
+		handleRefType( aggrType, type2 );
+	}
+
+	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;
+		}
+	}
+
+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: c/ResolvExpr/Unify.h
===================================================================
--- src/ResolvExpr/Unify.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/Unify.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/WidenMode.h
===================================================================
--- src/ResolvExpr/WidenMode.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/ResolvExpr/WidenMode.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/ResolvExpr/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/ResolvExpr/typeops.h
===================================================================
--- src/ResolvExpr/typeops.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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: c/SymTab/Demangle.cc
===================================================================
--- src/SymTab/Demangle.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/SymTab/Demangle.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/SymTab/Demangle.h
===================================================================
--- src/SymTab/Demangle.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/SymTab/Demangle.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/SymTab/FixFunction.cc
===================================================================
--- src/SymTab/FixFunction.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/SymTab/FixFunction.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/SymTab/FixFunction.h
===================================================================
--- src/SymTab/FixFunction.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/SymTab/FixFunction.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/SymTab/GenImplicitCall.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/SymTab/GenImplicitCall.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -16,5 +16,5 @@
 #pragma once
 
-#include "InitTweak/InitTweak.h"  // for InitExpander
+#include "InitTweak/InitTweak.hpp"  // for InitExpander
 
 namespace SymTab {
Index: c/SymTab/Mangler.cc
===================================================================
--- src/SymTab/Mangler.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(revision )
@@ -1,355 +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 );
-
-	/// 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;
-}
-
-// 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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/SymTab/Mangler.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -0,0 +1,355 @@
+//
+// 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 );
+
+	/// 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;
+}
+
+// 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: c/SymTab/Mangler.h
===================================================================
--- src/SymTab/Mangler.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/SymTab/Mangler.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/SymTab/ManglerCommon.cc
===================================================================
--- src/SymTab/ManglerCommon.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/SymTab/ManglerCommon.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/SymTab/demangler.cc
===================================================================
--- src/SymTab/demangler.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/SymTab/demangler.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/SymTab/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Tuples/Explode.cc
===================================================================
--- src/Tuples/Explode.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Tuples/Explode.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Tuples/Explode.h
===================================================================
--- src/Tuples/Explode.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Tuples/Explode.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Tuples/TupleAssignment.cc
===================================================================
--- src/Tuples/TupleAssignment.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Tuples/TupleAssignment.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Tuples/TupleExpansion.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Tuples/Tuples.cc
===================================================================
--- src/Tuples/Tuples.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Tuples/Tuples.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Tuples/Tuples.h
===================================================================
--- src/Tuples/Tuples.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Tuples/Tuples.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp
+void handleTupleAssignment(
+	ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
+	std::vector< ResolvExpr::CandidateFinder > & args );
+
+// TupleExpansion.cpp
+/// 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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Tuples/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/Autogen.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/CompoundLiteral.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/EliminateTypedef.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/EnumAndPointerDecay.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/FindSpecialDecls.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -14,5 +14,5 @@
 //
 
-#include "Validate/FindSpecialDecls.h"
+#include "Validate/FindSpecialDecls.hpp"
 
 #include "AST/Decl.hpp"
Index: c/Validate/FindSpecialDecls.h
===================================================================
--- src/Validate/FindSpecialDecls.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Validate/FindSpecialDecls.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/FixQualifiedTypes.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/FixReturnTypes.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/ForallPointerDecay.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/ImplementEnumFunc.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Validate/ReplacePseudoFunc.hpp
===================================================================
--- src/Validate/ReplacePseudoFunc.hpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(revision )
@@ -1,10 +1,0 @@
-#pragma once
-
-namespace ast {
-    class TranslationUnit;
-}
-
-namespace Validate {
-
-void replacePseudoFunc( ast::TranslationUnit & translationUnit );
-}
Index: src/Validate/ReplaceTypedef.cpp
===================================================================
--- src/Validate/ReplaceTypedef.cpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/ReplaceTypedef.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/VerifyCtorDtorAssign.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -17,5 +17,5 @@
 
 #include "AST/Pass.hpp"
-#include "CodeGen/OperatorTable.h"
+#include "CodeGen/OperatorTable.hpp"
 
 namespace Validate {
Index: src/Validate/module.mk
===================================================================
--- src/Validate/module.mk	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Validate/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -16,5 +16,5 @@
 
 SRC_VALIDATE = \
-	Validate/FindSpecialDecls.h
+	Validate/FindSpecialDecls.hpp
 
 SRC += $(SRC_VALIDATE) \
@@ -40,4 +40,6 @@
 	Validate/HoistTypeDecls.cpp \
 	Validate/HoistTypeDecls.hpp \
+	Validate/ImplementEnumFunc.cpp \
+	Validate/ImplementEnumFunc.hpp \
 	Validate/InitializerLength.cpp \
 	Validate/InitializerLength.hpp \
@@ -52,7 +54,5 @@
 	Validate/ReturnCheck.hpp \
 	Validate/VerifyCtorDtorAssign.cpp \
-	Validate/VerifyCtorDtorAssign.hpp \
-	Validate/ImplementEnumFunc.cpp \
-	Validate/ImplementEnumFunc.hpp
+	Validate/VerifyCtorDtorAssign.hpp
 
 SRCDEMANGLE += $(SRC_VALIDATE)
Index: c/Virtual/ExpandCasts.cc
===================================================================
--- src/Virtual/ExpandCasts.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Virtual/ExpandCasts.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Virtual/ExpandCasts.h
===================================================================
--- src/Virtual/ExpandCasts.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Virtual/ExpandCasts.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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: c/Virtual/Tables.cc
===================================================================
--- src/Virtual/Tables.cc	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Virtual/Tables.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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.cpp --
+//
+// 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: c/Virtual/Tables.h
===================================================================
--- src/Virtual/Tables.h	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ 	(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 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
+++ src/Virtual/Tables.hpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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 acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/Virtual/module.mk	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -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/include/cassert
===================================================================
--- src/include/cassert	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/include/cassert	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// assert.h --
+// cassert --
 //
 // Author           : Peter A. Buhr
Index: src/include/optional
===================================================================
--- src/include/optional	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/include/optional	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// optional.h --
+// optional --
 //
 // Author           : Michael L. Brooks
Index: src/main.cpp
===================================================================
--- src/main.cpp	(revision acb33f15d3e545defb3cca21e653745f42fa6553)
+++ src/main.cpp	(revision 31f48370c9fb2839ef3cfac9f848ba193f19dc78)
@@ -5,5 +5,5 @@
 // file "LICENCE" distributed with Cforall.
 //
-// main.cc --
+// main.cpp --
 //
 // Author           : Peter Buhr and Rob Schluntz
@@ -35,40 +35,39 @@
 #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
 #include "Validate/CompoundLiteral.hpp"     // for handleCompoundLiterals
 #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
@@ -77,4 +76,5 @@
 #include "Validate/HoistStruct.hpp"         // for hoistStruct
 #include "Validate/HoistTypeDecls.hpp"      // for hoistTypeDecls
+#include "Validate/ImplementEnumFunc.hpp"   // for implementEnumFunc
 #include "Validate/InitializerLength.hpp"   // for setLengthFromInitializer
 #include "Validate/LabelAddressFixer.hpp"   // for fixLabelAddresses
@@ -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
 
